All Posts

Pipe-Fed LinPEAS: Analysis of a Go Dropper That Executes PEASS-ng Entirely from Memory

Weaponized sh2bin packaging with hostname beaconing to an AWS Lambda endpoint
Zenyard
10
min read
May 4, 2026
This analysis was generated by Zenyard Agent without human-in-the-loop input and is presented as raw output, as produced by the system. However, in real reversing workflows, the agent is designed to augment the researcher, not replace them. The goal of this experiment is to demonstrate how far a purpose-built AI agent for reversing can go when applied to real-world samples, and the value it can deliver in helping researchers shorten time to insight.

Introduction

This report examines a Linux x86-64 Go-compiled launcher that wraps LinPEAS-ng inside a native executable, decodes the embedded payload at runtime, and streams it directly into a shell over standard input. The result is not a novel implant family, but a practical post-exploitation package that removes its second stage from ordinary filesystem-based inspection.

The sample also modifies the expected LinPEAS workflow by adding immediate hostname beaconing to an AWS Lambda endpoint. That callback gives the operator lightweight victim notification before the main enumeration phase runs.

The analysis focuses on the launcher’s pipe-fed execution model, the embedded LinPEAS payload, the Lambda beacon, and the detection opportunities that remain available even when the second stage never lands as a standalone file.

Key Judgments

The sample is a Go-compiled launcher that embeds a full LinPEAS payload, decodes it at runtime, and feeds it directly into a shell over standard input, which removes the second stage from ordinary filesystem-based inspection.

The modified payload performs immediate victim notification by POSTing the host hostname to an AWS Lambda URL with the fixed User-Agent: linpeas, creating a stable network fingerprint and a clear operator awareness mechanism.

The embedded tooling extends beyond privilege-escalation checks into credential harvesting, cloud metadata access, container inspection, password attacks, and pivoting support, which makes the sample useful after compromise rather than as an initial-access vector.

The beacon receiver is not a bidirectional command channel. After this callback, the operator still needs a separate access path such as an existing shell, stolen SSH credentials, or another implant for follow-on action.

Analysts can recover the full payload offline with no key material, environment dependency, or runtime secret, which makes proactive payload extraction, diffing, and hunting practical across rebuilt variants.

Overview

This sample is a statically linked ELF64 executable for Linux x86-64 compiled in Go 1.23.12. Its role is narrow and operationally useful: it wraps LinPEAS-ng inside a native launcher, checks for an available shell, decodes a large embedded base64 blob, and streams the resulting script into the shell without creating a temporary file. The same embedded payload also carries linux-exploit-suggester.sh, which expands the launcher from a simple wrapper into a compact post-exploitation package covering 21 kernel CVE checks.

Zenyard Agent produced this analysis from a raw ELF64 binary with no prior context, labeling, or human analyst involvement. Symbol and build-path artifacts identify the wrapper as a build derived from the PEASS-ng sh2bin workflow rather than a bespoke loader. The malicious value sits in the attacker modification around that wrapper: the embedded script performs immediate external hostname reporting to a Lambda URL, giving the operator a lightweight victim-notification channel before the main enumeration phase runs.

The supplied source material identifies the platform, Go version, and embedded payload structure, but it does not include a verified sample SHA-256, a trustworthy compilation timestamp, or the exact on-disk ELF size. Those fields are left unclaimed here rather than reconstructed speculatively.

This sample matters because it wraps legitimate red-team tooling in a minimal native launcher and shifts detection from static artifact matching to execution behavior, shell telemetry, and network traces.

Technical Findings

Embedded payload staging and execution flow

The launcher begins in main.main and first calls a helper that probes for an executable shell. The search order is /bin/sh, /bin/zsh, /bin/ksh, and /bin/csh. If no supported shell is found, execution ends with the message:

No shell was found, I cannot execute.

That failure path is useful because it confirms the binary does not interpret the embedded content itself. It relies on a system shell to execute the second stage.

The payload is stored in a global variable identified as main.scriptB64, with the base64 text compiled into the binary's read-only data. At runtime, Go's standard base64 routines decode that blob into an approximately 952 KB shell script. The recovered content begins with a normal #!/bin/sh header and LinPEAS banner material, which confirms the launcher is staging a verbatim script rather than generating one dynamically.

The following pseudocode is a behavioral reconstruction from binary analysis, not recovered source. It captures the execution model at the point where the launcher selects a shell, decodes the embedded payload, and feeds it into the child process:

shell = find_first_existing("/bin/sh", "/bin/zsh", "/bin/ksh", "/bin/csh")
script = base64_decode(main.scriptB64)
pipe = create_pipe()
spawn(shell, stdin=pipe.reader, stdout=buffer)
write(pipe.writer, script)
print(buffer)

The use of io.Pipe() is the critical implementation detail. The shell receives the decoded payload over standard input, so there is no script pathname to inspect later. At the OS level, defenders should expect the child shell to show /bin/sh or its equivalent with no script-file operand in /proc/<pid>/cmdline, while the actual content arrives through stdin.

