diff --git a/CHANGELOG b/CHANGELOG index 2cbd0fb..4b701dc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +2017-12-22:250 star 成就达成 2017-12-15:中文名“CTF从入门到放弃” 2017-12-13:开始写 Web 部分(@phantom0301) 2017-12-13:100 star 成就达成 diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md index c830a57..f2d9822 100644 --- a/CONTRIBUTION.md +++ b/CONTRIBUTION.md @@ -3,7 +3,11 @@ 市场上已经充斥着大量的 ACM 书籍,而 CTF 以其知识内容之分散、考察面之广泛、题目类型之多变,让许多新手不知所措,同时也加大了该方面书籍的编写难度。 -此书本着开源之精神,以分享他人提高自己为目的,将是一本大而全的 CTF 领域指南。因本人能力和时间有限,不可能精通竞赛中各个类别的知识,欢迎任何人提出建议或和我一起完成此书。 +此书本着开源之精神,以分享他人提高自己为目的,将是一本大而全的 CTF 领域指南。因本人能力和时间有限,不可能精通各个类别的知识,欢迎任何人提出任何建议,和我一起完成此书。 + +千万不要觉得自己是初学者就不敢提交 PR(issue),千万不要担心自己提交的 PR(issue) 会有问题,毕竟最后合并的人是我,背锅的也是我:) + +如果还有其他想法,请直接给我发邮件 firmianay@gmail.com。 **You think you understand something until you try to teach it.** diff --git a/doc/2.8_zio.md b/doc/2.8_zio.md index 1b4a903..45c7eee 100644 --- a/doc/2.8_zio.md +++ b/doc/2.8_zio.md @@ -45,7 +45,7 @@ io.read_until(">>") io.interact() ``` -需要注意的的是,zio 正在逐步被开发更活跃,功能更完善的 pwntools 取代,但如果你使用的是 32 位 Linux 系统,zio 可能是你唯一的选择。 +需要注意的的是,zio 正在逐步被开发更活跃,功能更完善的 pwntools 取代,但如果你使用的是 32 位 Linux 系统,zio 可能是你唯一的选择。而且在线下赛中,内网环境通常都没有 pwntools 环境,但 zio 是单个文件,上传到内网机器上就可以直接使用。 ## 安装 diff --git a/doc/3.3.4_rop.md b/doc/3.3.4_rop.md index 71cc090..8ad676e 100644 --- a/doc/3.3.4_rop.md +++ b/doc/3.3.4_rop.md @@ -414,7 +414,7 @@ Dump of assembler code for function usefulFunction: 0x0000000000400817 <+16>: ret End of assembler dump. ``` -64 位程序的第一个参数通过 edi 传递,所以我们在调用需要一个 gadgets 来将字符串的地址存进 edi。 +64 位程序的第一个参数通过 edi 传递,所以我们再调用需要一个 gadgets 来将字符串的地址存进 edi。 我们先找到需要的 gadgets: ``` diff --git a/doc/4.7_common_gadget.md b/doc/4.7_common_gadget.md index 3bc676d..bcb4977 100644 --- a/doc/4.7_common_gadget.md +++ b/doc/4.7_common_gadget.md @@ -93,8 +93,8 @@ ret #跳转到part2 ```python def com_gadget(part1, part2, jmp2, arg1 = 0x0, arg2 = 0x0, arg3 = 0x0): payload = p64(part1) # part1 entry pop_rbx_pop_rbp_pop_r12_pop_r13_pop_r14_pop_r15_ret - payload += p64(0x0) # rbx be 0x0 - payload += p64(0x1) # rbp be 0x1 + payload += p64(0x0) # rbx must be 0x0 + payload += p64(0x1) # rbp must be 0x1 payload += p64(jmp2) # r12 jump to payload += p64(arg1) # r13 -> edi arg1 payload += p64(arg2) # r14 -> rsi arg2 @@ -104,7 +104,7 @@ def com_gadget(part1, part2, jmp2, arg1 = 0x0, arg2 = 0x0, arg3 = 0x0): return payload ``` -上面的 gadget 是显而易见的,但如果有人精通汇编字节码,可以找到一些比较隐蔽的 gadget,比如将指定一个位移点再反编译: +上面的 gadget 是显而易见的,但如果有人精通汇编字节码,可以找到一些比较隐蔽的 gadget,比如说指定一个位移点再反编译: ``` gdb-peda$ disassemble /r 0x0000000000400831,0x0000000000400835 Dump of assembler code from 0x400831 to 0x400835: @@ -122,7 +122,7 @@ End of assembler dump. ``` `5e` 和 `5f` 分别是 `pop rsi` 和 `pop rdi` 的字节码,于是我们可以通过这种方法轻易地控制 `rsi` 和 `rdi`。 -在 6.1 pwn hctf2016 brop 的exp中,我们使用了偏移后的 `pop rdi; ret`,而没有用 `com_gadget()` 函数,感兴趣的童鞋可以尝试使用它重写exp。 +在 6.1.1 pwn HCTF2016 brop 的 exp 中,我们使用了偏移后的 `pop rdi; ret`,而没有用 `com_gadget()` 函数,感兴趣的童鞋可以尝试使用它重写 exp。 除了上面介绍的 `__libc_csu_init()`,还可以到下面的函数中找一找: ``` @@ -137,7 +137,7 @@ __libc_csu_init __libc_csu_fini _fini ``` -总之,多试试总不会错。 +总之,多试一试总不会错。 ## 参考资料 diff --git a/doc/4.8_dynelf.md b/doc/4.8_dynelf.md index 0255b3b..cf293ea 100644 --- a/doc/4.8_dynelf.md +++ b/doc/4.8_dynelf.md @@ -95,7 +95,7 @@ DynELF 使用了两种技术: ssize_t write(int fd, const void *buf, size_t count); ``` -write 函数用于向文件描述符中写入数据,三个参数分别是文件描述符,一个指针指向的数据和写入数据的长度。该函数的有点是可以读取任意长度的内存数据,即打印数据的长度只由 count 控制,缺点则是需要传递 3 个参数。32 位程序通过栈传递参数,直接将参数布置在栈上就可以了,而 64 位程序首先使用寄存器传递参数,所以我们通常使用通用 gadget(参见章节4.7) 来为 write 函数传递参数。 +write 函数用于向文件描述符中写入数据,三个参数分别是文件描述符,一个指针指向的数据和写入数据的长度。该函数的优点是可以读取任意长度的内存数据,即打印数据的长度只由 count 控制,缺点则是需要传递 3 个参数。32 位程序通过栈传递参数,直接将参数布置在栈上就可以了,而 64 位程序首先使用寄存器传递参数,所以我们通常使用通用 gadget(参见章节4.7) 来为 write 函数传递参数。 例子是 xdctf2015-pwn200,[文件地址](../src/writeup/6.2_pwn_xdctf2015_pwn200)。在这个程序中也只有 write 可以利用: ``` @@ -220,7 +220,7 @@ io.interactive() int puts(const char *s); ``` -puts 函数使用的参数只有一个,即需要输出的数据的起始地址,它会一直输出直到遇到 `\x00`,所以它输出的数据长度是不容易控制的,我们无法预料到零字符会出现在哪里,截止后,puts 还会自动在末尾加上换行符 `\n`。该函数的优点是在 64 位程序中也可以很方便地使用。缺点是会受到零字符截断的影响,在写 leak 函数时需要特殊处理,在打印出的数据中正确地筛选我们需要的部分,如果打印处了空字符串,则要手动赋值`\x00`,包括我们在 dump 内存的时候,也常常收这个问题的困扰,可以参考章节 6.1 dump 内存的部分。 +puts 函数使用的参数只有一个,即需要输出的数据的起始地址,它会一直输出直到遇到 `\x00`,所以它输出的数据长度是不容易控制的,我们无法预料到零字符会出现在哪里,截止后,puts 还会自动在末尾加上换行符 `\n`。该函数的优点是在 64 位程序中也可以很方便地使用。缺点是会受到零字符截断的影响,在写 leak 函数时需要特殊处理,在打印出的数据中正确地筛选我们需要的部分,如果打印出了空字符串,则要手动赋值`\x00`,包括我们在 dump 内存的时候,也常常受这个问题的困扰,可以参考章节 6.1 dump 内存的部分。 所以我们常常需要这样做: ```python diff --git a/doc/5.11_retdec.md b/doc/5.11_retdec.md index 048efd5..3e73031 100644 --- a/doc/5.11_retdec.md +++ b/doc/5.11_retdec.md @@ -80,6 +80,10 @@ a.out a.out.c a.out.c.backend.bc a.out.c.backend.ll a.out.c.frontend.dsm a. - 核心阶段:接下来才是重头戏,调用 `bin2llvmir` 将二进制文件转换成 LLVM IR,并输出 `a.out.c.frontend.dsm`、`a.out.c.backend.ll` 和 `a.out.c.backend.bc` - 后端阶段:这个阶段通过一系列代码优化和生成等操作,将 LLVM IR 反编译成 C 代码 `a.out.c`,还有 CFG 等。 +整个过程的结构如下: + +![](../pic/5.11_top_level.png) + `decompile.sh` 有很多选项,使用 `decompile.sh -h` 查看。 比如反编译指定函数: diff --git a/doc/6.1.1_pwn_hctf2016_brop.md b/doc/6.1.1_pwn_hctf2016_brop.md index ad08f35..f1ae1d5 100644 --- a/doc/6.1.1_pwn_hctf2016_brop.md +++ b/doc/6.1.1_pwn_hctf2016_brop.md @@ -61,7 +61,7 @@ done ## BROP 原理及题目解析 -BROP 即 Blind ROP,需要我们在无法获得二进制文件的情况下,通过 ROP 进行远程攻击,劫持该应用程序的控制流,可用于开启了 ASLR、NX和栈canaries的 64-bit Linux。这一概念是是在 2014 年提出的,论文和幻灯片在参考资料中。 +BROP 即 Blind ROP,需要我们在无法获得二进制文件的情况下,通过 ROP 进行远程攻击,劫持该应用程序的控制流,可用于开启了 ASLR、NX 和栈 canary 的 64-bit Linux。这一概念是是在 2014 年提出的,论文和幻灯片在参考资料中。 实现这一攻击有两个必要条件: 1. 目标程序存在一个栈溢出漏洞,并且我们知道怎样去触发它 @@ -92,7 +92,7 @@ def get_buffer_size(): ``` [*] buffer size: 72 ``` -要注意的是,崩溃意味着我们覆盖到了返回地址,所以缓冲区应该是发送的字符数减一,即 buf(64)+ebp(8)=72。该题并没有开启canary,所以跳过爆破的过程。 +要注意的是,崩溃意味着我们覆盖到了返回地址,所以缓冲区应该是发送的字符数减一,即 buf(64)+ebp(8)=72。该题并没有开启 canary,所以跳过爆破的过程。 #### stop gadget 在寻找通用 gadget 之前,我们需要一个 stop gadget。一般情况下,当我们把返回地址覆盖后,程序有很大的几率会挂掉,因为所覆盖的地址可能并不是合法的,所以我们需要一个能够使程序正常返回的地址,称作 stop gadget,这一步至关重要。stop gadget 可能不止一个,这里我们之间返回找到的第一个好了: diff --git a/pic/5.11_top_level.png b/pic/5.11_top_level.png new file mode 100644 index 0000000..5a97935 Binary files /dev/null and b/pic/5.11_top_level.png differ