earmusic任意文件读取漏洞[代码审计]

怎么发现的,我也不懂啊。师傅怎么说,我跟着照做,毕竟技术不到位啊。

功能点分析,反正这个下载的按钮可以存在漏洞。
image

复现过程

概述

这个漏洞产生在用户端,首先去注册一个用户账号。
登录后,进入上传音乐这里
image
上传音乐这里音频地址可以自己写想读的文件地址。
image
搞到之后点击提交。
之后我们可以下载这段音频,在下载的时候我们可以读取到我们需要的文件内容。
image
为什么在下载的时候我们可以读取到需要的内容呢,这个就得看代码了。

代码

首先看一下刚刚下载那里对应的代码。文件路径为:image
对应的下载代码为:
image
在下载音频的时候会执行readfile函数,这里的$file就是我们上传音乐时填写的文件路径。
通过上面的代码发现$file = geturl($row['in_audio']);接着我们来看一下$row是啥
audio.php代码的上半部分有写$row哪来的,$row就是music表中的一条数据。
image
我们来看一下music表中的数据长啥样。
image
根据music表我们得知,in_audio对应的就是音频的下载地址。同时也是之前readfile($file)里的$file的值。
至于geturl()函数是啥,它的作用是拼接出一个完整的url地址。

geturl函数
function geturl($file, $type=''){
	if(preg_match('/^(data\/attachment|plugin.php)/', $file)){
		$url = "http://".$_SERVER['HTTP_HOST'].IN_PATH.$file;
	}elseif(empty($file)){
		switch($type){
                        case 'lyric':
		                $url = "http://".$_SERVER['HTTP_HOST'].IN_PATH."static/user/nolyric.lrc";
		                break;
                        case 'cover':
		                $url = "http://".$_SERVER['HTTP_HOST'].IN_PATH."static/user/images/nocover.png";
		                break;
                        case 'avatar':
		                $url = "http://".$_SERVER['HTTP_HOST'].IN_PATH."static/user/images/noavatar.jpg";
		                break;
                        case 'photo':
		                $url = "http://".$_SERVER['HTTP_HOST'].IN_PATH."static/user/images/nophoto.png";
		                break;
                        default:
		                $url = NULL;
		                break;
		}
	}else{
		$url = $file;
	}

	return $url;
}

小结,我们似乎只要在上传音乐的时候,在音频地址那栏写下想要读取的文件地址即可读取任意文件了,接下来我们验证一下。

验证

读取同一级目录下的文件

我将读取与readfile函数所在文件audio.php处于同一级目录下的a.txt文件中的内容。
情况如下:
image

1.上传一个音频,并且音频地址填上a.txt
image
2.去下载页面下载音频。
image
似乎都非常顺利,我们打开下载的a.txt,结果发现里面的内容和我们想象的不一样。
image
出了点问题,接下来我们需要抓包分析一下。
3.去抓下载时的数据
image
send to repeater,在这里我们发现响应的下载数据包里其实是有我们想要的内容的,但不知为何没有写入文件中。
image

响应包报文
HTTP/1.1 200 OK
Server: nginx/1.15.11
Date: Sun, 18 Dec 2022 09:27:22 GMT
Content-Type: application/force-download
Content-Length: 209
Connection: close
X-Powered-By: PHP/5.5.9
Content-Disposition: attachment; filename=a.txt

