Project Awesome project awesome

ICS/SCADA honeypots > GasPot

Veeder Root Gaurdian AST, common in the oil and gas industry.

Package 146 stars GitHub

GasPot v2.0 — Veeder-Root TLS ATG Honeypot

A honeypot that emulates a Veeder-Root TLS-350 / TLS-450 Automatic Tank Gauge (ATG) controller commonly found at gas stations worldwide. All connection attempts and commands are logged for threat-intelligence collection.

Originally created by Kyle Wilhoit and Stephen Hilt — modernised in 2026 with expanded command support, structured logging, and cleaner architecture.


What's New in v2.0

Area Before (v1) After (v2)
Commands 5 inquiry + S602xx 21 inquiry + 3 set/write command families
Architecture Globals + string concatenation Dataclasses (Tank, StationState) + clean handler functions
Logging Manual file.write() Python logging module with dual output (file + console)
JSON Logs No --json-log flag for SIEM / Splunk / ELK ingest
Code duplication S602xx copy-pasted 5× Single consolidated cmd_S602xx() handler
Graceful shutdown Ctrl-C sometimes leaked sockets Signal handlers (SIGINT/SIGTERM) + clean socket teardown
Socket handling send() (partial writes possible) sendall() for reliable delivery
SO_REUSEADDR No (port stuck after restart) Yes
Error handling Bare except Exception Targeted exception types with proper logging
Docker Python 3 Alpine 3.18 Removed — see note below
Python 3.x (minimal) 3.10+ (type hints, f-strings, dataclasses)

Supported Veeder-Root TLS Commands

Inquiry Commands (read-only)

Command Description
I10100 System Status Report — serial #, software version, power/battery/printer status
I10200 System Configuration — tanks, capacity, language, units
I11100 Priority Alarm History Report — recent alarm events with category, type, state, timestamps (per manual 576013-635, Function Code 111)
I20100 In-Tank Inventory — volume, TC volume, ullage, height, water, temp (most commonly probed)
I20200 Delivery Report — fuel delivery start/end, amounts
I20300 In-Tank Leak Detect Report
I20400 Shift Report — starting/ending values, totals
I20500 In-Tank Status Report — alarm states per tank
I20600 In-Tank Alarm History — timestamped alarm/clear events
I20700 In-Tank Diagnostic Report — probe, sensor, leak status
I20800 Tank Test Results — last test date, pass/fail, leak rate
I20900 Tank Tightness Test Results — precision test data
I21400 Overfill/High Product Alarm History — timestamps, gallon levels
I25100 Line Leak Test Results
I30100 Sensor Status Report — sump/dispenser pan sensors
I30200 Sensor Alarm History
I60100 Tank Configuration Data — capacity, diameter, alarm thresholds
I50100 Date/Time Query — current date, time, day of week (recon scanning)
I60200 Tank Product Label Configuration
I60900 Sensor Configuration Data
I90200 Alarm Reset — logged as WARNING; active tampering attempt

Set Commands (write — high-value honeypot events)

Command Description
S50100 Set Date/Time — logged as WARNING; attackers changing time is significant
S60100 Set Station Name — logged as WARNING
S6020x Set Product LabelS60200 sets all tanks, S60201S60204 set individual tanks

All set commands are logged at WARNING level because they represent active tampering attempts.


Quick Start

Bare Metal

# Clone
git clone https://github.com/sjhilt/GasPot.git
cd GasPot

# Configure
cp config.ini.dist config.ini
# Edit config.ini to localise station names, products, etc.

# Run
python3 GasPot.py

# With JSON logging for SIEM ingest
python3 GasPot.py --json-log --log /var/log/gaspot.json

# Quiet mode (no console output)
python3 GasPot.py --quiet --json-log

Docker

Note: A Dockerfile was previously included in this repository but was contributed by an external party. It has been removed from the project. If you need to run GasPot in a container, you are welcome to write your own Dockerfile, but please review and use any community-provided container configurations with caution.

Test a Command

# Send an In-Tank Inventory request (I20100)
echo -ne '\x01I20100\n' | nc localhost 10001

Example Output

Here's what the responses look like when you send commands to GasPot. These are the same formats a real Veeder-Root TLS system would return.

I20100 — In-Tank Inventory (most commonly probed)

I20100
04/30/2026 15:10

    SHELL STATION


IN-TANK INVENTORY

TANK PRODUCT             VOLUME TC VOLUME   ULLAGE   HEIGHT    WATER     TEMP
  1  SUPER                   1906        1916      4110    33.99      0.14    52.53
  2  UNLEAD                  1406        1415      3106    33.57      0.38    50.80
  3  DIESEL                   716         720      1958    20.05      0.55    52.21
  4  PREMIUM                 4412        4430      3108    54.56      0.40    54.15

I10100 — System Status

