pwnlib.encoders — Encoding Shellcode

pwnlib.encoders.encoder.alphanumeric(raw_bytes) str[source]

Encode the shellcode raw_bytes such that it does not contain any bytes except for [A-Za-z0-9].

Accepts the same arguments as encode().

pwnlib.encoders.encoder.encode(raw_bytes, avoid, expr, force) str[source]

Encode shellcode raw_bytes such that it does not contain any bytes in avoid or expr.

Parameters
  • raw_bytes (str) – Sequence of shellcode bytes to encode.

  • avoid (str) – Bytes to avoid

  • expr (str) – Regular expression which matches bad characters.

  • force (bool) – Force re-encoding of the shellcode, even if it doesn’t contain any bytes in avoid.

pwnlib.encoders.encoder.line(raw_bytes) str[source]

Encode the shellcode raw_bytes such that it does not contain any NULL bytes or whitespace.

Accepts the same arguments as encode().

pwnlib.encoders.encoder.null(raw_bytes) str[source]

Encode the shellcode raw_bytes such that it does not contain any NULL bytes.

Accepts the same arguments as encode().

pwnlib.encoders.encoder.printable(raw_bytes) str[source]

Encode the shellcode raw_bytes such that it only contains non-space printable bytes.

Accepts the same arguments as encode().

pwnlib.encoders.encoder.scramble(raw_bytes) str[source]

Encodes the input data with a random encoder.

Accepts the same arguments as encode().

Encoder to convert shellcode to shellcode that contains only ascii characters

class pwnlib.encoders.i386.ascii_shellcode.AsciiShellcodeEncoder(slop=20, max_subs=4)[source]

Pack shellcode into only ascii characters that unpacks itself and executes (on the stack)

The original paper this encoder is based on: https://julianor.tripod.com/bc/bypass-msb.txt

A more visual explanation as well as an implementation in C: https://vincentdary.github.io/blog-posts/polyasciishellgen-caezar-ascii-shellcode-generator/index.html#22-mechanism

Init

Parameters
  • slop (int, optional) – The amount esp will be increased by in the allocation phase (In addition to the length of the packed shellcode) as well as defines the size of the NOP sled (you can increase/ decrease the size of the NOP sled by adding/removing b’P’-s to/ from the end of the packed shellcode). Defaults to 20.

  • max_subs (int, optional) – The maximum amount of subtractions allowed to be taken. This may be increased if you have a relatively restrictive avoid set. The more subtractions there are, the bigger the packed shellcode will be. Defaults to 4.

__call__(raw_bytes, avoid=None, pcreg=None)[source]

Pack shellcode into only ascii characters that unpacks itself and executes (on the stack)

Parameters
  • raw_bytes (bytes) – The shellcode to be packed

  • avoid (set, optional) – Characters to avoid. Defaults to allow printable ascii (0x21-0x7e).

  • pcreg (NoneType, optional) – Ignored

Raises
  • RuntimeError – A required character is in avoid (required characters are characters which assemble into assembly instructions and are used to unpack the shellcode onto the stack, more details in the paper linked above \ - % T X P).

  • RuntimeError – Not supported architecture

  • ArithmeticError – The allowed character set does not contain two characters that when they are bitwise-anded with eachother their result is 0

  • ArithmeticError – Could not find a correct subtraction sequence to get to the the desired target value with the given avoid parameter

Returns

bytes – The packed shellcode

Examples

