# Inside the GSocket Backdoor

Source: https://www.egnworks.com/blog/inside-the-gsocket-backdoor  
Author: Jacob Strix  
Published: 2026-09-09  
Updated: 2026-09-09  
Category: Blue Team  
Tags: GSocket, Linux Forensics, Backdoor Detection, Detection Engineering, Incident Response

> Attackers turn a legitimate encrypted relay into a persistent Linux backdoor. Dissect GSRN, process masquerading, secret files, and host detection.

---

An exposed Linux server can be backdoored without listening on a new inbound port. The operator does not need a stable address, the victim does not need a public IP, and the connection can remain encrypted while crossing a relay on an egress port that administrators normally permit.

Global Socket, usually shortened to GSocket, provides exactly those properties. It is an open-source networking toolkit, not a malware family. Two endpoints that know the same secret connect outward to the Global Socket Relay Network, or GSRN. The relay pairs them and forwards an end-to-end encrypted stream. The project legitimately supports interactive shells, file transfer, TCP forwarding, proxying, and optional Tor connectivity.

Attackers repeatedly turn that functionality into a backdoor by adding malicious deployment, persistence, deceptive process names, hidden secret files, and anti-forensic behavior. Elastic observed it in the REF6138 Linux intrusion in 2024. Sansec found it on compromised Adobe Commerce systems and later saw criminals scanning for other operators' exposed GSocket keys. JPCERT/CC documented a renamed copy after React2Shell exploitation. SANS ISC analyzed a 2026 Bash deployer. Yandex Cloud then reported GSocket alongside Linux ransomware operations and warned that an implant can remain dormant for years.

The defensive problem is therefore larger than blocking one domain. A quiet implant may not resolve a relay while an investigation is running. A custom relay can remove the default domain indicator. A renamed static binary can avoid package-based inventory. The reliable strategy is to correlate what the executable is, how it starts, what its process claims to be, which files it reads, and where its sockets lead.

## GSocket Is the Transport, Not the Initial Exploit

GSocket does not exploit the target and should not be described as a vulnerability. Documented intrusions gained execution through another route, then staged GSocket for continued access. Sansec connected one deployment to Adobe Commerce exploitation. JPCERT/CC observed `npm-cli` after exploitation of CVE-2025-55182. Yandex Cloud described exposed application interfaces, stolen credentials, VPN sessions, and vulnerable web applications as initial access paths in its broader intrusion set.

This distinction changes incident response. Finding `gs-netcat` answers how an intruder maintained an access channel, but not how the intruder arrived. Removing the binary without reconstructing initial access leaves the original weakness, stolen credential, web shell, or second backdoor untouched. Sansec reported repeated reinfection on systems where visible malware was cleaned but GSocket persistence or the underlying compromise survived.

The upstream project openly calls `gs-netcat` an encrypted reverse backdoor in its feature documentation. That description refers to capability, not intent. An approved administrator may use the same binary for legitimate remote access. Malice is established by context: unauthorized installation, deceptive placement, an unapproved shell, process masquerading, encoded persistence, timestomping, or a relay relationship outside policy.

| Question | Safe conclusion |
| --- | --- |
| Is GSocket malware by origin? | No. It is a dual-use open-source networking toolkit. |
| Does it create initial access? | No. It is normally installed after code execution is already obtained. |
| Must the victim expose a port? | No. Both endpoints can initiate outbound relay connections. |
| Does a relay expose the operator IP to the victim? | Normally no. The victim connects to the relay, not directly to the operator. |
| Can the relay read the shell stream? | The upstream design provides endpoint-to-endpoint encryption, so GSRN relays encrypted application traffic. |
| Does a YARA hit prove compromise? | No. It proves tool identity or strong similarity. Behavior and authorization determine intent. |

## Inside the GSRN Rendezvous

Traditional reverse shells embed or receive an operator-controlled host and port. GSocket replaces that direct addressing model with a shared-secret rendezvous. Each peer derives connection material locally, selects a relay host, and connects to GSRN. The listening endpoint registers an address and token. The connecting endpoint presents the corresponding address. When the relay finds both sides, it returns a start signal and becomes a byte forwarder between the encrypted endpoints.

