Parsing Telnet IAC Command Sequences and Reconstructing Plaintext Sessions from PCAP
Theory
Why This Matters
Telnet's complete absence of encryption makes it a forensics analyst's most direct window into attacker activity when the protocol appears in a PCAP. In the 2016 Mirai botnet outbreak, post-compromise forensics of infected IoT devices showed that virtually all initial access was achieved via Telnet using default credentials — and the PCAP evidence from affected network segments allowed investigators to reconstruct the exact commands the botnet loader executed, the credentials used, and the timing of each infection. Industrial control networks, embedded devices, legacy networking equipment (routers, managed switches, serial console servers), and operational technology environments continue to run Telnet in 2024. An analyst encountering a PCAP from any of these environments must be able to reconstruct the complete interactive session, including accurately handling the echo and control character artefacts that make Telnet streams non-trivial to parse.
Core Concept
Telnet (port 23, RFC 854) transmits every keystroke as it is typed, one character at a time, over a plaintext TCP connection. This character-at-a-time mode creates an important forensic artefact: in a typical Telnet session the server echoes each character back to the client so it appears on the user's terminal. This means each character the user types appears twice in the PCAP — once in the client→server direction and once in the server→client direction — unless the server suppresses echo for sensitive input like passwords.
IAC (Interpret As Command) sequences control terminal options. IAC is byte 0xFF. A three-byte sequence 0xFF 0xFB 0x01 means "IAC WILL ECHO" — the server is offering to echo characters. 0xFF 0xFD 0x01 is "IAC DO ECHO" — the server is requesting the client to echo. These negotiation sequences appear at session start and must be stripped from the data stream before reconstructing user input. Critical: when the server sends IAC WILL ECHO and the client accepts, the server takes over echoing and the client stops sending its own echo — this is the password prompt phase. The absence of server-side echoing for a sequence of characters identifies the password field.
Backspace and delete: byte 0x7F (DEL) or 0x08 (BS) in the client→server stream means the user pressed backspace. Accurate command reconstruction requires processing these deletions to determine what was actually submitted, not just what bytes were sent.
Technical Deep-Dive
# View all Telnet data frames (strips IAC sequences automatically in dissector)
tshark -r capture.pcap -Y "telnet" -T fields
-e frame.number -e frame.time_relative
-e ip.src -e ip.dst -e telnet.data
-E header=y -E separator="|"
# Follow a Telnet stream as ASCII — shows full session transcript
# Find stream number first:
tshark -r capture.pcap -Y "telnet" -T fields -e tcp.stream | sort -u
# Then follow (replace 0 with actual stream number):
tshark -r capture.pcap -z "follow,tcp,ascii,0" 2>/dev/null
# Separate client→server from server→client to isolate typed input
# Client→server: dst port 23 (user's keystrokes)
tshark -r capture.pcap
-Y "tcp.dstport == 23 and tcp.len > 0"
-T fields -e frame.time_relative -e ip.src
-e data.data
| xxd -r -p 2>/dev/null || true
# Count bytes by direction to understand echo phase
tshark -r capture.pcap -Y "tcp.port == 23" -T fields
-e ip.src -e ip.dst -e tcp.len
| awk '{src[$1]+=$3} END{for(k in src) print k, src[k]}'
# Python/Scapy: reconstruct Telnet session with backspace handling
from scapy.all import rdpcap, TCP, Raw
def strip_iac(data: bytes) -> bytes:
"""Remove IAC option-negotiation sequences from Telnet data."""
out, i = [], 0
while i < len(data):
if data[i] == 0xFF and i+1 < len(data):
cmd = data[i+1]
if cmd in (0xFB, 0xFC, 0xFD, 0xFE) and i+2 < len(data):
i += 3 # 3-byte IAC command
elif cmd == 0xFF:
out.append(0xFF); i += 2 # escaped IAC literal
else:
i += 2
else:
out.append(data[i]); i += 1
return bytes(out)
def apply_backspace(chars: bytes) -> str:
"""Process BS (0x08) and DEL (0x7F) to get final typed string."""
result = []
for b in chars:
if b in (0x08, 0x7F):
if result: result.pop()
elif b >= 0x20 or b == 0x0A or b == 0x0D:
result.append(chr(b))
return "".join(result).strip()
pkts = rdpcap("capture.pcap")
CLIENT_PORT = 23 # destination port for client→server
for pkt in pkts:
if pkt.haslayer(TCP) and pkt.haslayer(Raw):
if pkt[TCP].dport == CLIENT_PORT:
raw = strip_iac(bytes(pkt[Raw]))
if raw:
text = apply_backspace(raw)
if text:
print(f"[{float(pkt.time):.3f}] C→S: {text!r}")
Analytical Methodology
- Open the PCAP in Wireshark. Apply display filter
telnetto isolate Telnet traffic. Note source and destination IPs and session count — multiple sessions from different sources to the same device may indicate scanning or credential stuffing. - Right-click any Telnet frame → Follow → TCP Stream. Read the full session in ASCII. The stream view shows both directions interleaved; colour-coded text distinguishes client (one colour) from server (another).
- In the Follow TCP Stream view, identify the login sequence: the server sends a login prompt, the client types the username (echoed back by server), then the server sends a password prompt. During password entry, client keystrokes are NOT echoed — this is the no-echo phase.
- Locate the no-echo phase by noting where the server stops echoing. The bytes between the last echoed character before the password prompt and the first echoed character after the prompt (or the shell prompt appearing) constitute the password.
- Apply display filter
tcp.dstport == 23to see only client→server traffic. These are the raw keystrokes typed by the user. Process with the Python IAC-stripping and backspace-handling script to reconstruct exact commands. - Use tshark
-z "follow,tcp,ascii,N"for programmatic transcript export. Process the output with Python to separate the two directions and reconstruct the command history. - Reconstruct the full command history in order. Note any
sudoorsucommands — these trigger a second password prompt within the session, identifiable by another no-echo phase mid-stream. - In NetworkMiner, the Credentials tab captures Telnet login credentials automatically. The Sessions tab provides session metadata.
- Cross-reference the reconstructed commands with file system and process artefacts from endpoint forensics to confirm attacker activity. Telnet session timestamps bound the window of live attacker interaction.
Common Analytical Errors
- Reading the echoed characters as separate input: In a full-duplex Telnet session, each character appears in both the client→server and server→client directions. Analysts who read the full bidirectional stream without splitting by direction will see doubled characters and incorrectly reconstruct commands.
- Missing IAC sequences before stripping: IAC option negotiation at session start (typically 20–50 bytes of 0xFF-prefixed sequences) will corrupt credential and command reconstruction if not stripped before processing. Always strip IAC sequences first.
- Ignoring backspace characters in passwords: Users frequently mistype passwords. The backspace characters in the no-echo phase still appear in the client→server stream and must be processed to determine the actual password submitted. A raw byte read of the password field is not sufficient.
- Assuming character-at-a-time mode universally: Some Telnet implementations or line-mode clients batch input and send complete lines. In line mode, each line arrives as a unit rather than character-by-character. The presence of IAC LINEMODE negotiation in the option exchange identifies this variant.
- Not identifying session boundaries in multi-session PCAPs: A PCAP may contain dozens of Telnet sessions. Each TCP stream is a separate session;
tcp.stream eq Nin Wireshark isolates each one. Analyse sessions individually — attacker activity may span only one or two out of many background legitimate sessions.
NICE Framework Alignment
| Code | Knowledge/Skill/Task Statement | How This Card Develops It |
|---|---|---|
| K0046 | Knowledge of intrusion detection systems and methodologies | Identifying Telnet as a high-risk plaintext protocol that IDS rules flag; recognising Telnet-based brute force patterns in PCAP |
| K0093 | Knowledge of network protocols | Understanding Telnet's character-at-a-time transmission, IAC option negotiation, echo vs no-echo phases, and line termination conventions |
| K0221 | Knowledge of OSI model and network layers | Locating Telnet at the application layer over a TCP stream, understanding how TCP payload reassembly affects character-level session reconstruction |
| S0046 | Skill in performing packet-level analysis | Using Wireshark Follow TCP Stream, tshark directional extraction, and Python IAC/backspace processing to reconstruct accurate Telnet sessions |
| T0023 | Collect intrusion artifacts for use in forensic analysis | Extracting Telnet credentials and complete command histories from PCAP as timestamped forensic evidence of attacker activity |
Further Reading
- RFC 854: Telnet Protocol Specification — Postel & Reynolds (IETF)
- Network Forensics: Tracking Hackers Through Cyberspace — Sherri Davidoff & Jonathan Ham, Chapter 8: Plaintext Protocol Forensics (Prentice Hall)
- The Practice of Network Security Monitoring — Richard Bejtlich, Chapter 7: Identifying and Responding to Intrusions (No Starch Press)
Challenge Lab
Reinforce your learning with a hands-on generated challenge based on this card's competency.