- 简介
- 一、基础知识篇
- 二、工具篇
- 三、分类专题篇
- 四、技巧篇
- 五、高级篇
- 六、题解篇
- 6.1 Pwn
- 6.1.1 pwn HCTF2016 brop
- 6.1.2 pwn NJCTF2017 pingme
- 6.1.3 pwn XDCTF2015 pwn200
- 6.1.4 pwn BackdoorCTF2017 Fun-Signals
- 6.1.5 pwn GreHackCTF2017 beerfighter
- 6.1.6 pwn DefconCTF2015 fuckup
- 6.1.7 pwn 0CTF2015 freenote
- 6.1.8 pwn DCTF2017 Flex
- 6.1.9 pwn RHme3 Exploitation
- 6.1.10 pwn 0CTF2017 BabyHeap2017
- 6.1.11 pwn 9447CTF2015 Search-Engine
- 6.1.12 pwn N1CTF2018 vote
- 6.1.13 pwn 34C3CTF2017 readme_revenge
- 6.1.14 pwn 32C3CTF2015 readme
- 6.1.15 pwn 34C3CTF2017 SimpleGC
- 6.1.16 pwn HITBCTF2017 1000levels
- 6.1.17 pwn SECCONCTF2016 jmper
- 6.1.18 pwn HITBCTF2017 Sentosa
- 6.1.19 pwn HITBCTF2018 gundam
- 6.1.20 pwn 33C3CTF2016 babyfengshui
- 6.1.21 pwn HITCONCTF2016 Secret_Holder
- 6.1.22 pwn HITCONCTF2016 Sleepy_Holder
- 6.1.23 pwn BCTF2016 bcloud
- 6.1.24 pwn HITCONCTF2016 HouseofOrange
- 6.1.25 pwn HCTF2017 babyprintf
- 6.1.26 pwn 34C3CTF2017 300
- 6.1.27 pwn SECCONCTF2016 tinypad
- 6.1.28 pwn ASISCTF2016 b00ks
- 6.1.29 pwn Insomni'hackteaserCTF2017 TheGreatEscapepart-3
- 6.1.30 pwn HITCONCTF2017 Ghostinthe_heap
- 6.1.31 pwn HITBCTF2018 mutepig
- 6.1.32 pwn SECCONCTF2017 vmnofun
- 6.1.33 pwn 34C3CTF2017 LFA
- 6.1.34 pwn N1CTF2018 memsafety
- 6.1.35 pwn 0CTF2018 heapstorm2
- 6.1.36 pwn NJCTF2017 messager
- 6.1.37 pwn sixstarctf2018 babystack
- 6.1.38 pwn HITCONCMT2017 pwn200
- 6.1.39 pwn BCTF2018 houseofAtum
- 6.1.40 pwn LCTF2016 pwn200
- 6.1.41 pwn PlaidCTF2015 PlaidDB
- 6.1.42 pwn hacklu2015 bookstore
- 6.1.43 pwn 0CTF2018 babyheap
- 6.1.44 pwn ASIS2017 start_hard
- 6.1.45 pwn LCTF2016 pwn100
- 6.2 Reverse
- 6.3 Web
- 6.1 Pwn
- 七、实战篇
- 7.1 CVE
- 7.1.1 CVE-2017-11543 tcpdump sliplink_print 栈溢出漏洞
- 7.1.2 CVE-2015-0235 glibc _nsshostnamedigitsdots 堆溢出漏洞
- 7.1.3 CVE-2016-4971 wget 任意文件上传漏洞
- 7.1.4 CVE-2017-13089 wget skipshortbody 栈溢出漏洞
- 7.1.5 CVE–2018-1000001 glibc realpath 缓冲区下溢漏洞
- 7.1.6 CVE-2017-9430 DNSTracer 栈溢出漏洞
- 7.1.7 CVE-2018-6323 GNU binutils elfobjectp 整型溢出漏洞
- 7.1.8 CVE-2010-2883 Adobe CoolType SING 表栈溢出漏洞
- 7.1.9 CVE-2010-3333 Microsoft Word RTF pFragments 栈溢出漏洞
- 7.1 CVE
- 八、学术篇
- 8.1 The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls (on the x86)
- 8.2 Return-Oriented Programming without Returns
- 8.3 Return-Oriented Rootkits: Bypassing Kernel Code Integrity Protection Mechanisms
- 8.4 ROPdefender: A Detection Tool to Defend Against Return-Oriented Programming Attacks
- 8.5 Data-Oriented Programming: On the Expressiveness of Non-Control Data Attacks
- 8.7 What Cannot Be Read, Cannot Be Leveraged? Revisiting Assumptions of JIT-ROP Defenses
- 8.9 Symbolic Execution for Software Testing: Three Decades Later
- 8.10 AEG: Automatic Exploit Generation
- 8.11 Address Space Layout Permutation (ASLP): Towards Fine-Grained Randomization of Commodity Software
- 8.13 New Frontiers of Reverse Engineering
- 8.14 Who Allocated My Memory? Detecting Custom Memory Allocators in C Binaries
- 8.21 Micro-Virtualization Memory Tracing to Detect and Prevent Spraying Attacks
- 8.22 Practical Memory Checking With Dr. Memory
- 8.23 Evaluating the Effectiveness of Current Anti-ROP Defenses
- 8.24 How to Make ASLR Win the Clone Wars: Runtime Re-Randomization
- 8.25 (State of) The Art of War: Offensive Techniques in Binary Analysis
- 8.26 Driller: Augmenting Fuzzing Through Selective Symbolic Execution
- 8.27 Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware
- 8.28 Cross-Architecture Bug Search in Binary Executables
- 8.29 Dynamic Hooks: Hiding Control Flow Changes within Non-Control Data
- 8.30 Preventing brute force attacks against stack canary protection on networking servers
- 8.33 Under-Constrained Symbolic Execution: Correctness Checking for Real Code
- 8.34 Enhancing Symbolic Execution with Veritesting
- 8.38 TaintEraser: Protecting Sensitive Data Leaks Using Application-Level Taint Tracking
- 8.39 DART: Directed Automated Random Testing
- 8.40 EXE: Automatically Generating Inputs of Death
- 8.41 IntPatch: Automatically Fix Integer-Overflow-to-Buffer-Overflow Vulnerability at Compile-Time
- 8.42 Dynamic Taint Analysis for Automatic Detection, Analysis, and Signature Generation of Exploits on Commodity Software
- 8.43 DTA++: Dynamic Taint Analysis with Targeted Control-Flow Propagation
- 8.44 Superset Disassembly: Statically Rewriting x86 Binaries Without Heuristics
- 8.45 Ramblr: Making Reassembly Great Again
- 8.46 FreeGuard: A Faster Secure Heap Allocator
- 8.48 Reassembleable Disassembling
- 九、附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
6.2.3 re CodegateCTF2017 angrybird
题目解析
看题目就知道,这是一个会让我们抓狂的程序,事实也确实如此。
$ file angrybird_org
angrybird: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=089c3a14bcd7ffb08e94645cea46f1162b171445, stripped
$ ./angrybird_org
$
一运行就退出,应该是需要程序流上有问题。
main
函数的开头有一些坑需要 patch,才能使程序正常运行,然后经过很多很多轮的运算和判断,可以看到 main 函数长达 18555 行:
[0x00400600]> pd 60 @ main
/ (fcn) main 18555
| main ();
| : ; DATA XREF from 0x0040061d (entry0)
| : 0x00400761 55 push rbp
| : 0x00400762 4889e5 mov rbp, rsp
| : 0x00400765 4883c480 add rsp, 0xffffffffffffff80
| : 0x00400769 64488b042528. mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
| : 0x00400772 488945f8 mov qword [local_8h], rax
| : 0x00400776 31c0 xor eax, eax ; 将 eax 置 0
| : 0x00400778 83f800 cmp eax, 0 ; 比较 eax 和 0
| `=< 0x0040077b 0f845ffeffff je sym.imp.exit ; eax == 0 时退出,所以需要将 je 换成 jne,或者把上一行的 0 换成 1
| 0x00400781 48c745901860. mov qword [local_70h], reloc.strncmp_24 ; 0x606018
| 0x00400789 48c745982060. mov qword [local_68h], reloc.puts_32 ; 0x606020
| 0x00400791 48c745a02860. mov qword [local_60h], reloc.__stack_chk_fail_40 ; 0x606028
| 0x00400799 48c745a83860. mov qword [local_58h], reloc.__libc_start_main_56 ; 0x606038
| 0x004007a1 b800000000 mov eax, 0
| 0x004007a6 e84bffffff call sub.you_should_return_21_not_1_:__6f6 ; 该函数中需要返回 21
| 0x004007ab 89458c mov dword [local_74h], eax ; [local_74] = 21
| 0x004007ae b800000000 mov eax, 0
| 0x004007b3 e854ffffff call sub.stack_check_70c ; 栈检查函数,直接 nop 掉,或者进入函数修改逻辑
| 0x004007b8 b800000000 mov eax, 0
| 0x004007bd e868ffffff call sub.hello_72a
| 0x004007c2 488b15a75820. mov rdx, qword [obj.stdin] ; [0x606070:8]=0 ; 从标准输入读入
| 0x004007c9 8b4d8c mov ecx, dword [local_74h]
| 0x004007cc 488d45b0 lea rax, [local_50h]
| 0x004007d0 89ce mov esi, ecx ; esi = 21
| 0x004007d2 4889c7 mov rdi, rax
| 0x004007d5 e8f6fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream) ; patch 成功后就能调用 fgets
| 0x004007da 0fb655b0 movzx edx, byte [local_50h] ; 读入的第一个字符
| 0x004007de 0fb645b1 movzx eax, byte [local_4fh] ; 读入的第二个字符
| 0x004007e2 31d0 xor eax, edx
| 0x004007e4 8845d0 mov byte [local_30h], al
| 0x004007e7 0fb645d0 movzx eax, byte [local_30h]
| 0x004007eb 3c0f cmp al, 0xf ; 15 ; 对处理后的输入字符做判断
| ,=< 0x004007ed 7f14 jg 0x400803 ; 若不满足条件,跳转失败,程序退出
| | 0x004007ef bf94504000 mov edi, str.melong ; 0x405094 ; "melong"
| | 0x004007f4 e897fdffff call sym.imp.puts ; int puts(const char *s)
| | 0x004007f9 bf01000000 mov edi, 1
| | 0x004007fe e8ddfdffff call sym.imp.exit ; void exit(int status)
| | ; JMP XREF from 0x004007ed (main)
| `-> 0x00400803 0fb655b0 movzx edx, byte [local_50h] ; 第二轮运算
| 0x00400807 0fb645b1 movzx eax, byte [local_4fh]
| 0x0040080b 21d0 and eax, edx
| 0x0040080d 8845d0 mov byte [local_30h], al
| 0x00400810 0fb645d0 movzx eax, byte [local_30h]
| 0x00400814 3c50 cmp al, 0x50 ; 'P' ; 80
| ,=< 0x00400816 7e14 jle 0x40082c
| | 0x00400818 bf94504000 mov edi, str.melong ; 0x405094 ; "melong"
| | 0x0040081d e86efdffff call sym.imp.puts ; int puts(const char *s)
| | 0x00400822 bf01000000 mov edi, 1
| | 0x00400827 e8b4fdffff call sym.imp.exit ; void exit(int status)
| | ; JMP XREF from 0x00400816 (main)
| `-> 0x0040082c c645d000 mov byte [local_30h], 0 ; 第三轮运算
| 0x00400830 0fb645d0 movzx eax, byte [local_30h]
| 0x00400834 3c01 cmp al, 1 ; 1
| ,=< 0x00400836 7e14 jle 0x40084c
| | 0x00400838 bf94504000 mov edi, str.melong ; 0x405094 ; "melong"
| | 0x0040083d e84efdffff call sym.imp.puts ; int puts(const char *s)
| | 0x00400842 bf01000000 mov edi, 1
| | 0x00400847 e894fdffff call sym.imp.exit ; void exit(int status)
| | ; JMP XREF from 0x00400836 (main)
| `-> 0x0040084c 0fb655c2 movzx edx, byte [local_3eh] ; 第 n 轮运算
| 0x00400850 0fb645b1 movzx eax, byte [local_4fh]
| 0x00400854 21d0 and eax, edx
| 0x00400856 8845d0 mov byte [local_30h], al
| 0x00400859 0fb645d0 movzx eax, byte [local_30h]
第一处 patch,将指令 je
改成 jne
:
[0x00400600]> s 0x0040077b
[0x0040077b]> pd 1
| `=< 0x0040077b 0f845ffeffff je sym.imp.exit
[0x0040077b]> wx 0f85
[0x0040077b]> pd 1
| `=< 0x0040077b 0f855ffeffff jne sym.imp.exit
第二处 patch,函数 sub.you_should_return_21_not_1_:__6f6
:
[0x0040077b]> pdf @ sub.you_should_return_21_not_1_:__6f6
/ (fcn) sub.you_should_return_21_not_1_:__6f6 22
| sub.you_should_return_21_not_1_:__6f6 ();
| ; CALL XREF from 0x004007a6 (main)
| 0x004006f6 55 push rbp
| 0x004006f7 4889e5 mov rbp, rsp
| 0x004006fa bf64504000 mov edi, str.you_should_return_21_not_1_:_ ; 0x405064 ; "you should return 21 not 1 :("
| 0x004006ff e88cfeffff call sym.imp.puts ; int puts(const char *s)
| 0x00400704 8b0556592000 mov eax, dword [0x00606060] ; [0x606060:4]=1 ; 修改 [0x606060:4] = 21 = 0x15
| 0x0040070a 5d pop rbp
\ 0x0040070b c3 ret
[0x0040077b]> ?v 21
0x15
[0x0040077b]> s 0x00606060
[0x00606060]> px 16
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x00606060 0100 0000 0000 0000 0000 0000 0000 0000 ................
[0x00606060]> wx 15
[0x00606060]> px 16
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x00606060 1500 0000 0000 0000 0000 0000 0000 0000 ................
另外该函数结尾处指令是 pop rbp
,而不是正确情况下的 leave
,我们把它改过来:
[0x00606060]> s 0x0040070a
[0x0040070a]> pd 1
| 0x0040070a 5d pop rbp
[0x0040070a]> wx c9
[0x0040070a]> pd 1
| 0x0040070a c9 leave
第三处 patch,将调用 sub.stack_check_70c
的指令直接 nop
掉:
[0x00606060]> pdf @ sub.stack_check_70c
/ (fcn) sub.stack_check_70c 30
| sub.stack_check_70c ();
| : ; CALL XREF from 0x004007b3 (main)
| : 0x0040070c 55 push rbp
| : 0x0040070d 4889e5 mov rbp, rsp
| : 0x00400710 bf82504000 mov edi, str.stack_check ; 0x405082 ; "stack check"
| : 0x00400715 e876feffff call sym.imp.puts ; int puts(const char *s)
| : 0x0040071a 678b0424 mov eax, dword [esp]
| : 0x0040071e 83f800 cmp eax, 0
| `=< 0x00400721 0f85b9feffff jne sym.imp.exit
| 0x00400727 90 nop
| 0x00400728 5d pop rbp
\ 0x00400729 c3 ret
[0x00400600]> s 0x004007b3
[0x004007b3]> pd 1
| 0x004007b3 e854ffffff call sub.stack_check_70c
[0x004007b3]> wx 9090909090
[0x004007b3]> pd 5
| 0x004007b3 90 nop
| 0x004007b4 90 nop
| 0x004007b5 90 nop
| 0x004007b6 90 nop
| 0x004007b7 90 nop
第四处 patch 是将 sub.hello_72a
函数中的 je
改成 jne
:
[0x0040077b]> pdf @ sub.hello_72a
/ (fcn) sub.hello_72a 55
| sub.hello_72a ();
| ; var int local_8h @ rbp-0x8
| ; CALL XREF from 0x004007bd (main)
| 0x0040072a 55 push rbp
| 0x0040072b 4889e5 mov rbp, rsp
| 0x0040072e 4883ec10 sub rsp, 0x10
| 0x00400732 48c745f83860. mov qword [local_8h], reloc.__libc_start_main_56 ; 0x606038
| 0x0040073a 488b45f8 mov rax, qword [local_8h]
| 0x0040073e ba05000000 mov edx, 5
| 0x00400743 be8e504000 mov esi, str.hello ; 0x40508e ; "hello"
| 0x00400748 4889c7 mov rdi, rax
| 0x0040074b e830feffff call sym.imp.strncmp ; int strncmp(const char *s1, const char *s2, size_t n) ; 如果相等则返回 0
| 0x00400750 85c0 test eax, eax
| ,=< 0x00400752 740a je 0x40075e ; 如果 eax 为 0,则跳转
| | 0x00400754 bf01000000 mov edi, 1
| | 0x00400759 e882feffff call sym.imp.exit ; void exit(int status)
| | ; JMP XREF from 0x00400752 (sub.hello_72a)
| `-> 0x0040075e 90 nop
| 0x0040075f c9 leave
\ 0x00400760 c3 ret
总的来说就是修改了下面几个地方:
$ radiff2 angrybird_org angrybird_mod
0x0000070a 5d => c9 0x0000070a
0x00000722 85 => 84 0x00000722
0x00000752 74 => 75 0x00000752
0x0000077c 84 => 85 0x0000077c
0x000007b3 e854ffffff => 9090909090 0x000007b3
0x00006060 01 => 15 0x00006060
这样程序的运行就正常了,它从标准输入读入字符,进行一系列的判断,由于程序执行流非常长,我们不可能一个一个地去 patch。radare2 里输入命令 VV @ main
可以看到下面的东西:
不如使用 angr 来解决它,指定好目标地址,让它运行到那儿,在大多数情况下,这种方法都是有效的。
[0x00400761]> pd -20 @ main+18555
| 0x00404f8e d00f ror byte [rdi], 1
| 0x00404f90 b645 mov dh, 0x45 ; 'E' ; 69
| 0x00404f92 d03c78 sar byte [rax + rdi*2], 1
| ,=< 0x00404f95 7e14 jle 0x404fab
| | 0x00404f97 bf94504000 mov edi, str.melong ; 0x405094 ; "melong"
| | 0x00404f9c e8efb5ffff call sym.imp.puts ; int puts(const char *s)
| | 0x00404fa1 bf01000000 mov edi, 1
| | 0x00404fa6 e835b6ffff call sym.imp.exit ; void exit(int status)
| | ; JMP XREF from 0x00404f95 (main)
| `-> 0x00404fab 488d45b0 lea rax, [local_50h]
| 0x00404faf 4889c6 mov rsi, rax
| 0x00404fb2 bf9b504000 mov edi, str.you_typed_:__s_n ; 0x40509b ; "you typed : %s\n"
| 0x00404fb7 b800000000 mov eax, 0
| 0x00404fbc e8efb5ffff call sym.imp.printf ; int printf(const char *format)
| 0x00404fc1 b800000000 mov eax, 0
| 0x00404fc6 488b4df8 mov rcx, qword [local_8h]
| 0x00404fca 6448330c2528. xor rcx, qword fs:[0x28]
| ,=< 0x00404fd3 7405 je 0x404fda
| | 0x00404fd5 e8c6b5ffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
| | ; JMP XREF from 0x00404fd3 (main)
| `-> 0x00404fda c9 leave ; 选择一个目标地址
\ 0x00404fdb c3 ret
因为每次错误退出之前,都会调用 puts
函数,所以应该避免其出现,将地址设置为参数 avoid。
[0x00400600]> is~puts
vaddr=0x00400590 paddr=0x00000590 ord=002 fwd=NONE sz=16 bind=GLOBAL type=FUNC name=imp.puts
对于使用 angr 来说,上面的 patch 完全没有必要,只要选择一个合适的初始化地址,如 0x004007da
,也就是 fget
函数的下一条指令,就可以跑出结果:
import angr
main = 0x004007da
find = 0x00404fda # leave;ret
avoid = 0x00400590 # puts@plt
p = angr.Project('./angrybird_org')
init = p.factory.blank_state(addr=main)
pg = p.factory.simgr(init, threads=4)
ex = pg.explore(find=find, avoid=avoid)
final = ex.found[0].state
flag = final.posix.dumps(0)
print "Flag:", final.posix.dumps(1)
Bingo!!!(不能保证每次都有效,多试几次)
$ python2 solve.py
WARNING | 2017-12-03 17:33:58,544 | angr.state_plugins.symbolic_memory | Concretizing symbolic length. Much sad; think about implementing.
Flag: you typed : Im_so_cute&pretty_:)�
然后用我们 patch 过的程序来验证 flag:
$ ./angrybird_mod
you should return 21 not 1 :(
Im_so_cute&pretty_:)
you typed : Im_so_cute&pretty_:)
同样需要一定的运气才能通过,祝好运:)
参考资料
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论