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개중에 한개를 선택하도록 되어 있음
      1. your office
        • "Man, this is a nice office" 출력
      2. the admin's office
        • 사용자의 Hash(YOURUSER) 값과 hash(ADMIN) 값을 비교
        • 같을 경우 FLAG를 출력
  • 즉, 사용자가 입력한 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개로 좁혀짐
    1. CTF Server에는 존재하지만, Source Code에는 없는 숨겨진 로직이 존재한다?
      • 만약, 제공한 Source Code에 없는 숨겨진 어떠한 로직을 찾아내는 것은 현실적으로 어렵다.
    2. 나의 환경이 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' 만 입력 받음

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 충돌 값) 찾기


  1. id_는 숫자로 구성되어 있어야 한다.
  2. 입력한 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