The current upstream source defines separate `LISTEN`, `CONNECT`, `PING`, `PONG`, `START`, `ACCEPT`, and `STATUS` message types. `LISTEN` and `CONNECT` are assigned type values `0x01` and `0x02`. The control structures contain a compact address derived from the shared secret and reserved space. After rendezvous, the peers negotiate the protected application channel using OpenSSL SRP and AES-256 according to the project documentation.

```mermaid
flowchart TD
    A["Initial access through another weakness"] --> B["Stage or rename gs-netcat"]
    B --> C["Store shared-secret material"]
    C --> D["Add cron, profile, rc.local, or systemd persistence"]
    D --> E["Launch with deceptive argv0 or volatile storage"]
    E --> F["Victim opens outbound connection to GSRN"]
    G["Operator derives the same rendezvous identity"] --> H["Operator opens a separate outbound connection"]
    F --> I{"Relay pairs matching identities"}
    H --> I
    I --> J["START response"]
    J --> K["End-to-end protected stream"]
    K --> L["Shell, forwarding, proxy, or file transfer"]
    E --> M["Host artifacts: procfs, secret file, persistence"]
    F --> N["Network artifacts: DNS, destination, timing, opening frame"]
```

The relay architecture hides a useful piece of attribution data. Socket telemetry on the victim normally reveals GSRN, not the operator's real network address. Capturing only the victim's flow records is therefore unlikely to identify who connected from the other side. The secret file becomes high-value evidence because possession of the same secret can grant another party access to the rendezvous.

Sansec demonstrated the consequence in April 2025. After detecting scans for `defunct.dat` and `qfile`, it checked 80,000 stores and found 45 with running GSocket backdoors. Those files contained connection keys. A key exposed through a web-accessible path could let a competing criminal reuse another operator's backdoor. Treating the file as ordinary configuration understates its role. Operationally, it is closer to a bearer credential.

The protocol also creates a narrow network-detection opportunity. Application traffic is protected after the peers are paired, but the relay must process control information first. A sensor with reassembled TCP visibility may identify candidate `LISTEN` or `CONNECT` openings from the current message layout. This should never become a three-byte blocking rule. Segmentation, implementation changes, custom relays, Tor, and coincidental binary prefixes can all invalidate a simplistic signature.

| Protocol property | Current implementation | Defensive meaning |
| --- | --- | --- |
| Relay selection | Letter-prefixed host derived from the address | Match the parent relay domain rather than one subdomain. |
| Default transport | Outbound TCP, commonly port 443 | Port allowlisting does not establish that the flow is HTTPS. |
| Alternate transport | `GS_PORT` or `GSOCKET_PORT` can change the port | JPCERT/CC observed GSocket configured over TCP 53. |
| Rendezvous identity | Derived locally from shared-secret material | The human-readable secret is not sent to the relay as a password. |
| Application protection | End-to-end SRP/TLS design with AES-256 | Payload inspection loses value after session establishment. |
| Operator visibility | Both endpoints communicate through a relay | Victim flow logs usually do not reveal the operator endpoint. |

## How a Legitimate Client Becomes a Persistent Implant

The most instructive deployments use ordinary Linux features rather than a kernel rootkit. A static or portable `gs-netcat` is copied into a writable path. The secret is placed next to it or disguised as another file type. A cron entry, shell profile, `rc.local`, or systemd unit restarts the process. The client attaches a shell to the encrypted relay and requires only outbound access.

Elastic's REF6138 investigation recorded `/dev/shm/.gs-1000`, a hidden path under `.config/htop`, cron persistence, timestamp manipulation, and a process launched under a kernel-thread-like name using `exec -a`. Sansec later showed the pair `.config/htop/defunct` and `.config/htop/defunct.dat`, with the process presented as names such as `[raid5wq]`, `[kswapd0]`, or `[slub_flushwq]`.

`exec -a` changes the first argument displayed to userspace tools. It does not turn an ELF executable into a kernel thread, change the inode referenced by `/proc/<pid>/exe`, erase the parent process, or remove its file descriptors. That gap between displayed identity and runtime truth is one of the strongest durable detection opportunities.

