아래와 같은 Python bytecode를 준다. Python bytecode는 Python 코드의 중간 단계라고 생각하면 된다. 그래서 그런지 살짝 assembly와 유사하게 생겼다. C로 작성된 Python Compiler가 Python 코드를 bytecode로 바꾼다. Python bytecode는 __pycache__ directory에 .pyc 파일로 저장이 된다. Python bytecode는 여러 platform(Linux, Windows, MacOS)에서 모두 동작해야 하기 때문에 platform 독립적이다.
1 0 LOAD_CONST 0 (4)
2 LOAD_CONST 1 (54)
4 LOAD_CONST 2 (41)
6 LOAD_CONST 3 (0)
8 LOAD_CONST 4 (112)
10 LOAD_CONST 5 (32)
12 LOAD_CONST 6 (25)
14 LOAD_CONST 7 (49)
16 LOAD_CONST 8 (33)
18 LOAD_CONST 9 (3)
20 LOAD_CONST 3 (0)
22 LOAD_CONST 3 (0)
24 LOAD_CONST 10 (57)
26 LOAD_CONST 5 (32)
28 LOAD_CONST 11 (108)
30 LOAD_CONST 12 (23)
32 LOAD_CONST 13 (48)
34 LOAD_CONST 0 (4)
36 LOAD_CONST 14 (9)
38 LOAD_CONST 15 (70)
40 LOAD_CONST 16 (7)
42 LOAD_CONST 17 (110)
44 LOAD_CONST 18 (36)
46 LOAD_CONST 19 (8)
48 LOAD_CONST 11 (108)
50 LOAD_CONST 16 (7)
52 LOAD_CONST 7 (49)
54 LOAD_CONST 20 (10)
56 LOAD_CONST 0 (4)
58 LOAD_CONST 21 (86)
60 LOAD_CONST 22 (43)
62 LOAD_CONST 23 (59)
64 LOAD_CONST 24 (124)
66 LOAD_CONST 21 (86)
68 LOAD_CONST 3 (0)
70 LOAD_CONST 25 (69)
72 LOAD_CONST 23 (59)
74 LOAD_CONST 26 (47)
76 LOAD_CONST 27 (93)
78 LOAD_CONST 28 (78)
80 BUILD_LIST 40
82 STORE_NAME 0 (input_list)
2 84 LOAD_CONST 29 ('J')
86 STORE_NAME 1 (key_str)
3 88 LOAD_CONST 30 ('_')
90 LOAD_NAME 1 (key_str)
92 BINARY_ADD
94 STORE_NAME 1 (key_str)
4 96 LOAD_NAME 1 (key_str)
98 LOAD_CONST 31 ('o')
100 BINARY_ADD
102 STORE_NAME 1 (key_str)
5 104 LOAD_NAME 1 (key_str)
106 LOAD_CONST 32 ('3')
108 BINARY_ADD
110 STORE_NAME 1 (key_str)
6 112 LOAD_CONST 33 ('t')
114 LOAD_NAME 1 (key_str)
116 BINARY_ADD
118 STORE_NAME 1 (key_str)
9 120 LOAD_CONST 34 (<code object <listcomp> at 0x7f747ebded40, file "snake.py", line 9>)
122 LOAD_CONST 35 ('<listcomp>')
124 MAKE_FUNCTION 0
126 LOAD_NAME 1 (key_str)
128 GET_ITER
130 CALL_FUNCTION 1
132 STORE_NAME 2 (key_list)
11 >> 134 LOAD_NAME 3 (len)
136 LOAD_NAME 2 (key_list)
138 CALL_FUNCTION 1
140 LOAD_NAME 3 (len)
142 LOAD_NAME 0 (input_list)
144 CALL_FUNCTION 1
146 COMPARE_OP 0 (<)
148 POP_JUMP_IF_FALSE 162
12 150 LOAD_NAME 2 (key_list)
152 LOAD_METHOD 4 (extend)
154 LOAD_NAME 2 (key_list)
156 CALL_METHOD 1
158 POP_TOP
160 JUMP_ABSOLUTE 134
15 >> 162 LOAD_CONST 36 (<code object <listcomp> at 0x7f747ebdedf0, file "snake.py", line 15>)
164 LOAD_CONST 35 ('<listcomp>')
166 MAKE_FUNCTION 0
168 LOAD_NAME 5 (zip)
170 LOAD_NAME 0 (input_list)
172 LOAD_NAME 2 (key_list)
174 CALL_FUNCTION 2
176 GET_ITER
178 CALL_FUNCTION 1
180 STORE_NAME 6 (result)
18 182 LOAD_CONST 37 ('')
184 LOAD_METHOD 7 (join)
186 LOAD_NAME 8 (map)
188 LOAD_NAME 9 (chr)
190 LOAD_NAME 6 (result)
192 CALL_FUNCTION 2
194 CALL_METHOD 1
196 STORE_NAME 10 (result_text)
198 LOAD_CONST 38 (None)
200 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x7f747ebded40, file "snake.py", line 9>:
9 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 12 (to 18)
6 STORE_FAST 1 (char)
8 LOAD_GLOBAL 0 (ord)
10 LOAD_FAST 1 (char)
12 CALL_FUNCTION 1
14 LIST_APPEND 2
16 JUMP_ABSOLUTE 4
>> 18 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x7f747ebdedf0, file "snake.py", line 15>:
15 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 16 (to 22)
6 UNPACK_SEQUENCE 2
8 STORE_FAST 1 (a)
10 STORE_FAST 2 (b)
12 LOAD_FAST 1 (a)
14 LOAD_FAST 2 (b)
16 BINARY_XOR
18 LIST_APPEND 2
20 JUMP_ABSOLUTE 4
>> 22 RETURN_VALUE
자세히 보면 (zip, join, map, chr) 같은 python 함수들이 보인다.
36 (<code object <listcomp> at 0x7f747ebdedf0, file "snake.py", line 15>)
35 ('<listcomp>')
0
5 (zip)
0 (input_list)
2 (key_list)
2
1
6 (result)
37 ('')
7 (join)
8 (map)
9 (chr)
6 (result)
2
1
10 (result_text)
38 (None)
앞에서 나온 괄호 안에 있는 숫자들을 list로 만드는 것을 알 수 있다.
80 BUILD_LIST 40
82 STORE_NAME 0 (input_list)
이 부분을 보면 'J', '_', o', '3', 't'로 key_str을 만드는 것을 알 수 있다.
2 84 LOAD_CONST 29 ('J')
86 STORE_NAME 1 (key_str)
3 88 LOAD_CONST 30 ('_')
90 LOAD_NAME 1 (key_str)
92 BINARY_ADD
94 STORE_NAME 1 (key_str)
4 96 LOAD_NAME 1 (key_str)
98 LOAD_CONST 31 ('o')
100 BINARY_ADD
102 STORE_NAME 1 (key_str)
5 104 LOAD_NAME 1 (key_str)
106 LOAD_CONST 32 ('3')
108 BINARY_ADD
110 STORE_NAME 1 (key_str)
6 112 LOAD_CONST 33 ('t')
114 LOAD_NAME 1 (key_str)
116 BINARY_ADD
118 STORE_NAME 1 (key_str)
MAKE_FUNCTION을 이용하기 때문에 key_str이 변형될 수도 있겠다는 생각이 든다.
9 120 LOAD_CONST 34 (<code object <listcomp> at 0x7f747ebded40, file "snake.py", line 9>)
122 LOAD_CONST 35 ('<listcomp>')
124 MAKE_FUNCTION 0
126 LOAD_NAME 1 (key_str)
128 GET_ITER
130 CALL_FUNCTION 1
132 STORE_NAME 2 (key_list)
11 >> 134 LOAD_NAME 3 (len)
136 LOAD_NAME 2 (key_list)
138 CALL_FUNCTION 1
140 LOAD_NAME 3 (len)
142 LOAD_NAME 0 (input_list)
144 CALL_FUNCTION 1
146 COMPARE_OP 0 (<)
148 POP_JUMP_IF_FALSE 162
마지막 부분을 보면 xor를 하는 것을 알 수 있다. xor을 하려면 2개 이상의 값이 필요하다.
여태까지 나온 변수는 input_list와 key_list 밖에 없으니 input_list와 key_list를 xor 할 가능성이 높아 보인다.
Disassembly of <code object <listcomp> at 0x7f747ebdedf0, file "snake.py", line 15>:
15 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 16 (to 22)
6 UNPACK_SEQUENCE 2
8 STORE_FAST 1 (a)
10 STORE_FAST 2 (b)
12 LOAD_FAST 1 (a)
14 LOAD_FAST 2 (b)
16 BINARY_XOR
18 LIST_APPEND 2
20 JUMP_ABSOLUTE 4
>> 22 RETURN_VALUE
이제 LOAD CONST 오른쪽 괄호 안에 input_list의 값들만 골라내야 되는데 생각보다 input_list를 뽑아내기가 복잡하다.
일단 ctrl+shift+l를 vscode에서 누르면 블록을 지정해서 괄호 안에 있는 숫자를 골라낼 수 있다. 추가로 선택한 블록 뒤에 있는 값들이 딸려오기는 하지만 무시하면 된다.
(4)
(54)
(41)
(0)
(112)
(32)
(25)
(49)
(33)
(3)
(0)
(0)
(57)
(32)
(108)
(23)
(48)
(4)
(9)
(70)
(7)
(110)
(36)
(8)
(108)
(7)
(49)
(10)
(4)
(86)
(43)
(59)
(124)
(86)
(0)
(69)
(59)
(47)
(93)
(78)
(inpu
('J')
(key_
('_')
(key_
(key_
(key_
('o')
(key_
(key_
('3')
(key_
('t')
(key_
(key_
(<cod
('<li
(key_
(key_
(len)
(key_
(len)
(inpu
(<)
(key_
(exte
(key_
(<cod
('<li
(zip)
(inpu
(key_
(resu
('')
(join
(map)
(chr)
(resu
(resu
(None
(.0)
(to 1
(char
(ord)
(char
(.0)
(to 2
(a)
(b)
(a)
(b)
일단 여기서 괄호를 IDE로 다 없애면 다음과 같다. Vs code에서 [()]를 입력한 후 replace를 누르면 된다.
[()]
괄호 안에 있는 숫자만 고르고 밑에 있는 부분은 다 버렸다.
4
54
41
0
112
32
25
49
33
3
0
0
57
32
108
23
48
4
9
70
7
110
36
8
108
7
49
10
4
86
43
59
124
86
0
69
59
47
93
78
아래의 command를 입력하면 숫자 사이사이에 있는 '\n'을 지우고 separator를 ', '로 만들 수 있다. 이 command는 ChatGPT가 만들어줬다. 나는 아직 terminal에서 텍스트를 저렇게 다룰 수 있는 실력을 가지고 있지 않다.
참고로 grep, tr, sed를 처음 들어봤다면 무조건 이 책을 읽자. 처음이 아니더라도 꼭 읽는 것을 추천한다.
grep -v '^[[:space:]]*$' snake | tr '\n' ',' | sed 's/,$//
그래도 설명은 해 보겠다.
grep -v(invert)는 매칭되는 패턴을 제외한 나머지 부분을 보여준다.
[[:space:]]는 whitespace(' ', '\t' , '\n' , '\r')를 가리킨다.
Regular Expression에서 ^는 특정한 패턴으로 시작해야 한다는 뜻이다. *는 패턴에 매칭하는 부분이 최소 0개 이상이어야 한다. $는 line의 끝을 의미한다. tr '\n' ', '는 '\n'을 ', '로 바꾸는 역할이다. sed 's/,$//는 줄 끝에 있는 $를 없애준다.
즉 위의 command가 하는 역할을 한 줄로 하면 whitespace가 없는 줄만 선택해서 newline을 ','로 바꾸고 줄 끝에 있는 ', '는 없애는 것이다.
4,54,41,0,112,32,25,49,33,3,0,0,57,32,108,23,48,4,9,70,7,110,36,8,108,7,49,10,4,86,43,59,124,86,0,69,59,47,93,78
key_list는 몇 글자 안 돼서 손으로 고른 후에 input_list랑 xor 하는 Python 코드를 짰는데 flag가 개판으로 나온다.
from itertools import cycle
input_list=[4, 54, 41, 0, 112, 32, 25, 49, 33, 3, 0, 0, 57, 32, 108, 23, 48, 4, 9, 70, 7, 110, 36, 8, 108, 7, 49, 10, 4, 86, 43, 59, 124, 86, 0, 69, 59, 47, 93, 78]
key_list='J_o3t'
result=[a ^ ord(b) for a,b in zip(input_list,cycle(key_list))]
flag=''.join(chr(c) for c in result)
print(flag)
NiF3jF^wJ_V]ok:2M1K;Mne7"adetd@n:
어쩔 수 없이 cyberchef를 이용했다. 여기서 XOR 키를 J_o3t로 주면 flag가 안 나온다. XOR키에 역으로 picoCTF를 주면 XOR의 교환법칙 때문에 어떤 키를 XOR 해야지 picoCTF가 나오는지를 알 수 있다.
t_Jo3을 키로 주면 picoCTF가 나오는 것 같다.
t_Jo3를 키로 설정한 후 cyberchef에서 flag를 얻어도 되고 Python 코드를 살짝 수정해도 된다.
itertools의 cycle은 xor cipher 하는데 굉장히 유용하게 쓰인다. 말 그대로 같은 elements를 반복해서 xor 하는 데 사용된다.
input_list는 이미 숫자이고 key_list만 string이기 때문에 보기 안 좋지만 한쪽에만 ord()를 걸어줬다. 참고로 숫자끼리만 xor을 할 수 있다.
from itertools import cycle
input_list=[4, 54, 41, 0, 112, 32, 25, 49, 33, 3, 0, 0, 57, 32, 108, 23, 48, 4, 9, 70, 7, 110, 36, 8, 108, 7, 49, 10, 4, 86, 43, 59, 124, 86, 0, 69, 59, 47, 93, 78]
key_list='t_Jo3'
result=[a ^ ord(b) for a,b in zip(input_list,cycle(key_list))]
flag=''.join(chr(c) for c in result)
print(flag)
flag가 나온다. Not so confusing snake
picoCTF{N0t_sO_coNfus1ng_sn@ke_d6931de2}
key_list를 섞어놔서 cyberchef에서 올바른 키를 찾아야 한다는 점은 좀 마음에 안 들었다. Cyberchef를 쓰지 않고 올바른 key를 구하는 사람이 있다면 어떻게 key를 찾았는지 물어보고 싶다. 내가 byte code를 해석하는 능력이 약해서 이렇게 푼 것인지 아니면 문제에서 이게 의도한 풀이인지 잘 모르겠다. 처음부터 그냥 올바른 key_list를 주어줘서 xor 하여 flag를 구하는 문제였으면 더 좋았을 것 같다.
개인적으로는 이 문제는 좀 어려웠다.
'picoCTF' 카테고리의 다른 글
No Sql Injection (4) | 2024.11.07 |
---|---|
ASCII Numbers (3) | 2024.11.06 |
SansAlpha (0) | 2024.11.06 |
Verify (0) | 2024.10.29 |
Trickster (0) | 2024.10.29 |