이번 seccon은 연합팀 'KOREAN'으로 나가게 되어 최종 4위를 하였습니다. 하지만 명예 한국인 한 분도 계셨다는ㅋㅋ.
사실 조금 늦게 참여하였는데 운이 좋게 리버싱 한 문제를 풀 수 있었습니다.
다음은 리버싱 문제였던 'flag' 풀이입니다.
문제 클릭시 웹 사이트로 리다이렉트되며 알맞는 플레그를 웹에서 체크하는 문제였습니다. 리버싱 태그로 나왔기 때문에 살펴보면 wasm 웹 어셈블리어 바이너리가 존재하는걸 볼 수 있습니다. 즉, 해당 문제에서 플래그 문자열을 입력하고 체크 버튼을 클릭시 내부적으로 웹 어셈블리어 바이너리에서 검증을 하게 되는겁니다.
웹 어셈블리어를 분석할 때는 유명한 팁이 존재합니다. 바로 wasm 바이너리를 디컴파일 후 다시 elf로 빌드해서 보는 것입니다. 사실 디컴파일 소스로 봐도 무방은 하나 IDA Pro 에서 만들어주는 의사코드에 익숙해져 있기 때문에...
이렇게 만들어진 웹 어셈블리어 바이너리를 IDA Pro로 로딩했을 때 특징들이 존재합니다.
첫 번째로는 init이 존재한다는 점입니다. 여기서 전역 변수 설정 등 여러가지 초기화를 거치게 됩니다.
두 번째로는 다음과 같이 store, load 등이 존재합니다. 이렇게 생긴 함수들을 살펴보면 i32는 int32를 나타내는 것이며 'store' 저장하겠다. 인자 3개는 메모레, 위치, 값 이라고 생각하면됩니다.
즉, i32_store(memory, offset, value)는 offset에 value를 저장, i32_load(memory, offest, value)는 offset 값을 가져온다는 뜻으로 생각하시면 됩니다.
다시 문제 페이지의 자바 스크립트를 확인하면 다음과 같은 인자와 호출되는 함수를 확인할 수 있습니다.
flag.wasm 안의 check 함수를 실행하며 매번 똑같은 key값과 enc 값을 넘겨주는 것을 볼 수 있습니다. 그렇다면 이제부터 check 함수를 분석해보면 됩니다.
check 함수 초반 부분부터 enum 을 사용해서 분석하면 다음과 같이 인자로 받은 3개의 값을 저장 후 어떠한 루틴을 통해 결과를 내는 것을 볼 수 있습니다.
이것들을 분석한 뒤 Python으로 포팅한 결과입니다.
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
|
import sys
def w2c_f23(key, idx1, idx2, idx3, idx4):
key[idx2] = ((( ((key[idx4] + key[idx1]) & 0xff) >> 7) | (2 * ((key[idx4] + key[idx1]) & 0xff))) ^ key[idx2]) & 0xff
key[idx3] = ((( ((key[idx1] + key[idx2]) & 0xff) >> 6) | (4 * ((key[idx1] + key[idx2]) & 0xff))) ^ key[idx3]) & 0xff
key[idx4] = ((( ((key[idx2] + key[idx3]) & 0xff) >> 5) | (8 * ((key[idx2] + key[idx3]) & 0xff))) ^ key[idx4]) & 0xff
key[idx1] = ((( ((key[idx3] + key[idx4]) & 0xff) >> 4) | (16 * ((key[idx3] + key[idx4]) & 0xff))) ^ key[idx1]) & 0xff
def reverse_w2c_f23(key, idx1, idx2, idx3, idx4):
key[idx1] = ((( ((key[idx3] + key[idx4]) & 0xff) >> 4) | (16 * ((key[idx3] + key[idx4]) & 0xff))) ^ key[idx1]) & 0xff
key[idx4] = ((( ((key[idx2] + key[idx3]) & 0xff) >> 5) | (8 * ((key[idx2] + key[idx3]) & 0xff))) ^ key[idx4]) & 0xff
key[idx3] = ((( ((key[idx1] + key[idx2]) & 0xff) >> 6) | (4 * ((key[idx1] + key[idx2]) & 0xff))) ^ key[idx3]) & 0xff
key[idx2] = ((( ((key[idx4] + key[idx1]) & 0xff) >> 7) | (2 * ((key[idx4] + key[idx1]) & 0xff))) ^ key[idx2]) & 0xff
myinput = b"a"* 55 + b"}"
enc = b"6dbf84f73cf6a112268b09525ea550a665e21cb2e3e13af7e3ea0ecb52f5b9cda5b6522b1e978734553f1d7956d4af94bfc3f4d68c8fba9eeecf4035550b9106f70d57d1a6cdaf3211eaaa78d71a9038b71be621241e8b608a43b107f8860f543ab0189aa063800de4bae7d0b11045b8"
key = b"NekoPunch"
key = key[:8]
data_segment_data_1 = b"0123456789abcdef"
if len(key) >= 8 and bytes([myinput[-1]]) == b"}" and len(myinput) * 4 == len(enc):
dup_key = bytearray(key[:8] + (0x10 - len(key)) * b"\x00")
result = 0
for loop in range(0, len(myinput), 8):
for idx1 in range(8):
char = myinput[idx1+loop]
dup_key[8+idx1] = char
for idx2 in range(0x80):
w2c_f23(dup_key, 0, 4, 8, 12)
w2c_f23(dup_key, 5, 9, 13, 1);
w2c_f23(dup_key, 10, 14, 2, 6);
w2c_f23(dup_key, 15, 3, 7, 11);
w2c_f23(dup_key, 0, 1, 2, 3);
w2c_f23(dup_key, 5, 6, 7, 4);
w2c_f23(dup_key, 10, 11, 8, 9);
w2c_f23(dup_key, 15, 12, 13, 14);
print()
for idx3 in range(16):
tmp1 = enc[loop + idx3]
tmp2 = dup_key[idx3] // 16
value = (tmp1 != data_segment_data_1[tmp2]) | result
result = value
tmp1 = enc[loop + idx3 + 1]
tmp2 = dup_key[idx3] % 16
value = (tmp1 != data_segment_data_1[tmp2]) | result
result = value
print(result)
|
cs |
여기서 알 수 있는 사실은 인자로 받은 enc는 역산이 가능한 인코딩된 플래그 값입니다.
비트연산을 역으로 짜서 다시 돌리게 되면 플래그를 도출해낼 수 있습니다.
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
|
import sys
def w2c_f23(key, idx1, idx2, idx3, idx4):
key[idx2] = ((( ((key[idx4] + key[idx1]) & 0xff) >> 7) | (2 * ((key[idx4] + key[idx1]) & 0xff))) ^ key[idx2]) & 0xff
key[idx3] = ((( ((key[idx1] + key[idx2]) & 0xff) >> 6) | (4 * ((key[idx1] + key[idx2]) & 0xff))) ^ key[idx3]) & 0xff
key[idx4] = ((( ((key[idx2] + key[idx3]) & 0xff) >> 5) | (8 * ((key[idx2] + key[idx3]) & 0xff))) ^ key[idx4]) & 0xff
key[idx1] = ((( ((key[idx3] + key[idx4]) & 0xff) >> 4) | (16 * ((key[idx3] + key[idx4]) & 0xff))) ^ key[idx1]) & 0xff
def reverse_w2c_f23(key, idx1, idx2, idx3, idx4):
key[idx1] = ((( ((key[idx3] + key[idx4]) & 0xff) >> 4) | (16 * ((key[idx3] + key[idx4]) & 0xff))) ^ key[idx1]) & 0xff
key[idx4] = ((( ((key[idx2] + key[idx3]) & 0xff) >> 5) | (8 * ((key[idx2] + key[idx3]) & 0xff))) ^ key[idx4]) & 0xff
key[idx3] = ((( ((key[idx1] + key[idx2]) & 0xff) >> 6) | (4 * ((key[idx1] + key[idx2]) & 0xff))) ^ key[idx3]) & 0xff
key[idx2] = ((( ((key[idx4] + key[idx1]) & 0xff) >> 7) | (2 * ((key[idx4] + key[idx1]) & 0xff))) ^ key[idx2]) & 0xff
enc = b"\x6D\xBF\x84\xF7\x3C\xF6\xA1\x12\x26\x8B\x09\x52\x5E\xA5\x50\xA6\x65\xE2\x1C\xB2\xE3\xE1\x3A\xF7\xE3\xEA\x0E\xCB\x52\xF5\xB9\xCD\xA5\xB6\x52\x2B\x1E\x97\x87\x34\x55\x3F\x1D\x79\x56\xD4\xAF\x94\xBF\xC3\xF4\xD6\x8C\x8F\xBA\x9E\xEE\xCF\x40\x35\x55\x0B\x91\x06\xF7\x0D\x57\xD1\xA6\xCD\xAF\x32\x11\xEA\xAA\x78\xD7\x1A\x90\x38\xB7\x1B\xE6\x21\x24\x1E\x8B\x60\x8A\x43\xB1\x07\xF8\x86\x0F\x54\x3A\xB0\x18\x9A\xA0\x63\x80\x0D\xE4\xBA\xE7\xD0\xB1\x10\x45\xB8"
myinput = enc
key = b"NekoPunch"
key = key[:8]
data_segment_data_1 = b"0123456789abcdef"
flag = b""
dup_key = bytearray(key[:8] + (0x10 - len(key)) * b"\x00")
result = 0
for loop in range(0, len(myinput), 16):
for idx1 in range(16):
char = myinput[idx1+loop]
dup_key[idx1] = char
for idx2 in range(0x80):
reverse_w2c_f23(dup_key, 15, 12, 13, 14);
reverse_w2c_f23(dup_key, 10, 11, 8, 9);
reverse_w2c_f23(dup_key, 5, 6, 7, 4);
reverse_w2c_f23(dup_key, 0, 1, 2, 3);
reverse_w2c_f23(dup_key, 15, 3, 7, 11);
reverse_w2c_f23(dup_key, 10, 14, 2, 6);
reverse_w2c_f23(dup_key, 5, 9, 13, 1);
reverse_w2c_f23(dup_key, 0, 4, 8, 12)
flag += dup_key[8:]
print(flag)
|
cs |
최종적으로 다음과 같은 결과를 얻을 수 있습니다.
b'SECCON{w'
b'SECCON{wh4ts_Ur_'
b'SECCON{wh4ts_Ur_r3c0mm3n'
b'SECCON{wh4ts_Ur_r3c0mm3nd3d_w4y_'
b'SECCON{wh4ts_Ur_r3c0mm3nd3d_w4y_2_d3c0mp'
b'SECCON{wh4ts_Ur_r3c0mm3nd3d_w4y_2_d3c0mp1l3_WASM'
b'SECCON{wh4ts_Ur_r3c0mm3nd3d_w4y_2_d3c0mp1l3_WASM?}\x00\x00\x00\x00\x00\x00'
'# write-up > - ctf' 카테고리의 다른 글
Codegate 2019 - The Matirx (5) | 2019.01.30 |
---|---|
GoogleCTF 2017 - food (1) | 2017.06.19 |
HUST 2017 - Mystic Crypt (0) | 2017.06.06 |
[DEFCON 2017] - pegem (2) | 2017.05.10 |
[Plaid CTF 2017] zamboni (0) | 2017.05.02 |