Last year you’ve enjoyed pwning a binary from the MS-DOS era.
This year, we challenge you to reverse engineer one such binary.
you’re given CHAL.COM
which from the hint in the description is an MS-DOS binary for the 8086 processor.
Set up the environment#
- install dosbox
sudo pacman -S dosbox
- install borlandc++ (comes with turbo debugger)
i just followed this amazing guide but without the editor (i did try but i couldn’t the source anymore)
Learn 8086 assembly#
- check out these resources
- https://jishanshaikh4.github.io/8086-cheatsheet/main.pdf
- https://www.gabrielececchetti.it/Teaching/CalcolatoriElettronici/Docs/i8086_instruction_set.pdf
The runtime memory prefixes an extra 0x100
bytes to the binary to include runtime information. This is important because every index you see in the next step is going to offset by 0x100
in the original binary, and because information like command line arguments and their length is stored in this space. Namely the length of the cli argument is stored at 0x80
8086 has 4 main registers
ax
,bx
,cx
anddx
which are all 16 bit.- There are other general purpose and special purpose ones too
It also has a lot of weird/cool instructions, notably
xlatb
: Translate byte from table. this is just one instruction crazyloop
: made simple loops nice and consciselodsb
: get value from an index and autoincrement the indexscasb
: compare value at 2 indices and autoincrement them- and a bunch more that i would need to open the disassembly again to remember
i’m halfassing this section but the above resources just make it so simple already
Understand the code#
- spend hours understanding the code
- i thought it was rc4 first so i spent a long time scouring the assembly looking for the key, but it wasn’t standard rc4, just similar.
- eventually i realised i can view the data dump in Turbo Debugger and that helped me move ahead
What actually happens#
- so what the binary does is initially validate your command line input
- it looks for
FortID{
then a 31 (0x21 - 2) length string then ends with}
(the whitespace is important)
- then it enters a function to init the state matrix
- First it creates an array of 255 characters all initialised to their index value
- Then based on a fixed value in the file that it refreshes by xoring, it swaps keeps swapping characters in the array
- so you get a sort of random array by the end (not anymore since clearly we can reconstruct it, the fixed value isn’t random lol)
00000140 72 65 63 74 21 0d 0a 24 0d 0a 4e 6f 70 65 2e 0d |rect!..$..Nope..|
00000150 0a 24 00 00 c1 b4 00 00 00 00 00 00 00 00 00 00 |.$..............|
00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
The fixed value is taken from address 0x254-5
which is also copied to 0x252-3
and 0x256-7
to give 0xb4c1
Once all this is set up we enter the main logic
- We first create our correct ciphertext
This is done by taken some bytes from the tail of our file and xoring them with [0x252] ^ 0xa5
which is 0xc1 ^ 0xa5 = 0x64
00000270 00 00 00 00 00 00 00 00 00 42 45 4e 43 21 2a 35 |.........BENC!*5|
00000280 a9 11 e3 6f 17 79 11 e3 79 88 94 b2 01 fd 68 11 |...o.y..y.....h.|
00000290 6f 01 b7 11 ac 6f 53 01 ce e2 11 84 35 35 51 |o....oS.....55Q|
Once we have this ciphertext the way we check is by taking every byte in our submitted flag, going to that index in the statematrix created in step 2, and comparing that with the expected ciphertext
- ATTACK
- now that we have the statematrix and the final ciphertext we can simply get the correct flag by reversing the substitution
Solve script#
|
|
FortID{N0w_S4v3_S3t71ng5_4nd_L4unch_D00M}
this here is a nice writeup too which also contains screenshots of the debugger which i am not including but i think look cool and would like you to see. They also include the download instructions which is nice
Reply by Email