Fun CTF during Halloweekend. Played with Kernel Sanders and secured 15th place. Pretty decent for only a couple people playing :P
weirdcomputer - 22 solves
This is a weird computer… nc chal.cybersecurityrumble.de 2384 Author: lukas2511 | RedRocket
I was excited to try out this challenge because it was a challenge designed around a CHIP-8 emulator, and I have been working on my own CHIP-8 emulator the week prior. My solution was definitely very cheese and I might have used a forbidden CTF tatic.
After connecting to the server, we could either press ‘L’ to get the code running the challenge or send base64 encoded CHIP-8 ROM to the challenge. I immediately picked up that the code was a CHIP-8 emulator made in Python. I used this reference to check if the functionality of their emulator matched.
Inside the jump instruction block, I noticed they added something interesting:
# First nibble = 1 elif instr>>12 == 1: # Checking if the address provided equals the address in program counter if PC == (instr ^ 0x1000): print("endless loop detected, aborting. current framebuffer content:") for y in range(32): # Dumping data from frame buffer print("".join(list([bin(p)[2:].rjust(8, "0") for p in MEM[FB+y*8:FB+y*8+8]])).replace("0", " ").replace("1", "\u2588")) break PC = instr ^ 0x1000 continue
This gave us the ability to dump data from inside the program. The flag was loaded into a random address from 2500 to 3500.
start = random.randrange(2500, 3500) start += start%2 # Ensures that start is an even number for i, d in enumerate(open("flag.txt","rb").read().strip()): MEM[start] = (0x1000 | start) >> 8 MEM[start+1] = (0x1000 | start) & 0xff MEM[start + 2 + i] = d
So the only way to display data from the program is using that functionality of jump into the PC. The frame buffer is set using the
elif instr>>12 == 0xD: reg1 = instr>>8 & 0x0f reg2 = (instr >> 4) & 0x0F height = instr & 0xf X = V[reg1] Y = V[reg2] V[0xF] = 0 for y in range(height+1): sprbyte = I + y for x in range(8): if (X+x) > 63: continue fbbyte = FB + ( (Y+y) * 8 ) + (X+x)//8 newbit = int(bool(MEM[sprbyte] & (0x1 << (7-(x%8))))) if newbit: MEM[fbbyte] ^= newbit << (7 - (X+x) % 8) V[0xF] = 1
It does some fancy magic setting the memory, but the important part is that it starts loading sprites at
My plan of attack was this:
- The random range is small, so we can just load
Isomewhere in that range of 2500 to 3500. (I chose 3000)
- We use the
DRWinstruction to set up the frame buffer, and if we get lucky a part of the flag will be in that frame buffer
- We leak the data with
JP to PC, so we cause a crash and dump the frame buffer
- The dumped frame buffer will output data in
█, which we can convert into binary and get the characters of the flag
0xABB8 ; Set I to 0xbb8 (3000) 0xD01f ; DRW 0x1204 ; JP to PC
This is by no means the best way to do it, but I was lazy. After setting up a script to implement my plan, I ran into an issue. I got individual pieces of the flag, which I could piece together to form:
However, the flag ended with a bunch of
A’s terminated by
Y}. I had no way to determine the number of
A’s in the flag with my attack method, I could only determine the minimum number of
A’s that had to be at the end.
Forbidden Tatic - Literally Brute Forcing Flag Submission
Alright, ultimate meme time. I was lazy and didn’t want to start coding more CHIP-8 instructions. So the only other option – brute force flag.
I captured a valid flag submission request on their platform and wrote a script to send new requests that just changed the number of
A’s. Worked like a charm.