JPCERT/CC observed the same pattern under different names in December 2025. An hourly cron job started a Global Socket binary called `npm-cli`; `npm-cli.dat` held the secret; Bash was selected as the shell; and `GS_PORT='53'` moved the relay connection to a port normally associated with DNS. The traffic was not automatically DNS simply because its destination port was 53.

SANS ISC's March 2026 sample added another anti-forensic layer. Its Bash deployer copied `gs-netcat` beneath `.ssh/putty`, stored the shared secret in a fake SSH-key file, added cron and `.profile` persistence, and tracked file timestamps so they could be restored after modification. SANS did not determine the script's delivery method, so the sample proves deployment mechanics but does not justify a campaign attribution or initial-access claim.

Yandex Cloud's June 2026 bulletin connected GSocket use with targeted Linux ransomware activity. It listed `systemd` and cron persistence, `exec -a` masquerading, execution from `memfd`, and default relay domains among useful artifacts. It also warned that the implant may remain dormant for years. A clean five-minute packet capture is not proof that the host is clean.

## Hunt the Lie in `/proc`

Linux exposes several independent descriptions of a process. `/proc/<pid>/cmdline` contains the arguments a process presents. `/proc/<pid>/comm` contains a short task name. `/proc/<pid>/exe` links to the executable mapping. `/proc/<pid>/status` exposes the parent and user IDs. `/proc/<pid>/maps` can reveal deleted or anonymous executable mappings. `/proc/<pid>/fd` provides socket and file references.

A kernel-thread-style argument enclosed in brackets is only one weak signal. It becomes stronger when the process has a normal executable path, a non-kernel parent, an executable marked deleted, environment variables associated with GSocket, or an outbound socket. The following collector inventories those facts without executing the discovered file or using any recovered secret.

```python
#!/usr/bin/env python3
import argparse
import hashlib
import json
import os
import re
from pathlib import Path


BRACKETED = re.compile(r"^\[[^\]]+\]$")
GS_ENV_NAMES = {
    "GS_ARGS",
    "GS_HOST",
    "GS_PORT",
    "GSOCKET_ARGS",
    "GSOCKET_DOMAIN",
    "GSOCKET_HOST",
    "GSOCKET_PORT",
    "GSOCKET_SECRET",
}


def read_bytes(path: Path, limit: int = 1_048_576) -> bytes:
    try:
        with path.open("rb") as handle:
            return handle.read(limit)
    except (FileNotFoundError, PermissionError, ProcessLookupError, OSError):
        return b""


def read_text(path: Path, limit: int = 1_048_576) -> str:
    return read_bytes(path, limit).decode("utf-8", errors="replace")


def parse_nul(data: bytes) -> list[str]:
    return [part.decode("utf-8", errors="replace") for part in data.split(b"\0") if part]


def parse_status(text: str) -> dict[str, str]:
    result: dict[str, str] = {}
    for line in text.splitlines():
        key, separator, value = line.partition(":")
        if separator:
            result[key] = value.strip()
    return result


def safe_readlink(path: Path) -> str:
    try:
        return os.readlink(path)
    except (FileNotFoundError, PermissionError, ProcessLookupError, OSError):
        return ""


def sha256_path(path: Path) -> str:
    digest = hashlib.sha256()
    try:
        with path.open("rb") as handle:
            while chunk := handle.read(1_048_576):
                digest.update(chunk)
    except (FileNotFoundError, PermissionError, ProcessLookupError, OSError):
        return ""
    return digest.hexdigest()


def socket_inodes(fd_dir: Path) -> list[str]:
    inodes: list[str] = []
    try:
        entries = list(fd_dir.iterdir())
    except (FileNotFoundError, PermissionError, ProcessLookupError, OSError):
        return inodes
    for entry in entries:
        target = safe_readlink(entry)
        if target.startswith("socket:[") and target.endswith("]"):
            inodes.append(target[8:-1])
    return sorted(set(inodes))


def inspect_process(proc_dir: Path) -> dict | None:
    pid = proc_dir.name
    cmdline = parse_nul(read_bytes(proc_dir / "cmdline"))
    status = parse_status(read_text(proc_dir / "status"))
    environ = parse_nul(read_bytes(proc_dir / "environ"))
    exe = safe_readlink(proc_dir / "exe")
    comm = read_text(proc_dir / "comm", 4096).strip()
    maps = read_text(proc_dir / "maps")
    env_names = sorted(
        item.partition("=")[0]
        for item in environ
        if item.partition("=")[0] in GS_ENV_NAMES
    )
    bracketed_argv0 = bool(cmdline and BRACKETED.fullmatch(cmdline[0]))
    deleted_or_memfd = "(deleted)" in exe or exe.startswith("/memfd:") or "memfd:" in maps
    suspicious = bracketed_argv0 or deleted_or_memfd or bool(env_names)
    if not suspicious:
        return None
    executable_hash = ""
    if exe and not deleted_or_memfd:
        executable_hash = sha256_path(proc_dir / "exe")
    return {
        "pid": int(pid),
        "ppid": int(status.get("PPid", "0")),
        "uid": status.get("Uid", "").split("\t", 1)[0],
        "comm": comm,
        "argv": cmdline,
        "exe": exe,
        "sha256": executable_hash,
        "gs_environment_names": env_names,
        "kernel_style_argv0": bracketed_argv0,
        "deleted_or_memfd": deleted_or_memfd,
        "socket_inodes": socket_inodes(proc_dir / "fd"),
    }


def collect(proc_root: Path) -> list[dict]:
    findings: list[dict] = []
    for entry in sorted(proc_root.iterdir(), key=lambda path: path.name):
        if not entry.name.isdigit():
            continue
        finding = inspect_process(entry)
        if finding is not None:
            findings.append(finding)
    return findings


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--proc-root", type=Path, default=Path("/proc"))
    parser.add_argument("--output", type=Path)
    args = parser.parse_args()
    payload = json.dumps(collect(args.proc_root), indent=2, sort_keys=True)
    if args.output:
        args.output.write_text(payload + "\n", encoding="utf-8")
    else:
        print(payload)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
```

