Gallopsled / Gallopsled/pwntools
Yet another shellcode encoder bug in encoders.encoder.printable.
- Dominant language
- Python
- Stars
- 13.7k
- Forks
- 1.9k
- Avg merge
- 6d 23h
- Merged PRs (30d)
- 3
Description
I was trying to create ascii shellcode through `encoders.encoder.printable` which was producing wrong results but even after seeing the fixes to encoder in #1923 still the `encoders.encoder.printable` was misbehaving.
In i386 arch i tried this code:
```python3
from pwn import *
context.arch = 'i386'
_asm_ = asm(shellcraft.i386.linux.sh())
print(f'ascii_shellcode = {encoders.i386.ascii_shellcode.encode(_asm_)}')
print(f'ascii_shellcode through printable = {encoders.encoder.printable(_asm_)}')
Results:
ascii_shellcode = b"TX-&!!!-~_``-~~~~P\\%!!!!%@@@@-!!6!-V!~!-~e~P-7&!!-~~!I-~~.~P-!!!!-!~!!-6~U=P-^!!!-~!`!-~>~PP-!6!!-a~Vf-~~~~P-!!!<-&'k~-~~~~P-\\YY!-~~~}PPPPPPPPPPPPPPPPPPPPP"
[ERROR] No encoders for i386 which can avoid '[^\\x21-\\x7e]' for
00000000 6a 68 68 2f 2f 2f 73 68 2f 62 69 6e 89 e3 68 01 │jhh/│//sh│/bin│··h·│
00000010 01 01 01 81 34 24 72 69 01 01 31 c9 51 6a 04 59 │····│4$ri│··1·│Qj·Y│
00000020 01 e1 51 89 e1 31 d2 6a 0b 58 cd 80 │··Q·│·1·j│·X··│
0000002c
```
In a patched version of pwntools where #1923 is applied and some other modifications i found that again `encoders.encoder.printable` was giving wrong results because of a minor mistake in [AsciiShellcodeEncoder](https://github.com/Gallopsled/pwntools/blob/dev/pwnlib/encoders/i386/ascii_shellcode.py). The `arch` variable of Encoder is not being set which results to `AsciiShellcodeEncoder` not being appened to i386 arch but instead to `None`. When `encoders.encoder.printable` is being called in i386 context `AsciiShellcodeEncoder` is not applied. The patch is trivial just add arch = 'i386' to `AsciiShellcodeEncoder`.
In addition because i don't want to open a new issue, i will address another one bug in [i386XorEncoder](https://github.com/Gallopsled/pwntools/blob/dev/pwnlib/encoders/i386/xor.py#L72) here. In [line 72](https://github.com/Gallopsled/pwntools/blob/dev/pwnlib/encoders/i386/xor.py#L72) there is no check for the result of `xor_pair`. If we call for example `encode` with a lot of restrictions on bad-chars, `xor_pair` may be not be able to find a satisfying pair to do the job and so returns `None`. This may be happen through `encoders.encoder.printable` because it enforces to many restrictions to `encode`.
```python3
from pwn import *
context.arch = 'i386'
_asm_ = asm(shellcraft.i386.linux.sh())
# Same avoid as printable.
avoid = b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
encoders.i386.xor.encode(_asm_, avoid)
...
a, b = xor_pair(raw_bytes, avoid)
TypeError: cannot unpack non-iterable NoneType object
```
After testing the above bug to arm arch, i found that the same bug is also present to [ArmXorEncoder](https://github.com/Gallopsled/pwntools/blob/dev/pwnlib/encoders/arm/xor.py#L51).
```python3
from pwn import *
context.arch = 'arm'
_asm_ = asm(shellcraft.arm.linux.sh())
avoid = set(encoders.encoder.all_chars) - set(encoders.arm.xor.ArmXorEncoder.blacklist)
avoid = set(map(ord, avoid))
encoders.arm.xor.encode(_asm_, avoid)
Results:
key, xordata = xor_key(raw_bytes, avoid, size=1)
TypeError: cannot unpack non-iterable NoneType object
```
As a workaround for the above two problems we could check for `xor_pair` and `xor_key` if they return `None` and if they do we can raise a `RuntimeError`? and continue from `encode` when we hit a `RuntimeError` as we currently do for `NotImplementedError` [here](https://github.com/Gallopsled/pwntools/blob/dev/pwnlib/encoders/encoder.py#L84)?
Contributor guide
Assessment
This issue has not been assessed yet.