>>> context.update(arch='i386', os='linux')
>>> sc = b"\x83\xc4\x181\xc01\xdb\xb0\x06\xcd\x80Sh/ttyh/dev\x89\xe31\xc9f\xb9\x12'\xb0\x05\xcd\x80j\x17X1\xdb\xcd\x80j.XS\xcd\x801\xc0Ph//shh/bin\x89\xe3PS\x89\xe1\x99\xb0\x0b\xcd\x80"
>>> encoders.i386.ascii_shellcode.encode(sc)
b'TX-!!!!-"_``-~~~~P\\%!!!!%@@@@-!6!!-V~!!-~~<-P-!mha-a~~~P-!!L`-a^~~-~~~~P-!!if-9`~~P-!!!!-aOaf-~~~~P-!&!<-!~`~--~~~P-!!!!-!!H^-+A~~P-U!![-~A1~P-,<V!-~~~!-~~~GP-!2!8-j~O~P-!]!!-!~!r-y~w~P-c!!!-~<(+P-N!_W-~1~~P-!!]!-Mn~!-~~~<P-!<!!-r~!P-~~x~P-fe!$-~~S~-~~~~P-!!\'$-%z~~P-A!!!-~!#!-~*~=P-!7!!-T~!!-~~E^PPPPPPPPPPPPPPPPPPPPP'
>>> avoid = {'\x00', '\x83', '\x04', '\x87', '\x08', '\x8b', '\x0c', '\x8f', '\x10', '\x93', '\x14', '\x97', '\x18', '\x9b', '\x1c', '\x9f', ' ', '\xa3', '\xa7', '\xab', '\xaf', '\xb3', '\xb7', '\xbb', '\xbf', '\xc3', '\xc7', '\xcb', '\xcf', '\xd3', '\xd7', '\xdb', '\xdf', '\xe3', '\xe7', '\xeb', '\xef', '\xf3', '\xf7', '\xfb', '\xff', '\x80', '\x03', '\x84', '\x07', '\x88', '\x0b', '\x8c', '\x0f', '\x90', '\x13', '\x94', '\x17', '\x98', '\x1b', '\x9c', '\x1f', '\xa0', '\xa4', '\xa8', '\xac', '\xb0', '\xb4', '\xb8', '\xbc', '\xc0', '\xc4', '\xc8', '\xcc', '\xd0', '\xd4', '\xd8', '\xdc', '\xe0', '\xe4', '\xe8', '\xec', '\xf0', '\xf4', '\xf8', '\xfc', '\x7f', '\x81', '\x02', '\x85', '\x06', '\x89', '\n', '\x8d', '\x0e', '\x91', '\x12', '\x95', '\x16', '\x99', '\x1a', '\x9d', '\x1e', '\xa1', '\xa5', '\xa9', '\xad', '\xb1', '\xb5', '\xb9', '\xbd', '\xc1', '\xc5', '\xc9', '\xcd', '\xd1', '\xd5', '\xd9', '\xdd', '\xe1', '\xe5', '\xe9', '\xed', '\xf1', '\xf5', '\xf9', '\xfd', '\x01', '\x82', '\x05', '\x86', '\t', '\x8a', '\r', '\x8e', '\x11', '\x92', '\x15', '\x96', '\x19', '\x9a', '\x1d', '\x9e', '\xa2', '\xa6', '\xaa', '\xae', '\xb2', '\xb6', '\xba', '\xbe', '\xc2', '\xc6', '\xca', '\xce', '\xd2', '\xd6', '\xda', '\xde', '\xe2', '\xe6', '\xea', '\xee', '\xf2', '\xf6', '\xfa', '\xfe'}
>>> sc = shellcraft.echo("Hello world") + shellcraft.exit()
>>> ascii = encoders.i386.ascii_shellcode.encode(asm(sc), avoid)
>>> ascii += asm('jmp esp') # just for testing, the unpacker should also run on the stack
>>> ELF.from_bytes(ascii).process().recvall()
b'Hello world'
_calc_subtractions(last, target, vocab)[source]
Given target and last, return a list of integers that when

subtracted from last will equal target while only constructing integers from bytes in vocab

int_size is taken from the context

Parameters
Raises

ArithmeticError – If a sequence of subtractions could not be found

Returns

List[bytearray] – List of numbers that would need to be subtracted from last to get to target

Examples

>>> context.update(arch='i386', os='linux')
>>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
>>> print(encoders.i386.ascii_shellcode.encode._calc_subtractions(bytearray(b'\x10'*4), bytearray(b'\x11'*4), vocab))
[bytearray(b'!!!!'), bytearray(b'`___'), bytearray(b'~~~~')]
>>> print(encoders.i386.ascii_shellcode.encode._calc_subtractions(bytearray(b'\x11\x12\x13\x14'), bytearray(b'\x15\x16\x17\x18'), vocab))
[bytearray(b'~}}}'), bytearray(b'~~~~')]
_find_negatives(vocab)[source]

