https://github.com/davidbuchanan314/unsafe-python
A library to assist writing memory-unsafe code in "pure" python, without any imports (i.e. no ctypes etc.)
https://github.com/davidbuchanan314/unsafe-python
Last synced: 10 months ago
JSON representation
A library to assist writing memory-unsafe code in "pure" python, without any imports (i.e. no ctypes etc.)
- Host: GitHub
- URL: https://github.com/davidbuchanan314/unsafe-python
- Owner: DavidBuchanan314
- License: mit
- Created: 2020-12-05T15:42:54.000Z (about 5 years ago)
- Default Branch: main
- Last Pushed: 2025-02-06T17:06:02.000Z (11 months ago)
- Last Synced: 2025-03-28T10:09:27.750Z (10 months ago)
- Language: Python
- Homepage:
- Size: 47.9 KB
- Stars: 221
- Watchers: 3
- Forks: 6
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# unsafe-python
A library to assist writing memory-unsafe code in "pure" python, without any imports (i.e. no ctypes etc.)
Supports CPython 3.11 and below (3.12 support coming soon?)
**Note: This is a toy.** You probably shouldn't use it for anything serious (or anything at all, really).
# Core features:
- `addrof(obj)` - A trivial alias of the `id()` builtin.
- `fakeobj(addr)` - Allows for crafting fake heap objects.
- `getmem()` - Returns a bytearray view of the current process's virtual memory.
- `setrip(addr)` - Sets the RIP register. Argument passing etc. coming soon™.
- `do_rop(payload)` - Execute a ROP payload.
A trivial example, showing how to dereference a null pointer in pure python. The future is now!
```python
>>> import unsafe # (If "no imports" is a requirement, then you can just copy-paste the code)
>>> mem = unsafe.getmem()
>>> mem[0]
Segmentation fault (core dumped)
```
For a less trivial example, check out `shellcode_example_nocheats.py`, which uses ROP to
`mprotect` a shellcode buffer, and then jump into it.
# Why?
I don't know.
# How?
The CPython bytecode interpreter has documented memory-unsafety bug/features. Notably, the `LOAD_CONST` opcode (used to load a constant from the `co_consts` tuple) does not have any bounds checks, presumably for performance reasons. This has been used in the past to [execute arbitrary code](https://doar-e.github.io/blog/2014/04/17/deep-dive-into-pythons-vm-story-of-load_const-bug/) and [escape sandboxes](https://www.da.vidbuchanan.co.uk/blog/35c3ctf-collection-writeup.html). Until now, these exploits have relied on custom bytecode generation, which is inherently fragile because the bytecode specification changes between different versions and/or implementations of python.
This project uses CPython's code object introspection APIs, along with a heap grooming technique, in order to craft "vulnerable" code objects using CPython's own bytecode compiler. This technique allows us to craft fake python objects on the heap, similar to the "`fakeobj()`" primitive you might see in exploits for JavaScript engines. For example, we can craft a bytearray with a base address of 0 and a length of `SSIZE_MAX`, giving us read and write access to raw memory.
# TODO:
- Perform ROP gadget search without "cheating" by reading `/proc/self/maps`.
i.e. find a reliable way to leak libc base. (Edit: the buffered reader exploit linked below implements this)
# See Also:
An incomplete list of known CPython memory safety issues:
- https://pwn.win/2022/05/11/python-buffered-reader.html - https://github.com/kn32/python-buffered-reader-exploit/blob/master/exploit.py