In EDR and process telemetry, that often presents as a non-interactive parent spawning a shell with an unusually small command line and an unusually large volume of stdin data.

This is not a native-code packer in the classical sense. The launcher does not rebuild executable sections or unpack a second ELF in memory. It is better described as a script-bearing dropper that executes its second stage filelessly once the wrapper itself is launched from disk.

Anti-analysis and execution tradecraft

The sample does not depend on debugger checks, virtualization probes, or exotic anti-instrumentation logic. Its evasion value comes from simple execution choices that reduce visibility in common response workflows.

The first choice is static Go compilation. That removes shared-library pivots, keeps the wrapper self-contained, and buries a small amount of user logic inside a comparatively noisy runtime. In practice, that does not prevent reverse engineering, but it slows shallow triage and reduces the number of external artifacts defenders can inspect before execution.

The second choice is fileless second-stage delivery. The decoded script never lands as a standalone file in /tmp, the working directory, or any other obvious location. Analysts relying on file creation telemetry will see the disk-backed ELF, but they may not see the script that actually performs the post-exploitation work unless they capture memory, instrument standard-input flows, or inspect child-process behavior.

The third choice is the repurposing of legitimate offensive tooling. LinPEAS is a real and widely used enumeration framework. That lowers development cost and lets the operator inherit mature privilege-escalation, credential-harvesting, and cloud-enumeration logic without maintaining a custom implant.

It also creates ambiguity during incident response, because defenders may initially treat the activity as leftover red-team tooling or a pentest artifact rather than as hostile post-exploitation behavior.

Payload handling, offline extraction, and embedded capability

The payload protection layer is base64 encoding rather than cryptography. There is no key derivation, no environment-bound secret, and no runtime-only decryption condition that prevents offline recovery. Analysts with a copy of the ELF can extract the blob, decode it, and inspect the full payload without executing the sample.

That matters operationally because it enables proactive hunting. Defenders can decode the script once, extract stable strings, review the embedded control flow, and compare it against clean upstream LinPEAS builds to isolate the attacker modifications.

The absence of key material also makes cluster analysis easier: rebuilt launchers may differ in wrapper metadata or compilation details, but the embedded post-exploitation logic remains recoverable.

The payload includes the full LinPEAS-ng script and also carries linux-exploit-suggester.sh. In this specimen, the embedded exploit-suggester logic covers 21 kernel or service CVE checks, which is enough to move the sample beyond basic host triage and into practical escalation planning.

The embedded script also reaches into cloud metadata services, container contexts, credentials, local network state, and password brute-force attempts. The offensive value is not just “run LinPEAS.” It is “run LinPEAS immediately, notify the operator, and surface the next step fast.”

C2 protocol design and network fingerprinting

The most important network behavior is the external-hostname callback executed near startup. The embedded function sends an HTTPS POST to the Lambda URL:

2e6ppt7izvuv66qmx2r3et2ufi0mxwqs.lambda-url.us-east-1.on.aws

The JSON content has the following form:

{"hostname":"<victim_hostname>"}

The request carries:

Content-Type: application/json and a fixed User-Agent: linpeas.

The script prefers curl and falls back to wget if curl is unavailable. In this sample, both branches explicitly set the same User-Agent: linpeas header, so the network fingerprint remains stable across either execution path. That matters for detection engineering because the header-based logic does not collapse when the system lacks curl.

This endpoint behaves like a notifier rather than an interactive controller. It gives the operator awareness that a staged payload ran on a host and identifies that host by name, but it does not provide a bidirectional tasking channel on its own.

The operational implication is straightforward: if this beacon fires, defenders should look immediately for the separate mechanism that provides persistence or follow-on access, such as an existing SSH session, a web shell, a cron job, or another implant already present on the system.

The wider payload then performs the real post-exploitation work. It queries metadata endpoints for AWS, Google Cloud, Alibaba Cloud, Tencent Cloud, IBM Cloud, ECS, and Lambda-adjacent runtime data; inspects Kubernetes and Docker contexts; harvests credentials; enumerates privilege-escalation paths; and tests local network reachability.

The callback tells the operator where to look. The embedded tooling tells the operator what to do next.

Attribution Considerations

There is insufficient evidence to attribute this sample to a named threat actor. The evidence supports a narrower and more credible conclusion: the specimen is a weaponized build of a legitimate open-source post-exploitation toolkit, packaged through the PEASS-ng sh2bin mechanism and modified to add external victim notification.

That points to opportunistic operator tradecraft rather than to a bespoke malware-development program.

The presence of 10.10.14.8 is relevant because it is a non-routable address in Hack The Box's standard VPN range, which makes it more consistent with testing or lab residue than with a production command-and-control node. That artifact is useful context, but it is not attribution evidence.

The sample is best described as attacker-modified offensive tooling.

Detection Opportunities

Network

