/\
_ _ __________________________________________________________ / \__ _ _
__ __ __ ____________________________________________________ \/ /\____ ___
| Notes from the author: \ / |
| \/ |
| If you or a loved one are fond of wifi #hacking tools, #ESP32 |
| based devices, #LLMs and other octothorpable terms please keep |
| reading. |
| |
| Since I have enough #microcontrollers to be an Adafruit Industries |
| distribution center I thought I should probably start using them. |
| |
| Captured Portal is an ESP32-based captive portal scanner and |
| credential enumerator. In simple terms, it finds an open AP, |
| decides if its a captive portal then enumerates it and will tell |
| you exactly what room and name combinations work. It tests for |
| administrative and other goodies by using the known vendors that |
| supply the majority of these services for industry. |
| |
| It automatically detects hotel/hospital/airport/conference/etc |
| style WiFi captive portals, analyzes login forms using on device |
| TINY LLMs to identify field types (room number, last name, access |
| codes), then brute-forces valid combinations using built-in |
| wordlists. |
| |
| It runs standalone on the device, or during build configure it to |
| host a web server for all the work. |
| |
| Speaking of configuration, it even includes an interactive build |
| system with automatic hardware detection, the ability to detect |
| when the device is on battery or USB power and gain new |
| capabilities when plugged in. Best of all, because I hate having |
| to setup the environment when building these things, ive automated |
| that for you, and builds and flashes everything for you. |
| |
| remember, theres no cheating in hacking. |
| |
| Greetz to the real ones. cory |
|*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^*|
NAME..............................Captured Portal v1.0
Collective................................haKC.ai
System.............................ESP32 / ESP32-S3
Size.................1 MCU + Wordlists + Audacity
Supplied by............................/dev/COR.23
Release date..................................2025
GROUP NEWS: haKC.ai Still Looking For Embedded Devs
& RF Enthusiasts. Hit cory@haKC.ai
GR33TZ: SecKC, haKC.ai, Adafruit, the ESP32 silicon
gods, anyone still flashing firmware at 3am
SHOUTZ:
[*] Badge lords & their blinkenlights
[*] Packet sniffers with purpose
[*] Anyone who reads datasheets for fun
FU to [LAMERZ] still asking for the WiFi
password at DEF CON
----- + signed, /dev/COR.23: + -----
flowchart TB
subgraph Parse["HTML PARSING"]
HTML[/"Portal HTML"/]
Extract["Extract input fields"]
Detect["Detect field types"]
end
subgraph Fields["FIELD DETECTION"]
Room["room, number, rm"]
Name["last, surname, family"]
Email["email, mail"]
Code["code, access, pin"]
end
subgraph Wordlists["WORDLISTS"]
Rooms["~90 Room Numbers<br>101, 102... 1103<br>A1, A2, B1, C3"]
Names["~60 Surnames<br>Smith, Johnson<br>Kennedy, Garcia, Patel"]
end
subgraph Bruteforce["ENUMERATION"]
Combo["Generate Combinations"]
Test1["101 + Smith"]
Test2["101 + Johnson"]
Test3["102 + Smith"]
TestN["... + ..."]
POST["POST to /login"]
end
subgraph Response["RESPONSE ANALYSIS"]
Check{{"Check Response"}}
Success["SUCCESS<br>welcome, connected<br>authenticated, success"]
Failure["FAILURE<br>invalid, error<br>incorrect, wrong"]
end
subgraph Results["RESULTS"]
Log["Log to successes"]
JSON@{ label: "{'room':'101','name':'Kennedy'}" }
Stats["Valid combos found<br>Estimated room count<br>Venue insights"]
end
HTML --> Extract --> Detect
Detect --> Room & Name & Email & Code
Room & Name --> Rooms & Names
Rooms & Names --> Combo
Combo --> Test1 & Test2 & Test3 & TestN
Test1 & Test2 & Test3 & TestN --> POST
POST --> Check
Check -- Keywords found --> Success
Check -- Error keywords --> Failure
Success --> Log --> JSON --> Stats
Failure -. Next combo .-> Combo
JSON@{ shape: lean-r}
style HTML fill:#e94560,stroke:#fff,color:#fff
style Check fill:#ff00ff,stroke:#fff,color:#fff
style JSON fill:#00ff41,stroke:#000,color:#000
style Fields fill:#16213e,stroke:#0f3460,stroke-width:2px,color:#fff
style Wordlists fill:#0f3460,stroke:#00ff41,stroke-width:2px,color:#fff
style Bruteforce fill:#1a1a2e,stroke:#ff00ff,stroke-width:3px,color:#fff
style Parse fill:#1a1a2e,stroke:#e94560,stroke-width:2px,color:#fff
style Response fill:#16213e,stroke:#00ff41,stroke-width:2px,color:#fff
style Results fill:#0f3460,stroke:#e94560,stroke-width:2px,color:#fff
- Captive Portal Detection - Automatically scans for and identifies captive portals
- LLM-Powered Analysis - On-device AI analyzes portal responses to extract venue information
- Credential Enumeration - Tests room numbers and surnames to discover valid combinations
- Hacker Aesthetic UI - Matrix-style animations, decrypt effects, synthwave colors
- Dual Interface - Physical display + web dashboard
- Dual Power Mode - Battery-powered standalone OR USB-powered with boosted capabilities
- Data Logging - Stores all findings for later review
build23b.mp4
graph TB
subgraph Device[ESP32-S3 Device]
Scanner[WiFi Scanner]
Portal[Portal Detector]
Enum[Enumerator]
LLM[LLM Engine]
UI[Display UI]
Web[Web Server]
Power[Power Manager]
end
subgraph External[External]
WiFi[Open WiFi Networks]
CP[Captive Portals]
Browser[Web Browser]
end
WiFi --> Scanner
Scanner --> Portal
Portal --> CP
CP --> Enum
CP --> LLM
LLM --> UI
Enum --> UI
Web --> Browser
Power --> Scanner
Power --> Web
style Device fill:#1a1a2e,stroke:#0f3460,color:#fff
style External fill:#16213e,stroke:#0f3460,color:#fff
style Scanner fill:#e94560,stroke:#0f3460,color:#fff
style Portal fill:#e94560,stroke:#0f3460,color:#fff
style Enum fill:#e94560,stroke:#0f3460,color:#fff
style LLM fill:#16213e,stroke:#0f3460,color:#fff
style UI fill:#16213e,stroke:#0f3460,color:#fff
style Web fill:#16213e,stroke:#0f3460,color:#fff
style Power fill:#16213e,stroke:#0f3460,color:#fff
style WiFi fill:#0f3460,stroke:#e94560,color:#fff
style CP fill:#0f3460,stroke:#e94560,color:#fff
style Browser fill:#0f3460,stroke:#e94560,color:#fff
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#fff', 'primaryBorderColor': '#e94560', 'lineColor': '#e94560', 'secondaryColor': '#16213e', 'tertiaryColor': '#0f3460', 'actorBkg': '#e94560', 'actorTextColor': '#fff', 'actorLineColor': '#0f3460', 'signalColor': '#e94560', 'signalTextColor': '#fff'}}}%%
sequenceDiagram
participant D as Device
participant W as WiFi Network
participant C as Captive Portal
participant L as LLM Engine
D->>W: Scan for open networks
W-->>D: Network list
D->>W: Connect to network
D->>C: Request connectivity check
C-->>D: 302 Redirect
D->>D: Portal detected!
D->>C: Fetch portal HTML
C-->>D: Login page HTML
D->>L: Analyze HTML
L-->>D: Venue info + fields
D->>D: Begin enumeration
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#e94560', 'primaryTextColor': '#fff', 'primaryBorderColor': '#0f3460', 'lineColor': '#e94560', 'secondaryColor': '#16213e', 'tertiaryColor': '#1a1a2e', 'background': '#1a1a2e', 'mainBkg': '#1a1a2e', 'stateBkg': '#16213e', 'stateTextColor': '#fff', 'stateLabelColor': '#fff', 'transitionColor': '#e94560', 'transitionLabelColor': '#fff'}}}%%
stateDiagram-v2
[*] --> Boot
Boot --> DetectPower
DetectPower --> BatteryMode: No USB
DetectPower --> USBMode: USB Connected
BatteryMode --> Scanning: Start scan
USBMode --> Scanning: Start scan
Scanning --> PortalFound: Portal detected
Scanning --> Idle: No portals
PortalFound --> Analyzing: USB Mode
PortalFound --> Enumerating: Battery Mode
Analyzing --> Enumerating: Analysis complete
Enumerating --> Idle: Complete
Idle --> DeepSleep: Timeout
Idle --> Scanning: User input
DeepSleep --> [*]
- Scans for open WiFi networks
- Connects and requests
http://connectivitycheck.gstatic.com/generate_204 - If redirected (302) or content differs, it's a captive portal
- Captures portal HTML for analysis
- Analyzes portal form fields (room number, last name, etc.)
- Uses wordlists (
data/wordlists/room_numbers.txt,data/wordlists/surnames.txt) - Tests combinations based on detected field types
- Logs successful combinations
- Estimates venue size from valid room numbers
- Feeds captured HTML to on-device LLM
- Extracts: venue name, type, form fields, security issues
- Provides enumeration strategy recommendations
# Clone this repo
git clone https://github.com/haKC-ai/CapturedPortal.git
cd CapturedPortal
# Run the installer (creates venv, installs deps, launches build tool)
./installer.shThe installer will:
- Create a Python 3.13 virtual environment
- Install all dependencies (hakcer, platformio)
- Launch the interactive build tool with menus for:
- Build & Flash - Configure hardware, colors, network, then build/upload
- Test Portal - Run a fake captive portal server for testing
| Board | Price | Display | Best For |
|---|---|---|---|
| LILYGO T-Display S3 | ~$20 | 1.9" LCD | Recommended - Great balance |
| LILYGO T-Deck | ~$50 | 2.8" LCD + Keyboard | Full Featured - Keyboard + Trackball |
| Waveshare LCD 1.47 | ~$13 | 1.47" LCD | USB Dongle form factor |
| ESP32-S3 Round 1.28" | ~$15 | 1.28" Round | Compact watch-like |
| Waveshare Touch 1.69 | ~$25 | 1.69" Touch | Touch interface |
Price: ~$20 | Display: 1.9" color LCD (170x320)
| Component | Link | Price |
|---|---|---|
| LILYGO T-Display-S3 | Amazon | ~$20 |
| 3.7V LiPo Battery (optional) | Amazon | ~$10 |
Specs:
- ESP32-S3 dual-core @ 240MHz
- 16MB Flash, 8MB PSRAM
- Two programmable buttons
- Battery charging built-in
- WiFi + Bluetooth 5.0
Assembly: No soldering required! Just flash and go. Optional battery connects via JST connector.
Price: ~$50 | Display: 2.8" LCD (320x240) + Physical Keyboard
| Component | Link | Price |
|---|---|---|
| LILYGO T-Deck | Amazon | ~$50 |
| LILYGO T-Deck (with LoRa) | LILYGO Store | ~$60 |
Specs:
- ESP32-S3 dual-core @ 240MHz
- 16MB Flash, 8MB PSRAM
- Full QWERTY keyboard - Type room numbers, names directly
- Trackball navigation - Scroll through networks smoothly
- Built-in speaker and microphone
- Optional LoRa radio (433/868/915MHz)
- SD card slot for extended logging
- Battery charging built-in
Why T-Deck?
- Type credentials directly for manual testing
- Trackball makes navigation fast
- Larger screen shows more networks at once
- LoRa version can share findings with other devices
Assembly: Ready to use out of the box. Power on and the keyboard/trackball work automatically.
Controls:
| Input | Action |
|---|---|
| Trackball Up/Down | Navigate list |
| Trackball Left/Right | Switch screens |
| Trackball Click | Select / Short: action, Long: scan |
| Keyboard | Type directly into fields |
USB dongle form factor. Plugs directly into any USB port. Great for covert use.
Compact watch-like form factor. Built-in IMU. Smallest option.
Capacitive touch screen. No buttons needed - tap to interact.
- Python 3.13 (recommended) or 3.9+
- USB cable for your board
git clone https://github.com/haKC-ai/CapturedPortal.git
cd CapturedPortal
./installer.shThe installer:
- Creates a Python 3.13 virtual environment in
.venv/ - Installs dependencies from
tools/requirements.txt - Launches
tools/build.pywith an interactive menu
The build tool uses the hakcer library for cool terminal effects and provides:
- Menu 1: Build & Flash - Hardware selection, color scheme, network config, build/upload
- Menu 2: Test Portal - Run fake captive portals for device testing
# Clone repo
git clone https://github.com/haKC-ai/CapturedPortal.git
cd CapturedPortal
# Create venv and install deps
python3.13 -m venv .venv
source .venv/bin/activate
pip install -r tools/requirements.txt
# Build for your board
pio run -e lilygo-t-display-s3 # T-Display S3
pio run -e lilygo-t-deck # T-Deck
pio run -e waveshare-esp32s3-lcd147 # Waveshare Dongle
# Upload firmware
pio run -t upload
# Upload web interface
pio run -t uploadfsEdit include/config.h or use the build tool:
// Color scheme
#define COLOR_SCHEME COLOR_MATRIX // COLOR_MATRIX, COLOR_SYNTHWAVE, COLOR_CYBERPUNK, COLOR_DRACULA
// WiFi AP settings
#define AP_SSID_PREFIX "CapturedPortal_"
#define AP_PASSWORD "" // Empty = open network
// Enable web server on battery (uses more power)
#define WEB_SERVER_ON_BATTERY false
// Scan intervals
#define BATTERY_SCAN_INTERVAL 10000 // 10s on battery
#define USB_SCAN_INTERVAL 3000 // 3s on USBRun a fake captive portal on your computer to test the device:
# List available portal types
python3 tools/test_portal.py --list
# Run hotel portal (default)
python3 tools/test_portal.py
# Run specific portal type
python3 tools/test_portal.py -t airport
python3 tools/test_portal.py -t cafe
python3 tools/test_portal.py -t conference
python3 tools/test_portal.py -t hospitalTo test:
- Create a WiFi hotspot on your computer
- Run
python3 tools/test_portal.py - Connect your Captured Portal device to the hotspot
- The device will detect and analyze the test portal
Available test portals:
| Type | Fields | Valid Test Credentials |
|---|---|---|
| hotel | Room + Last Name | Rooms: 101, 201, 420 / Names: smith, guest |
| airport | Any valid email format | |
| cafe | Access Code | COFFEE2024, LATTE, 1234 |
| conference | Badge ID + Email | TC001, TC002, SPEAKER, VIP |
| hospital | Patient Room + Name | ICU1, 101, ER1 |
| Mode | Power Source | Capabilities |
|---|---|---|
| Standalone | Battery | Scanning, display UI, logging |
| Boosted | USB | + Web server, LLM analysis, faster scans |
Set WEB_SERVER_ON_BATTERY true in config to enable web server on battery (drains faster).
T-Display S3 / Button boards:
| Button | Short Press | Long Press |
|---|---|---|
| Left | Scroll up | Start scan |
| Right | Scroll down | Select/Connect |
T-Deck:
| Input | Action |
|---|---|
| Trackball | Navigate in all directions |
| Trackball Click | Short: Select, Long: Scan |
| Keyboard | Type directly |
When web server is running:
- Connect to WiFi:
CapturedPortal_XXXX - Open:
http://192.168.4.1 - Features:
- Real-time network scanner
- Portal analysis dashboard
- Enumeration controls
- LLM insights
- Activity log
CapturedPortal/
├── installer.sh # Main installer script
├── banner # ASCII art banner
├── platformio.ini # Build configuration
├── DISCLAIMER.md # Legal disclaimer
├── USAGE_GUIDELINES.md # Acceptable use policy
├── RESPONSIBLE_DISCLOSURE.md # Vulnerability disclosure policy
├── CODE_OF_CONDUCT.md # Community standards
├── src/
│ ├── main.cpp # Entry point
│ ├── core/
│ │ ├── scanner.cpp # WiFi scanning & portal detection
│ │ ├── enumerator.cpp # Credential enumeration
│ │ └── power.cpp # Power management
│ ├── display/
│ │ ├── ui.cpp # Display UI
│ │ └── effects.cpp # Hacker animations
│ ├── web/
│ │ └── server.cpp # Web server & API
│ └── llm/
│ └── engine.cpp # LLM inference
├── include/
│ └── config.h # Configuration
├── data/
│ ├── web/ # Web UI files
│ └── wordlists/ # Enumeration wordlists
└── tools/
├── requirements.txt # Python dependencies (hakcer, platformio, pyserial)
├── build.py # Build & test menu tool
└── test_portal.py # Test captive portal server
This tool is intended for authorized security testing only.
Before using this tool, you must:
- Read the Disclaimer
- Review the Usage Guidelines
- Understand the Responsible Disclosure Policy
- Agree to the Code of Conduct
Unauthorized access to computer systems is a crime. The authors assume no liability for misuse.
By using this software, you accept full responsibility for ensuring your use complies with all applicable laws.
MIT License - hack responsibly.


