Skip to content

Instantly share code, notes, and snippets.

@iglosiggio
Last active March 7, 2023 23:40
Show Gist options
  • Save iglosiggio/63d082a6c9ff4f12d195f164d6cf0d03 to your computer and use it in GitHub Desktop.
Save iglosiggio/63d082a6c9ff4f12d195f164d6cf0d03 to your computer and use it in GitHub Desktop.

Lets say you want to limit access to arbitrary code execution from a regular python runtime. What do you need to limit? This is a list of examples on how that is a futile mission (unless you REALLY want to limit the power of python).

Examples

  1. 01_using_os.py: The simplest thing, just do os.spawn
  2. 02_using_subprocess.py: The os module was deprecated a loooong time ago. Why don't we use a modern library?
  3. 03_using_ctypes.py: It's obvious to any well meaning programmer that os and subprocess are hairy modules. But do you remember that ctypes exists?
  4. 04_using_mmap.py: Woah! We can create executable memory FROM INSIDE python? That sounds incredible cursed.
  5. 05_using_open.py: ... just open? How? What do you mean with "/proc/self/mem bypasses memory protections"?

References

import os
os.system('bash -c "echo Hola mundo!"')
from subprocess import run
run(['bash', '-c', 'echo Hola mundo!'])
from ctypes import cdll, c_char_p
libc = cdll.LoadLibrary("libc.so.6")
libc.system(b'bash -c "echo Hola mundo!"')
from mmap import mmap, PROT_WRITE, PROT_EXEC, MAP_ANON, MAP_PRIVATE
from ctypes import CFUNCTYPE, c_void_p, addressof
mem = mmap(-1, 4096, prot=PROT_WRITE | PROT_EXEC, flags=MAP_ANON | MAP_PRIVATE)
payload = bytes([
# payload:
0x48, 0x8D, 0x05, 0x3F, 0x00, 0x00, 0x00, # lea rax, [rip+0x3F] # <filename>
0x48, 0x89, 0x05, 0x56, 0x00, 0x00, 0x00, # mov [rip+0x56], rax # <argv+0x00>
0x48, 0x8D, 0x05, 0x3B, 0x00, 0x00, 0x00, # lea rax, [rip+0x3B] # <dash_c>
0x48, 0x89, 0x05, 0x50, 0x00, 0x00, 0x00, # mov [rip+0x50], rax # <argv+0x08>
0x48, 0x8D, 0x05, 0x30, 0x00, 0x00, 0x00, # lea rax, [rip+0x30] # <echo_hola_mundo>
0x48, 0x89, 0x05, 0x4A, 0x00, 0x00, 0x00, # mov [rip+0x4A], rax # <argv+0x10>
0xB8, 0x3B, 0x00, 0x00, 0x00, # mov eax, 0x3B # sys_execve
0x48, 0x8D, 0x3D, 0x10, 0x00, 0x00, 0x00, # lea rdi, [rip+0x10] # <filename>
0x48, 0x8D, 0x35, 0x27, 0x00, 0x00, 0x00, # lea rsi, [rip+0x27] # <argv>
0x48, 0x8D, 0x15, 0x40, 0x00, 0x00, 0x00, # lea rdx, [rip+0x40] # <envp>
0x0F, 0x05, # syscall
# filename:
*b'/bin/bash', 0,
# dash_c:
*b'-c', 0,
# echo_hola_mundo:
*b'echo Hola mundo!', 0,
# argv:
0, 0, 0, 0, 0, 0, 0, 0, # arg[0]
0, 0, 0, 0, 0, 0, 0, 0, # arg[1]
0, 0, 0, 0, 0, 0, 0, 0, # arg[2]
0, 0, 0, 0, 0, 0, 0, 0, # sentinel
# envp:
0, 0, 0, 0, 0, 0, 0, 0, # sentinel
])
mem.write(payload)
fun_addr = addressof(c_void_p.from_buffer(mem))
fun_type = CFUNCTYPE(None)
fun = fun_type(fun_addr)
fun()
import sqlite3
for line in open("/proc/self/maps").readlines():
if line.find("libsqlite3") != -1 and line.find("r-xp") != -1:
sqlite_text = line
memory_range = sqlite_text[:sqlite_text.find(' ')]
sqlite_text_from, sqlite_text_to = memory_range.split('-')
sqlite_text_from = int(sqlite_text_from, 16)
sqlite_text_to = int(sqlite_text_to, 16)
sqlite_text_size = sqlite_text_to - sqlite_text_from
print("sqlite3 .text ranges from", hex(sqlite_text_from), "to", hex(sqlite_text_to))
payload = bytes([
# payload:
0x48, 0x83, 0xEC, 0x20, # sub rsp, 0x20 # char* argv[4]
0x48, 0x8D, 0x05, 0x3B, 0x00, 0x00, 0x00, # lea rax, [rip+0x3B] # &filename
0x48, 0x89, 0x04, 0x24, # mov [rsp+0x00], rax # argv[0] = &filename
0x48, 0x8D, 0x05, 0x3A, 0x00, 0x00, 0x00, # lea rax, [rip+0x3A] # &dash_c
0x48, 0x89, 0x44, 0x24, 0x08, # mov [rsp+0x08], rax # argv[1] = &dash_c
0x48, 0x8D, 0x05, 0x31, 0x00, 0x00, 0x00, # lea rax, [rip+0x31] # &echo_hola_mundo
0x48, 0x89, 0x44, 0x24, 0x10, # mov [rsp+0x10], rax # argv[2] = &echo_hola_mundo
0x48, 0x31, 0xC0, # xor rax, rax # NULL
0x48, 0x89, 0x44, 0x24, 0x18, # mov [rsp+0x18], rax # argv[3] = NULL
0xB8, 0x3B, 0x00, 0x00, 0x00, # mov eax, 0x3B # sys_execve
0x48, 0x8D, 0x3D, 0x0B, 0x00, 0x00, 0x00, # lea rdi, [rip+0x0B] # <filename>
0x48, 0x8D, 0x34, 0x24, # lea rsi, [rsp] # &argv
0x48, 0x8D, 0x54, 0x24, 0x18, # lea rdx, [rsp+0x18] # &argv[3:] (envp)
0x0F, 0x05, # syscall
# filename:
*b'/bin/bash', 0,
# dash_c:
*b'-c', 0,
# echo_hola_mundo:
*b'echo Hola mundo!', 0,
])
mem = open('/proc/self/mem', 'r+b')
mem.seek(sqlite_text_from)
mem.write(b'\x90' * sqlite_text_size)
mem.seek(sqlite_text_to - len(payload))
mem.write(payload)
mem.flush()
sqlite3.connect('Let the fun begin!')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment