作者:swing
本文為作者投稿,Seebug Paper 期待你的分享,凡經采用即有禮品相送! 投稿郵箱:paper@seebug.org
這兩天和 @leommxj 一起分析了和寫了一下 CVE-2023-27997 的漏洞利用, 順便一想想 CVE-2022-42475 這個漏洞也過去蠻久的了,于是準備把這篇 CVE-2022-42475 漏洞分析分享出來。注:本文不含完整的漏洞利用腳本。
下圖為 CVE-2023-27997 的利用錄屏, 與本文要講的 CVE-2022-42475 無關
I learned a lot from @cfreal_ , and it's great to write exploits together with @leommxj.#CVE-2023-27997 pic.twitter.com/nEFndgvoVD
— swing (@bestswngs) June 17, 2023
正文
2022 年 12 月 12 日,Fortinet 官方發布了影響 FortiGate SSLVPN 的 RCE 漏洞 CVE-2022-42475 相關信息。本文對此漏洞的成因進行分析。
1. 環境配置
測試版本為 7.2.2, 環境的安裝和部署可以參考這篇文章: https://blog.csdn.net/meigang2012/article/details/87903878。 另外感謝下 @explorer 網管大哥在部署環境上的幫助。
導入虛擬機后需要配置下網絡, 參考 https://docs.fortinet.com/document/fortigate-private-cloud/7.2.0/vmware-esxi-administration-guide/615472/configuring-port-1
有個重點 dns 要設置下:
config system dns
set primary <Primary DNS server>
set secondary <Secondary DNS server>
end
# The default DNS servers are 208.91.112.53 and 208.91.112.52.
需要配置一個 sslvpn ,然后能訪問即可。
2. 設備權限獲取
掛載虛擬機 vmdk 硬盤后, 可以看到有個 rootfs.gz 文件。
root@Jas-22:/home/user/Desktop/fuck-fortigate/rootfs# ls
bin bin.tar.xz boot data data2 dev etc fortidev init lib lib64 migadmin.tar.xz node-scripts.tar.xz proc sbin sys tmp usr usr.tar.xz usr.tar.xz.chk var
可以看到有如下內容, 我們需要進一步解壓 bin.tar.xz
文件夾,使用 sbin 目錄自帶的命令解壓
chroot . sbin/ftar -cf bin.tar bin
chroot . sbin/xz --check=sha256 -e bin.tar
然后需要在 bin 目錄中放入后門,第一個是生成一個反彈shell替換 smartctl 文件, 以及我這里放入一個 busybox ,做一個軟鏈接 ln -sn /bin/busybox bin/sh
設備中默認沒有 bash (sh)文件 (或者說他的 sh 功能比較雞肋), 然后重新打包。
# 重新打包 bin 文件夾
chroot . sbin/ftar -cf bin.tar bin
chroot . sbin/xz --check=sha256 -e bin.tar
# 重新打包 rootfs
find . | cpio -H newc -o > ../rootfs.raw
cat ./rootfs.raw | gzip > rootfs.gz
重打包完成后, 我們需要過幾個校驗,才能正常啟動系統。
vmlinux :
解下來是 bin/init:
這里會校驗 fgtsum , 失敗直接給你重啟最后是 rootfs 檢查
由于,我是采用 vmware + gdb 的調試方式, 即 使用VMware和GDB進行Linux內核調試 (bestwing.me), 因此我直接寫了一個 gdb python 腳本動態修改返回值即可:
這里皮一句,依稀記得這段代碼是 chatGPT 幫我生成的。
import gdb
class SetRaxBreakpoint(gdb.Breakpoint):
def __init__(self, bp_expr, rax_value, temporary=False):
gdb.Breakpoint.__init__(self, bp_expr, gdb.BP_BREAKPOINT, False, temporary )
# super(SetRaxBreakpoint, self).__init__(spec, temporary)
self.rax_value = rax_value
self.silent = True
def stop(self):
gdb.execute('set $rax = {}'.format(self.rax_value))
gdb.execute('set architecture i386:x86-64')
gdb.execute('set pagination off')
r1 = SetRaxBreakpoint('*0xffffffff807ac11c', 0)
r2 = SetRaxBreakpoint('*0x4518C9', 1) #
r3 = SetRaxBreakpoint('*0x277fccc', 1)
當系統成功執行后,使用 diagnose hardware smartctl
即可運行我們的后門文件。
3. 漏洞分析
Root Cause
在處理 用戶 post 數據的時候,
會根據 http header 中的 content-lenght
字段分配 buffer , 然而在分配之前, 即在調用 pool_alloc 函數之前
pool_alloc 有兩個參數, 第二個為即將要分配的buffer 大小
rax 為用戶請求結構體指針,偏移位置 0x18 存放了 CL 值。先將 CL 放在 eax 寄存器中,使用 lea 指令將其加一后放在 esi 寄存器,再用 movsxd 擴展為 64 bit 值。
在調用 pool_alloc 函數時使用 32 位數值 + 1 拓展成 64 位的方法,這里存在整數溢出。那么我們可以構造特殊的 CL 值,比如 0x1b00000000,經過運算拓展之后會變成 0x1 。會分配一個小的內存空間導致溢出
上面這個是斷點是初始化 buffer , 可以看到大小是 1, 之后在 memcpy 處就 會溢出。
exploit
這里首先明確一下,我不會公開完整的利用,這里這提一點利用上的思路。利用整體思路參考 Orange 2017 年的文章, 大致思路就是進行進行競爭, 一邊在堆上布局 SSL 結構體,一邊觸發漏洞,然后溢出覆蓋 SSL 結構體。
之后就可以控制 PC , 當我們控制 PC 后我們需要確定 padding , 這個步驟比較繁瑣, 我拿 PoC 改了一個循環 fuzz 的腳本。
def do_exploit(padding):
...
payload = p64(ret) * padding + 'A' * 0x1000
...
for i in range(123, 384):
padding = int(i )
if do_exploit(padding):
continue
else:
print('timeout ...')
break
然后對 ret 這個gadget 下一個斷點, 當觸發斷點的時候, 腳本會因為timeout 觸發異常,然后這附近大概就是咱們的padding。
然后這個時候只需要找棧遷移的gadget 即可。這里我找了的 push rdx ; add bl, byte ptr [rbx + 0x41] ; pop rsp ; pop rbp ; ret
, 這個gadget , 正好可以將棧遷移到 rdi 寄存器所指向的內存地址上。然后將剩下 ret 指令的替換成 pop rax ; ret
這樣的gadget, 這樣就能一直遷移到可控制的 AAAAAAA
的地方進行 rop 鏈了。
再閱讀這一部分的內容,我突然反應過來其實不需要替換指令, 當前的 ret 指令就足夠遷移到可控的
AAAA
的位置進行 ROP 了
由于 fortigate 的這個程序很大,正如 CVE-2023-27997 的作者所說的,
該程序很大,想找到適合的 gadget 來組成 ropchain 僅僅需要花費一點時間就行了。
4. 參考連接
[2]Configuring port 1 | FortiGate Private Cloud 7.2.0 (fortinet.com)
[3]attacking-ssl-vpn-part-2-breaking-the-Fortigate-ssl-vpn
[4]使用VMware和GDB進行Linux內核調試 (bestwing.me)
[5]CVE-2022-42475 | CataLpa's Site (wzt.ac.cn)
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/2082/
暫無評論