Run the collector with sufficient read permissions and send output to an evidence location on a trusted mounted volume:

```bash
sudo python3 proc_truth.py --output /mnt/evidence/proc-truth.json
python3 -m json.tool /mnt/evidence/proc-truth.json >/dev/null
sha256sum /mnt/evidence/proc-truth.json
```

The script reports environment variable names, never their values. That prevents a GSocket shared secret from being copied into routine logs. Hashing `/proc/<pid>/exe` is skipped for an obviously deleted or anonymous mapping because the acquisition workflow should copy that executable into controlled evidence storage first and record provenance.

The collector is a triage aid, not a verdict engine. Browsers, language runtimes, legitimate updaters, and security tools may execute deleted or anonymous mappings. Containers also create unfamiliar ancestry and namespaces. Escalation should depend on combined evidence rather than one boolean field.

## Detect the Binary on Disk and in Memory

Renaming `gs-netcat` to `defunct`, `npm-cli`, `id_rsa`, or a monitoring-like name does not remove compiled strings. Elastic maintains `Multi_Hacktool_Gsocket_761d3a0f`, a cross-platform YARA rule for file and memory scanning. It matches combinations of GSocket-specific strings such as `GSOCKET_SECRET`, `GS_HIJACK_PORTS`, `GSOCKET_NO_GREETINGS`, `GS-NETCAT(1)`, and related tool names.

Use the maintained rule from Elastic's repository instead of freezing a copied version in an article. This keeps metadata, architecture coverage, and tuning aligned with the publisher. An offline workflow can scan staged evidence as follows:

```bash
git clone --depth 1 https://github.com/elastic/protections-artifacts.git
yara -r \
  protections-artifacts/yara/rules/Multi_Hacktool_Gsocket.yar \
  /mnt/evidence/acquired-files
```

Memory scanning is especially important when `/proc/<pid>/exe` ends in `(deleted)`, when the executable came from `memfd`, or when the original staging directory has been cleaned. A YARA hit should initially be classified as `dual-use/hacktool`. Raise confidence when the same host also has encoded cron content, an unauthorized relay connection, a fake secret file, deceptive `argv[0]`, or unexplained shell access.

## Hunt Persistence by Structure

