WMCTF2022 Writeups - CNSS
WMCTF2022 Writeup - CNSS
Web
easyjeecg
/api/../
权限认证绕过
随便找个GetShell就行
CgUploadController 路由传 upload目录访问马是 nginx 403
iconController 路由传 plug-in/accordion/images目录 404
upload 目录禁止访问 jsp 后缀
另外有个未授权 /webpage/system/druid/websession.json
可以查看所有人的session,这道题应该没用
admin 重置密码的洞也失败
poc
1 | POST /api/../cgUploadController.do?ajaxSaveFile&sessionId=12DFB3DED5E177EBA04AED2EE3C86996 HTTP/1.1 |
传jspx就可以了
exp
1 | POST /api/../cgUploadController.do?ajaxSaveFile&sessionId=12DFB3DED5E177EBA04AED2EE3C86996 HTTP/1.1 |
拿了shell之后看了下服务器配置
1 | #!/bin/sh</br> |
Crypto
ecc
通过计算可得知
然后通过求gcd分解n,得到明文m之后发现似乎并不是flag,第一个为不可见字符。左思右想良久之后发现m的比特位只有200出头,而output说flag有606比特,然后突然想起来还有椭圆曲线的a和b没用到,发现a和b的比特位刚好也是200出头,加起来正好606.由于比特字符8位一分组,所以从1-8简单的爆破了一下偏移,发现了a和b中有可打印字符,拼接起来就行
homo
先把所有p_i求出来,构造如下格子
1 | pk=[] |
由于
整理得
然后构造如下矩阵,求出r_i
1 | q=[] |
然后求得sk,解密即可
1 | c = [] #数据省略 |
不过事后发现,似乎并不一定要原本的sk才能解密(事实上这样求出来的sk,r_i也应该不是原本的,都并非素数),可以直接用二元coppersmith,先求出sk
的近似sk_0,然后得到pk_0=p_0(sk_0+x)+2*y 利用small_roots梭出来的也能用来解密flag
nanoDiamod
很朴素的想法,前面12轮查询如果出现了两次不同,那最后2轮一定是返回正确的结果(如果恰好错误都落在对同一个变量的查询上就寄)
其他情况就当给的都是正确的
有时候,知道的太多反而不好.jpg
1 | from pwn import * |
nanoDiamond-rev
首先我们有异或运算:
r1 xor r2 = ((r1 and r2) == 0 ) and (r1 or r2)
考虑先询问每个值一次,询问方法类似:
B0 == 1
得到每个bool变量的初始值。
由于可能说谎,验证一下,询问:
B0 xor B1 == 1
B2 xor B3 == 1
B4 xor B5 == 1
假设上面的询问与第一轮得到的值有矛盾,则说明A、B、A xor B 中有一个假信息。(有两个假信息概率较小)
此时已经出现一次错误,我们认定后面的回答都是正确的(再次出现假信息概率较小)
考虑再次询问 A xor B:
若答案和之前相同,则认为A xor B正确,询问A可以得到A和B哪个正确,更新A和B的值。
若答案和之前不同则认为A xor B错误,保持A和B值不变。
由于最多可能出现两次矛盾,每次矛盾需要2次询问验证,总询问次数最多为6+3+2+2=13。
但是前面忽略的几种“概率较小”的情况加起来并且在连续进行50轮的情况下出现的概率是非常高的。
但是不怕,我是欧皇,跑了几百次就出flag了。
1 | from pwn import * |
PWN
Ubuntu
flag在附件里
Reverse
BabyDriver
通过字符串分析找到主函数 0x140006810
flag 为 32 位
sub_140006750 中有 flag 异或和结构体赋值
sub_140010100 为垃圾代码,功能等于 strcmp,通过引用找到密文
分析 sub_140006750 中后面的调用,找到一些无关的函数调用,通过 API 文档和网络搜索可以发现这符合驱动注入代码,在 NtQueryInformationFile 中 FileInformationClass 为 FileUnusedInformation 时触发,合理猜测他为类似 hook 的功能,其注入代码为 WMCTF101.txt
用 IDA 分析注入代码可发现 AES 的 S 盒,已知密文和在结构题赋值中的密钥,猜加密方式写脚本得:
1 | from Crypto.Cipher import AES |
Archgame
load_code
处对bin文件进行了一个解密
1 | for ( i = 0LL; i < size; ++i ) { |
round()函数有两个switch,手动修复一下,大概逻辑是这个样子。
是一些关于unicorn虚拟机的操作,查一下unicorn引擎的文档可以得到函数作用。
1 | __int64 round() |
程序的逻辑整体是把challs.bin解密之后加载进来,和输入的fake_flag一起加载到虚拟机中,当flag正确时会返回一个正确的round_key。
global_key 是 round_key 的异或和。每轮使用 round_key 在 map 里寻找对应的 code_info,最后所有 round_key 按顺序拼起来就是 flag。
比较关心的是 g_arch 和 g_mode。查一下unicorn.h的结构体的定义。
1 | typedef enum uc_arch { |
所有的code_info信息在init()函数里可以查到。
1 | v87 = 1995092961; |
大概梳理一下 code_info 的存储方式
1 | *v84 = 34; //chall 编号 |
程序启动时 round_key 是 0,找到对应文件是 chall14.bin
,架构是 ARM64,载入之后大概长这个样子:
1 | __int64 sub_0() |
观察到程序的返回值只有几种,而程序判断 fake_flag 是否正确的逻辑是在 map 里寻找有没有对应的 key。考虑无视程序逻辑,直接在 init 程序中遍历搜索这几种返回值直到找到一个存在的round_key。
虽然有多个返回值可以搜到,但由于challs是用前面所有round_key的异或和解密,可以认为如果按照对应架构载入能被IDA正确解析则key正确,否则key错误。
1 | from Crypto.Util.number import * |
这样找到的 round 顺序最终为[14, 12, 33, 36, 37, 25, 2, 40, 17, 47, 9, 26, 8, 19, 34]
将round_key拼接得到flag。
wmctf{76eab3e1b8d86c635cbecc04249d8564fdbe8083aa0040605edf7b8f1bfa27689229867ce07cf1acf81e45b33cd13a4965d55f831953fc29b7620858}
seeeeee
- 先把jump out 处的 跳转的 data 转成 代码, 然后动静结合 大概分析出流程
- sub_B470 处理命令行参数,sub_7100 接受
- 要求两个命令行参数,第一个 len =24,第二个len=64
- 第一个参数 —- 看不出来 是什么算法,但通过动调发现,对输入只进行了位置的变化,然后与 内存中的值比较,
所以dump出值, 改一下 位置即可 得到输入为 h7BOJpTCYsuoAUQn6qFxXyVE
- 第二个参数 :接着 动调 ,发现
k etyb-23 dnapxe
字样
考虑 chacha20加密 ,
sub7FF637CD7830() 生成 异或流
然后 与 输入的63 字节值进行异或加密,最终比较
解法, 因为我们已经得到第一个命令行参数,直接动调得到 生成的 异或值 和最终比较的结果即可
1 | xor= [0xD0, 0x6E, 0xF8, 0xF7, 0x84, 0xAD, 0xDD, 0x5E, 0x29, 0xE7, 0x82, 0x12, 0x20, 0x00, 0x5A, 0xA2, 0x50, 0x48, 0x0B, 0xE6, 0x64, 0x2D, 0x23, 0x7C, 0xD9, 0x2E, 0x1B, 0x6E, 0xDF, 0x34, 0xDE, 0xCA, 0x39, 0x70, 0x4C, 0x8B, 0x4F, 0x18, 0xAE, 0x4C, 0x35, 0x7E, 0x3E, 0xE4, 0x2B, 0x0B, 0xEA, 0xC5, 0xD8, 0xB2, 0xD6, 0x6D, 0x4E, 0x9C, 0x30, 0x77, 0x98, 0xC8, 0x19, 0x98, 0x5B, 0xB1, 0x36, 0xAB] |
GAME
pvz-game
可以和自己匹配
CE 修改器把阳光拉满,放僵尸时找到对阳光数操作的汇编,把僵尸消耗阳光的sub指令nop掉,加速500倍,用连点器(Python 实现)放置僵尸,2个小时多出flag