Find two bitwise negatives in the vocab so that when they are and-ed the result is 0.

int_size is taken from the context

Parameters

vocab (bytearray) – Allowed characters

Returns

Tuple[int, int] – value A, value B

Raises

ArithmeticError – The allowed character set does not contain two characters that when they are bitwise-and-ed with eachother the result is 0

Examples

>>> context.update(arch='i386', os='linux')
>>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
>>> a, b = encoders.i386.ascii_shellcode.encode._find_negatives(vocab)
>>> a & b
0
_get_allocator(size, vocab)[source]

Allocate enough space on the stack for the shellcode

int_size is taken from the context

Parameters
  • size (int) – The allocation size

  • vocab (bytearray) – Allowed characters

Returns

bytearray – The allocator shellcode

Examples

>>> context.update(arch='i386', os='linux')
>>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
>>> encoders.i386.ascii_shellcode.encode._get_allocator(300, vocab)
bytearray(b'TX-!!!!-!_``-t~~~P\\%!!!!%@@@@')
_get_subtractions(shellcode, vocab)[source]

Covert the sellcode to sub eax and posh eax instructions

int_size is taken from the context

Parameters
  • shellcode (bytearray) – The shellcode to pack

  • vocab (bytearray) – Allowed characters

Returns

bytearray – packed shellcode

Examples

>>> context.update(arch='i386', os='linux')
>>> sc = bytearray(b'ABCDEFGHIGKLMNOPQRSTUVXYZ')
>>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
>>> encoders.i386.ascii_shellcode.encode._get_subtractions(sc, vocab)
bytearray(b'-(!!!-~NNNP-!=;:-f~~~-~~~~P-!!!!-edee-~~~~P-!!!!-eddd-~~~~P-!!!!-egdd-~~~~P-!!!!-eadd-~~~~P-!!!!-eddd-~~~~P')
class pwnlib.encoders.i386.xor.i386XorEncoder[source]

Generates an XOR decoder for i386.

>>> context.clear(arch='i386')
>>> shellcode = asm(shellcraft.sh())
>>> avoid = b'/bin/sh\xcc\xcd\x80'
>>> encoded = pwnlib.encoders.i386.xor.encode(shellcode, avoid)
>>> assert not any(c in encoded for c in avoid)
>>> p = run_shellcode(encoded)
>>> p.sendline(b'echo hello; exit')
>>> p.recvline()
b'hello\n'
>>> encoders.i386.xor.encode(asm(shellcraft.execve('/bin/sh')), avoid=bytearray([0x31]))
b'\xd9\xd0\xd9t$\xf4^\xfcj\x07Y\x83\xc6\x19\x89\xf7\xad\x93\xad1\xd8\xabIu\xf7\x00\x00\x00\x00h\x01\x01\x01\x00\x00\x00\x00\x01\x814$\x00\x00\x00\x00.ri\x01\x00\x00\x00\x00h/bi\x00\x00\x00\x01n\x89\xe30\x00\x01\x00\x00\xc90\xd2j\x00\x00\x00\x00\x0bX\xcd\x80'

Shellcode encoder class

Implements an architecture-specific shellcode encoder

__call__(raw_bytes, avoid, pcreg='')[source]

avoid(raw_bytes, avoid)

Parameters
  • raw_bytes (str) – String of bytes to encode

  • avoid (set) – Set of bytes to avoid

  • pcreg (str) – Register which contains the address of the shellcode. May be necessary for some shellcode.

arch = 'i386'[source]

Architecture which this encoder works on

blacklist = {'\x14', '$', '1', 'I', '^', 't', 'u', '\x83', '\x89', '\x93', '«', '\xad', 'Æ', 'Ø', 'Ù', 'ô', '÷', 'ü'}[source]

Blacklist of bytes which are known not to be supported

class pwnlib.encoders.i386.delta.i386DeltaEncoder[source]

