A flexible serial interface tool for penetration testers, with an interactive terminal UI and a command-line interface powered by ctui.
ctserial 0.6.0 requires Python 3.11 or later. Install with pip:
python -m pip install ctserialOr use an isolated environment with uv:
uv tool install ctserial
# Try it without installing:
uvx ctserialSerial communication uses serialx, which supports Linux, macOS, and Windows. Your account needs access to the serial port.
Start ctserial, then enter commands in its terminal UI:
ctserial> devices
ctserial> connect /dev/ttyUSB0 115200 none
ctserial> send hex deadc0de
ctserial> send hex "de ad c0 de"
ctserial> send utf8 "Dead Code 国"
ctserial> fuzz hex "de[0-3]?"
ctserial> fuzz utf8 "user[0-3]"
ctserial> close
ctserial> exit
connect DEVICE [BAUDRATE] [PARITY] defaults to 9600 baud and no parity, using
8 data bits and 1 stop bit. Parity can be none, even, odd, mark, or space.
Use a device path, symlink, Windows COM port, or a serialx URL. Paths are opened
directly, including virtual ports omitted from devices.
After connect , device suggestions show available native port paths, with the
device-reported manufacturer and product in the details column. For example:
/dev/ttyACM0 VelocioNetworks | Virtual COM Port
You can also type a path or serialx URL directly if it is absent from suggestions.
send hex uses ctui's HexBytes conversion. It accepts contiguous hexadecimal,
byte separators, 0x prefixes, and \xNN notation. Quote values containing spaces
or backslashes, for example send hex '\xde\xad'.
send utf8 accepts one string argument and encodes it as UTF-8. Quote text
containing spaces. Quoting is parsed once by ctui; literal backslashes remain
literal, so send utf8 '\n' sends a backslash and n. Use send hex 0a for a
newline byte. ctui 1.0.0 does not provide a StringBytes type.
fuzz hex PATTERN uses ctui's FuzzyHexPattern. ? matches any hexadecimal
nibble, [0-3] selects a range, and {n} repeats the preceding atom. For example,
fuzz hex "de[0-3]?" sends 64 two-byte payloads in expansion order.
fuzz utf8 PATTERN uses ctui's FuzzyStringPattern and encodes each generated
string as UTF-8. It supports character classes, literal alternatives such as
{admin,user}, fixed repetition, Unicode literals, and ASCII wildcard classes
such as \d. For example:
ctserial> fuzz utf8 "国{admin,user}[0-1]"
ctserial> fuzz utf8 'user\d' --limit 10
ctserial> fuzz hex "??" --limit 256
Both commands expand lazily and send one payload at a time, waiting for each
response. The UI updates after every send and receive; CLI output flushes each
transaction row immediately. The default maximum is 256 payloads. Set --limit N
to change it, up to 65,536. A pattern larger than the limit is rejected before
sending anything; it is never silently truncated. A missing response is displayed
as No response received and fuzzing continues; an I/O error stops the sequence. A running fuzz command holds the connection
until it finishes or is cancelled, so other sends and close wait their turn.
Each exchange has a one-second write/response deadline. After the first response bytes, 100 ms of silence ends collection. A response larger than 65,536 bytes raises an error. These timing boundaries do not identify protocol frames; late bytes remain buffered and may appear in the next exchange. Adjust the application for protocols requiring explicit framing or different timings.
The UI displays the most recent 1,000 transaction rows, one line per send or
receive, with hexadecimal grouped every four bytes and a single UTF-8 decode.
Invalid UTF-8 bytes and control characters are replaced with �. Long rows can
be scrolled horizontally in the UI. Transaction rows are kept in memory. ctui's
project commands manage persistent command history in the platform's user-data
directory. The status bar shows the active project and connection. Serial ports
close on exit, including CLI command failures.
Use repeatable --command (or -c) arguments to keep one session across commands:
ctserial -c 'connect /dev/ttyUSB0 115200' \
-c 'send hex deadc0de' \
-c 'fuzz utf8 "user[0-3]"' \
-c close
ctserial --help
ctserial help fuzz hexUse --file commands.txt for a UTF-8 command file with one command per line.
Blank lines and lines starting with # are ignored. Execution stops at the first
error. The connection closes automatically when the batch ends.
- Python 3.8–3.10 are no longer supported because ctui 1.0 requires Python 3.11+.
- The application uses ctui's
CtuiApp/@commandAPI and serialx's native async API. pyserial is no longer a dependency. - Use
send utf8 "text with spaces"; baresend TEXTand joining multiple unquoted words are unsupported. UTF-8 input is parsed only once. - Hexadecimal syntax follows ctui's validation rather than stripping arbitrary prefixes and separators.
- ctui 1.0 project storage differs from ctui 0.8; old project data is not imported automatically. Transaction output remains in memory.
git clone https://github.com/ControlThings-io/ctserial.git
cd ctserial
uv sync --locked --python 3.11
uv run python -m unittest discover -s tests -v
uv run black --check src tests
uv run isort --check-only src tests
uv lock --checkTo refresh Black and Pylint to the latest compatible stable releases:
uv lock --upgrade-package black --upgrade-package pylint
uv sync --locked --python 3.11Review the lockfile changes and rerun checks after updating developer tools; new versions can change diagnostics or formatting. Keep the tested lockfile checked in so development and CI use the same versions.
The Linux integration test uses a pseudo-terminal with the real serialx backend; no physical serial device is required. It is skipped on other platforms. See current status, decisions, and the release checklist for validation gaps and release steps.
Justin Searle justin@controlthings.io