1. 문제
2. 코드 분석
#!/usr/bin/env python3
import random
import signal
import sys
# 메뉴를 전역 변수를 지정했다.
MENU_GAMBLE = 1
MENU_VERIFY = 2
MENU_FLAG = 3
MENU_LEAVE = 4
# 현재 소유한 돈과 로봇이 아닌지 인증했는지에 대한 전역 변수들
money = 500
verified = False
# 메뉴 보여주는 함수
def show_menu():
print('=======================================')
print('1. go to gamble')
print('2. verify you\\'re a robot')
print('3. buy flag')
print('4. leave')
# 10,048,575 까지의 랜덤한 수를 얻는다.
def get_randn():
return random.randint(0, 0xfffffffe)
# 게임을 하는 함수이다.
def gamble():
# 돈과 인증 여부를 전역 변수로 사용
global money
global verified
# 인증이 False 상태이면 로봇이 아니라고 출력하고 중단
if verified is False:
print('you\\'re are not verified as a robot ;[')
return
# 인증이 True 상태이면 로봇이라고 출력
print('greetings, robot :]')
# 배팅을 진행하는 코드이다.
# if 문으로 배팅 머니가 현재 돈 보다 많으면 충분한 돈이 없다고 출력
bet = int(input('how much money do you want to bet (your money: ${0})? '.format(money)))
if money < bet:
print('you don\\'t have enough money (your money: ${0}).'.format(money))
return
# 10,048,575 까지의 랜덤한 수를 얻는다.
# 답은 randn % 5 + 1 이다. 따라서 1 ~ 5 까지의 수를 얻는다.
randn = get_randn()
answer = randn % 5 + 1
# 1~5 까지의 박스 중에 한개를 선택한다.
print('[1] [2] [3] [4] [5]')
user_answer = int(input('pick one of the box > '))
# 답을 출력한다.
print('answer is [{0}]!'.format(answer))
# 답이 같으면 배팅한 돈을 얻고 아니면 잃는다.
if user_answer == answer:
print('you earned ${0}.'.format(bet))
money += bet
else:
print('you lost ${0}.'.format(bet))
money -= bet
# 돈이 0 이하이면 게임 종료
if money <= 0:
print('you busted ;]')
sys.exit()
# 시간에러를 다루는 class
class MyTimeoutError(Exception):
def __init__(self):
pass
def timeout_handler(signum, frame):
raise MyTimeoutError()
# 인증을 하는 함수이다.
def verify():
# 인증 전역변수를 불러온다.
global verified
# 로봇이라고 인증되면 이미 인증되었다고 출력하고 종료한다.
if verified is True:
print('you have already been verified as a robot :]')
return
# randn224 =
randn224 = (get_randn() | get_randn() << 32 | get_randn() << 64 |
get_randn() << 96 | get_randn() << 128 | get_randn() << 160)
# challenge에 randn224 XOR 16진수 값을 한다.
challenge = randn224 ^ 0xdeaddeadbeefbeefcafecafe13371337DEFACED0DEFACED0
# 3초 동안 입력을 기다린다.
signal.alarm(3)
signal.signal(signal.SIGALRM, timeout_handler)
# 똑같이 입력하기를 기다린다.
try:
print('please type this same: "{0}"'.format(challenge))
user_challenge = input('> ')
# 만약 사용자 입력 값이 challenge와 같으면 인증을 True로 바꾼다.
if user_challenge == str(challenge):
verified = True
print('you\\'re are now verified as a robot :]')
else:
print('you\\'re not a robot ;[')
signal.alarm(0)
# 실패했다고 알리는 except 문
except MyTimeoutError:
print('\\nyou failed to verify! robots aren\\'t that slow ;[')
# flag에 관한 함수이다.
def flag():
global money
# flag 값이다.
print('price of the flag is $10,000,000,000.')
# 돈 없으면 종료
if money < 10000000000:
print('you don\\'t have enough money (your money: ${0}).'.format(money))
return
# 돈이 충분하면 flag를 출력한다.
with open('./flag', 'rb') as f:
print(b'flag is ' + f.read())
sys.exit()
# main 함수이다.
def main():
while True:
# 메뉴를 보여주고 선택하라고 한다.
show_menu()
menu = int(input('> '))
if menu == MENU_GAMBLE:
gamble()
elif menu == MENU_VERIFY:
verify()
elif menu == MENU_FLAG:
flag()
elif menu == MENU_LEAVE:
sys.exit()
else:
print('wrong menu :[')
if __name__ == '__main__':
main()
3. 풀이
인증을 하려면 암호화된 문자열을 3초안에 그대로 제출해야 한다.
출력되는 문자는 이런식으로 무작위 수에 대한 bit 연산을 수행하고
16진수 값에 XOR 연산을 수행하여 출력하고 그 문자열을 3초 안에 다시 입력 해야한다.
따라서 로봇을 인증하는 방법은 단순히 출력된 문자열을 다시 입력하면 된다.
pwn 모듈을 사용하여 원격 접속을 시도하고 인증을 구현하는 코드를 작성해봤다.
위의 코드를 실행하면 로봇 인증을 우회할 수 있다.
인증 우회가 끝났음으로 돈을 얻을 수 있는 방법을 찾아야 한다.
해당 코드에서 money < bet 이면 돈이 없음으로 게임을 종료시킨다.
또한, 박스 중 한 개를 선택해서 정답이면 + 하고 아니라면 -를 한다.
하지만, 돈을 입력받는 코드에서 음수의 검증 과정은 없다. 따라서, -1000000000000 식으로 입력하고 틀린다면 money -= (-1000000000000) 식으로 되기 때문에 오히려 돈이 증가한다.
위의 논리대로 코드를 작성해보면
및의 코드처럼 나오게 된다.
만약 gamble 함수에서 음수를 입력하고 정답이 아니라면 돈은 증가할 것이다.
성공했다.
'Wargame > wargame 암호학' 카테고리의 다른 글
[Dreamhack] likeb64 (0) | 2024.05.11 |
---|---|
[Dreamhack] DARIMCHAL_001 (0) | 2024.04.30 |
[Dreamhack] basic crypto (0) | 2024.04.29 |
[Dreamhack] ROT128 (0) | 2024.04.29 |
[Dreamhack] SingleByteXor (0) | 2024.04.29 |