-
-
[原创] HITCON CTF 2024 re AntiVirus wp clamav bytecode signature逆向
-
发表于: 2024-7-15 12:55 6232
-
hint
It seems to be hard to reverse-engineer the anti-virus signature???
print_flag.cbc
不知道是什么格式,看起来是plaintext,都是可见ascii字符
run.sh
中出现了clamav、clamscan等关键词
首先找到clamav的官方文档,得知clamav是一个反病毒引擎
然后搜工具clanscan的资料,看到一个Debian上clanscan的手册
--database=FILE/DIR
- load virus database from FILE or load all supported db files from DIR
[--bytecode-unsigned[=yes/no(*)]](https://manpages.debian.org/testing/clamav/clamscan.1.en.html#bytecode)
Allow loading bytecode from outside digitally signed .c[lv]d files. Caution: You should NEVER run bytecode signatures from untrusted sources. Doing so may result in arbitrary code execution.
手册指出run.sh
中命令是加载print_flag.cbc
这个病毒数据库,并允许加载不受信任的字节码
即print_flag.cbc
这个文件是一个病毒数据库
搜索clamav病毒数据库的格式是cvd,结合官方文档,cbc后缀的似乎是bytecode signatures
搜索bytecode signatures到的一份官方比较旧的相关资料ClamAV® blog: Brief Re-introduction to ClamAV Bytecode Signatures,其中关于字节码数据库内容摘抄如下:
Bytecode Databases
Bytecode signatures are stored in a separate database from the standard ClamAV signatures. In fact, it is impossible to generate database files (with sigtool) that contain both bytecode signatures and standard signatures. Bytecode databases are generated by building a database that is named “bytecode.*” which triggers specialized handling by sigtool. Sigtool will add all bytecode signatures in the specified directory regardless if the name of the signature matches the name of the database. Bytecode signatures thus can be named anything provided the extension is “.cbc”.
总结一下就是cbc后缀的是字节码签名,一般从clamav 的 cvd后缀数据库中通过clamav提供的工具可以解压出若干字节码签名。
字节码签名允许拓展反病毒引擎匹配病毒的机制,如其名字,clamav中包含一个vm可以执行字节码签名中包含的字节码(意味着字节码签名有任意代码执行的风险,不受信任的来源获取字节码签名是不安全的,虽然这与本题无关)
继续搜索Bytecode Signatures相关信息,官方文档 Bytecode Signatures章节提到编译字节码签名的项目是ClamAV Bytecode Compiler.
项目中有个过期pdf documentatio,没啥太大作用,提到一个测试字节码签名的工具叫clambc,该工具在安装clamav时会被一起安装
试用一下clamscan
前面过期文档提到的用GDB调试字节码的方法,没用上,需要libtool没有细究是啥
据说clamav配套的sigtool工具可以解析字节码签名,但不行,和这个工具应该是无关的
ubuntu上安装官网下载的deb安装包安装失败,apt install的版本是0.x的
于是在windows上安装了官网下载的1.0.6版本msi安装包,试了一下加载字节码签名扫描
sigtool照样不行,这个工具应该是和字节码签名无关的?
看clambc的help,终于找到解析字节码签名(cbc后缀)方法,该工具-c
方法可以输出 字节码汇编/字节码中间代码(bcir是bytecode ir吧)
--info
方法可以看到字节码签名的一些基本信息,没啥用
输出的字节码汇编 bytecode.txt
搜索clambc --help
中输出IR描述提到的clamav IR of bytecode signature "setvirusname"
找到一篇wp:SECCON CTF 2022 Quals writeup - st98 の日記帳 - コピー (hateblo.jp)
结合wp和字节码汇编的描述,推断字节码汇编的格式
开头是类型信息表,在本题中只在OP_BC_GEP1中发现类型信息似乎有用:
接着是函数定义,包括:三行井号的开头、全局变量表(globals)、变量表(values)、常量表(constants)、函数字节码
本题有两个函数,分别是F.0和F.1
函数F.0的三行井号的开头:
三个变量表中都包含ID,其中变量表和常量表的ID是顺序编号不重复的,用于在本函数内索引
全局变量表GID和ID似乎是一样的,索引方式和变量/常量表不一样(详见 索引全局变量 章节)
全局变量表(globals),两个函数全局变量表是一样的:
其中id为0的全局变量是不可用的
VALUE列是变量的类型,但没有区分有无符号,只是区分数组、指针、长度
[32 x 8]
表示数组,等价于c语言的char g1[32];
i8*
表示8位整型的指针,等价于c语言的char*
i64
表示64位整型,其他以此类推
变量表(values),其中包括函数参数(arguments)和局部变量(locals),这里列F.1的变量表(F.0没有函数参数):
其中ID为0、1的变量VALUE列中标注了argument,表示是函数参数(argument)
其余为函数的局部变量(local)
常量表(constants):
常量表的VALUE是常量的十进制和十六进制值,没有给出常量的长度
接着是函数字节码:
比较有用的信息:BB列表示的是基本块号,OPCODE是字节码类型,INST列是字节码汇编文本
在指令中,变量(value,包括函数参数argument和局部变量local)和常量(constant)由上述 ID 引用。全局变量见 索引全局变量 章节。
比如BB=0, IDX=1
的指令20 = 19 & 303
,中的20、19、303都是变量/常量的ID,查变量表和常量表可知是id=19的局部变量 与 id=303的常量,赋值给id=20的局部变量。
如果 ID 以 p 为前缀,则表示局部变量被视为指针,相当于c语言的(char*)value
,所以直接把p无视即可。
字节码的功能根据INST列来猜就行,大部分比较好猜,猜不到就看项目源码
字节码的功能实现在bytecode_vm.c中
OPCODE=OP_BC_CALL_API调用VM提供的API,API的功能需要参考源码
比如
表示v7 = setvirusname(g2147483660, v36)
(-2147483636=2147483660=0x8000000c,详见 索引全局变量 章节)
bytecode_api.h是API的定义,bytecode_api.c是API的实现
本题出现了setvirusname、seek、read三个api
setvirusname需要配合函数返回值使用,返回0表示未匹配,返回1表示匹配,通过该api设置匹配的病毒的名称
seek类似c语言中的fseek,不同是不需要传入fp这个参数,相当于fp是要扫描的文件,返回值是相对文件开头的偏移(相当于c语言的fseek+return ftell)
read是读取要扫描的文件的内容,类似fread
转换成c语言(没有仔细处理signed和unsigned,只是在注释里标注)
结果:bytecode.c
分析见 阶段四:分析字节码功能 章节
关于负数id,其实是32位整型按有符号输出了,最高位为1(有符号负数)表示是索引全局变量表
比如id=-2147483636,这实际上是32位有符号数,转无符号是0x8000000c,即对应全局变量表(globals)中id=0xc的全局变量
参考bytecode_vm.c中处理操作数的代码:
字节码汇编,转C+O2优化+ida f5分析
输入是扫描的文件,f1对扫描文件进行加密,然后把加密结果和全局变量的内容比较。IR中没有找到全局变量初始化相关的IR
cli_vm_execute往上找找到cli_bytecode_load
clamav/libclamav/bytecode.c/cli_bytecode_load这个函数是从cbc文件中加载bytecode
同文件中的parseGlobals(struct cli_bc *bc, unsigned char *buffer)函数处理全局变量的加载,加载cbc文件中以G开头的一行内容
本题中唯一一个SGT
id=308是常量 255(0xFF)
这个有符号大于应该解释为 v30 = (((char)v29) > (-1));
题目大意是f1异或加密输入f1(input) = input ^ xor
,然后比较f1(input) == secret
一个思路是输入396个A,dump f1加密的结果:f1('A'*396) = fake_secret
然后解出异或的数据xor = fake_secret ^ 'A'*396
最后secret ^ xor = input
得到正确输入
但是发现有问题,xor数组不是完全固定不变的,其中几个元素会受input影响,即f1(input) = input ^ fxor(input, xor)
这个fxor是啥懒得去研究了,input对fxor结果影响很小,fxor结果基本等于xor
解决方法是先按上面思路得出一个近似异或数组,以及近似正确输入,然后迭代直到得到正确结果
具体实现,试了一下迭代3、4轮就相等了,就没有做退出的判定:
OPCODE=OP_BC_GEP1的第4个id猜测是tid
比如
65是tid,查到是 65: DPointerType i8*
,i8是1字节
这条字节码猜测为 v28 = v0 + (v27 * 1);
这题做得不是很理想,很多细节没到位,头脑有些混方向不明确,导致做题时间很长,花了一天半
第二天(13号)早上11点半跟随官方文档找到编译字节码的源码,这里算是被误导了;头脑有些混乱,当时目标是找cbc文件(即字节码签名)的格式,应该找解释、运行cbc文件的虚拟机的代码,而不是找编译cbc文件的代码
第二天(13号)下午1点半找到输出字节码汇编(bcir),花了很多时间在百度,但是似乎clamav在互联网的资料很少,收效甚少;应该早点看工具的--help的
接着卡在负数id上一段时间,直到晚上9点找到虚拟机源码,本来应该是步入正轨了,但后面又犯病了
解决完负数id又卡在全局变量初始化上,傻傻的又去百度找资料,不幸的是没有找到;明明都找上源码了,应该去细读源码的
第三天(14号)百度无果,终于又回到正轨去看源码了,然后在下午3点找上虚拟机加载cbc的代码
后面4、5点左右又犯大病了,此时已经完成字节码到c的转换,gcc -O2+ida f5分析,妈个鸡都有源码了,直接源码里输出就好了,还非要在ida里dump,一番调试浪费了很多时间,直到6点多才幡然醒悟,直接改转换出来的c代码去解密
总结,没有一直提醒自己当前阶段明确的目标是什么,以至于收集信息、逆向分析阶段找错方向,解密阶段也绕弯路浪费了不少时间
clamscan --bytecode-unsigned -d
/home/yssfpwn/
下载
/hitcon-av/print_flag
.cbc run.sh
/home/yssfpwn/
下载
/hitcon-av/run
.sh: OK
/home/yssfpwn/
下载
/hitcon-av/print_flag
.cbc: OK
----------- SCAN SUMMARY -----------
Known viruses: 1
Engine version: 0.103.11
Scanned directories: 1
Scanned files: 2
Infected files: 0
Data scanned: 0.02 MB
Data
read
: 0.01 MB (ratio 2.00:1)
Time: 0.005 sec (0 m 0 s)
Start Date: 2024:07:13 00:11:41
End Date: 2024:07:13 00:11:41
clamscan --bytecode-unsigned -d
/home/yssfpwn/
下载
/hitcon-av/print_flag
.cbc run.sh
/home/yssfpwn/
下载
/hitcon-av/run
.sh: OK
/home/yssfpwn/
下载
/hitcon-av/print_flag
.cbc: OK
----------- SCAN SUMMARY -----------
Known viruses: 1
Engine version: 0.103.11
Scanned directories: 1
Scanned files: 2
Infected files: 0
Data scanned: 0.02 MB
Data
read
: 0.01 MB (ratio 2.00:1)
Time: 0.005 sec (0 m 0 s)
Start Date: 2024:07:13 00:11:41
End Date: 2024:07:13 00:11:41
.
/libtool
--mode=execute gdb clamscan
/clamscan
.
/libtool
--mode=execute gdb clamscan
/clamscan
sigtool --decode-sigs < print_flag.cbc
sigtool --decode-sigs < print_flag.cbc
> clamscan --bytecode-unsigned -d
'print_flag.cbc'
Loading: 0s, ETA: 0s [========================>] 1
/1
sigs
Compiling: 0s, ETA: 0s [========================>] 40
/40
tasks
C:\Users\wind\Downloads\hitcon-av\print_flag.cbc: OK
----------- SCAN SUMMARY -----------
Known viruses: 1
Engine version: 1.0.6
Scanned directories: 1
Scanned files: 1
Infected files: 0
Data scanned: 0.02 MB
Data
read
: 0.01 MB (ratio 2.00:1)
Time: 0.362 sec (0 m 0 s)
Start Date: 2024:07:13 13:15:26
End Date: 2024:07:13 13:15:26
> sigtool --decode-sigs < print_flag.cbc
ERROR: decodesig: Invalid or not supported signature
format
TOKENS COUNT: 2
> clamscan --bytecode-unsigned -d
'print_flag.cbc'
Loading: 0s, ETA: 0s [========================>] 1
/1
sigs
Compiling: 0s, ETA: 0s [========================>] 40
/40
tasks
C:\Users\wind\Downloads\hitcon-av\print_flag.cbc: OK
----------- SCAN SUMMARY -----------
Known viruses: 1
Engine version: 1.0.6
Scanned directories: 1
Scanned files: 1
Infected files: 0
Data scanned: 0.02 MB
Data
read
: 0.01 MB (ratio 2.00:1)
Time: 0.362 sec (0 m 0 s)
Start Date: 2024:07:13 13:15:26
End Date: 2024:07:13 13:15:26
> sigtool --decode-sigs < print_flag.cbc
ERROR: decodesig: Invalid or not supported signature
format
TOKENS COUNT: 2
> clambc --help
...
--printbcir -c Print IR of bytecode signature
...
> clambc print_flag.cbc -c > bytecode.txt
> clambc print_flag.cbc --info
Bytecode
format
functionality level: 6
Bytecode metadata:
compiler version: 0.105.0
compiled on: (1719557581) Fri Jun 28 14:53:01 2024
compiled by:
target exclude: 0
bytecode
type
: logical only
bytecode functionality level: 0 - 0
bytecode logical signature: PRINT_FLAG.{Fake,Real};Engine:56-255,Target:0;0;0:4d5a
virusname prefix: (null)
virusnames: 0
bytecode triggered on: files matching logical signature
number of functions: 2
number of types: 25
number of global constants: 13
number of debug nodes: 0
bytecode APIs used:
read
, seek, setvirusname
> clambc print_flag.cbc --input
file
.bin --debug
> clambc --help
...
--printbcir -c Print IR of bytecode signature
...
> clambc print_flag.cbc -c > bytecode.txt
> clambc print_flag.cbc --info
Bytecode
format
functionality level: 6
Bytecode metadata:
compiler version: 0.105.0
compiled on: (1719557581) Fri Jun 28 14:53:01 2024
compiled by:
target exclude: 0
bytecode
type
: logical only
bytecode functionality level: 0 - 0
bytecode logical signature: PRINT_FLAG.{Fake,Real};Engine:56-255,Target:0;0;0:4d5a
virusname prefix: (null)
virusnames: 0
bytecode triggered on: files matching logical signature
number of functions: 2
number of types: 25
number of global constants: 13
number of debug nodes: 0
bytecode APIs used:
read
, seek, setvirusname
> clambc print_flag.cbc --input
file
.bin --debug
found
25
extra types of
89
total, starting at tid
69
TID KIND INTERNAL
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
65
: DPointerType i8
*
66
: DPointerType i16
*
67
: DPointerType i32
*
68
: DPointerType i64
*
......
found
25
extra types of
89
total, starting at tid
69
TID KIND INTERNAL
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
65
: DPointerType i8
*
66
: DPointerType i16
*
67
: DPointerType i32
*
68
: DPointerType i64
*
......
########################################################################
####################### Function id 0 ################################
########################################################################
########################################################################
####################### Function id 0 ################################
########################################################################
found a total of
13
globals
GID
ID
VALUE
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
0
[
0
]: i0 unknown
1
[
1
]: [
32
x i8] unknown
2
[
2
]: [
396
x i8] unknown
......
5
[
5
]: i8
*
unknown
......
found a total of
13
globals
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)