Filenames are fragile indicators. Attackers can replace `defunct` and `npm-cli` without changing how the implant survives. Detection should inspect cron, systemd units, shell profiles, and `rc.local` for structural combinations: `GS_ARGS`, `GSOCKET_SECRET`, `exec -a`, `pkill -0`, `gs-netcat`, a hidden executable path, or Base64 content decoded into a shell.

The following Sigma-style rule is deliberately behavioral. It is a starting point that must be translated to the organization's Linux process schema and validated against approved administration:

```yaml
title: Suspicious GSocket-Style Linux Persistence
id: 5704145e-e72c-4ae5-a856-ecdf386004e9
status: experimental
description: Detects shell execution patterns associated with persistent GSocket deployments
author: Egnworks
date: 2026-09-09
logsource:
  product: linux
  category: process_creation
detection:
  shell:
    Image|endswith:
      - /bash
      - /dash
      - /sh
      - /zsh
  gsocket_markers:
    CommandLine|contains:
      - 'GS_ARGS='
      - 'GSOCKET_SECRET='
      - 'gs-netcat'
      - 'GS_HIDDEN_NAME='
      - '.gs-'
  masquerade:
    CommandLine|contains|all:
      - 'exec -a'
      - '['
  encoded_shell:
    CommandLine|contains|all:
      - 'base64'
      - 'bash'
  condition: shell and (2 of gsocket_markers or masquerade or encoded_shell)
falsepositives:
  - Authorized GSocket administration or security testing
level: high
tags:
  - attack.persistence
  - attack.t1053.003
  - attack.defense-evasion
  - attack.t1036.011
```

File-integrity monitoring should include system and user crontabs, systemd unit directories, `/etc/rc.local`, `.profile`, `.bashrc`, `.zshrc`, `.ssh`, `.config/htop`, `.config/dbus`, `/dev/shm`, and `/tmp`. A web server writing an executable and a small adjacent secret file beneath its own home directory deserves immediate investigation even before a relay connection appears.

## Network Detection After Encryption

The default relay domain remains a strong policy signal. Alert on unauthorized queries and connections involving `gs.thc.org` and its subdomains, then preserve historical DNS because the implant may be inactive during triage. Do not hard-code only current IP addresses. Relay infrastructure can change, and GSocket supports alternate hosts, direct IP configuration, proxies, and Tor.

Process-aware egress is more durable than a port rule. Web servers, database daemons, application-service accounts, and hypervisor management processes rarely need long-lived external TCP sessions to an unapproved relay. Port 443 should be evaluated against protocol and process identity. Port 53 should be verified as DNS rather than trusted by number.

A candidate opening-frame detector can be developed from the current upstream structures, but it must be validated in an authorized lab before production. Capture both `LISTEN` and `CONNECT` paths against a controlled relay, enable stream reassembly, test deliberately segmented frames, compare several GSocket versions, and measure collisions against normal non-TLS traffic. Publish a Suricata signature only after the PCAP and regression set prove the offset and reserved-byte assumptions.

This restraint matters. The first type byte alone is not distinctive, and a short pattern such as `01 01 03` can occur in unrelated protocols. An untested rule creates false confidence and may block legitimate traffic. Domain, endpoint process, binary identity, persistence, and protocol evidence should contribute to one scored decision.

| Signal | Suggested score |
| --- | ---: |
| Elastic GSocket YARA hit in file or memory | 40 |
| Kernel-thread-style `argv[0]` with userspace executable | 35 |
| Unauthorized default GSRN domain or relay connection | 30 |
| Validated GSRN opening-frame match | 30 |
| Persistence references hidden binary and secret file | 30 |
| Executable mapping is deleted or backed by `memfd` | 25 |
| GSocket environment variable name in process | 25 |
| Timestamp inconsistency around persistence creation | 15 |
| Known filename such as `defunct` or `npm-cli` | 10 |

A practical starting threshold is 50 for investigation and 80 for probable unauthorized GSocket access. These weights are an engineering proposal, not vendor-tested values. Tune them with local package inventory, sanctioned penetration-testing tools, container workloads, and known remote-administration paths.

## Preserve Volatile Evidence Before Cleanup

Do not begin with `kill`, deletion, or reboot. Isolate the host while preserving the ability to collect memory and runtime evidence. Record the process tree, namespaces, cgroups, `/proc` links, maps, environment variable names, file descriptors, socket ownership, DNS cache, and flow telemetry. If an executable is deleted or anonymous, acquire it from `/proc/<pid>/exe` before stopping the process.

