2022-03-01 | UNLOCK

# Web

## rubbish maker

This challange was more rev than web.

### Statement

Going to the link gives us:

And downloading index.txt gives us heavily obfuscated php code. This file also changes every two hours so we must write general automated deobfuscator since doing it manually and case by case will be useless after two hours. This writeup is for specific version I got but should be general enough.
Here’s my index.php

### Deobfuscation

• Observe that there is a big function at the start which is called many times.
• We see that the argument that this function takes is xor’d to something constant so first we replace all f({x}) with f({x} xor {that constant}) and also replace the xor’s in the function itself. This will get rid of most bad bytes. We must also be careful to do everything as bytes and not uf8 strings.
• Then we observe that we can try to call the function with these values and see if result is always same. As it turns out function is only dependent on it’s argument. So we generate php code to call each and replace the calls with this value everywhere with the resulting value.
• sometimes the value that this function returns is a closure and we use closure dumping code from stackoverflow for that
• At this point we can remove the function in the beggining and code would still work
• There’s a lot of encodings(rot13, base64, strrev…) so we try to get them replaced as well This is where my deobfuscator ends and I figured I could do rest in less than 2 hours and faster than writing code for it.

### Manual labor :)

Main obfuscation that is left is goto‘s I started from first goto and would cut and paste goto where it was intended. going along reading code and understanding which branch was useless. There was also some xdebug stuff which I don’t know what it is. I guess it’s anti debugging feature.
So here’s the important code left: I’ve renamed variables that we might control as $g_x. We can either control g_4 or g_5. But controlling g_5 loses us control over the big switch so I chose to control g_4. to get flag using final readfile g_5 needs to be equal to g_4. There’s more so I won’t bore you with it… Basically after going though many cases I’ve figured out final parameters that was needed for solution: It’s just case work and not really interesting. It took me 2 restarts of the challenge so about 4-5 hours. ## baby gadget v1.0 /;/admin 可以绕过登录，但是得在 burp ，浏览器会被跳转 在队友的 Fuzz 下发现 密码 是个 弱密码 admin:admin123 发现 /;/admin/download.jsp?filename=lib.zip 下到了依赖，看到有 fastjson ，一顿搜。然后根据 红队武器库: fastjson 小于 1.2.68 全漏洞RCE利用 exploit 直接试一下能不能产生 dnslog 但是比较蛋疼的是我这个脚本小子用一些 JNDI 工具竟然打不了，触发了恶意类访问但是没反弹回来 shell ，于是根据文章自己手动搭建一个恶意的服务 发现可以产生请求，于是刚开始尝试列目录，失败。 try/catch 发现是个不知道什么鬼 java.lang.reflect.InvocationTargetException 的报错，可能是什么 waf ，没细究，直接读 /flag ，发现可以读到，完事。 //你要问我为啥行，俺也不知道，俺就是个脚本小子 hhhh ## baby gadget v1.0’s rrrevenge 同上做法 ## baby gadget v2.0 登陆抓包发现 xxe，简单测试后发现有 waf，用 utf-16be 可以绕，依照提示读 hint.txt 发现提示 name过长，用dtd外带即可： 文件 xxe.xml icon转一下： 通过hint下载到源码是 java 字节码 可以直接反序列化，发现有黑名单类，但没有对 JRMPListener 做过滤，所以直接通过 jrmp 打 cc 就可以直接rce ## baby gadget v2.0’ revenge 同上做法 # Misc ## Checkin Discord频道给机器人私发”>flag”可以得到一闪而过的flag，考虑使用录屏来获取。 ## ra2 直接玩游戏即可得到flag：SUSCTF{RED_ALERT_WINNER!!!} ## Tanner 首先根据校验矩阵还原码字 然后在ID3处发现的hint：THE FLAG IS the sha256 of the sum ofthe proper codewords(binary plus)which satisfy the condition.(note: with no zeros front) 于是将所有满足的码字的二进制数据相加之后去sha256得到flag：SUSCTF{c17019990bf57492cddf24f3cc3be588507b2d567934a101d4de2fa6d606b5c1} ## AUDIO 听fromfreiend的音频可以在30s部分听见比较明显的morse电码声音,而原始音频无杂音。因此猜测是否两者可以进行叠加而消除而只保留morse电码声音,网上搜了一下,发现b站教程一大堆,大部分都是用au进行。随便乱调,发现在等于-6dB时可以只听见清晰的电码声音,于是开始导出文件看频谱,可以很清晰得看到明显的morse电码 … ..- … -.-. - ..-. – .- … - . .-. — ..-. .- ..- -.. .. — 最后加上{} SUSCTF{MASTEROFAUDIO} ## misound ### 非预期 音频丢进Silenteye得到base64解码后内容为 207 359 220 224 352 315 359 374 290 310 277 507 391 513 423 392 508 383 440 322 420 427 503 460 295 318 245 302 407 414 410 130 369 317 Au看频谱得到字符串 AnEWmuLTiPLyis_etimes_wiLLbEcomE_B 分割一下，大概是 a new multiply is e times _ will become b 翻译得到：一个新的乘法是e乘以_将变成b hint:乘法使用ASCII码相乘 _的ASCII值是95 （属于是经典非预期了，由于乘积出来的数足够大，这里的 +1 其实在一定范围内取值都行，都没用到 SSTV 那东西 hhh # Pwn ## rain 这题也不需要完全逆完，就抓可能产生漏洞的地方分析构造就好。首先，很容易关注到configrealloc那里，当size = 0的时候，相当于free，然后再通过realloc往这个已经在tcache中的堆块写入数据，修改其next指针（其实就是个UAF），就可以进行劫持了，这里由于是2.27(1.2)版本的libc，因此还可以进行double free。不过，这里有两个地方要考虑一下，第一个就是需要泄露libc的基地址，第二个就是如何将我们伪造的堆块申请出来。对于第一个问题，我们容易想到，可以通过更改存放字母表的地址，再打印出来，就能造成信息泄露了，既然要更改存放字母表的地址，自然最方便的就是劫持整个结构体了，我们用raining刷新一下后，会通过malloc(0x40)申请一个堆块存放这个结构体，而我们可以在之前通过realloc那里double free一个0x50的堆块，这里就会申请出其中一个存放这个结构体，而在之后我们再用realloc申请出另外一个，就可以劫持到结构体了，这里由于没开PIE，故直接将存放字母表的地址改成某个elfgot表地址，就可以泄露出libc基地址了。再考虑第二个问题，如何申请出伪造的堆块，其实思路是类似地，先用raining刷新后，通过申请结构体那里申请出一个堆块，再在之后realloc申请出的就是伪造的堆块了，也就可以进行任意写了，这里劫持的是__free_hook，再通过realloc(0)调用free即可。 ## happytree 二叉排序树在删除根节点时,会把他第一个右子树的最小左子树和根节点内容互换然后删除最小左子树的堆块,但是删除堆块时左右子树的指针并未清空,如果重新将其malloc出来就会导致原来被删除的那个节点他的左右子树指针仍得到保留,因此可以得到一次double free 2.27未做限制,直接修改free_hook为system即可 ## kqueue ### 非预期 权限没配好,根目录ctf权限,直接非预期 exp如下： ## kqueue’s revenge diff了一下直接有flag ## mujs ### 题目描述 出题人给出的题目描述如下 在附件中有mujs的源码，这个是一个在嵌入式设备上常用的js代码解释器。这个源码的代码量还是很大的。同时附件里还有一个编译好的二进制文件，以及libc文件。从libc文件可以得知远程的运行环境是libc.2.31 题目描述中给出的这个hash字符告诉我们这个源码是来自于这个hash对应的commit的mujs源码 所以使用diff对比了这两个源码。发现主要的差别在两个地方 • 一些内置方法在main.c中被禁用了 • 新增了dataview.c文件。 这个是DataView 方法的一个简化版的实现 ### 寻找漏洞点 队友的思路首先是从最近的CVE里寻找一些漏洞，但是没有发现有用的信息，所以这个题应该是魔改的这个版本的源码。而且被魔改的部分其实代码量不算大，直接审就好了。 首先我们需要理解DataView都做了什么，都有哪些方法。一些常用的用法如下所示。 其实从jsB_initdataview函数当中大概可以看出来都有哪些方法，然后自己试一下就可以试出来这些方法怎么用 然后经过一阵审计，很容易就能发现这里存在一个越界写操作，可以越界写9字节 值得注意的是这里同时也存在一个整数溢出（但是是无符号的)，可以让我们可以前溢9字节。但是由于这里没有什么free的操作，所以很难利用。因此还是后溢9字节可用性高一点。 ### 利用漏洞 #### 类型混淆 因为说溢出9字节，这个多出的一字节很容易令人联想到类型混淆。下面是 js_Object 的结构。可见只要溢出一字节就可以覆盖它的type字段。 下面给出类型混淆的poc 输出为 #### 越界写Dataview的Length字段 js_Objec 使用了 C语言里的union结构，所以不同类型可以共用相同的内存。队友的想法是利用与DataView里Length字段占用内存相同的其他类型的字符来修改DataLength。这样我们就可以扩大任意地址读写的范围，起码可以拓展到整个堆上了，而不仅仅是越界9字节。 整个Js_Objec 结构体如下： 比如js_Object.u.dataview.length 在结构体内所处的偏移是和js_Object.u.number 以及s_Object.u.c.name这两个是相同的。 所以我们可以修改js_Object.u.number，队友找到了下面的代码 让我们试一下 JS_CDATE的值是10，我们需要把这个DataView结构的type字段溢出成10就可以了 结果： Emmm，居然是报错了。难道进行了类型混淆还是不能调用setTime方法么？队友曾经为了这个问题困扰了许久，他意识到了对象的prototype 在我们一创建的时候其实就已经确定了。所以当我们改变type的时候prototype并没有改变。而prototype基本就已经定义了这个对象可以调用哪些方法，可恶。 这时无敌的队友发现，js里有个讨厌的东西叫 this，这个东西在这个时候算是雪中送碳吧 我们仍然可以通过js的bind调用setTime : 成功了！ 看到这里大家可能会有些疑问，就是u.number是8字节的double类型，而我们要覆盖的u.dataview.length只有四字节，这样会不会覆盖到后面紧跟着的四字节的u.dataview.data，毕竟这个是个指针，覆盖掉了容易导致crash。其实是不会的，因为这个结构体有8字节对齐。 #### 使用堆上的越界读写来实现代码执行 到了这个阶段，我们已经可以通过修改dataview的length字段来实现堆上的任意地址读写了。并且堆布局也是我们相对可控的了。为了更好的控制堆上的结构，我的队友在c后面又申请了两个Dataview。并且我们知道，如果我们申请的堆的大小大于128k的话我们会使用mmap来申请空间，这个是malloc函数的一个策略。而这个mapp的地址往往距离libc地址很近，因此我们可以通过这种方法来泄漏libc基地址。 所以我们用上述的方法泄漏了libc地址之后，可以伪造一个JS_CCFUNCTION类型，他有一个字段叫做u.c.function我们可以轻易用下面的方式调用这个函数指针 ### 最终exp 队友表示他之前也没做过这种mujs的利用，但是这些堆利用的基本思路和很多大型项目比如v8的利用是共通的，但是那些大型项目由于运行时更为复杂，堆空间要相对更不可控一些。 # Crypto ## large case 思路如下： 这题没有提供e，给了p、q、r，并且条件里说了e由三个素因子组成，所以不难想到分解p-1，q-1，r-1，从而对e的可能值进行组合。不过就算组合过了，也不能用常规方法解题，因为本题e phi不互素，所以考虑对其开根，又考虑到这题的e会相对较大，所以用amm算法对其开根。 在这之前可以对e的因子进行猜测，由于需要使用到crt组合，所以因子不会太大，并且因子不会是共有的因数，于是可以猜测e使用p-1中的757，q-1中的66553，r-1中的5156273（如果不对，再进行调整，可供调整的选择不多，一些小的因数比如3、7，可以直接跑，很快就可以知道不满足）。 这是可以注意到flag的长度在1025-2048比特之间，所以我们不需要考虑r的部分，只考虑p、q。有了猜测的e，我们就可以把多于2048的pad去掉，留下一部分\x00 $$c\equiv{m^e(2^{1024})^e}\pmod{n}$$ $$c2^{-1024e}\equiv{m^e}\pmod{n}$$ 之后分别计算模p和模q的情况 $$cp\equiv{m^{e1 \times e2 \times e3}}\pmod{p}$$ $$cp^{-e2 \times e3}\equiv{m^{e1}}\pmod{p}$$ $$cq\equiv{m^{e1 \times e2 \times e3}}\pmod{q}$$ $$cq^{-e1 \times e3}\equiv{m^{e2}}\pmod{q}$$ 对上面的式子使用amm算法，就可以得到mp、mq的列表，然后使用crt对其组合，用SUSCTF校验即可。 ## InverseProblem 小数矩阵乘法求逆，在这个过程中无论如何计算都会产生精度损失（做题过程中尝试了使用Rational域计算都没用），后来意识到也许可以用格来做，构造一个这样的格： 其左边如果乘以flag构成的向量，那么得到的目标向量会是：$ (errors, flag) $，理想状态下，errors等于0，但是受到精度影响，errors不等于0，但是很小，同时flag也很小，因此可以达成目的。 exp如下： ## SpecialCurve3 题目分成了三个部分，该曲线是过原点的圆锥曲线，a>0时为双曲线，a=0时为抛物线，a<0时为椭圆，三个Problem分别对这三种情况进行了讨论。 最简单的是第二部分，根据加法公式，这种情况下的纵坐标与点的映射关系是线性的，即$(nG)_y=2n*G_y$，可以立刻算出结果 其次是第三部分，虽然p很大，但是p+1光滑，且曲线的阶是p+1，可以直接使用bsgs算法结合crt算出结果。这里使用了sagemath中自带的bsgs（sage自带的bsgs算法有bug，文件/opt/sagemath-9.3/local/lib/python3.7/site-packages/sage/groups/generic.py中468行没写全，这导致bsgs自定义群的计算报错，解决方案就是把这一行参数补全即可。c = op(inverse(b), multiple(a, lb, operation=operation, identity=identity, inverse=inverse, op=op)) 第一部分比较复杂，我参考了去年D\^3CTF的做法，根据Pell方程的矩阵形式推导迭代公式，最终可以将点运算映射到GF(p)上的运算，再根据p-1的小因子，结合cado-nfs和bsgs算法计算出其余的解。这里的映射关系详见wp，推导方法与D\^3一致，在结果上会有一个$(-1)^n$的差别，不过问题不大，正着解不出结果就取负解。 exp如下： ## Ez_Pager_Tiper # Rev ## DigitalCircuits 题目目测用Python 生成的exe，首先用pyinstxtractor.py 解包exe python pyinstxtractor.py DigitalCircuits.exe 之后对比 struct 与 DigitalCircuits 2个文件的16进制码 复制插入16进制码 修复Python版本和时间戳之后 将DigitalCircuits后缀改为.pyc 用python在线反编译出py代码 其实本质是一个TEA（包括魔数都一样了），直接写出TEA解密算法即可 ## tttree 在ghidra中打开二进制文件，看到入口函数非常小，没有调用自身以外的东西 用RET代替跳转和调用。所以它是面向返回的编程，但又有所不同 :D RET将跳转的地址用mov [rsp+0x10], rax写入，而rax则用pop rax设置；add rax$x

pushfq和popfd的使用可能是为了使混淆不会扰乱x86的标志。

①:把rip的相对地址推到堆栈，然后RET ->相当于JMP
②:推送两个rip相对地址到堆栈，然后RET ->我把这解释为如果CALL发生在第二个推送的值上，而调用所推送的返回地址是第一个推送值。

Patch exp

1.如果我们知道哪个cur_node_idx(后序遍历索引)对应哪个idx，那么我们可以通过从int_targets[cur_node_idx]中减去(rng()%107 + 97 + idx)来找到idx处的标志字符。
2.当我们知道该节点的字符时，我们可以用check_l和check_r找到子节点的索引。但是，当节点的两边都没有孩子的时候，这就有点棘手。

exp