i386 encoder built on delta-encoding.

In addition to the loader stub, doubles the size of the shellcode.

Example

>>> sc = pwnlib.encoders.i386.delta.encode(b'\xcc', b'\x00\xcc')
>>> e  = ELF.from_bytes(sc)
>>> e.process().poll(True)
-5

Shellcode encoder class

Implements an architecture-specific shellcode encoder

__call__(raw_bytes, avoid, pcreg='')[source]

avoid(raw_bytes, avoid)

Parameters
  • raw_bytes (str) – String of bytes to encode

  • avoid (set) – Set of bytes to avoid

  • pcreg (str) – Register which contains the address of the shellcode. May be necessary for some shellcode.

arch = 'i386'[source]

Architecture which this encoder works on

blacklist = {24, 36, 40, 94, 116, 117, 128, 131, 137, 147, 170, 172, 198, 208, 216, 217, 235, 244, 245, 247, 252}[source]

Blacklist of bytes which are known not to be supported

class pwnlib.encoders.arm.xor.ArmXorEncoder[source]

Generates an XOR decoder for ARM.

>>> context.clear(arch='arm')
>>> shellcode = asm(shellcraft.sh())
>>> avoid = b'binsh\x00\n'
>>> encoded = pwnlib.encoders.arm.xor.encode(shellcode, avoid)
>>> assert not any(c in encoded for c in avoid)
>>> p = run_shellcode(encoded)
>>> p.sendline(b'echo hello; exit')
>>> p.recvline()
b'hello\n'

Shellcode encoder class

Implements an architecture-specific shellcode encoder

__call__(raw_bytes, avoid, pcreg='')[source]

avoid(raw_bytes, avoid)

Parameters
  • raw_bytes (str) – String of bytes to encode

  • avoid (set) – Set of bytes to avoid

  • pcreg (str) – Register which contains the address of the shellcode. May be necessary for some shellcode.

arch = 'arm'[source]

Architecture which this encoder works on

blacklist = {'\x01', '\x03', '\x04', '\x07', '\x0c', '\x0f', '\x16', '\x1c', '$', '%', "'", '-', '/', '@', 'A', 'D', 'G', 'O', 'P', 'T', '_', '`', 'p', '\x80', '\x81', '\x84', '\x85', '\x87', '\x8f', '\x9f', '\xa0', '°', '½', 'Â', 'Æ', 'È', 'Ø', 'à', 'á', 'â', 'ã', 'å', 'ç', 'è', 'é', 'ê', '÷'}[source]

Blacklist of bytes which are known not to be supported

class pwnlib.encoders.mips.xor.MipsXorEncoder[source]

Generates an XOR decoder for MIPS.

>>> context.clear(arch='mips')
>>> shellcode = asm(shellcraft.sh())
>>> avoid = b'/bin/sh\x00'
>>> encoded = pwnlib.encoders.mips.xor.encode(shellcode, avoid)
>>> assert not any(c in encoded for c in avoid)
>>> p = run_shellcode(encoded)
>>> p.sendline(b'echo hello; exit')
>>> p.recvline()
b'hello\n'

Shellcode encoder class

Implements an architecture-specific shellcode encoder

__call__(raw_bytes, avoid, pcreg='')[source]

avoid(raw_bytes, avoid)

Parameters
  • raw_bytes (str) – String of bytes to encode

  • avoid (set) – Set of bytes to avoid

  • pcreg (str) – Register which contains the address of the shellcode. May be necessary for some shellcode.

arch = 'mips'[source]

Architecture which this encoder works on

blacklist = {1, 2, 3, 5, 8, 11, 12, 14, 16, 17, 20, 23, 24, 30, 33, 35, 36, 38, 39, 40, 43, 44, 49, 50, 64, 70, 73, 74, 83, 84, 88, 90, 96, 112, 128, 130, 134, 143, 163, 164, 165, 166, 175, 184, 192, 200, 206, 226, 235, 238, 239, 240, 248, 250, 251, 252, 253, 255}[source]

Blacklist of bytes which are known not to be supported