# How the Decoy Website Feature Captures Blue Team Operators in LazyOwn

> Learn how LazyOwn's decoy website feature actively captures blue team operators by exfiltrating webcam microphone and screen data to the C2 server.

- Repository: [Grisuno/lazyown](https://github.com/grisuno/lazyown)
- Tags: deep-dive
- Published: 2026-03-02

---

**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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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.

```python

# 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:

```python
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 in [`lazyc2.py`](https://github.com/grisuno/lazyown/blob/main/lazyc2.py) filters traffic by comparing `request.remote_addr` against the C2 host and localhost, serving [`templates/decoy.html`](https://github.com/grisuno/lazyown/blob/main/templates/decoy.html) to external visitors.
- The decoy page executes aggressive JavaScript that captures webcam snapshots, microphone audio, and screen recordings without user consent, exfiltrating data to `/capture` and `/audio` endpoints.
- All captured media is stored in `sessions/captured_images` with 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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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.