문제 분석
먼저 주어진 소스코드를 자세히 보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
if __name__ == "__main__":
s = Seed()
seed = s.GenerateSeed()
print(f"Seed: {seed}")
while 1:
k = input("Key: ").strip().split() # input ex) 41 42 43 44
kl = [int(x) for x in k]
if s.Authentication(seed, kl):
break
print('DH{fake_flag}')
|
cs |
main문을 먼저 살펴보면,
s = Seed() 함수의 리턴 값을 저장하고,
seed는 Seed를 생성하는 함수인 것 같다.
이후 while문을 돌리며 k에는 "Key: " 이후의 입력값을 저장하고,
kl은 입력받은 k값을 리스트로 저장한다.
이후 seed와 kl을 비교하여 True면 break 이후 flag를 출력한다.
1
2
3
|
def GenerateSeed() -> List[int]:
seed: bytearray = bytearray(random.getrandbits(8) for x in range(4))
return list(seed)
|
cs |
먼저 GenerateSeed함수를 보면,
seed는 8비트 4개를 int로 저장하는 list이다.
1
2
3
4
5
6
7
8
9
|
def Authentication(self, seed: List[int], k: List[int]) -> bool:
key = self.CalculateKey(seed)
if key == k:
print('Correct!!')
return True
else:
print('Invalid Key!!')
return False
|
cs |
Authentication 함수는 seed와 입력된 k 리스트를 비교하고,
key는 다시 CalculateKey함수를 호출한다.
이후 key값과 k 값이 일치하면 Correct를 반환한다.
1
2
3
4
5
6
7
8
9
10
|
def __init__ (self) -> None:
self.a: int = os.urandom(1)
def CalculateKey(self, seed: List[int]) -> bool:
key: List[int] = [0] * len(seed)
for i in range(len(seed)):
result: bytes = bytes([self.a[j] ^ seed[i] for j in range(len(self.a))])
key[i] = int.from_bytes(result, byteorder='little')
return key
|
cs |
CalculateKey 함수는 0 ~ 255가 저장된 배열 a와 seed 값을 XOR한 값을 key 배열에 저장하는 함수이다.
그러므로 랜덤으로 생성된 seed값과 0 ~ 255까지의 값을 XOR한 값이 key에 저장되면 될 것이다.
Exploit
이를 위해 먼저 실행시켜보았다.
먼저 Seed 값은 대괄호 안에 존재하고,
Key는 공백을 기준으로 입력을 해주어야한다.
그러므로 대괄호를 기준으로 Seed 값을 새로운 seed 배열에 저장을 먼저 해주었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
p.recvuntil(b"Seed: [")
seed = []
s = p.recvuntil(b", ").decode()
s = s[0:-2]
seed.append(s)
s = p.recvuntil(b", ").decode()
s = s[0:-2]
seed.append(s)
s = p.recvuntil(b", ").decode()
s = s[0:-2]
seed.append(s)
s = p.recvuntil(b"]").decode()
s = s[0:-1]
seed.append(s)
|
cs |
앞선 3개의 값은 ", "로 구분이 되므로 해당 인자 전까지 입력을 받고,
마지막은 "]"로 종료되므로 해당 인자 전까지 입력을 받았다.
이후 Key값을 입력해주어야 하는데,
해당 값은 Seed와 0 ~ 255 값을 XOR한 값이므로
for문을 돌려 Key를 저장해주었다.
1
2
3
4
5
6
|
for i in range(256):
key = []
for j in range(4):
key.append(str(i ^ int(seed[j])).encode())
success(seed)
success(key)
|
cs |
사실 이대로 key를 하나씩 전송했더니 오류를 출력했다.
그래서 코드를 읽어보니 Key 이후에 strip().split() 함수를 통해 인자를 구분하는 것을 보아
하나씩 전송이 아니라 마찬가지로 공백을 기준으로 분류해주어야겠다는 생각이 들어
key 4개를 하나의 문자열로 저장해주었다.
전체 코드
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
|
from pwn import *
p = remote("host3.dreamhack.games", 17660)
context.log_level = "debug"
p.recvuntil(b"Seed: [")
seed = []
s = p.recvuntil(b", ").decode()
s = s[0:-2]
seed.append(s)
s = p.recvuntil(b", ").decode()
s = s[0:-2]
seed.append(s)
s = p.recvuntil(b", ").decode()
s = s[0:-2]
seed.append(s)
s = p.recvuntil(b"]").decode()
s = s[0:-1]
seed.append(s)
for i in range(256):
key = []
for j in range(4):
key.append(str(i ^ int(seed[j])).encode())
success(seed)
success(key)
st = b""
for i in range(4):
st += key[i] + b" "
p.sendline(st)
print(p.recvline()[:-1])
|
cs |