<br />
<font size='1'><table class='xdebug-error xe-warning' dir='ltr' border='1' cellspacing='0' cellpadding='1'>
<tr><th align='left' bgcolor='#f57900' colspan="5"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> Warning: get_headers(): This function may only be used against URLs in C:\myProgram\phpstudy_pro\WWW\earmusic\template\default\source\audio.php on line <i>27</i></th></tr>
<tr><th align='left' bgcolor='#e9b96e' colspan='5'>Call Stack</th></tr>
<tr><th align='center' bgcolor='#eeeeec'>#</th><th align='left' bgcolor='#eeeeec'>Time</th><th align='left' bgcolor='#eeeeec'>Memory</th><th align='left' bgcolor='#eeeeec'>Function</th><th align='left' bgcolor='#eeeeec'>Location</th></tr>
<tr><td bgcolor='#eeeeec' align='center'>1</td><td bgcolor='#eeeeec' align='center'>0.0020</td><td bgcolor='#eeeeec' align='right'>259528</td><td bgcolor='#eeeeec'>{main}(  )</td><td title='C:\myProgram\phpstudy_pro\WWW\earmusic\template\default\source\audio.php' bgcolor='#eeeeec'>...\audio.php<b>:</b>0</td></tr>
<tr><td bgcolor='#eeeeec' align='center'>2</td><td bgcolor='#eeeeec' align='center'>0.0314</td><td bgcolor='#eeeeec' align='right'>557640</td><td bgcolor='#eeeeec'><a href='http://www.php.net/function.get-headers' target='_new'>get_headers</a>
( <span><font color='#cc0000'>string(5)</font></span>, <span><font color='#4e9a06'>long</font></span> )</td><td title='C:\myProgram\phpstudy_pro\WWW\earmusic\template\default\source\audio.php' bgcolor='#eeeeec'>...\audio.php<b>:</b>27</td></tr>
</table></font>
<br />
<font size='1'><table class='xdebug-error xe-warning' dir='ltr' border='1' cellspacing='0' cellpadding='1'>
<tr><th align='left' bgcolor='#f57900' colspan="5"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> Warning: array_key_exists() expects parameter 2 to be array, boolean given in C:\myProgram\phpstudy_pro\WWW\earmusic\template\default\source\audio.php on line <i>28</i></th></tr>
<tr><th align='left' bgcolor='#e9b96e' colspan='5'>Call Stack</th></tr>
<tr><th align='center' bgcolor='#eeeeec'>#</th><th align='left' bgcolor='#eeeeec'>Time</th><th align='left' bgcolor='#eeeeec'>Memory</th><th align='left' bgcolor='#eeeeec'>Function</th><th align='left' bgcolor='#eeeeec'>Location</th></tr>
<tr><td bgcolor='#eeeeec' align='center'>1</td><td bgcolor='#eeeeec' align='center'>0.0020</td><td bgcolor='#eeeeec' align='right'>259528</td><td bgcolor='#eeeeec'>{main}(  )</td><td title='C:\myProgram\phpstudy_pro\WWW\earmusic\template\default\source\audio.php' bgcolor='#eeeeec'>...\audio.php<b>:</b>0</td></tr>
<tr><td bgcolor='#eeeeec' align='center'>2</td><td bgcolor='#eeeeec' align='center'>0.0438</td><td bgcolor='#eeeeec' align='right'>558272</td><td bgcolor='#eeeeec'><a href='http://www.php.net/function.array-key-exists' target='_new'>array_key_exists</a>
( <span><font color='#cc0000'>string(14)</font></span>, <span><font color='#75507b'>bool</font></span> )</td><td title='C:\myProgram\phpstudy_pro\WWW\earmusic\template\default\source\audio.php' bgcolor='#eeeeec'>...\audio.php<b>:</b>28</td></tr>
</table></font>
hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
hello
hello
hello
hellohellohello
hello
hello
hellohello
hello
hello
hellohello
hello

OK,到这里我们已经成功读取在同一路径下的文件了,那么似乎读取其他位置的文件也只需要利用../进行目录跳转即可,但是事实并非这样,这套程序存在对./的正则过滤,接下来让我们验证一下。

读取不同级目录下的文件

失败

为了方便,我选择在audio.php所在目录的上一级目录下创建了b.txt文件。具体如下:
image
上传音频
image
然后我们发现刚刚上传的音频的文件地址被过滤修改了
image
通过抓包分析发现,对上传音频时进行过滤的文件所在为:
image
其中对in_auido进行过滤的代码为:
image
其中,对其进行正则过滤的是checkrename函数,具体如下:

checkrename函数
function checkrename($file, $dir, $old='', $mode='add', $table='', $field='', $id=0){
	$file = preg_match('/(\.\/|\?iframe=|.php\?)/i', $file) ? 'Safety filter' : $file;
	if($mode == 'add'){
		if(preg_match("/^data\/tmp\/\d/", $file)){
			$var = str_replace('tmp', $dir, $file);
			@rename(IN_ROOT.$file, IN_ROOT.$var);
			return $var;
		}else{
			return $file;
		}
	}else{
		if($file !== $old){
			SafeDel($table, $field, $id);
			if(preg_match("/^data\/tmp\/\d/", $file)){
			        $var = str_replace('tmp', $dir, $file);
			        @rename(IN_ROOT.$file, IN_ROOT.$var);
			        return $var;
			}else{
			        return $file;
			}
		}else{
			return $file;
		}
	}
}
正则表达式,看不懂过滤的啥,但是./是肯定被过滤了。
$file = preg_match('/(\.\/|\?iframe=|.php\?)/i', $file) ? 'Safety filter' : $file;

如何绕过呢?使用绝对路径

成功

上传音频,使用绝对路径:C:/myProgram/phpstudy_pro/WWW/earmusic/template/default/b.txt
image

抓包结果,成功读取。
image

posted @ 2022-12-18 18:34  请去看诡秘之主  阅读(80)  评论(0编辑  收藏  举报