How the Decoy Website Feature Captures Blue Team Operators in LazyOwn
The decoy website feature in LazyOwn serves a malicious HTML page to unauthorized visitors that silently captures webcam, microphone, and screen data via JavaScript, exfiltrating media to the C2 server for blue team identification.
LazyOwn is an open-source command and control (C2) framework designed for red team operations. The decoy website feature functions as a low-interaction honeypot that activates when unexpected traffic hits the Flask server, tricking blue team operators into revealing their environment through aggressive browser-based surveillance.
How the Decoy Website Feature Works in LazyOwn
Server-Side Detection Logic in lazyc2.py
The detection mechanism resides in lazyc2.py, where the decoy() helper function evaluates incoming requests. It inspects request.remote_addr to determine if the client IP differs from the configured C2 host (lhost) and is not localhost (127.0.0.1). When these conditions are met, the function renders templates/decoy.html, serving the malicious payload to the unauthorized visitor.
Flask error handlers for HTTP 404 and 500 errors (page_not_found and internal_server_error) automatically invoke decoy() when external IPs trigger routing errors, ensuring any mistaken browse or scan by blue team operators activates the trap.
The Malicious Decoy Page (templates/decoy.html)
The client-side attack executes from templates/decoy.html, which contains aggressive JavaScript payloads designed to harvest multimedia data without user consent.
Webcam and Microphone Capture: The script calls navigator.mediaDevices.getUserMedia({ video: true, audio: true }) to access the camera and microphone. It draws 640×480 canvas frames every five seconds, converting them to PNG data and POSTing them to the /capture endpoint. Simultaneously, it records 10-second audio clips via MediaRecorder and exfiltrates them to /audio.
Screen Capture: The code invokes navigator.mediaDevices.getDisplayMedia({ video: true }) to request screen sharing, capturing 1280×720 canvas frames every five seconds and transmitting them to the same /capture endpoint.
Data Exfiltration Endpoints
The Flask server defines two specific routes to receive the exfiltrated data. The /capture endpoint accepts POST requests containing base64-encoded PNG images, decodes the payload, and writes files to sessions/captured_images with timestamped filenames like capture_20250607_123045.png.
The /audio endpoint handles WebM audio files, storing them in the same directory with names such as audio_<timestamp>.webm. The constant SAVE_DIR = "sessions/captured_images", defined around line 1598 in lazyc2.py, centralizes all captured media for later analysis.
Configuring the Decoy Website Feature
No additional configuration is required to enable the decoy functionality. When the Flask application initializes, the error handlers are automatically registered. Any HTTP request originating from an IP address other than the C2 host or localhost that results in a 404 or 500 error will trigger the decoy page.
To verify the feature is active, attempt to access a non-existent path from an external IP address. The server should return the decoy HTML rather than a standard Flask error page.
# Example: Verifying decoy activation in lazyc2.py
# When an external visitor requests /nonexistent:
# 1. Flask raises 404
# 2. page_not_found() handler calls decoy()
# 3. decoy() checks request.remote_addr != lhost
# 4. Renders templates/decoy.html with surveillance JavaScript
Analyzing Captured Blue Team Data
After a blue team operator triggers the decoy, captured media accumulates in the sessions/captured_images directory. Red team operators can analyze these artifacts to gather intelligence about the target environment, including operating system details, desktop configurations, and physical surroundings.
Access the captured files programmatically to automate analysis:
from pathlib import Path
import datetime
# Locate all captured media
capture_dir = Path("sessions/captured_images")
captures = list(capture_dir.glob("capture_*.png"))
audio_files = list(capture_dir.glob("audio_*.webm"))
print(f"Total image captures: {len(captures)}")
print(f"Total audio recordings: {len(audio_files)}")
# Display most recent captures
for img in sorted(captures)[-5:]:
timestamp = img.stem.split('_')[1:]
print(f"Captured: {'_'.join(timestamp)} -> {img.name}")
Summary
- The decoy website feature in LazyOwn acts as a low-interaction honeypot that activates when unauthorized IPs trigger 404 or 500 errors on the Flask C2 server.
- The
decoy()function inlazyc2.pyfilters traffic by comparingrequest.remote_addragainst the C2 host and localhost, servingtemplates/decoy.htmlto external visitors. - The decoy page executes aggressive JavaScript that captures webcam snapshots, microphone audio, and screen recordings without user consent, exfiltrating data to
/captureand/audioendpoints. - All captured media is stored in
sessions/captured_imageswith timestamped filenames, enabling red team operators to analyze blue team environments for operational intelligence.
Frequently Asked Questions
What triggers the decoy website feature in LazyOwn?
The decoy activates when a client requests a non-existent route or triggers a server error (HTTP 404 or 500) on the LazyOwn Flask C2 server. The page_not_found and internal_server_error handlers in lazyc2.py invoke the decoy() function, which checks if the source IP (request.remote_addr) differs from the configured C2 host and is not localhost. When these conditions are met, the decoy page is served instead of a standard error response.
What data does the LazyOwn decoy website capture from blue team operators?
The decoy captures three categories of multimedia data through browser-based JavaScript executed in templates/decoy.html. First, it captures webcam snapshots as 640×480 PNG images every five seconds. Second, it records 10-second audio clips from the microphone using the MediaRecorder API. Third, it captures screen recordings at 1280×720 resolution every five seconds via the getDisplayMedia API. All data is silently transmitted to the C2 server without user notification.
Where are the captured blue team artifacts stored in LazyOwn?
All exfiltrated media is stored in the sessions/captured_images directory, as defined by the SAVE_DIR constant in lazyc2.py. Image captures from both webcam and screen sharing are saved as timestamped PNG files (e.g., capture_20250607_123045.png), while audio recordings are stored as WebM files (e.g., audio_20250607_123055.webm). This centralized storage allows red team operators to easily review intelligence gathered from blue team interactions with the decoy.
How can red team operators distinguish between legitimate C2 traffic and blue team operators hitting the decoy?
LazyOwn distinguishes traffic through IP validation logic in the decoy() function within lazyc2.py. The function compares request.remote_addr against the configured C2 host (lhost) and explicitly excludes localhost (127.0.0.1). Requests originating from the red team’s configured C2 host proceed to legitimate C2 endpoints, while requests from any other IP address trigger the decoy response. This IP-based filtering ensures that only unauthorized visitors—typically blue team operators scanning or browsing the C2 server—receive the surveillance payload.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →