Skip to content

Commit 9e510d9

Browse files
committed
some updates to code.
clean up the code separate directory for case_studies add some files for case_studies
1 parent a5bd505 commit 9e510d9

20 files changed

+1427
-67
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import subprocess
2+
import os
3+
import re
4+
5+
SYSCALLS = ["mprotect", "mmap", "execve", "execveat", "mremap", "ptrace"]
6+
7+
def run_command(cmd, require_sudo=False):
8+
"""Run command, adding sudo only if required and not already root"""
9+
if require_sudo and os.geteuid() != 0 and cmd[0] != "sudo":
10+
cmd.insert(0, "sudo")
11+
return subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode()
12+
13+
def check_apparmor():
14+
print("[AppArmor ] Checking AppArmor status...")
15+
try:
16+
output = run_command(["aa-status"]) # no sudo needed
17+
enforce_profiles = re.search(r"(\d+)\s+profiles are in enforce mode", output)
18+
python_profile = "/usr/local/bin/python3.14" in output
19+
if enforce_profiles:
20+
print(f"[AppArmor ] ✅ {enforce_profiles.group(1)} profiles in enforce mode")
21+
else:
22+
print("[AppArmor ] ❌ Not in enforce mode")
23+
print("[AppArmor profile] ✅ Loaded for Python3.14"
24+
if python_profile else "[AppArmor profile] ❌ Not loaded for Python3.14")
25+
except Exception as e:
26+
print(f"[AppArmor ] ❌ Error: {e}")
27+
28+
def check_seccomp():
29+
print("[Seccomp ] Checking seccomp mode...")
30+
try:
31+
with open("/proc/self/status") as f:
32+
for line in f:
33+
if line.startswith("Seccomp:"):
34+
mode = int(line.strip().split()[1])
35+
if mode == 0:
36+
print("[Seccomp ] ✅ Disabled (mode 0)")
37+
elif mode == 1:
38+
print("[Seccomp ] ⚠️ Strict mode (1)")
39+
elif mode == 2:
40+
print("[Seccomp ] ⚠️ Filter mode (2)")
41+
else:
42+
print(f"[Seccomp ] ❓ Unknown mode: {mode}")
43+
return
44+
print("[Seccomp ] ❌ Not found")
45+
except Exception as e:
46+
print(f"[Seccomp ] ❌ Error: {e}")
47+
48+
def check_auditd():
49+
print("[Auditd ] Checking auditd status...")
50+
try:
51+
status = subprocess.check_output(["systemctl", "is-active", "auditd"],
52+
stderr=subprocess.STDOUT).decode().strip()
53+
if status == "active":
54+
print("[Auditd ] ✅ Running (systemd)")
55+
else:
56+
print(f"[Auditd ] ❌ Status: {status}")
57+
except subprocess.CalledProcessError:
58+
print("[Auditd ] ❌ Not running or not installed")
59+
60+
def apply_syscall_rules():
61+
print("[Auditd rules ] ➕ Applying missing syscall rules...")
62+
for syscall in SYSCALLS:
63+
try:
64+
run_command(["auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", syscall], require_sudo=True)
65+
print(f"✅ Rule added for: {syscall}")
66+
except subprocess.CalledProcessError as e:
67+
print(f"❌ Failed to add rule for {syscall}: {e}")
68+
except Exception as e:
69+
print(f"❌ Exception adding rule for {syscall}: {e}")
70+
71+
def check_auditd_rules():
72+
print("[Auditd rules ] Checking syscall audit rules...")
73+
try:
74+
rules_output = run_command(["auditctl", "-l"], require_sudo=True)
75+
syscall_rules = [line for line in rules_output.splitlines() if re.search(r"-S\s+\w+", line)]
76+
77+
if syscall_rules:
78+
print(f"[Auditd rules ] ✅ Found {len(syscall_rules)} syscall rules")
79+
else:
80+
print("[Auditd rules ] ❌ No syscall rules — applying now")
81+
apply_syscall_rules()
82+
83+
except subprocess.CalledProcessError as e:
84+
print(f"[Auditd rules ] ❌ Error: {e.output.decode().strip()}")
85+
except Exception as e:
86+
print(f"[Auditd rules ] ❌ Exception: {e}")
87+
88+
if __name__ == "__main__":
89+
print("=== [ Trampoline Overwrite Experiment Pre-Check + Auto Patch ] ===")
90+
check_apparmor()
91+
check_seccomp()
92+
check_auditd()
93+
check_auditd_rules()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <tunables/global>
2+
3+
/usr/local/bin/python3.14 {
4+
include <abstractions/base>
5+
deny capability,
6+
allow capability dac_override,
7+
allow capability dac_read_search,
8+
allow capability sys_admin,
9+
10+
deny /proc/** r,
11+
deny /dev/** rw,
12+
13+
/usr/local/bin/python3.14 rix,
14+
/usr/local/lib/python3.14/** rix,
15+
/usr/local/lib/python3.14/lib-dynload/** r,
16+
/users/seehwan/cpython/** rix,
17+
/users/seehwan/python-arm64/bin/** rix,
18+
/users/seehwan/python-arm64/lib/python3.14/** r,
19+
/users/seehwan/python-arm64/lib/python3.14/site-packages/** rix,
20+
/users/seehwan/cpython/jit_poc/** rix,
21+
}
22+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/bin/bash
2+
3+
# Exit on any error
4+
set -e
5+
6+
echo "Step 1: Building Python extensions..."
7+
python3.14 setup.py build_ext --inplace
8+
if [ $? -ne 0 ]; then
9+
echo "Error: Failed to build Python extensions"
10+
exit 1
11+
fi
12+
13+
echo "Step 2: Copying .so files to ../run_dir..."
14+
# Create run_dir if it doesn't exist
15+
mkdir -p ../run_dir
16+
cp *.so ../run_dir/
17+
cp *.bin ../run_dir/
18+
if [ $? -ne 0 ]; then
19+
echo "Error: Failed to copy .so files"
20+
exit 1
21+
fi
22+
23+
echo "Step 3: Copying AppArmor profile..."
24+
sudo cp apparmor_profile_usr.local.bin.python3.14 /etc/apparmor.d/usr.local.bin.python3.14
25+
if [ $? -ne 0 ]; then
26+
echo "Error: Failed to copy AppArmor profile"
27+
exit 1
28+
fi
29+
30+
echo "Step 4: Reloading AppArmor profile..."
31+
sudo apparmor_parser -r /etc/apparmor.d/usr.local.bin.python3.14
32+
if [ $? -ne 0 ]; then
33+
echo "Error: Failed to reload AppArmor profile"
34+
exit 1
35+
fi
36+
37+
echo "Step 5: Running security environment check..."
38+
python3.14 00.check_security_env.py
39+
if [ $? -ne 0 ]; then
40+
echo "Error: Security environment check failed"
41+
exit 1
42+
fi
43+
44+
echo "It's ready to go!"

case_studies/00.set_env/jitleak.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#define PY_SSIZE_T_CLEAN
2+
#include <Python.h>
3+
#include <stdio.h>
4+
#include <stdint.h>
5+
6+
// executor 구조 추정
7+
typedef struct {
8+
PyObject_HEAD
9+
void* unused0;
10+
void* jit_code; // native function pointer
11+
// ... 생략된 필드들
12+
} _PyExecutorObject;
13+
14+
// _Py_GetExecutor 선언 (CPython 내부 API)
15+
extern _PyExecutorObject* _Py_GetExecutor(PyCodeObject *code, int offset);
16+
17+
static PyObject* leak_executor_jit(PyObject* self, PyObject* args) {
18+
PyObject* py_func;
19+
20+
if (!PyArg_ParseTuple(args, "O", &py_func)) {
21+
return NULL;
22+
}
23+
24+
if (!PyFunction_Check(py_func)) {
25+
PyErr_SetString(PyExc_TypeError, "Expected a Python function");
26+
return NULL;
27+
}
28+
29+
PyCodeObject* code = (PyCodeObject*)PyFunction_GetCode(py_func);
30+
printf("[*] code object @ %p\n", code);
31+
32+
// 여러 바이트코드 offset에서 executor를 시도해봄
33+
for (int offset = 0; offset < 512; offset += 2) {
34+
_PyExecutorObject* executor = _Py_GetExecutor(code, offset);
35+
if (executor && executor->jit_code) {
36+
printf("[*] executor found at offset %d\n", offset);
37+
printf("[*] executor @ %p\n", executor);
38+
printf("[*] executor->jit_code @ %p\n", executor->jit_code);
39+
PyErr_Clear();
40+
return PyLong_FromVoidPtr(executor->jit_code);
41+
}
42+
}
43+
44+
PyErr_SetString(PyExc_RuntimeError, "No JIT code found in any offset");
45+
return NULL;
46+
}
47+
48+
static PyMethodDef Methods[] = {
49+
{"leak_executor_jit", leak_executor_jit, METH_VARARGS, "Leak executor JIT native code"},
50+
{NULL, NULL, 0, NULL}
51+
};
52+
53+
static struct PyModuleDef mod = {
54+
PyModuleDef_HEAD_INIT,
55+
"jitexecleak",
56+
NULL,
57+
-1,
58+
Methods
59+
};
60+
61+
PyMODINIT_FUNC PyInit_jitexecleak(void) {
62+
return PyModule_Create(&mod);
63+
}
64+

case_studies/00.set_env/jitremap.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// jitremap.c - C 확장 모듈: JIT code cache에 대해 mprotect 시도
2+
3+
#define PY_SSIZE_T_CLEAN
4+
#include <Python.h>
5+
#include <sys/mman.h>
6+
#include <stdint.h>
7+
#include <errno.h>
8+
9+
static PyObject* jit_remap(PyObject* self, PyObject* args) {
10+
unsigned long addr;
11+
size_t size;
12+
int prot;
13+
14+
if (!PyArg_ParseTuple(args, "kki", &addr, &size, &prot)) {
15+
return NULL;
16+
}
17+
18+
int result = mprotect((void*)addr, size, prot);
19+
if (result != 0) {
20+
return Py_BuildValue("(i,s)", result, strerror(errno));
21+
}
22+
23+
Py_RETURN_TRUE;
24+
}
25+
26+
static PyMethodDef JitRemapMethods[] = {
27+
{"remap", jit_remap, METH_VARARGS, "Change memory protection of given address."},
28+
{NULL, NULL, 0, NULL}
29+
};
30+
31+
static struct PyModuleDef jitremapmodule = {
32+
PyModuleDef_HEAD_INIT,
33+
"jitremap", // name of module
34+
"JIT remapping module for testing mprotect.",
35+
-1,
36+
JitRemapMethods
37+
};
38+
39+
PyMODINIT_FUNC PyInit_jitremap(void) {
40+
return PyModule_Create(&jitremapmodule);
41+
}
42+

case_studies/00.set_env/ok.bin

34 Bytes
Binary file not shown.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#define _GNU_SOURCE
2+
#include <stddef.h> // 👈 offsetof
3+
#include <linux/seccomp.h>
4+
#include <linux/filter.h>
5+
#include <linux/audit.h>
6+
#include <linux/unistd.h>
7+
#include <sys/prctl.h>
8+
#include <sys/syscall.h>
9+
#include <unistd.h>
10+
#include <errno.h>
11+
#include <stdio.h>
12+
13+
int install_seccomp() {
14+
struct sock_filter filter[] = {
15+
// Load syscall number
16+
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
17+
18+
// If syscall == mprotect → kill
19+
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mprotect, 0, 1),
20+
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
21+
22+
// Otherwise → allow
23+
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
24+
};
25+
26+
struct sock_fprog prog = {
27+
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
28+
.filter = filter,
29+
};
30+
31+
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
32+
perror("prctl(NO_NEW_PRIVS)");
33+
return -1;
34+
}
35+
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
36+
perror("prctl(SECCOMP)");
37+
return -1;
38+
}
39+
40+
return 0;
41+
}

case_studies/00.set_env/setup.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from setuptools import setup, Extension
2+
3+
setup(
4+
name="jitexecleak",
5+
version="0.1",
6+
ext_modules=[Extension("jitexecleak", ["jitleak.c"])],
7+
)
8+
40 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)