728x90
반응형
Capture
Description
Brand new authentication server, zero security vulnerabilities
-----------------------------------------------------------------------
새로운 인증 서버, 보안 취약점 없음
SourceCode
FLAG = "byuctf{}"
class User:
def __init__(self, username, id):
self.username = username
self.id = id
def __eq__(self, other):
return self.id == other.id
def __hash__(self):
return hash(self.id)
ADMIN = User('admin', 1337)
print("Welcome to onboarding! I'm Jacob from HR, and I'm here to make your experience as seamless as possible joining the company")
print("Go ahead and tell me your name:")
name = input()
print("Welcome to the company, " + name)
print("We also give you a user id, but in an attempt to make this company feel like home, we've decided to give you a choice in that, too. Go ahead and choose that now:")
id_ = input()
if not all([i in '0987654321' for i in id_]):
print("That's not an id!")
quit()
id_ = int(id_)
if id_ == 1337:
print("Sorry, the admin already claimed that id, no can do")
quit()
YOURUSER = User(name, id_)
print("Okay, you're all set! Just head into your office. The admin's is right next door, but you can just ignore that")
print("""*You realize you have freedom of choice. Choose a door*
1) your office
2) the admin's office
""")
choice = int(input())
if choice == 1:
if hash(YOURUSER) == hash(YOURUSER):
print("Man, this is a nice office")
quit()
else:
print("Hey, HR, my key doesn't work yet!")
quit()
elif choice == 2:
if hash(YOURUSER) == hash(ADMIN):
print(FLAG)
quit()
else:
print("The HR guy tackles you to the ground for insolence")
quit()
Analysis
1. nc psycho.chal.cyberjousting.com 1353
name 과 id_ 값을 사용자에게 입력받도록 되어 있음
id_ 값이 숫자로 이루어지지 않은 경우
- "That's not an id!" 출력
id_ 값이 1337 일 경우
- "Sorry, the admin already claimed that id, no can do" 출력
즉, id_값이 숫자로 이루어져있으면서 1337이 아닌경우
- name, id_ 값을 User Class로 이동하여 id_ 값을 hash로 묶음
- 아래의 2개중에 한개를 선택하도록 되어 있음
- your office
- "Man, this is a nice office" 출력
- the admin's office
- 사용자의 Hash(YOURUSER) 값과 hash(ADMIN) 값을 비교
- 같을 경우 FLAG를 출력
- your office
즉, 사용자가 입력한 id_ 값을 Hash로 암호화하여 1337을 만드는 문제
Execute
1-1. name : 1 / id_ : 1 입력
- id_ 값이 숫자로 이루어졌으므로, 1337인지 확인하는 구문으로 이동해야 함
- 그러나, "That's not an id!" 가 출력 되고 있음
1-2. Local Test 진행 (name : 1 / id_ : 1)
- Local 테스트 결과 1337인지 확인하고, User Class로 이동 하여 hash(id_) 후 선택창 출력
- 여기서 생각해야 하는 것은 2개로 좁혀짐
- CTF Server에는 존재하지만, Source Code에는 없는 숨겨진 로직이 존재한다?
- 만약, 제공한 Source Code에 없는 숨겨진 어떠한 로직을 찾아내는 것은 현실적으로 어렵다.
- 나의 환경이 Windows 라서 그런것이다.
- Windows 에서는 기본적으로 Enter를 누를 때, '\r\n' 이 입력됨
- '\r' : Carriage Return / '\n' : Line Feed
- Local Test : Windows / 사용자 : Windows
- CTF Server : Windows 가 아님 / 사용자 : Windows
- CTF Server : Windows 가 아닌 Unix/Linux 계열
- Linux 계열은 '\n' 만 입력 받음
- CTF Server에는 존재하지만, Source Code에는 없는 숨겨진 로직이 존재한다?
1-3. '\r'을 제거하고 보내도록 Python 코드 작성
import socket
import time
HOST = 'psycho.chal.cyberjousting.com'
PORT = 1353
def recv_until(s, keyword):
data = b''
while True:
chunk = s.recv(1024)
if not chunk:
break
data += chunk
if keyword.encode() in data:
break
return data.decode()
def send_line(s, line):
s.sendall((line + '\n').encode())
def main():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
print(recv_until(s, "Go ahead and tell me your name:"))
send_line(s, "TTTTT")
print(recv_until(s, "Go ahead and choose that now:"))
send_line(s, "1111")
print(recv_until(s, "Choose a door"))
send_line(s, "2")
data = s.recv(4096)
print(data.decode())
if __name__ == "__main__":
main()
- 결과는 정상적으로 인식
1-4. User Class 확인
- Python의 Hash 내장 기능을 사용
class User:
def __init__(self, username, id):
self.username = username
self.id = id
def __eq__(self, other):
return self.id == other.id
def __hash__(self):
return hash(self.id)
YOURUSER = User(name, id_)
print(YOURUSER.id)
1-5. Python Hash 테스트 진행
[ Try_1 ]
# Input_id_ : 1337
# print(hash(id_))
# Print YOURUSER.id = 1337
------------------------------------------
[ Try_2 ]
# Input_id_ : 112312412
# print(hash(id_))
# Print YOURUSER.id = 112312412
------------------------------------------
[ Try_3 ]
# Input_id_ : 12801928490128093481209
# print(hash(id_))
# Print YOURUSER.id = 2193945982878359208
1-6. 테스트로 알 수 있는 것?
- Python의 Hash 내장 기능은
- 특정 범위내에서 사용자가 입력한 값에 대해 그대로 출력한다
- 즉, hash(x) == x
- 특정 범위를 넘으면 사용자가 입력한 값에 대해 암호화를 적용한다
- 즉, hash(x) != x
- 특정 범위는 아래 관련 문서의 long_hash 함수를 확인
- 64비트 기준), -2^63 <= x <= 2^63 -1
- 32비트 기준), -2^31 <= x <= 2^31 -1
- 특정 범위내에서 사용자가 입력한 값에 대해 그대로 출력한다
Python Hash 내장 기능 관련 문서 : https://github.com/python/cpython/blob/main/Objects/longobject.c
long_hash 함수 확인
1-7. 조건에 맞는 값(Hash 충돌 값) 찾기
- id_는 숫자로 구성되어 있어야 한다.
- 입력한 id_ 가 hash(id_) 로 변환했을 때의 값은 "1337" 이여야 한다.
target = 1337
i = 2**63 # 큰 숫자부터 시작
while True:
if hash(i) == target and i != target and all(c in '0123456789' for c in str(i)):
print("Collision value found:", i)
break
i += 1
1-8. \r을 제거하여 전송하는 코드에 id_ 값을 1-7에서 나온 Hash 충돌 값 넣어서 전송
import socket
import time
HOST = 'psycho.chal.cyberjousting.com'
PORT = 1353
def recv_until(s, keyword):
data = b''
while True:
chunk = s.recv(1024)
if not chunk:
break
data += chunk
if keyword.encode() in data:
break
return data.decode()
def send_line(s, line):
s.sendall((line + '\n').encode())
def main():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
print(recv_until(s, "Go ahead and tell me your name:"))
send_line(s, "TTTTT")
print(recv_until(s, "Go ahead and choose that now:"))
send_line(s, "9223372036854777141")
print(recv_until(s, "Choose a door"))
send_line(s, "2")
data = s.recv(4096)
print(data.decode())
if __name__ == "__main__":
main()
FLAG
byuctf{who_kn3w_h4sh_w4snt_h4sh}
728x90
반응형
'CTF & Challenge > [CTF] BYUCTF 2025' 카테고리의 다른 글
[MISC] Survey (0) | 2025.05.19 |
---|---|
[MISC] Sanity Check (0) | 2025.05.19 |