-
Notifications
You must be signed in to change notification settings - Fork 2
/
16.zh.srt
804 lines (603 loc) · 16.8 KB
/
16.zh.srt
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
1
00:00:06,399 --> 00:00:12,269
在上一集中,我们首次来了个栈缓存溢出漏洞的完整实践,
2
00:00:12,269 --> 00:00:17,069
通过覆盖栈上的返回指针,并跳转到
3
00:00:17,070 --> 00:00:18,320
我们放置在栈上的 shellcode
4
00:00:18,320 --> 00:00:23,760
在本集中,我们将查看第6层栈,
5
00:00:23,759 --> 00:00:25,789
它会查看当您有返回地址的限制,会发生什么。
6
00:00:25,789 --> 00:00:31,239
要理解此视频,您必须了解正常缓冲区溢出和跳向 Shellcode
7
00:00:31,240 --> 00:00:32,370
是怎么工作的。
8
00:00:32,369 --> 00:00:35,369
我希望我在过去的视频中,解释得足够好。
9
00:00:35,370 --> 00:00:38,950
但如果还有未解决的问题,请将其张贴到 Reddit上。
10
00:00:38,949 --> 00:00:42,319
我真的需要你,能够理解之前哪些东西,不然下面没法继续。
11
00:00:42,320 --> 00:00:44,840
提示,关于这一级告诉我们,
12
00:00:44,840 --> 00:00:49,730
它可以用两种方法完成,例如,查找有效负载的副本
13
00:00:49,730 --> 00:00:55,680
(objdump -s),或 ret2libc,甚至面向返回编程。
14
00:00:55,680 --> 00:00:59,730
在本视频中,我们将无法探索面向返回编程。
15
00:00:59,730 --> 00:01:04,850
但是,我会给你看一些其他的,
16
00:01:04,850 --> 00:01:05,850
最终会导致面向返回编程的技巧。
17
00:01:05,850 --> 00:01:07,420
让我们来看一下源代码。
18
00:01:07,420 --> 00:01:12,860
同样,它与之前的级别非常相似,只是有一些小的变化。
19
00:01:12,860 --> 00:01:18,130
不是所有东西都在 mina 中,main 调用这个函数 get_path。
20
00:01:18,130 --> 00:01:21,700
得到 path 分配了 64个字符的字符串缓冲区。
21
00:01:21,700 --> 00:01:23,440
然后,用 gets 读取字符串。
22
00:01:23,440 --> 00:01:28,550
我们现在都知道,用 gets 我们可以任意地读取许多字符,
23
00:01:28,550 --> 00:01:31,310
以此,我们可用来,覆盖栈上的返回指针。
24
00:01:31,310 --> 00:01:33,480
但下一行代码很有趣。
25
00:01:33,480 --> 00:01:38,460
__builtin_return_address 是编译器中的一个函数,
26
00:01:38,460 --> 00:01:40,630
您可以使用它来读取,栈的当前返回地址。
27
00:01:40,630 --> 00:01:45,990
如果返回地址,以 hex 0xbf 开头,则检查该返回地址。
28
00:01:45,990 --> 00:01:49,230
这个符号,会对地址,执行“与(AND)”位操作。
29
00:01:49,230 --> 00:01:53,560
在操作之后,只有他们相同的部分,存活下来。
30
00:01:53,560 --> 00:01:55,520
基本上来说,剩余的位都成了 0。
31
00:01:55,520 --> 00:01:58,500
检查前面是否,仍然是 hex bf 。
32
00:01:58,500 --> 00:02:05,070
如果返回地址以 0xbf 开头,则打印此返回地址,并退出。
33
00:02:05,070 --> 00:02:06,830
当我们使用上次的漏洞脚本时。
34
00:02:06,830 --> 00:02:11,150
并调整填充,去解释这个函数中,新的无符号int,
35
00:02:11,150 --> 00:02:12,750
我们看到它不起作用。
36
00:02:12,750 --> 00:02:14,410
bzzzt 失败了。
37
00:02:14,409 --> 00:02:15,409
好吧。
38
00:02:15,409 --> 00:02:19,389
所以,我们不能使用以 hex bf开头的地址。
39
00:02:19,389 --> 00:02:22,579
退出保护这个函数,不被这样滥用。
40
00:02:22,579 --> 00:02:25,799
因为 exit 是一个系统调用,只是简单地退出程序。
41
00:02:25,799 --> 00:02:30,629
所以,即使我们破坏了返回指针,这个函数也不会返回。
42
00:02:30,629 --> 00:02:35,879
这样,你就可以看到,即使有一个缓冲区溢出,也不一定意味着
43
00:02:35,879 --> 00:02:37,099
你可以利用它。
44
00:02:37,099 --> 00:02:41,909
当我们现在在gdb中,打开这个级别时,在 getpath 时中断,运行它,
45
00:02:41,909 --> 00:02:43,919
然后,用“info proc map”查看映射内存。
46
00:02:43,920 --> 00:02:48,690
我们可以看到,只有以 bf 开头的地址在栈上。
47
00:02:48,689 --> 00:02:52,109
所以基本上,我们不能返回到栈地址。
48
00:02:52,109 --> 00:02:57,829
我去,那我们怎么运行我们的(漏洞)代码,如果,
49
00:02:57,829 --> 00:02:59,379
我们不能返回到栈中,我们代码的放置位置?
50
00:02:59,379 --> 00:03:01,989
打起精神来,所以首先,我们仍然可以控制返回指针。
51
00:03:01,989 --> 00:03:04,119
只要不是从 bf 开始的就行。
52
00:03:04,120 --> 00:03:08,620
现在出现了一个大胆的想法,关于返回到已知的代码中。
53
00:03:08,620 --> 00:03:10,140
让我们想想。
54
00:03:10,140 --> 00:03:15,730
返回指令,只查看栈顶部的当前地址,将其移除,
55
00:03:15,730 --> 00:03:17,280
然后就跳到那里。
56
00:03:17,279 --> 00:03:24,309
… 那如果您返回地址 0x080484f9 会发生什么。
57
00:03:24,310 --> 00:03:27,240
也就是返回指令本身。
58
00:03:27,239 --> 00:03:28,239
想想这个。
59
00:03:28,239 --> 00:03:32,279
我们用 hex 0x080 覆盖返回指针。
60
00:03:32,279 --> 00:03:37,499
我们到达返回指令,它将从栈中弹出这个地址,并跳转到它。
61
00:03:37,500 --> 00:03:39,360
可以说是,跳到自己身上。
62
00:03:39,360 --> 00:03:44,130
现在,它将读取栈上的下一个地址,并跳到那里。
63
00:03:44,129 --> 00:03:45,589
现在,你可以重复一遍。
64
00:03:45,590 --> 00:03:48,260
再次,将栈上的下一个地址设置为0x080。
65
00:03:48,260 --> 00:03:51,540
或者把栈的地址,放在这里。
66
00:03:51,540 --> 00:03:53,210
我们可以,再次返回到了栈中。
67
00:03:53,209 --> 00:03:55,599
从理论上讲,这是可行的。
68
00:03:55,599 --> 00:03:59,669
因为原始返回地址,被以0x080开头的地址覆盖。
69
00:03:59,670 --> 00:04:02,350
好吧,我们试试看。
70
00:04:02,349 --> 00:04:04,439
让我们修改我们的漏洞代码。
71
00:04:04,439 --> 00:04:08,659
我们不再使用shellcode,而是使用 int 3,cc 指令。
72
00:04:08,659 --> 00:04:09,729
制造一个陷阱。
73
00:04:09,730 --> 00:04:13,710
我们现在都知道,当我们触碰它们时,我们要有代码执行,而我们只需要找到
74
00:04:13,709 --> 00:04:14,789
合适的 shellcode。
75
00:04:14,790 --> 00:04:17,720
所以,让我们集中在有趣的部分。
76
00:04:17,720 --> 00:04:23,590
在getpath的返回处,设置一个断点,然后使用我们的漏洞输入,运行它。
77
00:04:23,590 --> 00:04:24,590
我们到达了断点。
78
00:04:24,590 --> 00:04:27,020
我们通过了,返回指针的检查。
79
00:04:27,020 --> 00:04:29,140
那么,让我们现在看看这个栈。
80
00:04:29,139 --> 00:04:34,589
我们可以看到栈顶部的地址,现在是0x080。
81
00:04:34,590 --> 00:04:36,400
这是返回指令。
82
00:04:36,400 --> 00:04:38,280
所以,现在前进一步。
83
00:04:38,280 --> 00:04:40,960
就应该是从栈中弹出这个地址,并跳转到那里。
84
00:04:40,960 --> 00:04:41,970
确实如此。
85
00:04:41,970 --> 00:04:46,230
我们又碰到了断点,因为我们跳到了自己身上。
86
00:04:46,230 --> 00:04:50,720
现在查看栈时,栈上的下一个地址,是这个栈地址。
87
00:04:50,720 --> 00:04:53,340
我们现在,就返回到这个地址。
88
00:04:53,340 --> 00:04:58,810
因此,当我们继续时,我们将返回到栈中,就像在前面的漏洞中一样,
89
00:04:58,810 --> 00:05:00,430
我们击中了陷阱。
90
00:05:00,430 --> 00:05:01,430
开花!
91
00:05:01,430 --> 00:05:03,030
任意代码执行(漏洞完成)!
92
00:05:03,030 --> 00:05:04,030
顺便说一句。
93
00:05:04,030 --> 00:05:07,360
我们跳到的这个地址,0x080是一个小工具。
94
00:05:07,360 --> 00:05:11,200
当你读到关于面向返回的编程时,对小工具有所了解后。
95
00:05:11,199 --> 00:05:13,649
会发现,这就是一个简单的,无操作的小工具。
96
00:05:13,650 --> 00:05:15,900
这只是一个返回(操作),就什么都不做了。
97
00:05:15,900 --> 00:05:20,620
对于完整的面向返回编程,你能寻找,能做更多事情的小工具,
98
00:05:20,620 --> 00:05:22,430
在返回下一个地址之前。
99
00:05:22,430 --> 00:05:28,300
现在,当我们做这种事情的时候,你听到我说“返回到(return into)”,或者“跳到(jump to)”。
100
00:05:28,300 --> 00:05:30,590
在这种情况下,它们是等价的。
101
00:05:30,590 --> 00:05:35,860
虽然是的,我们执行一个返回指令,但不再
102
00:05:35,860 --> 00:05:36,860
没有返回到原始函数。
103
00:05:36,860 --> 00:05:39,310
我们,又返回到了别的地方。
104
00:05:39,310 --> 00:05:41,980
实际上,只是跳到别的地方。
105
00:05:41,979 --> 00:05:55,589
所以,我希望,这不会困惑你。
106
00:05:55,590 --> 00:06:04,630
让我们看一看,另一个技巧。
107
00:06:04,629 --> 00:06:22,249
这与我们在第4层栈中,所做的非常相似。
108
00:06:22,250 --> 00:06:25,480
那么,我们还能跳到哪里呢?
109
00:06:25,480 --> 00:06:52,840
其中一个提示是,
110
00:06:52,840 --> 00:06:56,350
说我们可以调查 ret2libc。
111
00:06:56,350 --> 00:07:03,330
既然你现在,几乎是漏洞行家,你就会明白,这意味着什么了。
112
00:07:03,330 --> 00:07:04,970
返回到 libc。
113
00:07:04,970 --> 00:07:09,990
就像我们刚刚返回到,某些代码一样,我们也可以返回到,巨大库 libc中。
114
00:07:09,990 --> 00:07:13,990
一定有什么有趣的东西,我们可以利用的。
115
00:07:13,990 --> 00:07:16,860
libc中,一个有趣的函数是system。
116
00:07:16,860 --> 00:07:19,470
它执行shell命令。
117
00:07:19,470 --> 00:07:22,150
通过 print system,我们可以找到它的地址。
118
00:07:22,150 --> 00:07:25,100
但是简单地返回到它,可能不会起作用。
119
00:07:25,100 --> 00:07:28,080
我们需要确保控制一些事情。
120
00:07:28,080 --> 00:07:32,430
让我们创建一个简单的C程序,它为我们调用libc system
121
00:07:32,430 --> 00:07:36,170
最后,我们希望以某种方式,使用 /bin/sh 执行 system。
122
00:07:36,169 --> 00:07:39,349
因为这样,我们就有了shell。
123
00:07:39,349 --> 00:07:41,339
如果我们在这里试一下,效果很好。
124
00:07:41,340 --> 00:07:43,540
现在让我们在 gdb 中看看。
125
00:07:43,539 --> 00:07:49,069
我们可以看到,在调用 system 之前,我们要执行的命令的地址,
126
00:07:49,069 --> 00:07:51,279
是放在栈的顶部。
127
00:07:51,280 --> 00:07:56,010
如我们所知,对函数的一次调用(call),会把返回地址放在栈上
128
00:07:56,009 --> 00:07:57,009
好。
129
00:07:57,009 --> 00:08:01,159
因此,如果我们画出这个,就是当 system 开始时,
130
00:08:01,159 --> 00:08:02,289
栈的样子。
131
00:08:02,289 --> 00:08:04,729
首先,命令的地址放在栈上。
132
00:08:04,729 --> 00:08:08,689
然后,是我们要返回的地址。
133
00:08:08,690 --> 00:08:12,640
现在想象一下,如果使用我们的缓冲区溢出,返回到 system 中。
134
00:08:12,639 --> 00:08:15,769
首先,我们没有执行一个 call 指令。
135
00:08:15,770 --> 00:08:18,620
因此,不会推入返回地址。
136
00:08:18,620 --> 00:08:20,760
但是,我们完全控制了栈。
137
00:08:20,759 --> 00:08:24,139
所以 system 希望,栈看起来像这样。
138
00:08:24,139 --> 00:08:25,969
我们可以手工建造(栈的样子)!
139
00:08:25,970 --> 00:08:29,800
所以首先,我们必须将 system 的返回地址放在栈上。
140
00:08:29,800 --> 00:08:32,680
但实际上,我们现在不在乎这个。
141
00:08:32,680 --> 00:08:35,370
但这仍也很重要,还很雕,要记住这一点。
142
00:08:35,370 --> 00:08:38,040
因为你可以把这些东西,连在一起。
143
00:08:38,039 --> 00:08:43,669
就像我们以前一个接一个地链接了两个返回(指针),我们可以
144
00:08:43,669 --> 00:08:48,769
把多个函数调用或其他小工具连起来,具体做法是,
145
00:08:48,770 --> 00:08:49,770
通过始终控制下一步的返回指针。
146
00:08:49,770 --> 00:08:55,390
所以在我们的例子中,当 system 完成时,我们会运行到一个segfault,
147
00:08:55,390 --> 00:08:57,400
因为它会返回到 0x41414141。
148
00:08:57,400 --> 00:09:03,010
栈上的下一个地址,必须是我们要执行的字符串。
149
00:09:03,010 --> 00:09:05,250
最好是“/bin/sh”。
150
00:09:05,250 --> 00:09:08,280
此类获取一个引用的字符串,其实有许多选项。
151
00:09:08,280 --> 00:09:10,460
一种选择是,使用栈地址。
152
00:09:10,460 --> 00:09:11,900
因为会有我们控制的字符串。
153
00:09:11,900 --> 00:09:13,930
或环境变量。
154
00:09:13,930 --> 00:09:16,680
因为它们位于栈的底部,并且更容易预测。
155
00:09:16,680 --> 00:09:20,150
但是,正如您所记得的,这个栈有点不可靠,而且会四处移动。
156
00:09:20,150 --> 00:09:22,920
这里有一种更可靠的技术。
157
00:09:22,920 --> 00:09:27,880
我们可以使用 find ,在 libc 的映射内存中,搜索字符串。
158
00:09:27,880 --> 00:09:30,970
很雕,显然在这个地址,我们可以找到 /bin/sh。
159
00:09:30,970 --> 00:09:33,390
我们来看看,这是不是真的。
160
00:09:33,390 --> 00:09:35,620
在此地址以字符串形式,检查内存。
161
00:09:35,620 --> 00:09:36,620
酷!
162
00:09:36,620 --> 00:09:37,950
WTF!gdb。
163
00:09:37,950 --> 00:09:39,720
你到底在干什么?
164
00:09:39,720 --> 00:09:43,390
你为什么说,你在那里找到了 bin/sh,如果没有呢?
165
00:09:43,390 --> 00:09:45,800
我不知道为什么会这样。
166
00:09:45,800 --> 00:09:47,300
在网上,找不到任何东西。
167
00:09:47,300 --> 00:09:48,300
不管怎样。
168
00:09:48,300 --> 00:09:49,300
忽略这一点。
169
00:09:49,300 --> 00:09:50,300
这是另一种技术。
170
00:09:50,300 --> 00:09:53,940
我们可以使用 strings,来查找libc 中的所有字符串。
171
00:09:53,940 --> 00:09:58,160
使用 -t,我们可以将文件中的偏移量,打印为 hex 。
172
00:09:58,160 --> 00:10:02,450
然后我们可以简单地把这个偏移量,添加到 libc 加载到的地址。
173
00:10:02,450 --> 00:10:05,070
这是 /bin/sh 的真实地址。
174
00:10:05,070 --> 00:10:07,620
好吧,让我们把这个地址,复制到我们的漏洞脚本中。
175
00:10:07,620 --> 00:10:11,250
然后我们试试看。
176
00:10:11,250 --> 00:10:15,210
记住要用上一个视频中,带括号和cat的技巧。
177
00:10:15,210 --> 00:10:18,450
因为漏洞脚本,将再次关闭输入(input)。
178
00:10:18,450 --> 00:10:19,450
酷!
179
00:10:19,450 --> 00:10:20,450
它起作用了。
180
00:10:20,450 --> 00:10:23,200
我们刚刚做的是一种叫做 ret2libc 的技术。
181
00:10:23,200 --> 00:10:25,440
而且我们从未在栈上,执行过任何代码!
182
00:10:25,441 --> 00:10:26,441
也许你会问自己,为什么TMD,这个栈一开始是可执行的。
183
00:10:26,441 --> 00:10:27,441
栈是可执行的,是不合理的。
184
00:10:27,441 --> 00:10:28,441
这就是为什么,现在有一个通用的内存策略。
185
00:10:28,441 --> 00:10:29,441
Wirte xor Execute。
186
00:10:29,441 --> 00:10:30,441
基本上,它意味着,永远不会有可写和可执行的内存页。
187
00:10:30,441 --> 00:10:31,441
因为这样攻击者,就不能执行任何 shellcode,
188
00:10:31,441 --> 00:10:32,441
因为,不能写入进程内存。
189
00:10:32,441 --> 00:10:33,441
因此,我们希望今天每个现代 system ,都使用DEP、数据执行预防和
190
00:10:33,441 --> 00:10:34,441
设置 nx 位,是内存页(如栈)的不可执行位。
191
00:10:34,441 --> 00:10:35,441
但现实,并非如此简单。
192
00:10:35,441 --> 00:10:36,441
随着物联网的兴起,嵌入式设备也在兴起,
193
00:10:36,441 --> 00:10:37,441
但它们通常不会支持这样的功能。
194
00:10:37,441 --> 00:10:38,441
或像 javascript 这样的现代编程语言使用JIT。
195
00:10:38,441 --> 00:10:39,441
实时编译器(Just in time compiler)。
196
00:10:39,441 --> 00:10:40,441
因此,当需要时,他们必须在内存中,即时编译代码并执行它。
197
00:10:40,441 --> 00:10:41,441
因此,它们需要可写和可执行的内存区域。
198
00:10:41,441 --> 00:10:42,441
但是,即使我们有DEP,我们也可以使用像 ret2libc 这样的漏洞技术,不需要执行实际的 shellcode,
199
00:10:42,441 --> 00:10:43,441
但仍然需要一个 system。
200
00:10:43,441 --> 00:10:44,441
在将来的某个时候,我会制作一个关于面向返回编程的视频。
201
00:10:44,441 --> 00:10:44,451
这是这种技术的,下一个高级形态。