Flare-On8由FireEye公司于今年9月份至10月份期间举办,共10道题目。笔者从中选取了自己感兴趣的三道题目进行复现,下文是前两道题目复现过程中所作笔记,第三道题目涉及到的技术点及分析过程笔者会单独写一篇文章。
- 题目下载:Flare-On8 Challenges
- WriteUp:Flare-On 8 Challenge Solutions
0x01 Challenge 5:Flare Linux VM
题目描述如下:
压缩包中文件:
使用VMWare导入FLARE Linux VM.ovf
文件,在打开虚拟机之前将网络连接修改为”桥接模式”,方便后续通过scp
复制文件:
以root
身份登录,密码为flare。进入到Documents
目录下查看:
新建一TXT文件,过一段时间查看发现其扩展名由.txt
变为.txt.broken
。使用crontab -l
命令列出计划任务(若读者之前未接触过,可翻阅《鸟哥的Linux私房菜》第15章或是crontab(5) — Linux manual page):
在另一Linux虚拟机中使用scp
命令将该文件复制出来,查看其详细信息:
将getenv
函数返回值与”/Documents”字符串拼接成绝对路径传递给opendir
函数,调用readdir
函数遍历该目录下文件。 将d_name
字段值与.
及..
进行比较,之后判断后缀是否为.broken
:
如果上述条件均不满足,则进行加密:
加密函数只对原文件前0x400字节进行加密并写入.broken
文件(若不足0x400字节,最后.broken
文件亦是0x400字节大小,具体见下面对encrypt
函数的分析),之后调用remove
函数来删除原文件。
可以看到其加密函数采用变形RC4算法,区别在于最后的异或运算。使用crontab -r
命令删除计划任务,将Documents
目录下所有文件的.broken
扩展名去掉:
#!/bin/bash
for name in `ls *.broken`
do
mv $name ${name%.broken}
done
再执行一次/usr/lib/zyppe
即可完成解密。逐一查看解密后文件,发现下面四个文件内容是未加密的:
按照ugali.txt
文件给出提示,将Sausages.txt
和Spaghetti.txt
拖入CyberChef,执行Arithmetic/Logic中的Rotate Left运算:
“c3BhZ2hldHRp”是”spaghetti”的Base64编码:
按照shopping_list.txt
文件给出提示,进入/usr/bin
目录,发现存在dot
文件:
使用IDA打开:
计算输入的SHA256值,与b3c20caa9a1a82add9503e0eac43f741793d2031eb1c6e830274ed5ea36238bf
进行比较,相等才会中止循环。按照shopping_list.txt
文件给出顺序,查看reeses.txt
:
笔者一开始按照提示使用Reese's
作为Key去解密ice_cream.txt
,尝试数次结果均为乱码。按shopping_list.txt
文件给出顺序,下一文件应为banana_chips.txt
,结果如下:
其他以B开头文件通过异或运算解码出内容如下:
其他以R开头文件亦可使用Base64解码出内容:
笔者起初以为ENCODED_BYTE + 27 + NUMBER1 * NUMBER2 - NUMBER3
公式中3个NUMBER
是前边解码出的三个字节,但尝试后发现并不是,而是环境变量:
代入三个环境变量,公式化简为ENCODED_BYTE-4
。笔者同时查看了.bashrc
文件:
使用公式解密ice_cream.txt
,iced_coffee.txt
及instant_noodles.txt
:
笔者在解码出ice_cream.txt
结果时,不知道其内容有何作用,等解码出instant_noodles.txt
结果时回看,发现每个数字对应字母各不相同,那么0xMS——0x64,iced_coffee.txt
结果中给出的SREFBE
——493513。以493513作为RC4算法的Key去解密nachos.txt
,natillas.txt
及nutella.txt
:
根据提示进行搜索,算法应使用Bifid Cipher:
Key应使用eggs
:
解密以D开头文件:
根据提示,于Documents目录下看到隐藏文件.daiquiris.txt
,使用Bifid Cipher解密:
搜索Giovan Battista Bellaso相关,确定下一步应使用维吉尼亚密码(Key是microwaves):
解密以O开头文件:
根据文件给出的推特账号进行搜索:
从中提取出AES解密所需Key及IV:
解密以T开头文件:
/etc/Quijote.txt
中内容来自《堂吉诃德》,使用在线西班牙语词频分析工具分析其中出现的独特单词个数:
将所有十六进制ASCII码转换成字符:
输入dot
,得到Flag:
这道题目前面部分对勒索软件zyppe
的分析是很有意思的,笔者之前未接触过Linux平台的恶意软件分析。后面部分环环相扣,梳理清楚逻辑之后按部就班使用CyberChef解密即可得出Flag,难度偏易。
0x02 Challenge 7:spel
题目只给了一个PE文件,查看基本信息:
拖入IDA查看,分析了很长一段时间才完成,其函数数量非常多:
放入沙箱中,没有检测出有效信息:
运行界面:
关闭之后,可以看到其仍在后台运行:
看到官方WriteUp上提到capa,之前一直没有使用过该工具。按照capa explorer documentation给出步骤搭建好环境,capa rules下载链接文中已经给出,下载到本地后于Settings——capa rules path一栏中填写即可。capa.exe spel.exe
部分输出结果:
将上面结果与IDA插件capa explorer分析结果结合,在Search一栏中输入”stackstring”,定位到相关函数后勾选”Limit results to current function”:
切换模式查看,发现是很长一段向栈中写值指令,直接跳转到0x14017973B处向上回溯:
可以看到总长度为0x2ED2D,之后调用VirtualAllocExNuma
函数申请同样大小且具有PAGE_EXECUTE_READWRITE权限的一块内存,并将内容复制到新内存块中,最终跳转到新内存块执行:
初始化部分寄存器之后跳转到Offset 0x40处执行:
以往笔者遇到下图向栈中写字符串操作,是通过动调在合适指令处设断点后观察内存:
在看官方WriteUp时,看到其提及FLOSS与Ironstrings两个工具:
- FLOSS:Github Releases
- Ironstrings:Github
关于Ironstrings的安装可以参考FLARE Script Series: Recovering Stackstrings Using Emulation with ironstrings一文。
以--no-static-strings
参数运行FLOSS:
执行ironstrings.py脚本:
之后通过RCX传递Hash值给sub_A1C
函数获取LdrLoadDll
与LdrGetProcedureAddress
调用地址:
将stackstring传递给上述两个函数获取地址并调用之,Offset 0xB28处存储一PE文件。GetNativeSystemInfo
判断当前运行环境,获取PE文件Image Optional Header中ImageBase及SizeOfImage字段值后调用VirtualAlloc
函数申请内存空间(第一次以ImageBase作为lpAddress
,申请失败则置为NULL)。接下来根据各区段VirtualAddress,PointerToRawData及SizeOfRawData加载区段至申请内存空间中,根据重定位表进行重定位。调用VirtualProtect
设置各区段flNewProtect
值,通过RtlAddFunctionTable
函数设置异常,最终通过PE文件Image Optional Header中AddressOfEntryPoint字段定位入口点完成加载。
关于以上描述过程使用的Reflective DLL Injection技术可参阅DOUBLEPULSAR Usermode Analysis: Generic Reflective DLL Loader一文。
将其加载DLL Dump出来之后,capa检测到其.data区段中存有另一PE文件:
从EntryPoint执行到DllMain,传递PE文件存储地址给加载函数进行加载:
之后调用sub_27327D0
获取其导出函数Start地址进行调用:
首先是异或解密stackstring:
其流程是申请一块内存—>向栈中写值—>XOR解密—>复制至申请内存空间,官方WriteUp中给出了解密IDAPython脚本,笔者分析时直接在函数结束处设置断点,观察内存内容:
官方WriteUp中提到一个IDA Plugin——Shellcode Hashes,笔者觉得很好用,可以提高静态分析的效率和体验。该插件与上文提及的Ironstrings都位于flare-ida中,将shellcode_hashes_search_plugin.py与flare
目录一同复制到IDA的Plugins
目录之后即可使用,选择\flare-ida\shellcode_hashes
目录下的sc_hashes.db
数据库文件即可:
前者是上文提及sub_A1C
函数,后者是最后Dump出来的DLL中函数。
接下来调用sub_180001A40
,sub_180001990
会调用该函数两次——第一次传递给它的第二个参数值为1,第二次传递有两种情况:2或是8,若Filename为”Spell.EXE”则传递2,否则传递8。第一次调用会解密一串字符串,获取Filename及加载名为PNG的资源:
sub_180001A40
保存获取到的相关信息时结构如下(后面还有一串字符inactive
,没有放在图中):
sub_180002E60
函数判断Filename是否等于”Spell.EXE”:
若不等返回0,那么传递给sub_180001A40
函数第二个参数值为8:
直接退出:
将程序命名为”Spell.EXE”之后再次调试,便可进入到sub_180001A40
函数处理第二个参数为2的情形:
第二个参数为2时会调用sub_180001F80
,该函数会向inactive[.]flare-on[.]com发起连接,根据接收到的不同命令执行不同的操作。后续在看官方WriteUp时,其提到FakeNet-NG工具,笔者在虚拟机里按照FakeNet Genie: Improving Dynamic Malware Analysis with Cheat Codes for FakeNet-NG一文进行了配置,之后再次进行调试,分析出sub_180002070
函数(由sub_180001F80
调用)会处理三种命令:
- exe:调用
sub_180002410
- run:调用
sub_180002590
- flare-on.com:返回1
sample_custom_response.ini
中TcpRawFile字段为同目录下文件,该文件存储要返回的命令:
当该函数返回1时,会进入到之后的sub_180002A20
函数,设置注册表键值。接下来调用sub_180002F70
,若返回1则进入sub_180002730
设置另一注册表键值。第一个设置的键值:
该结果由”flare-on.com”与0x1B8A7E991DC19F1415891D8A
异或而来:
sub_180002730
通过switch将Flag与0xFAE58ED80D859C9B8D87ACD7A7B7A4E2C3C1A806C29633
进行异或:
之后再次调用sub_180002A20
设置第二个键值:
这道题目很贴合实际的样本分析,官方WriteUp中有这样一段话可以概括之:This challenge was inspired by multiple malware samples we’ve analyzed over the last year。其使用的Reflective DLL Injection技术,stackstring,API Hashes,混淆字符串直到使用时才解密,C2等笔者在之前的样本分析过程都曾遇到过,而此题将其都融合在一个里面,是很有意思的一道题目。在分析过程中,结合官方WriteUp学习了几个新工具或是IDA Plugin(capa,FLOSS,Ironstrings,shellcode_hashes,FakeNet-NG)的使用,方便以后的样本分析。
0x03 参阅链接
- crontab(5) — Linux manual page
- getenv(3) — Linux manual page
- readdir(3) — Linux manual page
- Linux编程—readdir
- RC4—Wikipedia
- 教你三招快速文件批量重命名方法
- Bifid cipher—Wikipedia
- FireEye’s Open-Source Tool – CAPA to Identify Malware Capabilities
- DOUBLEPULSAR Usermode Analysis: Generic Reflective DLL Loader
- Using Precalculated String Hashes when Reverse Engineering Shellcode
- FakeNet Genie: Improving Dynamic Malware Analysis with Cheat Codes for FakeNet-NG