XXE漏洞类CTF实战


[NCTF2019]Fake XML cookbook

进入题目后,是一个登录框:

image-20201010114852347

sql注入后失败,我们查看源代码发现了源码如下:

function doLogin(){
    var username = $("#username").val();   // val() 方法返回或设置被选元素的值
    var password = $("#password").val();   // $() 相当于 document.getElementById()
    if(username == "" || password == ""){
        alert("Please enter the username and password!");
        return;
    }

    var data = "<user><username>" + username + "</username><password>" + password + "</password></user>"; 
    $.ajax({             // $.ajax() 方法用于执行 AJAX(异步 HTTP)请求,返回其创建的 XMLHttpRequest 对象
        type: "POST",
        url: "doLogin.php",
        contentType: "application/xml;charset=utf-8",
        data: data,
        dataType: "xml",
        anysc: false,
        success: function (result) {
            var code = result.getElementsByTagName("code")[0].childNodes[0].nodeValue; 
            // childNodes 属性返回包含被选节点的子节点的集合
            // 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);
        }   // XMLHttpRequest 对象用于在后台与服务器交换数据
    }); 
}

我们随便输入后抓个包看看:

image-20201010115539228

从上面我们可以看到,当前web应用正在解析xml的内容,接受用户输入的用户名,然后呈现给用户。为了验证,我们可以构造如下的输入:

image-20201010120114980

可以看到应用程序确实是直接解析了xml,将我们输入的用户名直接在回显之中显示了出来,联想到XXE显式攻击。而且观察这个http包,在发送方的http包头中可以通过Conetent-Type: text/xml 观察到服务端以xml的形式接收文件,符合xxe漏洞的条件,然后服务器会正常返回客户端在body中post过去的xml代码执行结果。此时就可以构造恶意的xml代码,可以看见服务器仍是正常返回,说明在服务器端并没有进行过滤,因此可以初步确定此应用应该存在XXE漏洞,且为显示。

所以我们可以想到用这样的方式让服务器返回它内部的文件,这样的话也就是可以读取到flag了。

paylaod如下:

image-20201010120501815

如上图,成功获取flag。

在一般的异步网站都会有异步数据与服务器的交互,一般传送数据为json或xml。但如果传送数据格式为json,我们可以将传送的数据格式改为xml,有很大的可能服务器会解析你异步上传的xml脚本执行你想要干的事。

[NCTF2019]True XML cookbook(XXE+ssrf)

image-20201010120816709

还是同样的登录框,查看源码得到如下代码:

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.php",
        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);
        }
    }); 
}

还是一样的源码,但总感觉肯定不能向上面那道题一样去做。

我们先尝试直接读取一下flag:

image-20201010121404714

发现一堆报错,可能flag并不在这个目录,也可能flag并不在这台主机上。

然后我们再利用php://filter伪协议来读一下doLogin.php的源码:

image-20201010121554660

解码得php源码:

//doLogin.php
<?php\n/**
* autor: c0ny1\n* date: 2018-2-7
*/

$USERNAME = 'admin'; //\xe8\xb4\xa6\xe5\x8f\xb7
$PASSWORD = '024b87931a03f738fff6693ce0a78c88'; //\xe5\xaf\x86\xe7\xa0\x81
$result = null;
libxml_disable_entity_loader(false);   // libxml_disable_entity_loader用于禁用或启用加载外部实体的功能。禁用(TRUE)或启用(FALSE)。
$xmlfile = file_get_contents('php://input');
try{
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);   // loadXML() 方法通过解析一个 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{       // sprintf() 函数把格式化的字符串写入变量中。
        $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;
?>

simplexml_import_dom() 函数把 DOM 节点转换为 SimpleXMLElement 对象。获取 DOM 文档节点并转换为 SimpleXML 节点:

<?php
$dom=new domDocument;
$dom->loadXML("<note><to>Tove</to><from>Jani</from></note>");
$x=simplexml_import_dom($dom);
echo $x->from;
?>

输出类似:

Jani

emm……没什么发现,但是读取源码的payload我还是在这里记录一哈。
这一次有所不同,上一题是我们读取文件得到flag。这次是刺探存活的服务器;

其实很多人都小看了xxe漏洞,觉得它只可以读读文件啊什么的,其实大错特错,它还可以访问内网的主机,和ssrf利用的dict伪协议一样,都可以刺探存活的主机并且链接访问;也可以看作这个题是在打内网;

好的我们老样子,我们利用ssrf读内网文件。先用file协议读取相关的文件 /etc/passwd 和 /etc/hosts;当我们读取到hosts文件的时候,我们会发现有几个ip地址:(到这里应该可以猜到是在打内网了)

只能想到利用xxe进行ssrf打内网,扫描一下内网ip的几个文件:/etc/hosts,/proc/net/arp,/proc/net/fib_trie

image-20201010122050212

然后我们开始猜内网。既然我们知道了这一点,我们就可以直接用http协议访问了。我们尝试访问173.181.213.10:

image-20201010122242475

发现报错,说明这台主机现在并未存活,其实我们已经知道是打内网了,那就多来试几台,当我们尝试到173.181.213.12时,得到了flag:

image-20201010122347692

(当然这里我们也可以发送到intruder进行爆破来探测内网主机)

还可以读取arp路由,可以直接发现内网中曾存在的主机:

image-20201010122540417

ISCC 未知的风险-1

说是只允许user用户进入,那么应该就是修改cookie。

进入后抓包

img

然后jwt爆破得到秘钥为123456,

img

然后我们伪造user用户

img

img

得到了一个登录框页面的源码:

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.php",
            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);
            }
        });
    }

和上面两个题的源码是一样的,哈哈哈。

猜测flag在根目录:

img

没有

img

也没有。会不会在web目录下呢:

img

base64解码即可得到flag:

img


Author: WHOAMI
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source WHOAMI !
评论
  TOC