CVE-2020-25223-Sophos UTM WebAdmin 遠程命令執行漏洞分析
漏洞概述
Sophos公司創建于1985年,致力于提供最好的IT安全及數據保護。其產品 Sophos UTM 9 在2020年9月被披露存在遠程命令執行漏洞:
官方通報
https://community.sophos.com/b/security-blog/posts/advisory-resolved-rce-in-sg-utm-webadmin-cve-2020-25223

影響版本:before v9.705 MR5, v9.607 MR7, and v9.511 MR11。
環境搭建
從官方網站下載固件,是一個完整打包好的ISO文件,直接使用 VMware Workstation 安裝,然后通過`https://192.168.247.200:4444/`即可訪問UTM頁面。
Sophos UTM 默認不允許 ssh 訪問,必須在 `Dashboard - Management - System Settings - Shell Access` 中進行開啟,并勾選 `“Allow password authentication”`。

然后就可以通過 shell 登錄,查看底層文件系統:

固件提取
Sophos UTM 9 的主體處理程序是 `/var/sec/chroot-httpd/var/webadmin/webadmin.plx`,借助 IDA Pro 工具可以快速確定該文件是 Perl 腳本使用 PerlAPP 工具編譯得到的 ELF 文件。
# file /var/sec/chroot-httpd/var/webadmin/webadmin.plx /var/sec/chroot-httpd/var/webadmin/webadmin.plx: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), stripped
如何反編譯 Perl ELF 文件,網絡上沒有任何有價值的信息。但 Justin Kennedy 在其文章中介紹了如何反編譯 `webadmin.plx`:
#!/bin/bash # usage: bfs_extract.sh webadmin.plx extracted/ python3 ./yank.py $1 stage1-$1 python3 ./bfs.py stage1-$1 stage2-$1 python3 ./bfs_extract.py stage2-$1 $2
其中,`bsf.py` 和 `bfs_extract.py` 腳本可從以下鏈接獲取:
bsf.py 和 bfs_extract.py https://github.com/the6p4c/bfs_extract
`yank.py` 腳本可從 `Justin Kennedy` 文章獲取。但我們發現,該方法只能夠正常反編譯`webadmin.plx`,對其他 `.plx` 文件會出錯,需要對 `bsf.py` 腳本進行如下修改才能使用所有 `.plx `文件。
with open(sys.argv[1], 'rb') as f, open(sys.argv[2], 'wb') as f2:
-- header = f.read(4 * 4)
++ header = f.read(4 * 3 + 2)
-- sig, unk, size, data_offset = struct.unpack('<IIII', header)
++ sig, unk, size, data_offset = struct.unpack('<IIIH', header)
++ dataoffset = dataoffset & 0xFFFF
至此,能夠完整獲取 Sophos UTM 9 的 Perl 源代碼,從而支撐后續的代碼審計和漏洞挖掘分析。
漏洞分析
通過版本比對發現,新版本代碼在 `switch_session` 子例程中對 `$sid` 增加檢查,確保`$sid`不包含字母數字以外的任何其他字符。所以漏洞應該是和`$sid`有關。

進行源碼審計,澄清`$sid`的傳遞過程:
1. webadmin.plx/asg.plx:
$sys->do_connect($config->{backend_address}, $req->{SID}) : undef;
↓
2. webadmin.plx/wfe/asg/modules/asg_connector.pm: ( sub do_connect )
return $self->switch_session(@_);
↓
3. webadmin.plx/wfe/asg/modules/asg_connector.pm: ( sub switch_session )
unless( $self->connect() ) {
↓
4. webadmin.plx/Astaro/ConfdPlRPC.pm: ( sub connect )
return $self->{sys} if $self->_login();
↓
5. confd.plx/sys.pm: ( sub new )
# Find out who we are
if( defined $options->{username} && defined $options->{password} && $options->{ip} ){
# ...
} elsif( $options->{SID} && $options->{ip} ){
# Attempt to instantiate from existing session.
my $pickup = Astaro::file::read_storage("$config::session_dir/$options->{SID}");
# ...
}
↓
6. webadmin.plx/Astaro/file.pm: ( sub read_storage )
eval { local $SIG{'__DIE__'}; $href = Storable::lock_retrieve($file); };
↓
7. webadmin.plx/Storable.pm: ( sub lock_retrieve )
_retrieve($_[0], 1);
↓
8. webadmin.plx/Storable.pm: ( sub _retrieve )
open(FILE, $file) || logcroak "can't open $file: $!";
可以看到,前端發送攜帶 `SID` 值的請求,與后端 confd 建立連接,confd 首先會檢查 `$config::session_dir` 目錄中是否存在名稱為 `$options->{SID}` 的文件(檢查該 session id 是否有效)。
很顯然,上述過程將用戶可控的 `SID` 值直接作為 `open()` 函數的第二個參數,從而導致命令注入!
通過閱讀 `webadmin.plx/asg.plx` 源碼,可以發現,共有三個地方調用 `do_connect()` 函數,因此最多有三種方式可以觸發漏洞。
0x01 通過terminate功能觸發
源碼:
if( edv( $req, 'terminate' ) ) {
# ...
$SID = $sys ? $sys->do_connect($config->{backend_address}, $req->{SID}) : undef;
# ...
}
POC就不放出來了。
0x02 通過POST請求觸發
源碼:
if ( $ENV{'REQUEST_METHOD'} eq 'POST' ) {
# ...
$SID = $sys ? $sys->do_connect($config->{backend_address}, $req->{SID}) : undef;
# ...
}
0x03 通過GET請求觸發
源碼:
if ( edv( \%ENV, 'HTTP_COOKIE' ) ) {
foreach my $cookie (split /\; /,$ENV{HTTP_COOKIE}) {
$cookies->{$1} = $2 if $cookie =~ /^(.+?)\=(.+)$/;
}
}
# ...
# GET request
if ( $ENV{REQUEST_URI} =~ /^\/var\/(.+)/ ) {
my $path = $ENV{REQUEST_URI};
$path =~ s/^\///;
$path =~ s/\%([A-Fa-f0-9]{2})/chr(hex($1))/eg;
$path =~ s/(rrd-images[a-zA-Z0-9\/_\.]+\.png)(\?[0-9]+)/$1/ig; # removing timestamp information for path checking of rrd-images
if ( -e $path ) {
if ( $cookies->{SID} and ( $cookies->{SID} eq $SID or $SID = $sys->do_connect($config->{backend_address}, $cookies->{SID}) ) ) {
# accept request
# ...
}
}
}
修復方式
在新版本的Sophos UTM固件之中,`webadmin.plx/wfe/asg/modules/asg_connector.pm` 腳本增加了對 SID 值的檢查,不允許非字母數字的其他任何字符存在,漏洞不再有效。
參考
https://community.sophos.com/b/security-blog/posts/advisory-resolved-rce-in-sg-utm-webadmin-cve-2020-25223
https://www.atredis.com/blog/2021/8/18/sophos-utm-cve-2020-25223
https://github.com/the6p4c/bfs_extract