Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
/.git exists, use gitdumper to get source Type juggling, password is sha256'd and compared with == Revert to get old user creds from git 34250003024812 is a magic sha256 string Andon1956:34250003024812
Right channel of audio is different Remove left channel, get full spectogram with https://convert.ing-now.com/mp3-audio-waveform-graphic-generator Wingdings2
Use dnspy to read Assembly-CSharp - tjctf{wh3rs_ Part 2 - Use AssetStudioGUI - VictorySound2.wav (1.wav is japanese) Voice says: Flag part 2 is: "the_T5sp1n" or 0x7468655f54357370316e (Hex gives capitalised T) Part 3: Strings level0 and grep for '}' (probably not intended lmao)
Windows 7 sp1 x64 memdump By grepping for urls, we can find https://super-secret-file-server.herokuapp.com/. Command history shows us the credentials with a username and password, and also the name of a deleted file. We download this file: _4nd_y0u_w1ll_n3v3r_f1nd_m333} We now see that mstsc is running, which is the rdp client. I used filescan to search for open files, and found an the rdp cache file. We can use https://github.com/ANSSI-FR/bmc-tools to parse it, and get 128 tiles from the image. By piecing this together (thanks will) we get the first half of the flag: tjctf{c00k1e_m0n5t3r_w4s_h3r3
So, what a mess this was. Firstly, we started by performing a timing attack. That managed to get us quite a lot of things, but it was annoying having to wait 5 minutes for some results. So lets look for a better option. Looking at the hint, it says to think about I/O. Hmmm, ok. Connecting through netcat, it prints out "Imagine having a usable terminal". Ok, interesting. If we try and enter in a command, it doesn't output anything. Odd. We can probably assume that all our commands are being sent to /dev/null
, where they just get wiped away. What if we could trick the connection to redirect our commands anywhere, instead of /dev/null
. If we know that 2>/dev/null
sends any errors there, we can redirect our output to the input as we're connected there, doing >&0
. Once we've done some enum, we can use the name of the challenge "TT Why" (sounding like TTY) to spawn a tty shell, where we can run sudo -l (as password.txt has problem-user password) to see that we can run /usr/bin/chguser
as root. Changing user, navigating to home folder, then to flag, we get flag.txt
This challenge was definitely one of the harder ones in the CTF, mainly because of it's deceiving point value, at only 5 points. The description doesn't provide any information at all, which was very strange, and the fact that the challenge was categorized as "Misc" also made it very confusing, as should the challenge not be OSINT if nothing at all is provided?
This ended up taking a very long time to figure out. Perhaps the title would give a bigger hint? It mentions Discord, which is a common text and voice messaging service used by gamers.
I myself, am not a gamer, so I wasn't too keen on the idea of registering at discord.com, but if I was going to get 5 points for it, I was going to do it nonetheless.
Registering however, was a challenge in itself.
The difficulty lies in that you have to put personal information in, but wait... Do I really trust a site like discord with my personal details?
Were these 5 points really worth it? I asked myself.
"Yes. Yes they are. We must beat our rivals Pwn to 0xE4!".
And so, reluctantly, I filled in my details, and added 2 Factor Authentication for safety.
These 5 points were going to be so worth it.
However, how was I to get into the server where the flag was?
I would need an invite link. But where was I to get that?
Well, I started by trying to OSINT the challenge creators, in the hopes that they would have left something in their social medias, but it ended up with no results, to my sadness.
I looked for minutes, hours, days for this link, but nothing showed up.
Eventually, I gave up and decided to come back to this challenge another day. I decided to look at other challenges.
What about that circle one? Or the difficult decryption one?
But still, there was a feeling in my heart.
A feeling that meant I would HAVE to get those 5 points.
After all, it could mean the difference between winning and losing...
One morning, while scrolling through the challenges, I noticed something rather interesting.
There was a logo that looked oddly familiar amongst the email, Facebook and Twitter logos.
At once I recognised what it was.
IT WAS THE DISCORD LOGO! Hovering over it, I saw it redirected to a discord.gg link.
Perhaps this was it?
Perhaps this was the link to join the discord?? With my right hand sweating and shivering, I moved my mouse towards the icon, and clicked.
For a moment... There was silence.
Nothing could be heard, except for the faint sound of rickrolls in the background, and the weird audio for arabfunny.
Suddenly, I was greeted with a page. "evanyeyeye invited you to join TJCTF".
This was it. Those 5 points were going to be mine, and we would crush Pwn to 0xE4 like twigs.
However, it turns out this was again, still not enough. The security of the server was set to very high, and I needed to pass a captcha.
"No problem!", I thought. "I can just use my OCR script!".
However, despite trying over 2 times to try and get the OCR script to work, I never managed to do it, and time was running out.
There were only 95 hours left in the CTF.
This needed to be quick.
We were running out of time.
This was an emergency.
Immediately I recruited help from our team's Asian, PotatoK. He was able to read the letters and numbers and so, after a long while of waiting, we got access to the server! Those 5 points were finally going to be ours! However, once again, due to the delicate crafting of this challenge by the creator, KyleForkBomb(who even is that guy), we were stopped in our tracks once again. "How many parts does this challenge have?", I thought to myself. I decided to go back to the challenge page, and luckily enough, there was a relevant hint!
"Type ?flag in chat"
So I proceeded to type in "?flag in chat", in the hopes that it would give me the flag, but after 5 seconds of me typing "?flag in chat" into notepad, it was clear that this was going nowhere. So, I kept on pushing. I found a shortcut for typing it as well, by simply CTRL-C to copy and CTRL-V to paste. However, even after a further 7 seconds of typing it, no flag was there. Where was this flag, and why was it so difficult just for 5 measly points? I had to beat Pwn to 0xE4. I had to.
After a few more seconds of trying to type "?flag into chat" into my notepad window, I decided to try and take the hint from another perspective. I decided to read the hint once more.
"Type ?flag in chat"
Ever since we discovered the hint, we had always interpreted it as 'Type "?flag i n chat"'. But what if it was meaning something else? For example, it could have meant 'Type "?flag" in chat'.
Now everything made sense.
The pain of trying to find the Discord link. The captcha which I had to get PotatoK to solve. The registering for the Discord account. It all made sense now. All the suffering we went through.
With palms still sweating, I slowly typed "?flag" into the #general chat.
There was no one else around as far as I could tell. I was going to crush Pwn to 0xE4.
I was going to get these points.
I was close, I could feel it.
I could definitely taste the flag.
I felt so close.
Once I typed "?flag" in chat, there was silence yet again.
I could feel the sense once again, just like when I clicked on the discord link.
A few seconds passed.
Then, I saw my message get deleted? Who could have done this? Was it the evil mind of the challenge creator? Was it... no... it couldn't be... Pwn to 0xE4???
Could they have done this in order to get the flag and the points before us?
This was a disaster.
We needed those points.
This was turning into a nightmare, and there were only 94 hours left in the CTF.
We needed a plan to take them down, and fast. We quickly retreated back to the Jackbox server in order to discuss.
This had to be done quick.
We would need to act fast.
Our plan was simple. We would just need to type the command, and the get output before Pwn to 0xE4 would have a chance to intervene.
Using my new techniques I learnt from Truly Terrible Why, and also the CTRL-C CTRL-V technique from earlier, I was set to get this flag. This was going to work.
I was going to get this flag, get those 5 points, and crush Pwn to 0xE4.
I could taste victory already, and those CTFTime points would definitely be ours.
We were almost ready to execute the plan.
This would be it.
The flag would be ours.
Those 5 points we worked so hard for.
Registering the account, finding the link, getting past the captcha, using the hint. All the pain and suffering would finally pay off.
We were ready to take on Pwn to 0xE4.
We waited for the perfect moment to strike, but we had to be extra careful, as we did not expect them last time...
The plan was in action! I pasted the command carefully, but swiftly, and awaited the response...
Yet again... silence...
Until...
We were able to retrieve a message link...
It appears this takes us to the announcements channel, which I scrolled through a couple times, but couldn't find anything of use.
Where was this flag, and why was this challenge only worth 5 points???
Then... something caught my eye...
It appeared to be... an image... with some text on it. The words looked familliar... especially the "tjctf" part...
Suddenly, I realised what this was.
This was the flag!! The very thing that took almost 2 minutes to find was right in front of us!!
I had to get PotatoK once again to read it for me, but we finally found the flag after a lot of sweat, pain, and effort.
Swiftly I wrote the flag down, and copied it into the flag box.
"Correct!" it read, and those 5 points were finally ours.
We would go on to get defeated by Pwn to 0xE4, as they solved Naughty, meaning that these 5 points made all the difference.
Thank you for making this one of the most difficult CTF challenges I have ever solved, and even though it was only worth 5 points, they were more than satisfying to get.
-@Willwam845
We start with a .zip file. When we unzip, we get a directory called 0. It contains one file, 1.tar.bz2
If we decompress this file, we get a directory called 1, which contains 1.txt and another compressed file. This repeats.
The text files seem to contain tjctf{n0t_th3_fl4g}
, I took a guess that at some level in this recursive compression has a .txt file that yields the flag.
This is relatively simple. Every file is either .kz3, .tar.bz2, or .tar.gz. We can use file extensions to detect which type of file it is, and then decompress. Script below(a bit bad, I'm aware) I unzipped up to directory 1 manually because it's different to the rest.
After running the script you'll be left with a file flags.txt. Majorly, flags.txt contains a bunch of ljctf{n0t_th3_fl4g}
but if you filter this out using grep -v
the flag will be revealed to you.
There is a function called "shell". This function loads rdi into rbp-8, checks it against the value 0xdeadcafebabebeef
, and if the check is successful it pops a shell for us.
Clearly, our goal is to call this function.
In main, there is a gets call. It reads as much input as we want into rbp-0xa
, opening up an avenue for buffer overflow. As the saved rbp is 8 bytes long, our padding will be 0xa + 8 bytes of junk.
We could use a pop rdi gadget so that the check works properly, but there's no need. Instead, we can jump straight to the instruction inside of shell that pops a shell for us.
So our payload is just padding + address of instruction in shell that pops a shell
Can perform XSS by reporting to admin Admin dashboard has a link to /admin_flag Requesting admin_flag from a page with {, ", ', `,} or gives an error
Takes the flag and forwards it to a request bin
Saves cookie with dict of liked moments
Replace cookie and go to vip area
/.git exists, use gitdumper to get source Type juggling, password is sha256'd and compared with == Revert to get old user creds from git 34250003024812 is a magic sha256 string Andon1956:34250003024812
If we open this up in a gcode editor, we can see a fake flag there. The hint hints at the movement speed of the arm, and so if we do a bit of research into this, we can see they are represented by "F" values. Therefore, we extract these from the gcode file. Most of them are either F10500
or F2400
, but some of them appear to be quite small, and some even appear to be within the range of numbers for standard ASCII chars. Perhaps there is something hidden in those values...
So, we filter out the F10500
and F2400
values.
At the end of the output, we can see a list of values that appear to be within the ASCII number range, so I converted these numbers to ASCII text to get the flag.
So i was looking at the csv and thought "kek tf does this mean"
but then i checked the range values for the csv and saw they were rly similar. i looked at the hint about tony stark and thought maybe i need to build an ai or some shit. woa kept telling me i was mental and had big confusion but i continued anyway. i built one (code below) and ran it (followed tutorial with adjustments for massive training data). the output it gave was in binary (like in training data). i converted to text and gave flag{mlWis_cool}
but it looked like some bits were flipped so i changed it to flag{ml_is_cool}
which is flag.
Unity webgl game, requires adding the Cetus Chrome Extension Use cetus to search (f32) for current health, narrow down results and freeze in place Increase attack damage to 5000 When placed behind a wall, use either 0x01a508b8
, 0x01a5ab60
, 0x01fc6560
or 0x01fc65b0
to move yourself out
Can perform XSS by reporting to admin Admin dashboard has a link to /admin_flag Requesting admin_flag from a page with {, ", ', `, or gives an error
Takes the flag and forwards it to a request bin
Basic LFI Vuln curl -XPOST 'https://file_viewer.tjctf.org/reader.php?file=php://input' -d '<?php system("whoami"); ?>' - www-data ls -la:
So after deobfuscating the script (which i didnt rly needed to) i found it was encrypted with a otp of random order of range(256). so then i thought "kek how can i break otp" but then i realised it never repeats itself. i also knew that it started with "tjctf{" and ended with "}". i created a list of possible values left and used that for each position of the flag. this gave me a bunch of characters possible for that position. i just needed to repeat it until there was 1 char left (connected with vpn cus doxx). i did it with this script.
So i just followed the tutorial kek. i spent so long understanding what it meant so heres a summary of it
did it this script (after chinese remainder i got 64460789473481109991812750133942026256
alice - doesnt need to be prime because they cycle)
There's no NX, AND a gets call. On top of this, the program sends us the buffer address. A simple ret to shellcode, right?
Wrong, in fact. We have to buffer overflow from main, which makes things more difficult. Instead of the classic leave ; ret, it pops a value into ecx, then loads ecx - 4 into esp. This removes our classic buffer overflow where we change EIP. Instead, we must stack pivot.
We have control of ESP. When you call ret, the program jumps to the address stored at ESP. We can set esp to the address of the buffer. Then, we change the beginning of our buffer to be the address of our shellcode.I put the shellcode after the esp.
We can use pattern.py to figure out the amount of bytes until esp, which is 32.
I created custom shellcode that had to have the address of a string /bin/sh which I also put in the input.
then i found that it showed 2 results for 31-33 range which looked promising. i xored them 2 at a time as well as parts of the flag to find the difference. i then asked willwam to find a solution but i also checked it with the solutions i generated. i then found 33 == 21 + 18
so the mid of 3 sections xored with flag part would get the other part of flag. this gave me "cap" as my 4th-6th characters. i used the chars i generated and bruteforced it and saw "hata o sagashiteimQCE ka? dozo"
, "tjctf{sE]Ymasen_flag_kudasaiYM"
which was using "isa". i brute forced the last char to get "isacapo" and
If we take a look at main, we can see that it basically says "starting weight is 211, try and get me down to 180 by day 7" (when we run the program, we're presented with 4 options to reduce weight. Have to do this in 7 days). If we rev main, we basically only care about this bottom bit
So here, we see that if we choose option 2, we subtract 1 from our weight. If our input isn't 2, then we go to the else. If input = 3, subtract 2. If not, then if our input is not 4, then increase the counter. But what we notice is that we always subtract 3 from our weight, if our answer wasn't 2. So now for some simple maths:
Input in: 3 3 3 3 3 3 2 Wait 3 seconds, boom, profit
( for help) So we download the file, and we find that it's pure assembly written in intel syntax, nice. Let's compile it: nasm -f elf64 -o asmr.o asmr.asm ldd -o asmr asmr.o When we run it, we don't get anything back. Odd. Lets see what's in the assembly. I decided to reverse all the assembly so that we can get a good picture of what's happening. Basically, in main, it's creating a socket, setting some options for the socket, binding the connection to port 1337, listening on any address, then accepts the connection. Once we connect, it sends "Enter Password:". The program then reads our input, and checks to see whether it's 17 chars (16 + null byte). If our input isn't 16 chars + null byte, it chucks us to label5, which is basically output "Nope" and then end the connection. So we don't want that. We see that label2 compares our last char to null byte. Whilst our input != a null byte, it xors it with key 0x69 (105 in decimal) (that's what label1 does). Label2 then checks to see if that's the password, and if it's correct, it spits out 56333 chars of hex at us. If we get the hex that it moves to the rax register (0x360c1f0605360c1e) and (0x0c0c10361b041a08), xor it with key 0x69 and reverse, we get "welove_asmr_yee". Input that as the password, and the hex gets spit out. If we restart it all again but save to a file, that's a lot nicer to work with. The flag isn't in the strings, but if we take a look at it in ghex, we see a whole load of things. Removing the "Enter password:" and looking at the file headers, we see that it's a .ogg file. Save the file as that, open it in an audio player, and we get "The flag is tjctf{Bravo Uniform Bravo 6 Lira Echo Whiskey Romeo 4 Pop _ Pop 0 Pop}" That's a very long flag, so probably not. Using the hint they gave us (NATO Phonetic Alphabet), we can swap the words for letters and we get a nice message from the team, as well as tjctf{s0m3_n1c3_s0und5_for_you!!!}
Saves cookie with dict of liked moments
Replace cookie and go to vip area
NX, no PIE. The whole cookie thing is largely irrelevant so I'm not gonna comment on it.
It calls gets on rbp-0x50, opening an avenue for buffer overflow. We can do a ret2plt attack, calling puts@plt on puts@got in order to leak a libc address, specifically that of puts. Then, we can subtract the appropriate offset to get the libc base.
From there, it's a simple ret2libc attack. We call system("/bin/sh") using pop rdi. The problem is, this doesn't seem to work. To make it work, we have to set rsi and rdx to 0. We don't have a pop rdx gadget inside of the binary, so it's impossible... right?
Wrong, actually. At this point in the exploit, we know the libc base, so we can use rop gadgets inside of the libc. So the full exploit is:
There is literally zero protections.
We could do ret to shellcode, but there's no jmp esp and the shifts with the buffer can be very temperamental. Instead, I used ret2libc, which is a lot more reliable.
Via the GOT and PLT, we can execute a simple leak of the address of puts. Feeding this value into libc database find, their libc version is libc6-i386_2.27-3ubuntu1_amd64
So, we can use the GOT and PLT to leak the address of puts and call main again. Then, we can subtract the appropriate value from this to get the libc base. Since we called main again, we get a second input, to which we can deliver the main payload of system("/bin/sh").
There is a function called "shell". This function loads rdi into rbp-8, checks it against the value 0xdeadcafebabebeef
, and if the check is successful it pops a shell for us.
Clearly, our goal is to call this function.
In main, there is a gets call. It reads as much input as we want into rbp-0xa
, opening up an avenue for buffer overflow. As the saved rbp is 8 bytes long, our padding will be 0xa + 8 bytes of junk.
We could use a pop rdi gadget so that the check works properly, but there's no need. Instead, we can jump straight to the instruction inside of shell that pops a shell for us.
So our payload is just padding + address of instruction in shell that pops a shell
This has been explained in countless writeups, so I won't go over it largely, but there's NX and no PIE. So we can use ret2plt, like always, to leak a libc address.
Main is quite a complex function. For the purposes of buffer overflow, this is irrelevant to us. However, if we just overwrite RBP with a bunch of As, it means that errors will be caused at an instruction like mov DWORD PTR [rbp-0x8], 0x9
, and whenever pop is called(after rsp is set to rbp). Thus, we must give rbp some form of authentic value so we can basically move the stack somewhere else. There is a page mapped read and write inside of the binary at around 0x602000-0x603000
. We can set rbp to a value around here to create a nice fake stack, then execute a ret2libc attack.
We start with a .zip file. When we unzip, we get a directory called 0. It contains one file, 1.tar.bz2
If we decompress this file, we get a directory called 1, which contains 1.txt and another compressed file. This repeats.
The text files seem to contain tjctf{n0t_th3_fl4g}
, I took a guess that at some level in this recursive compression has a .txt file that yields the flag.
This is relatively simple. Every file is either .kz3, .tar.bz2, or .tar.gz. We can use file extensions to detect which type of file it is, and then decompress. Script below(a bit bad, I'm aware) I unzipped up to directory 1 manually because it's different to the rest.
After running the script you'll be left with a file flags.txt. Majorly, flags.txt contains a bunch of ljctf{n0t_th3_fl4g}
but if you filter this out using grep -v
the flag will be revealed to you.
It gets a bunch of input from us using a function called input that does some multiplication a value, lets call this number "num", and then calls fgets(var,num,stdin), basically reading num bytes into "var". By stepping through the program and looking at every function call of input, we see the last one has the largest value, so I just went with that even though they'd all probably work.
Later on, there are some interesting instructions. Namely these two:
Note that these two lines are at main+247
- that's quite a jump. It compares some variable on the stack to 0xc0d3d00d
and jumps away if they aren't equal. If they are equal, it goes on to execute some instructions including fopen, fgets, puts etc. - probably printing the flag from flag.txt.
Using gdb, I set a breakpoint at the instruction that compares ebp-0xc
to 0xc0d3d00d
. Then at the fourth input I pasted in a de brujin pattern generated by pattern.py. At the breakpoint, I read ebp-0xc
, and pasted this value back into pattern.py. This gives 116 bytes until we overwrite this variable, so our payload is just
Use dnspy to read Assembly-CSharp - tjctf{wh3rs_ Part 2 - Use AssetStudioGUI - VictorySound2.wav (1.wav is japanese) Voice says: Flag part 2 is: "the_T5sp1n" or 0x7468655f54357370316e (Hex gives capitalised T) Part 3: Strings level0 and grep for '}' (probably not intended lmao)
This challenge was definitely one of the harder ones in the CTF, mainly because of it's deceiving point value, at only 5 points. The description doesn't provide any information at all, which was very strange, and the fact that the challenge was categorized as "Misc" also made it very confusing, as should the challenge not be OSINT if nothing at all is provided?
This ended up taking a very long time to figure out. Perhaps the title would give a bigger hint? It mentions Discord, which is a common text and voice messaging service used by gamers.
I myself, am not a gamer, so I wasn't too keen on the idea of registering at discord.com, but if I was going to get 5 points for it, I was going to do it nonetheless.
Registering however, was a challenge in itself.
The difficulty lies in that you have to put personal information in, but wait... Do I really trust a site like discord with my personal details?
Were these 5 points really worth it? I asked myself.
"Yes. Yes they are. We must beat our rivals Pwn to 0xE4!".
And so, reluctantly, I filled in my details, and added 2 Factor Authentication for safety.
These 5 points were going to be so worth it.
However, how was I to get into the server where the flag was?
I would need an invite link. But where was I to get that?
Well, I started by trying to OSINT the challenge creators, in the hopes that they would have left something in their social medias, but it ended up with no results, to my sadness.
I looked for minutes, hours, days for this link, but nothing showed up.
Eventually, I gave up and decided to come back to this challenge another day. I decided to look at other challenges.
What about that circle one? Or the difficult decryption one?
But still, there was a feeling in my heart.
A feeling that meant I would HAVE to get those 5 points.
After all, it could mean the difference between winning and losing...
One morning, while scrolling through the challenges, I noticed something rather interesting.
There was a logo that looked oddly familiar amongst the email, Facebook and Twitter logos.
At once I recognised what it was.
IT WAS THE DISCORD LOGO! Hovering over it, I saw it redirected to a discord.gg link.
Perhaps this was it?
Perhaps this was the link to join the discord?? With my right hand sweating and shivering, I moved my mouse towards the icon, and clicked.
For a moment... There was silence.
Nothing could be heard, except for the faint sound of rickrolls in the background, and the weird audio for arabfunny.
Suddenly, I was greeted with a page. "evanyeyeye invited you to join TJCTF".
This was it. Those 5 points were going to be mine, and we would crush Pwn to 0xE4 like twigs.
However, it turns out this was again, still not enough. The security of the server was set to very high, and I needed to pass a captcha.
"No problem!", I thought. "I can just use my OCR script!".
However, despite trying over 2 times to try and get the OCR script to work, I never managed to do it, and time was running out.
There were only 95 hours left in the CTF.
This needed to be quick.
We were running out of time.
This was an emergency.
Immediately I recruited help from our team's Asian, PotatoK. He was able to read the letters and numbers and so, after a long while of waiting, we got access to the server! Those 5 points were finally going to be ours! However, once again, due to the delicate crafting of this challenge by the creator, KyleForkBomb(who even is that guy), we were stopped in our tracks once again. "How many parts does this challenge have?", I thought to myself. I decided to go back to the challenge page, and luckily enough, there was a relevant hint!
"Type ?flag in chat"
So I proceeded to type in "?flag in chat", in the hopes that it would give me the flag, but after 5 seconds of me typing "?flag in chat" into notepad, it was clear that this was going nowhere. So, I kept on pushing. I found a shortcut for typing it as well, by simply CTRL-C to copy and CTRL-V to paste. However, even after a further 7 seconds of typing it, no flag was there. Where was this flag, and why was it so difficult just for 5 measly points? I had to beat Pwn to 0xE4. I had to.
After a few more seconds of trying to type "?flag into chat" into my notepad window, I decided to try and take the hint from another perspective. I decided to read the hint once more.
"Type ?flag in chat"
Ever since we discovered the hint, we had always interpreted it as 'Type "?flag i n chat"'. But what if it was meaning something else? For example, it could have meant 'Type "?flag" in chat'.
Now everything made sense.
The pain of trying to find the Discord link. The captcha which I had to get PotatoK to solve. The registering for the Discord account. It all made sense now. All the suffering we went through.
With palms still sweating, I slowly typed "?flag" into the #general chat.
There was no one else around as far as I could tell. I was going to crush Pwn to 0xE4.
I was going to get these points.
I was close, I could feel it.
I could definitely taste the flag.
I felt so close.
Once I typed "?flag" in chat, there was silence yet again.
I could feel the sense once again, just like when I clicked on the discord link.
A few seconds passed.
Then, I saw my message get deleted? Who could have done this? Was it the evil mind of the challenge creator? Was it... no... it couldn't be... Pwn to 0xE4???
Could they have done this in order to get the flag and the points before us?
This was a disaster.
We needed those points.
This was turning into a nightmare, and there were only 94 hours left in the CTF.
We needed a plan to take them down, and fast. We quickly retreated back to the Jackbox server in order to discuss.
This had to be done quick.
We would need to act fast.
Our plan was simple. We would just need to type the command, and the get output before Pwn to 0xE4 would have a chance to intervene.
Using my new techniques I learnt from Truly Terrible Why, and also the CTRL-C CTRL-V technique from earlier, I was set to get this flag. This was going to work.
I was going to get this flag, get those 5 points, and crush Pwn to 0xE4.
I could taste victory already, and those CTFTime points would definitely be ours.
We were almost ready to execute the plan.
This would be it.
The flag would be ours.
Those 5 points we worked so hard for.
Registering the account, finding the link, getting past the captcha, using the hint. All the pain and suffering would finally pay off.
We were ready to take on Pwn to 0xE4.
We waited for the perfect moment to strike, but we had to be extra careful, as we did not expect them last time...
The plan was in action! I pasted the command carefully, but swiftly, and awaited the response...
Yet again... silence...
Until...
We were able to retrieve a message link...
It appears this takes us to the announcements channel, which I scrolled through a couple times, but couldn't find anything of use.
Where was this flag, and why was this challenge only worth 5 points???
Then... something caught my eye...
It appeared to be... an image... with some text on it. The words looked familliar... especially the "tjctf" part...
Suddenly, I realised what this was.
This was the flag!! The very thing that took almost 2 minutes to find was right in front of us!!
I had to get PotatoK once again to read it for me, but we finally found the flag after a lot of sweat, pain, and effort.
Swiftly I wrote the flag down, and copied it into the flag box.
"Correct!" it read, and those 5 points were finally ours.
We would go on to get defeated by Pwn to 0xE4, as they solved Naughty, meaning that these 5 points made all the difference.
Thank you for making this one of the most difficult CTF challenges I have ever solved, and even though it was only worth 5 points, they were more than satisfying to get.
-@Willwam845
There's no NX, AND a gets call. On top of this, the program sends us the buffer address. A simple ret to shellcode, right?
Wrong, in fact. We have to buffer overflow from main, which makes things more difficult. Instead of the classic leave ; ret, it pops a value into ecx, then loads ecx - 4 into esp. This removes our classic buffer overflow where we change EIP. Instead, we must stack pivot.
We have control of ESP. When you call ret, the program jumps to the address stored at ESP. We can set esp to the address of the buffer. Then, we change the beginning of our buffer to be the address of our shellcode.I put the shellcode after the esp.
We can use pattern.py to figure out the amount of bytes until esp, which is 32.
I created custom shellcode that had to have the address of a string /bin/sh which I also put in the input.
Try filling out the form once -- it returns some of our own input. Sign of XSS or SSTI.
<b>1</b>
doesn't do anything, but {{config}}
does.
This line ^^^ in particular looks interesting. Visiting the link /secretserverfile.py
reveals the source code for the challenge, which includes some code about filtration.
So it seems like the challenge is to bypass the filter to read the strategy guide. Given flask is written in python, it would make sense to import a module that can execute commands; althought os is filtered, subprocess is not, and now the only challenge is figuring out how to import. After a fair bit of googling, it turns out
Can be used to import a module. Combining this with subprocess we get a final payload of:
and reading the file gives us: Best formation that wins every time:
DDDDD DLLLD DLHLD DLLLD DDDDD Key: D=Drone L=Landscaper H=HQ Beginning of game strategy
:
So i was looking at the csv and thought "kek tf does this mean"
but then i checked the range values for the csv and saw they were rly similar. i looked at the hint about tony stark and thought maybe i need to build an ai or some shit. woa kept telling me i was mental and had big confusion but i continued anyway. i built one (code below) and ran it (followed tutorial with adjustments for massive training data). the output it gave was in binary (like in training data). i converted to text and gave flag{mlWis_cool}
but it looked like some bits were flipped so i changed it to flag{ml_is_cool}
which is flag.
There is literally zero protections.
We could do ret to shellcode, but there's no jmp esp and the shifts with the buffer can be very temperamental. Instead, I used ret2libc, which is a lot more reliable.
Via the GOT and PLT, we can execute a simple leak of the address of puts. Feeding this value into libc database find, their libc version is libc6-i386_2.27-3ubuntu1_amd64
So, we can use the GOT and PLT to leak the address of puts and call main again. Then, we can subtract the appropriate value from this to get the libc base. Since we called main again, we get a second input, to which we can deliver the main payload of system("/bin/sh").
This has been explained in countless writeups, so I won't go over it largely, but there's NX and no PIE. So we can use ret2plt, like always, to leak a libc address.
Main is quite a complex function. For the purposes of buffer overflow, this is irrelevant to us. However, if we just overwrite RBP with a bunch of As, it means that errors will be caused at an instruction like mov DWORD PTR [rbp-0x8], 0x9
, and whenever pop is called(after rsp is set to rbp). Thus, we must give rbp some form of authentic value so we can basically move the stack somewhere else. There is a page mapped read and write inside of the binary at around 0x602000-0x603000
. We can set rbp to a value around here to create a nice fake stack, then execute a ret2libc attack.
So after deobfuscating the script (which i didnt rly needed to) i found it was encrypted with a otp of random order of range(256). so then i thought "kek how can i break otp" but then i realised it never repeats itself. i also knew that it started with "tjctf{" and ended with "}". i created a list of possible values left and used that for each position of the flag. this gave me a bunch of characters possible for that position. i just needed to repeat it until there was 1 char left (connected with vpn cus doxx). i did it with this script.
Windows 7 sp1 x64 memdump By grepping for urls, we can find . Command history shows us the credentials with a username and password, and also the name of a deleted file. We download this file: _4nd_y0u_w1ll_n3v3r_f1nd_m333} We now see that mstsc is running, which is the rdp client. I used filescan to search for open files, and found an the rdp cache file. We can use to parse it, and get 128 tiles from the image. By piecing this together (thanks will) we get the first half of the flag: tjctf{c00k1e_m0n5t3r_w4s_h3r3
NX, no PIE. The whole cookie thing is largely irrelevant so I'm not gonna comment on it.
It calls gets on rbp-0x50, opening an avenue for buffer overflow. We can do a ret2plt attack, calling puts@plt on puts@got in order to leak a libc address, specifically that of puts. Then, we can subtract the appropriate offset to get the libc base.
From there, it's a simple ret2libc attack. We call system("/bin/sh") using pop rdi. The problem is, this doesn't seem to work. To make it work, we have to set rsi and rdx to 0. We don't have a pop rdx gadget inside of the binary, so it's impossible... right?
Wrong, actually. At this point in the exploit, we know the libc base, so we can use rop gadgets inside of the libc. So the full exploit is:
then i found that it showed 2 results for 31-33 range which looked promising. i xored them 2 at a time as well as parts of the flag to find the difference. i then asked willwam to find a solution but i also checked it with the solutions i generated. i then found 33 == 21 + 18
so the mid of 3 sections xored with flag part would get the other part of flag. this gave me "cap" as my 4th-6th characters. i used the chars i generated and bruteforced it and saw "hata o sagashiteimQCE ka? dozo"
, "tjctf{sE]Ymasen_flag_kudasaiYM"
which was using "isa". i brute forced the last char to get "isacapo" and
If we open this up in a gcode editor, we can see a fake flag there. The hint hints at the movement speed of the arm, and so if we do a bit of research into this, we can see they are represented by "F" values. Therefore, we extract these from the gcode file. Most of them are either F10500
or F2400
, but some of them appear to be quite small, and some even appear to be within the range of numbers for standard ASCII chars. Perhaps there is something hidden in those values...
So, we filter out the F10500
and F2400
values.
At the end of the output, we can see a list of values that appear to be within the ASCII number range, so I converted these numbers to ASCII text to get the flag.
Extract red pixel vals from each frame (last bit) go to day's site
https://medium.com/bugbountywriteup/rsa-attacks-common-modulus-7bdb34f331a5
copy code with adjustments. convert output to text and bam!
It gets a bunch of input from us using a function called input that does some multiplication a value, lets call this number "num", and then calls fgets(var,num,stdin), basically reading num bytes into "var". By stepping through the program and looking at every function call of input, we see the last one has the largest value, so I just went with that even though they'd all probably work.
Later on, there are some interesting instructions. Namely these two:
Note that these two lines are at main+247
- that's quite a jump. It compares some variable on the stack to 0xc0d3d00d
and jumps away if they aren't equal. If they are equal, it goes on to execute some instructions including fopen, fgets, puts etc. - probably printing the flag from flag.txt.
Using gdb, I set a breakpoint at the instruction that compares ebp-0xc
to 0xc0d3d00d
. Then at the fourth input I pasted in a de brujin pattern generated by pattern.py. At the breakpoint, I read ebp-0xc
, and pasted this value back into pattern.py. This gives 116 bytes until we overwrite this variable, so our payload is just
So i just followed the tutorial kek. i spent so long understanding what it meant so heres a summary of it
did it this script (after chinese remainder i got 64460789473481109991812750133942026256
alice - doesnt need to be prime because they cycle)
If we take a look at main, we can see that it basically says "starting weight is 211, try and get me down to 180 by day 7" (when we run the program, we're presented with 4 options to reduce weight. Have to do this in 7 days). If we rev main, we basically only care about this bottom bit
So here, we see that if we choose option 2, we subtract 1 from our weight. If our input isn't 2, then we go to the else. If input = 3, subtract 2. If not, then if our input is not 4, then increase the counter. But what we notice is that we always subtract 3 from our weight, if our answer wasn't 2. So now for some simple maths:
Input in: 3 3 3 3 3 3 2 Wait 3 seconds, boom, profit
Unity webgl game, requires adding the Cetus Chrome Extension Use cetus to search (f32) for current health, narrow down results and freeze in place Increase attack damage to 5000 When placed behind a wall, use either 0x01a508b8
, 0x01a5ab60
, 0x01fc6560
or 0x01fc65b0
to move yourself out
So, what a mess this was. Firstly, we started by performing a timing attack. That managed to get us quite a lot of things, but it was annoying having to wait 5 minutes for some results. So lets look for a better option. Looking at the hint, it says to think about I/O. Hmmm, ok. Connecting through netcat, it prints out "Imagine having a usable terminal". Ok, interesting. If we try and enter in a command, it doesn't output anything. Odd. We can probably assume that all our commands are being sent to /dev/null
, where they just get wiped away. What if we could trick the connection to redirect our commands anywhere, instead of /dev/null
. If we know that 2>/dev/null
sends any errors there, we can redirect our output to the input as we're connected there, doing >&0
. Once we've done some enum, we can use the name of the challenge "TT Why" (sounding like TTY) to spawn a tty shell, where we can run sudo -l (as password.txt has problem-user password) to see that we can run /usr/bin/chguser
as root. Changing user, navigating to home folder, then to flag, we get flag.txt
( https://gist.github.com/bobbo/e1e980262f2ddc8db3b8 for help) So we download the file, and we find that it's pure assembly written in intel syntax, nice. Let's compile it: nasm -f elf64 -o asmr.o asmr.asm ldd -o asmr asmr.o When we run it, we don't get anything back. Odd. Lets see what's in the assembly. I decided to reverse all the assembly so that we can get a good picture of what's happening. Basically, in main, it's creating a socket, setting some options for the socket, binding the connection to port 1337, listening on any address, then accepts the connection. Once we connect, it sends "Enter Password:". The program then reads our input, and checks to see whether it's 17 chars (16 + null byte). If our input isn't 16 chars + null byte, it chucks us to label5, which is basically output "Nope" and then end the connection. So we don't want that. We see that label2 compares our last char to null byte. Whilst our input != a null byte, it xors it with key 0x69 (105 in decimal) (that's what label1 does). Label2 then checks to see if that's the password, and if it's correct, it spits out 56333 chars of hex at us. If we get the hex that it moves to the rax register (0x360c1f0605360c1e) and (0x0c0c10361b041a08), xor it with key 0x69 and reverse, we get "welove_asmr_yee". Input that as the password, and the hex gets spit out. If we restart it all again but save to a file, that's a lot nicer to work with. The flag isn't in the strings, but if we take a look at it in ghex, we see a whole load of things. Removing the "Enter password:" and looking at the file headers, we see that it's a .ogg file. Save the file as that, open it in an audio player, and we get "The flag is tjctf{Bravo Uniform Bravo 6 Lira Echo Whiskey Romeo 4 Pop _ Pop 0 Pop}" That's a very long flag, so probably not. Using the hint they gave us (NATO Phonetic Alphabet), we can swap the words for letters and we get a nice message from the team, as well as tjctf{s0m3_n1c3_s0und5_for_you!!!}
Basic LFI Vuln curl -XPOST 'https://file_viewer.tjctf.org/reader.php?file=php://input' -d '<?php system("whoami"); ?>' - www-data ls -la: