https://dreamhack.io/wargame/challenges/1613
insane python
I love Python, but I hate Python because it has secrets. :(
dreamhack.io
파이썬으로 작성된 웹 서버에 존재하는 취약점을 찾는 문제같다. 일단 사이트로 들어가보자.
아무것도 뜨지 않는다. 서버 코드를 확인해야 될 듯 싶다.
code
Dockerfile, template 파일들은 그닥 중요하지 않다.
app.py
from flask import Flask, render_template, request
import json
import sys
import io
app = Flask(__name__)
CONFIG = {
"SECRET": "DH{fake-fake-fake}"
}
class DataConfig(object):
def __init__(self, name):
self.name = name
def print_data(format_string, config_data):
return format_string.format(config_data=config_data)
config_data = DataConfig("How can I get the flag?")
@app.route('/',methods=['GET'])
def page1():
payload = request.args.get('body')
sys.stdin = io.StringIO(payload)
result = print_data(sys.stdin.readline(), config_data)
return render_template('index.html', value=result)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8080)
Flask 프레임워크를 이용하여 웹 사이트를 구성하였고, config의 secret 값을 구해내는 문제인 듯 하다.
payload = request.args.get('body')
sys.stdin = io.StringIO(payload)
result = print_data(sys.stdin.readline(), config_data)
return render_template('index.html', value=result)
'/' 경로에서 get 요청으로 body 파라미터 값을 받아와서 payload 변수에 저장하고 있다.
(http://host3.dreamhack.games:10488/?body=사용자입력 이런 식으로 요청이 들어가면 저장한다. )
payload에 저장하고, 해당 payload에 저장된 변수값을 sys.stdin으로 설정한다.
sys.stdin.readline() 함수는 사용자 입력값을 한줄 한줄 읽어와서 반환하는데, 아마 나중에 입력값 반환하는 데 쓰일 듯 싶다.
이렇게 입력받은 값을 정의된 함수 print_data에서 처리한다.
그 후, print_data에서 반환한 값을 index.html과 함께 렌더링한다.
def print_data(format_string, config_data):
return format_string.format(config_data=config_data)
받아온 데이터를 str.format() 함수로 처리하고 그 결과값을 반환하고 있다.
str.format() 함수는 {} 문을 통해 객체의 속성에 접근할 수 있는 데, 예를 들어 {config_data.name} 이런 식으로 format 함수의 인자값에 넣게 될 경우 해당 변수에 접근해서 읽어오게 된다.
format 함수는 객체 내부 속성(attributes)은 물론, 글로벌 변수까지도 접근할 수 있다.
-- 공격 벡터!! --
class DataConfig(object):
def __init__(self, name):
self.name = name
config_data 객체는 DataConfig 클래스의 인스턴스로, config_data.name에 문자열 "How can I get the flag?" 가 저장되어 있다.
config_data = DataConfig("How can I get the flag?")
config_data 객체는 DataConfig 클래스의 인스턴스이므로, __init__ 메소드를 내장하고 있는데, 해당 메소드는 글로벌 네임스페이스(__globals__)에 접근할 수 있다.
아까 플래그가 저장되어 있을 것이라고 추측한 CONFIG 변수는 글로벌 네임스페이스에 존재하는 딕셔너리 중 하나로, 해당 딕셔너리의 SECRET 키에 플래그가 저장되어 있을 것으로 추측된다. format 함수 인자에 {}를 이용한 페이로드를 넣으면 플래그를 알아낼 수 있을 듯 하다.
글로벌 네임스페이스 : 모듈 단위로 존재하며, 모듈에서 선언된 모든 전역 변수와 함수가 저장되는 공간으로 파이썬 프로그램이 시작될 때 생성되고, 프로그램이 종료되면 소멸되며 모듈의 최상위 레벨에서 정의된 모든 변수, 함수, 클래스가 이 네임스페이스에 저장된다고 보면 된다.
예를들어,
이렇게 전역변수로 선언할 경우, CONFIG 변수는 글로벌 네임스페이스에 속하게 된다.CONFIG = {"SECRET": "DH{fake-fake-fake}"}
__globals__
파이썬 함수 객체의 속성으로, 해당 함수가 정의된 시점의 글로벌 네임스페이스를 참조한다.
즉, 함수가 정의될 때 존재했던 모든 전역 변수와 객체를 포함하는 딕셔너리 반환하는 메소드이다.
Exploit
코드에서 분석한 것과 같이, 받아온 데이터를 str.format() 함수로 처리하고 그 결과값을 반환하고 있다.
문제는 str.format() 함수는 {} 문을 통해 객체의 속성에 접근할 수 있다는 점에서 발생한다. 예를 들어 {config_data.name} 이런 식으로 format 함수의 인자값에 넣게 될 경우 해당 변수에 접근해서 읽어오게 된다.
format 함수는 객체 내부 속성(attributes)은 물론, 글로벌 변수까지도 접근할 수 있다. format 함수로 들어가는 인자값인 config_data는 DataConfig 클래스의 인스턴스로, 내장함수로 __init__을 가지고 있다.
__init__ 메소드는 글로벌 네임스페이스(global namespace)에 속한 메소드 중 하나로, 파이썬 함수의 특징(속성을 통한 상위 값 접근) 을 이용해 글로벌 네임스페이스에 존재하는 값들에 접근 가능하다.
config_data.__init__.__globals__ 이런 식으로 __globals__ 메소드에 접근하면, 해당 메소드를 이용해 글로벌 네임스페이스에 속한 딕셔너리 값들을 출력할 것이고, 여기서 플래그를 찾으면 될 듯 하다.
※ config_data 객체 구조
config_data (객체)
├── __init__ (DataConfig 클래스의 메서드)
├── __globals__ (딕셔너리 형태의 글로벌 네임스페이스)
├── CONFIG (딕셔너리)
├── SECRET ("DH{fake-fake-fake}")
config_data.__init__.__globals__[CONFIG][SECRET]
CONFIG 딕셔너리의 SECRET 키에 접근해서 value 값을 알아낸다는 의미, 이걸 {}로 감싸서 페이로드로 보내주자.
({}로 감싸는 이유는 {}로 감쌀 경우 format 함수가 알아서 안의 내용을 해석하고 그 결과를 출력하기 때문)
curl로 요청을 보낼텐데, {, }, [, ], |, <, > 등은 url 에서 사용 불가능한 문자이므로 url 인코딩을 해준 후 curl로 페이로드를 보내야 한다.
curl "http://host3.dreamhack.games:10488/?body=%7Bconfig_data.__init__.__globals__%5BCONFIG%5D%5BSECRET%5D%7D"
{ -> %7B
} -> %7D
[ -> %5B
] -> %5D
'보안 > Web hacking' 카테고리의 다른 글
[Dreamhack] LiteBoard (0) | 2025.01.04 |
---|---|
[Dreamhack] Safe Input (0) | 2025.01.02 |
[Dreamhack] Replace Trick! (0) | 2024.12.23 |
[Dreamhack] Not-only (1) | 2024.12.20 |
[Dreamhack] Baby - ai (0) | 2024.12.18 |