CISCN2023 shell we go
在国赛上遇到的一道go语言的题,自己也是第一次遇到,在此记录一下复现的过程。
由于go语言编译都是静态的,而且程序把所有的符号表都去掉了,对于逆向的难度大大提升,虽然可以通过输出的字符串快速找到主函数,但是对于一些go语言的库函数根本就无法判断,分析这些库函数只会浪费大量时间;这里使用一下IDAGolangHelper,可以修复go的符号表。
进入程序后很快就想到输入常用的shell命令,但是输出只有“Cert Is A Must”,查找”Cert“字符串可以看到还有一个”Cert complete, you can explore more“,说明需要输入一些东西认证:
引用该字符串的函数为main_unk_func0b01,好像看不出什么东西:
接着向上找引用main_unk_func0b01的函数,看到main_unk_func0b05函数,很快就发现伪代码中没有调用过main_unk_func0b01函数的痕迹:
直接查看该函数的汇编,果然ida7.7的伪代码功能对go无法准确识别。函数中下面的块全是cmp,将一些奇怪的16进制数变成字符串(需要逆序看),可以清晰地看到基本上全是一个先比较字符串长度,再去比较字符内容的一个过程,shell中不同指令的实现是将该过程重复多次来判断指令的内容,进而跳转到相应的地方去实现不同指令的功能:
在cmp时,数据全是在rax的地址获取的,也很容易看到一个规律:[rax + i]为字符串的地址,[rax + i * 8]为字符串的大小,rax寄存器之前一次改变是在调用strings_genSplit函数后,通过gdb调试发现该函数通过空格来分割字符串,rax保存字符串分割后的信息,rbx是分割的块数:
接下来很多东西都可以顺利看懂了,想进入main_unk_func0b0函数,分割的块数为3,第一个字符串为”cert”,第二个字符串为”nAcDsMicN”,第三个字符串进入main_unk_func0b0函数后再去进一步比较。接下来再进入main_unk_func0b0分析,看到crypto_rc4_NewCipher、encoding_base64__ptr_Encoding_EncodeToString这些库函数,直接去网上搜一下,这些函数就是cr4加密后再进行base64编码,,最后与”JLIX8pbSvYZu/WaG”比较,通过这个网站就可以解密:https://www.lddgo.net/encrypt/rc4
输入”cert nAcDsMicN S33UAga1n@#!”后就能真正去使用这个shell,回过头去看main_unk_func0b05函数中比较的字符串,可以发现的指令有”ls”、”cd”、”cat”、”whoami”、”echo”、”exit”,其中只有”ls”和”cd”才能真正实现相应的功能,而cat匹配到flag时会输出一个假的flag(😦),真正有用的是echo会进入main_unk_func0b04函数。
main_unk_func0b04函数中通过输入的字符串的大小(除空格)作为循环的大小,将其单个字节循环复制到栈中,这里是一个echo的栈溢出,main_unk_func0b04函数会先对每一个字符串块的大小进行检查,直接使用空格绕过即可:
而在临近rbp的部分存着复制过程中循环的大小与栈的相对基址,如果直接填充数据溢出的数据将其改变就会使复制数据失败,不能覆盖返回地址了,这里使用”+”这个字符可以不对让数据不复制到栈上,而循环的大小依然再增加:
exp:
1 |
|