2026-10-01 23:08:50 +02:00
|
|
|
# NetScaler Triage
|
|
|
|
|
|
|
|
|
|
Offline forensic triage for Citrix NetScaler ADC and Gateway logs. Point it at a
|
|
|
|
|
folder or archive to find suspicious requests, public indicators, encoded
|
|
|
|
|
payloads, update records and related events across logs.
|
|
|
|
|
|
|
|
|
|
Original inputs are not modified. Payloads are decoded as data, never executed
|
|
|
|
|
or downloaded. A finding is an investigation lead, **not proof of compromise**.
|
|
|
|
|
|
|
|
|
|
## Quick start
|
|
|
|
|
|
|
|
|
|
Requires **Python 3.14 or newer**.
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
git clone https://tea.karlcom.de/karl/NetScaler_forensik.git
|
|
|
|
|
cd NetScaler_forensik
|
|
|
|
|
python3 -m venv .venv
|
|
|
|
|
source .venv/bin/activate
|
|
|
|
|
python -m pip install .
|
|
|
|
|
netscaler-triage ./logs.7z
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
On Windows PowerShell, create and activate the environment with:
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
py -3.14 -m venv .venv
|
|
|
|
|
.venv\Scripts\Activate.ps1
|
|
|
|
|
python -m pip install .
|
|
|
|
|
netscaler-triage .\logs.7z
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Installation may require internet access. Analysis runs offline.
|
|
|
|
|
|
|
|
|
|
## Everyday use
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
# Unpacked logs, an archive, or multiple appliance exports
|
|
|
|
|
netscaler-triage ./logs
|
|
|
|
|
netscaler-triage export.tar.gz
|
|
|
|
|
netscaler-triage ./appliance-a ./appliance-b export.zip
|
|
|
|
|
|
|
|
|
|
# Discover inputs in the current directory
|
|
|
|
|
netscaler-triage
|
|
|
|
|
|
|
|
|
|
# Encrypted archive: prompt for a password without displaying it
|
|
|
|
|
netscaler-triage export.7z --password
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
ZIP (including ZipCrypto/AES), TAR, TAR.GZ/TGZ, GZIP and 7z inputs are supported,
|
|
|
|
|
including nested archives within preparation limits. Unsupported formats are
|
|
|
|
|
reported. `--password VALUE` is also supported, but can expose the password in
|
|
|
|
|
shell history or process listings. Passwords are not stored in results.
|
|
|
|
|
|
|
|
|
|
The tool prints the result directory. Use `--output PATH` to choose a new one;
|
|
|
|
|
existing results are not overwritten. Appliance grouping is inferred from
|
|
|
|
|
directories; review the preparation manifest for unusual export layouts.
|
|
|
|
|
|
|
|
|
|
## Read the results
|
|
|
|
|
|
2026-10-05 06:54:18 +02:00
|
|
|
Start with these three files:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
triage-results-.../
|
|
|
|
|
SUMMARY.txt Start here
|
|
|
|
|
FINDINGS.csv Review matched events
|
|
|
|
|
IOCS.csv Search candidates for your SIEM
|
|
|
|
|
details/ Full evidence and machine-readable exports
|
|
|
|
|
inputs/ Prepared input copies, when preparation was needed
|
|
|
|
|
```
|
|
|
|
|
|
2026-10-01 23:08:50 +02:00
|
|
|
| File | Purpose |
|
|
|
|
|
| --- | --- |
|
2026-10-05 06:54:18 +02:00
|
|
|
| `SUMMARY.txt` | Readable overview: coverage, counts, limitations and what to open next |
|
|
|
|
|
| `FINDINGS.csv` | Only rule/indicator matches, with explanations and original source references |
|
|
|
|
|
| `IOCS.csv` | SIEM search candidates; not an automatic blocklist |
|
2026-10-01 23:08:50 +02:00
|
|
|
|
2026-10-05 06:54:18 +02:00
|
|
|
All technical exports are under `details/`: full events in `findings.jsonl`,
|
|
|
|
|
URL/Base64 decoding, updates, correlations, checksums, coverage and the SQLite
|
|
|
|
|
index. Prepared input copies, if needed, remain in `inputs/`.
|
|
|
|
|
|
|
|
|
|
`details/findings.jsonl` includes context and processing errors as well as matches;
|
|
|
|
|
use `FINDINGS.csv` to browse matched events. A match is not proof of execution.
|
|
|
|
|
The CSV keeps authorized-test and reference statuses visible. Spreadsheet-sensitive
|
|
|
|
|
cells are prefixed with an apostrophe; exact original values remain in JSON.
|
|
|
|
|
|
|
|
|
|
This layout applies to CLI scans. Direct Python calls to `engine.scan` retain
|
|
|
|
|
flat machine-readable exports. Failed or interrupted scans can also retain the
|
|
|
|
|
flat layout; inspect their `summary.json` before using partial results.
|
2026-10-01 23:08:50 +02:00
|
|
|
|
|
|
|
|
## Add indicators and Sigma rules
|
|
|
|
|
|
|
|
|
|
Put your packs in a directory and validate them before scanning:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
netscaler-triage rules --config ./my-rules
|
|
|
|
|
netscaler-triage ./logs --config ./my-rules
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Directories load Sigma YAML, JSON, CSV and TXT recursively. Repeat `--config`
|
|
|
|
|
to combine packs. No Python changes are needed to add an IP, path or rule.
|
|
|
|
|
For example, an IOC CSV can contain:
|
|
|
|
|
|
|
|
|
|
```csv
|
|
|
|
|
type,value,status,source,description
|
|
|
|
|
ipv4,192.0.2.42,reference_only,local-review,Investigate matching events
|
|
|
|
|
path,/example/suspicious-file,reference_only,local-review,Check file context
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
These are synthetic examples. Use original, non-defanged values for matching.
|
|
|
|
|
TXT accepts one value per line; unambiguous IP addresses, SHA-256 hashes, URLs
|
|
|
|
|
and Unix paths are inferred. Other values remain literal strings.
|
|
|
|
|
|
|
|
|
|
Sigma is parsed with pySigma and evaluated locally using the subset documented
|
|
|
|
|
in [RULES.txt](RULES.txt). Unsupported features fail explicitly. Sigma metadata
|
|
|
|
|
does not bypass detection conditions. Rule packs are data, not executable plugins.
|
|
|
|
|
|
|
|
|
|
Defaults include 47 Sigma rules and 246 public reference indicators. Attribution
|
|
|
|
|
and limitations are recorded in [THREAT_INTELLIGENCE.txt](THREAT_INTELLIGENCE.txt)
|
|
|
|
|
and [config/threat_intel.json](config/threat_intel.json). This is a dated reference
|
|
|
|
|
collection, not a promise of exhaustive intelligence or zero false positives.
|
|
|
|
|
|
|
|
|
|
Keep confidential packs in `private/`, excluded from Git and release archives.
|
|
|
|
|
Load them explicitly. Never put customer logs or generated findings in a public
|
|
|
|
|
repository. Examples under [examples/](examples/) use synthetic data.
|
|
|
|
|
|
|
|
|
|
## Limits and interpretation
|
|
|
|
|
|
|
|
|
|
- Exit `0`: configured processing completed; **not** a clean-system verdict.
|
2026-10-05 06:54:18 +02:00
|
|
|
- Exit `2`: an error or incomplete processing; inspect `SUMMARY.txt` and
|
|
|
|
|
`details/summary.json`. If the scan failed before presentation, inspect
|
|
|
|
|
`summary.json` at the result root instead.
|
2026-10-01 23:08:50 +02:00
|
|
|
- Missing years/timezones are not guessed. See [README.txt](README.txt) for time configuration.
|
|
|
|
|
- Binary appliance logs may need an external decoder. Decode failures can also come from ordinary text.
|
|
|
|
|
- Damaged inputs and regex timeouts are reported. Archive path traversal, unsafe links and exhausted quotas stop preparation.
|
|
|
|
|
- Processing uses one worker, incremental reads and disk-backed SQLite. There is no hard process-wide RAM/CPU quota or automatic resume.
|
|
|
|
|
- Default limits: 2 GiB preparation budget, 100,000 archive entries, depth 3, 1 MiB per line and 512 MiB expanded per log file. Advanced options can raise limits.
|
|
|
|
|
|
|
|
|
|
Tests were executed on macOS/ARM64. Windows and Linux instructions are provided,
|
|
|
|
|
but those platforms have not yet been validated in the recorded test environment.
|
|
|
|
|
|
|
|
|
|
## Help and development
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
netscaler-triage --help
|
|
|
|
|
netscaler-triage scan --help-all
|
|
|
|
|
netscaler-triage decode payload.txt
|
|
|
|
|
netscaler-triage inventory ./logs
|
|
|
|
|
|
|
|
|
|
uv sync --locked
|
|
|
|
|
uv run python -W error -m unittest discover -s tests
|
|
|
|
|
uv run mypy
|
|
|
|
|
uv run ruff check src tests typing_stubs
|
|
|
|
|
uv run ruff format --check src tests typing_stubs
|
|
|
|
|
uv build
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
See [README.txt](README.txt) for the detailed plain-text manual and
|
|
|
|
|
[VALIDATION.txt](VALIDATION.txt) for test scope and known coverage limits.
|
|
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
Project code is licensed under the [MIT License](LICENSE.txt).
|
|
|
|
|
Third-party dependencies and referenced external materials retain their own
|
|
|
|
|
licenses. Source attribution does not imply affiliation with Citrix or the
|
|
|
|
|
referenced researchers, and does not relicense their original publications.
|