I10100
04/30/2026 15:10

    SHELL STATION


SYSTEM STATUS REPORT

       SERIAL# : 0000112233
  SOFTWARE VER : V364.200

  POWER STATUS : AC ON
  BATTERY      : OK
  PRINTER      : ACTIVE

  COMM 1       : ENABLED  IDLE
  COMM 2       : DISABLED

I50100 — Date/Time Query

I50100
04/30/2026 15:10

    SHELL STATION


  DATE: 04/30/2026
  TIME: 15:10:04
  DAY : THURSDAY

I11100 — Priority Alarm History

I11100
04/30/2026 15:10
    SHELL STATION



PRIORITY ALARM HISTORY

ID  CATEGORY  DESCRIPTION          ALARM TYPE           STATE    DATE    TIME
    SYSTEM                         BATTERY IS OFF       CLEAR   4-30-26  9:25AM
    SYSTEM                         BATTERY IS OFF       ALARM   4-30-26  9:10AM

I20200 — Delivery Report

I20200
04/30/2026 15:10


    SHELL STATION


DELIVERY REPORT

T 1:SUPER
INCREASE   DATE / TIME             GALLONS TC GALLONS WATER  TEMP DEG F  HEIGHT

      END: 04/30/2026 08:29         1906       1916   0.11      52.32   33.99
    START: 04/30/2026 08:19         602        605    0.13      52.53   15.02
   AMOUNT:                          1304       1311

I20500 — In-Tank Status

I20500
04/30/2026 15:10


    SHELL STATION


TANK   PRODUCT                 STATUS

  1    SUPER                                    NORMAL

  2    UNLEAD                                   NORMAL

  3    DIESEL                                   NORMAL

  4    PREMIUM                                  NORMAL

Command-Line Options

usage: GasPot.py [-h] [--config CONFIG] [--log LOG] [--json-log] [--quiet] [--version]

Options:
  --config CONFIG   Configuration file path (default: config.ini)
  --log LOG         Log file path (default: gaspot.log)
  --json-log        Emit logs as JSON lines (for SIEM/Splunk/ELK)
  --quiet           Suppress console output; log only to file
  --version         Show version and exit

Configuration

The config.ini file controls tank parameters, station identity, and network settings. Copy config.ini.dist to config.ini and customise:

[host]
tcp_ip = 0.0.0.0
tcp_port = 10001
buffer_size = 1024

[products]
product1 = SUPER
product2 = UNLEAD
product3 = DIESEL
product4 = PREMIUM

[stations]
list = ['SHELL STATION', 'BP FUELS', 'EXXON STATION', ...]

[parameters]
decimal_separator = .
low_temperature = 50
high_temperature = 60
min_h2o = 0
max_h2o = 9
min_height = 25
max_height = 75
min_vol = 1000
max_vol = 9050
min_ullage = 3000
max_ullage = 9999

Tip: Localise station names and product labels to match the region where you deploy the honeypot. This makes it harder for attackers to fingerprint it as a honeypot.


Log Format

Text (default)

2026-04-30T13:42:01Z [INFO] Connection from 192.168.1.50:54321
2026-04-30T13:42:01Z [INFO] CMD I20100 from 192.168.1.50
2026-04-30T13:42:15Z [WARNING] SET CMD S50100 from 10.0.0.5 payload='2604301200'

JSON (--json-log)

{"ts":"2026-04-30T13:42:01.123456Z","level":"INFO","msg":"CMD I20100 from 192.168.1.50","remote_ip":"192.168.1.50","command":"I20100"}
{"ts":"2026-04-30T13:42:15.789012Z","level":"WARNING","msg":"SET CMD S50100 from 10.0.0.5 payload='2604301200'","remote_ip":"10.0.0.5","command":"S50100"}

Architecture

GasPot.py
├── Data Model
│   ├── Tank          — dataclass for per-tank state (volume, temp, etc.)
│   └── StationState  — dataclass for station-wide state (name, tanks[], serial#)
├── Command Handlers
│   ├── cmd_I10100()  — System Status
│   ├── cmd_I20100()  — In-Tank Inventory
│   ├── ...           — 21 inquiry handlers
│   ├── cmd_S50100()  — Set Date/Time
│   ├── cmd_S60100()  — Set Station Name
│   └── cmd_S602xx()  — Set Product Labels (consolidated)
├── Command Registry
│   ├── INQUIRY_COMMANDS dict
│   └── SET_COMMANDS dict
├── Network Server
│   ├── run_server()      — select-based TCP loop with signal handling
│   ├── handle_command()  — parse SOH prefix, dispatch to handler
│   └── _handle_client()  — per-connection read/write
└── Entry Point
    └── main()            — arg parsing, config, logging, server start

Stargazers over time

Stargazers over time


License

CC0 1.0 Universal — public domain.

Back to Honeypots