The strongest network signature is an HTTPS POST to the Lambda URL host with User-Agent: linpeas, Content-Type: application/json, and a body containing the hostname key.

Because both the curl and wget paths set the same user agent explicitly, that header remains valid even when the fallback path is used. In environments with HTTP telemetry, the combination of destination host, method, header, and JSON key produces a high-confidence detection.

The next priority is cloud metadata access, especially in cloud-native estates. Start with AWS Instance Metadata Service (IMDS), and prioritize:

169.254.169.254/latest/api/token

and subsequent IMDSv2 requests first, because those are the most common cloud-credential theft pivots in real Linux intrusions.

Then watch for 169.254.170.2 for ECS task credentials, followed by metadata.google.internal, Alibaba metadata at 100.100.100.200, and Tencent metadata at 169.254.0.23. These requests are far more suspicious when they originate from a short-lived shell process or from curl or wget spawned by that shell.

Host and Behavioral

The core host-side detection is a disk-backed Go ELF spawning /bin/sh, /bin/zsh, /bin/ksh, or /bin/csh with no script-file argument and a large standard-input stream from a non-interactive parent.

A practical threshold is stdin volume greater than 200 KB. That is comfortably below the embedded payload size in this sample and high enough to exclude most ordinary shell wrappers.

Process telemetry should also account for the forensic consequence of pipe-fed execution. The child shell will not carry a script pathname in /proc/<pid>/cmdline, and EDR will often show a minimal command line that looks deceptively harmless.

The distinguishing feature is the mismatch between a tiny shell command line and a large burst of stdin-fed script content, followed by extensive local enumeration and outbound HTTP activity.

Static and semi-static pivots remain useful even when hashes rotate. The build path referencing PEASS-ng/sh2bin/sh2bin.go, the error string No shell was found, I cannot execute, the fixed User-Agent: linpeas, and the Lambda URL hostname together form a durable triage cluster.

Those indicators are more valuable than a raw file hash because the wrapper can be rebuilt trivially while preserving behavior.

Conclusion

This sample is significant because it does not introduce a new implant family or a novel cryptographic loader. It wraps a trusted offensive utility in a minimal native launcher, executes the payload entirely through standard input, and adds a lightweight callback that tells the operator which host just ran the tool.

That makes the sample cheap to reproduce, easy to adapt, and disproportionately effective against defenders who over-index on file artifacts.

More broadly, it reflects a growing pattern in current intrusions: legitimate red-team tooling is being wrapped in thin loaders that shift the detection burden almost entirely onto behavior.

Annex A: Indicators of Compromise

Indicator Type Value Description
Domain / URL 2e6ppt7izvuv66qmx2r3et2ufi0mxwqs.lambda-url.us-east-1.on.aws Primary hostname-beacon receiver used at startup
HTTP Header User-Agent: linpeas Fixed header set in both curl and wget beacon paths
HTTP Pattern HTTPS POST with JSON body containing hostname Stable victim-notification request format
Build Artifact /home/runner/work/PEASS-ng/PEASS-ng/sh2bin/sh2bin.go Preserved build path linking the wrapper to PEASS-ng sh2bin
Process Behavior Untrusted Go ELF spawning /bin/sh or equivalent with no script-file argument and stdin volume >200 KB Fileless execution pattern for embedded second stage
Cloud Recon Behavior Shell-driven access to 169.254.169.254/latest/api/token, 169.254.170.2, or metadata.google.internal High-signal post-exploitation enumeration behavior

Annex B: YARA Hunting Rules

The rule below targets the weaponized wrapper rather than LinPEAS broadly. It avoids generic shell-script markers such as #!/bin/sh, which would create unnecessary false positives when scanning mixed content sets.

Instead, it keys on the PEASS-ng build-path artifact, the wrapper error string, the fixed user-agent string, and the Lambda infrastructure fragment.

The original sample SHA-256 was not included in the source material provided for this report, so the meta block records that absence explicitly rather than inventing a hash.

rule Linux_Go_LinPEAS_Dropper_Lambda_Beacon
{
    meta:
        description = "Detects a Go-compiled LinPEAS sh2bin wrapper modified with a Lambda hostname beacon"
        author = "Zenyard Agent"
        date = "2026-03-21"
        sample_sha256 = "UNAVAILABLE_IN_SOURCE_MATERIAL"

    strings:
        $elf = { 7F 45 4C 46 }
        $build_path = "/home/runner/work/PEASS-ng/PEASS-ng/sh2bin/sh2bin.go" ascii
        $ua = "User-Agent: linpeas" ascii
        $lambda = "lambda-url.us-east-1.on.aws" ascii
        $err = "No shell was found, I cannot execute" ascii

    condition:
        $elf at 0 and all of ($build_path, $ua, $lambda, $err)
}

Join Our Newsletter

This field is required
Thank you for subscribing!
Oops! Something went wrong while submitting the form.
By subscribing to our newsletter, you consent to the collection and use of your information as described in this Privacy Policy.