[Dreamhack] insane python
2024. 12. 26.

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 = {"SECRET": "DH{fake-fake-fake}"}​
이렇게 전역변수로 선언할 경우, CONFIG 변수는 글로벌 네임스페이스에 속하게 된다.

__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