前言:
很久没回来跟新自己的博客了,这几天借着来郑州学习的时间,温习了一下几种常见的上传漏洞,也是时候给自己的博客增加点内容了,证明一下自己还活着~~~~~
正文:
首先在这里表达一下自己对上传漏洞的理解:见名思义,在某个能够上传文件的地方(下面就叫它为up点)上传一些恶意的木马,在hacker或受害者的触发下对受害服务器产生恶意干扰。最典型的就是hacker选择绕过WAF将一句话木马导入up点,获取进入被攻击服务器的后台,利用菜刀工具进入该服务器获取后台文件。
一句话木马:<?php @eval($_POST[‘password’]);?>,该木马一般被hacker用作连接服务器后台的PHP文件内容,配合菜刀工具可以轻松进入服务器后台,所谓传小马,连webshell指的就是它。木马的password
是个可选变量,是菜刀连接该木马的密码。
一些常见的上传漏洞类型:
前端检测漏洞:
主要指那些在前端代码部分使用特定方法过滤的类型,例如直接在html代码中使用
<script></script>
直接限制关键字,这里的关键字主要是文件后缀.jpg、.png、.gif
等常见的无法解析内部恶意代码的文件。造成该漏洞的主要原因是程序员再编写代码的时候只是前端代码中限制可上传文件的后缀,换句话说只是在客户端浏览器上检测,这就造成了只要流量包通过客户端浏览器的检测,就会畅通无阻地上传到up点。代码举例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<script type="text/javascript">
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name + "|") == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
</script>该代码里面有个明显的参数allow_ext,不难看出是用来记录可上传文件类型的后缀,并且使用
<script>
标签修饰。hacker就可以通过一些插件(比如火狐的firebug)对此类参数进行修改,添加一些可执行文件的后缀即可上传恶意文件,从而获取webshell。当然,有些hacker可能会通过抓包工具(比如BurpSute)抓取前端检测可通过的文件但含有恶意代码的文件,修改文件后缀后放包。服务端MIME类型漏洞:
MIME简单说就是客户端上区分不同类型数据的软件,例如WEB浏览器判断文件类型时就是通过MIME类型来确定是jpg图片还是PostScript文件。一般图片的类型就是
imge/具体图片类型
(比如jpg图片的MIME类型就是imge/jpeg
),这些类型会被记录在HTTP包的Conent-Type里面,然后上传到服务端PHP的$_FILES超全局变量里面。造成该漏洞的原因是服务端辨别文件的一种方式就是通过检测流通文件的MIME类型,程序员在开发网站的时候考虑到在客户端判断绕过类型容易被抓漏洞,所以选择在服务端使用从客户端传来的辨别文件的MIME类型来过滤文件。代码举例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<?php
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = $UPLOAD_ADDR.'文件夹不存在,请手工创建!';
}
}
?>在该代码中可以看出服务端利用客户端传来MIME类型值来确定文件类型,这种辨别方式存在一个明显的漏洞,就是服务端只是简单通过客户端给文件记录的标记(也就是上面说的MIME类型)来判断文件类型,这种标识符是可以被更改的。hacker一般会通过抓取客户端的流量包并修改文件的MIME类型,使之可以通过服务端的检测,就可以上传恶意木马。
文件扩展名绕过漏洞:
这里的文件扩展名具体指的是服务端可解析的php文件多样,简单说来就是服务器可以解析的站点文件不只有
php
,还有php2,php3,php4,php5
,带着些后缀的站点文件效果与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<?php
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR. '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR .'/'. $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
?>从代码中可以看出,被限制的文件扩展名只有
'.asp','.aspx','.php','.jsp'
,所以,客户端在上传php2,php3,php4,php5
等文件的时候是被允许的(这里的一些可绕过关键字太多,就一一举例),这就造成了一些恶意代码可被上传的可能,只要不触碰被禁止的扩展名,使用一些常见的方式(大小写,文件流,不被禁止的可解析文件等等)都可以上传恶意代码到up点,当然,一些hacker喜欢利用抓包工具看流量包更改后缀。文件内容检测上传漏洞:
文件内容检测已经可以说是一种比较安全的检测方式,简单说,文件内容检测就是通过对上传文件解析后的2进制或16进制代码进行检测其内容是否合法或者是否存在恶意代码。例如png图片的开头一定是
89 50 4E 47 0d 0a 1a 0a
。但这种检测还会存在漏洞的原因就是这种判断方式太严格,只要能够满足站点文件的要求,就可能被某个可上传文件欺骗过去。代码举例: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<?php
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;
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_type = getReailFileType($temp_file);
if($file_type == 'unknown'){
$msg = "文件未知,上传失败!";
}else{
$img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}
?>可以看出,文件在上传之后只被允许读入2字节,然后判断是否为被允许文件的前缀,而被允许的文件还会重新组成文件名后才会被读入。虽然说这样的检测确实繁琐点,当确实有一定的可靠性。但道高一尺,魔高一丈啊,hacker利用抓包工具将流量包截取下来,并且在文件末尾加上恶意木马,修改扩展名后放包,绕过随机函数的这一块大部分会采用匹配up点的文件名的脚本来确定自己上传上去的文件被修改成是什么名,这类脚本只要懂一些http流量截取的人基本上都会写出,这里就不放出了。这样就可以通过菜刀工具进入up点了。
%00截断目录绕过漏洞:
谈到这个漏洞,我就想起当初在学习的时候遇到的坎坷,那时候我还不会这个类型的绕过,所以对这个绕过还是一直都是懵懵懂懂的状态,一直到前几天在做一道CTF时,突然感觉自己就悟了。我的感官中,这个漏洞已经超出那些只懂得编辑代码的程序员的能力范围。这个漏洞利用URL编码%00或者16进制下的0x00直接将后面的累赘代码省略掉,就就像c语言中在一段字符串中间加了一个不被转义的
\0
一样,提前结束了该字符串,这里也是同样的道理,程序在读URL代码中变量的时候,%00触发了char(0)
这个字符,程序就会误判到了结束符,提前结束读取变量,这样就保留下了截断符前面的部分。代码举例: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<?php
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
$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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
?>因为这个漏洞利用是服务端解析文件时截断目录的漏洞,所以这里我就贴了一个禁止所有可执行文件上传的代码。这里有必要解释一下客户端读取文件类型和服务端读取文件类型的方式了,就是它们都看后缀名,也就是看
.
后面的东西,一般都是直接看最后一个.
后面的东西就是后缀名。hacker就是也是利用这一点,上传一个被允许的文件,然后抓包截断,在上传路径后面添加一个含有两个文件后缀的字符串,中间用一个符号隔开,在burp的hex栏中修改这个符号的值为00,然后放包上传,最后到服务端就会被存储为可执行文件,以达到获取网站webshell的目的。目录路径检测解析漏洞:
目录路径解析是基于IIS6.0服务器管理软件的功能,是指上传上去文件路径在一定程度上可以被修改,说通俗点就是可以在站点文件下在建一个文件(一般这个文件是可执行文件)。这个漏洞是利用IIS6.0太过于遵守正常思维下文件目录格式,是文件夹就是文件夹,是可执行文件就是可执行文件。
因为这是服务器管理软件的漏洞,所以这里有没有代码就没必要了。hacker一般都是直接抓取流量包,在上传路径下加一个可执行文件的名字(比如,在burp抓到的包中原路径是
upload
,然后改成upload/test.php
),再在已经通过本地验证过的文件里加上恶意代码。因为计算机之间流通的是计算机语言,IIS6.0将原本hacker上传文件读入到新生成的可执行文件下,就是一串代码,这样hacker的恶意代码就会被执行。这里再说一个IIS6.0的文件解析漏洞:主要是用到
;
,IIS6.0在解析一个文件名的时候会将;
当成截断符,而不解析分号后面的内容。hacker利用该漏洞的思想有点类似%00截断,只是利用方法稍微会简单点,也就是抓包后在上传路径下加一个可执行文件的名字并用分号结束(比如,在burp抓到的包中原路径是upload
,然后改成upload/test.php;
),再在已经通过本地验证过的文件里加上恶意代码。
目前我知道的经典上传漏洞大致分为上面这几大类,这些应该只是冰山一角,还有更多的上传漏洞以后遇到了就补上来。如果有什么记录错或者记录的不完整的地方,还请大佬们点名批评,批评通道:2790169067
参考文献:upload-labs,杰奇网站漏洞。