Preserve suspicious `.dat` files and fake SSH keys with ownership, permissions, extended attributes, and filesystem timestamps. Never try a recovered secret against public GSRN. Doing so can contact a third party, alert an operator, contaminate evidence, or create unauthorized access.

After acquisition, export every system and user crontab, timer, systemd unit, `rc.local`, and shell profile. Decode suspicious Base64 only from a copied evidence file in an offline environment. Search fleet-wide for the same binary hash, YARA signature, secret-file hash, persistence fragment, process name, relay domain, service account, and deployment timestamp.

Then return to initial access. Review web access and error logs, application audit records, authentication history, VPN sessions, container events, cloud control-plane activity, vulnerable packages, and recently created credentials. GSocket is evidence that access was maintained, not an explanation of the first compromise.

Assume the attached shell could read everything available to its account. Rotate application secrets, SSH keys, database credentials, cloud tokens, backup credentials, and deployment keys according to exposure. Root-level persistence, timestomping, unknown initial access, or several unrelated malware families are strong reasons to rebuild from a known-good image instead of trusting file-by-file cleanup.

## Detection Must Outlive the IOC

GSocket is attractive because it solves difficult operator problems with legitimate code. It traverses NAT through outbound connections, avoids an inbound listener, hides the operator behind a relay, protects the application stream, and supports shells and forwarding across multiple platforms. Attackers do not need to invent a new C2 protocol to obtain those properties.

The same reuse gives defenders stable seams. A renamed process still maps an executable. A fake kernel thread still has userspace ancestry and sockets. A dormant implant still needs persistence and secret material. A custom relay removes a domain indicator but not process provenance. Encrypted content hides commands, not the existence and ownership of the process that carries them.

The durable Blue Team lesson is to compare independent truths. Command lines can lie, filenames can lie, timestamps can be restored, and a quiet packet capture can be incomplete. Procfs, executable identity, memory strings, persistence content, filesystem metadata, DNS history, and process-aware flow records are much harder to falsify together.

## References

1. The Hacker's Choice. [Global Socket repository](https://github.com/hackerschoice/gsocket). Accessed September 9, 2026.
2. The Hacker's Choice. [`gsocket.h` protocol definitions](https://github.com/hackerschoice/gsocket/blob/master/include/gsocket/gsocket.h). Accessed September 9, 2026.
3. The Hacker's Choice. [`gsocket-engine.c`](https://github.com/hackerschoice/gsocket/blob/master/lib/gsocket-engine.c) and [`deploy.sh`](https://github.com/hackerschoice/gsocket/blob/master/deploy/deploy.sh). Accessed September 9, 2026.
4. Elastic Security Labs. [Betting on Bots: Investigating Linux malware, crypto mining, and gambling API abuse](https://www.elastic.co/security-labs/threat-command/betting-on-bots). September 27, 2024.
5. Sansec Forensics Team. [Persistent backdoors injected on Adobe Commerce via new CosmicSting attack](https://sansec.io/research/cosmicsting-cnext-persistent-backdoor). August 27, 2024.
6. Sansec Forensics Team. [Found defunct.dat on your site? You've got a problem](https://sansec.io/research/gsocket). April 3, 2025.
7. JPCERT/CC. [Multiple Threat Actors Rapidly Exploit React2Shell: A Case Study of Active Compromise](https://blogs.jpcert.or.jp/en/2026/02/multiple-threat-actors-rapidly-exploit-react2shell-a-case-study-of-active-compromise.html). February 13, 2026.
8. Xavier Mertens, SANS Internet Storm Center. [GSocket Backdoor Delivered Through Bash Script](https://isc.sans.edu/diary/32816). March 20, 2026.
9. Yandex Cloud. [Targeted data encryption attacks in the Linux infrastructure of Russian organizations](https://yandex.cloud/en/docs/security/security-bulletins/#23-06-2026). June 23, 2026.
10. Elastic Security. [`Multi_Hacktool_Gsocket_761d3a0f` YARA rule](https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Multi_Hacktool_Gsocket.yar). Accessed September 9, 2026.
