SecurityAnalyticsSecurityOperations

The Art of Digital Deception: How I Built and Baited a Windows Honeypot to Trap Real Attackers

Rakshit Shetty

Rakshit Shetty

Security Expert

This is a two-part blog series where I share my journey of turning a Windows machine into a publicly exposed honeypot designed to lure, log, and learn from real-world attackers.

  • Part 1: Behind the Screens of a Trap MachineI walk you through how I architected and deployed a Windows EC2 instance on AWS with open services like SSH, RDP, HTTP(S), FTP, and Telnet. We’ll explore how each fake service was designed to appear vulnerable—yet safely monitored—and how all logs were forwarded to DNIF SIEM for real-time visibility.
  • Part 2: The Day I Became a Digital BeekeeperThis part dives into what happened once the trap went live. From Chile brute-force bots to SQL injection attempts from Vietnam, I unpack the attacks, analyze behaviors, and extract actionable threat intelligence. Real tools. Real TTPs. Real learning.

🧠 Whether you’re a red teamer, defender, or just cyber-curious—this series offers a close-up look at the reality of life on the edge of the internet.

Part 1: Building a Windows Honeypot – Behind the Screens of a Trap Machine

Prelude: Why Build a Honeypot Anyway?

It started with a simple idea: What if we could observe attackers in their natural habitat? Honeypots aren’t new, but running a high-interaction Windows honeypot on a public IP with real-looking services adds a dangerous allure. I wasn’t just simulating threats—I wanted to become bait.

“The moment you expose a service to the internet, someone, somewhere, is trying to break into it.”

Overview: What We Built

We designed a honeypot hosted on Windows EC2 on AWS, publicly exposed to gather real-world attack telemetry. Here’s a quick summary of what went live:

22Fake SSHSimulate remote access and log brute-force attempts.80HTTP HR PortalMimic an internal HR login system.443HTTPS Fake CorpSimulate a secure corporate intranet site.3389RDPCommon brute-force target.21FTPOften used for data exfiltration attempts.23TelnetEmulate outdated access protocols.445SMBSMB trap mimicking EternalBlue vulnerabilities

Each service was custom-coded or wrapped using Python modules to make the experience believable. The attacker should feel like they’ve hit an exposed internal server in a misconfigured corporate network.

Hosting Architecture: The Danger Zone

  • Platform: AWS EC2 (Windows 10 Pro)
  • Security Groups: 0.0.0.0/0 allowed on all major ports
  • Isolation: No VPC peering, no outbound connections
  • Monitoring: Log files + Windows Event Forwarding + DNIF SIEM

This machine was intentionally vulnerable—no updates, no firewalls, weak-looking passwords, and fake credentials in plaintext files.

Inside the Services: A Quick Tour

1. ssh_service.py – The Troll Tunnel

Using Python’s paramiko, we created a fake SSH server. Attackers were able to connect and issue commands, but every response was rigged.

ssh root@172.16.xx.xx -p 22
root@172.16.xx.xx's password: 
Permission denied, please try again.
root@172.16.xx.xx's password: 
C:\Users\root> ls
'ls' is not recognized as an internal or external command,
operable program or batch file.
C:\Users\root> dir
 Directory of C:\Users\root
04/22/2025  10:00 AM    <DIR>          Documents
04/22/2025  10:00 AM    <DIR>          Downloads
04/22/2025  10:00 AM             265 notes.txt
C:\Users\root> 

Behavior:

  • Logs IP, attempted credentials, commands
  • Accepts weak passwords (admin:admin), then drops into a fake shell
  • Mimics real command responses before sarcastically shutting them down

Below is how the logs would be logged at our honeypot end.

[2025-06-05 12:02:11] [+] New SSH connection from 172.16.xxx.xxx
[2025-06-05 12:02:14] [AUTH] Attempt from 172.16.xxx.xxx - Username: root, Password: test
[2025-06-05 12:02:15] [AUTH] Attempt from 172.16.xxx.xxx - Username: root, Password: test
[2025-06-05 12:02:18] [AUTH] Attempt from 172.16.xxx.xxx - Username: root, Password: test3
[2025-06-05 12:02:18] [AUTH] root exceeded max login attempts from 172.16.xxx.xxx
[2025-06-05 12:02:22] [AUTH] Attempt from 172.16.xxx.xxx - Username: root, Password: test
[2025-06-05 12:02:22] [AUTH] root exceeded max login attempts from 172.16.xxx.xxx
[2025-06-05 12:02:26] [AUTH] Attempt from 172.16.xxx.xxx - Username: root, Password: toor
[2025-06-05 12:02:28] [COMMAND] root@172.16.xxx.xxx: dir

2. hrms.py – The HR Portal Trap (Port 80)

Mimics a vulnerable HRMS portal with fake credentials and SQL injection-vulnerable parameters. Attracts both bots and human attackers:

  • Login form with fake user accounts
  • Password reset button that always “fails”
  • SQLi-prone GET parameters (e.g., employee?id=105 OR 1=1)

What it lures: Script kiddies, bots, or more advanced attackers who scan for open web ports and look for login pages.

Below is how the page would look to the attacker:

Login page for a corporate HR management system featuring username and password fields. Screenshot of a corporate HR management system dashboard titled 'CorpHRMS' with navigation options for HR Profiles, Internal Mail, Timesheets, and Internal Docs. A reminder message is displayed, emphasizing the need to update staff records by the end of the quarter. A webpage titled 'Finance Department - Internal Documents' displaying a list of confidential documents available for download, including 'budget-2024-draft.csv', 'executive-bonus-plan.pdf', and 'Q1-Payroll-Report.csv'.

3. https_service.py – Fake Corporate Dashboard (Port 443)

This site mimics a corporate finance dashboard, hosted via HTTPS.

  • Login form with TLS
  • Routes like /admin, /payroll, /upload
  • Triggers fake file upload responses or access denied

Below is how the page would look to the attacker:

Login form for a corporate intranet, featuring input fields for username and password, along with a login button. A dashboard interface for HelpDeskX support system displaying metrics such as open tickets, resolved tickets, pending escalation, and SLA breaches. A screenshot of an internal support documents portal, displaying a list of files with their names, sizes, last modified dates, and download buttons. A webpage displaying an index of directories in a Linux file system, including links to 'passwd', 'shadow', 'hostname', 'resolv.conf', 'ssh', and 'ssl'.

Behavior:

  • Logs IP, attempted credentials
  • Accepts weak passwords (admin:admin), then drops into a fake website
  • Mimics real website with vulnerabilities like SQLi, Directory traversal etc.

Below is how the logs would be logged at our honeypot end.

172.16.xxx.xxx - - [05/Jun/2025 12:20:40] "GET /documents HTTP/1.1" 302 -
172.16.xxx.xxx - - [05/Jun/2025 12:20:40] "GET / HTTP/1.1" 200 -
172.16.xxx.xxx - - [05/Jun/2025 12:21:11] "GET /static/css/style.css HTTP/1.1" 200 -
172.16.xxx.xxx - - [05/Jun/2025 12:21:11] "GET /static/bootstrap/js/bootstrap.bundle.min.js HTTP/1.1" 200 -
172.16.xxx.xxx - - [05/Jun/2025 12:21:11] "GET /static/bootstrap/css/bootstrap.min.css HTTP/1.1" 200 -
172.16.xxx.xxx - - [05/Jun/2025 12:21:12] "GET /favicon.ico HTTP/1.1" 404 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:30] "POST / HTTP/1.1" 302 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:31] "GET /dashboard HTTP/1.1" 200 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:32] "GET /static/bootstrap/css/bootstrap.min.css HTTP/1.1" 200 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:32] "GET /static/css/style.css HTTP/1.1" 200 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:32] "GET /static/bootstrap/js/chart.3.9.1.min.js HTTP/1.1" 200 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:32] "GET /static/bootstrap/js/bootstrap.bundle.min.js HTTP/1.1" 304 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:47] "GET /documents HTTP/1.1" 200 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:48] "GET /static/css/style.css HTTP/1.1" 304 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:48] "GET /static/bootstrap/css/bootstrap.min.css HTTP/1.1" 304 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:48] "GET /static/bootstrap/js/bootstrap.bundle.min.js HTTP/1.1" 304 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:48] "GET /static/img/txt_icon.png HTTP/1.1" 404 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:48] "GET /static/img/conf_icon.png HTTP/1.1" 404 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:48] "GET /static/img/pdf_icon.png HTTP/1.1" 404 -
172.16.xxx.xxx - - [05/Jun/2025 12:24:48] "GET /static/img/key_icon.png HTTP/1.1" 404 -
172.16.xxx.xxx - - [05/Jun/2025 12:25:00] "GET /documents?file=../../ HTTP/1.1" 200 -
172.16.xxx.xxx - - [05/Jun/2025 12:25:14] "GET /documents?file=../../home/ HTTP/1.1" 403 -
172.16.xxx.xxx - - [05/Jun/2025 12:25:18] "GET /documents?file=../../opt/ HTTP/1.1" 403 -
[ACCESS] 2025-06-05 12:24:47.174564 - IP: 172.16.xxx.xxx - User: admin - Visited: /documents?file=
<150>Jun 05 12:24:47 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:24:47 +0000] "GET /documents HTTP/1.1" 200 3657 "https://172.16.xx.xx/dashboard" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
<150>Jun 05 12:24:48 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:24:48 +0000] "GET /static/css/style.css HTTP/1.1" 304 1252 "https://172.16.xx.xx/documents" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
<150>Jun 05 12:24:48 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:24:48 +0000] "GET /static/bootstrap/css/bootstrap.min.css HTTP/1.1" 304 231913 "https://172.16.xx.xx/documents" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
<150>Jun 05 12:24:48 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:24:48 +0000] "GET /static/bootstrap/js/bootstrap.bundle.min.js HTTP/1.1" 304 80721 "https://172.16.xx.xx/documents" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
<150>Jun 05 12:24:48 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:24:48 +0000] "GET /static/img/txt_icon.png HTTP/1.1" 404 207 "https://172.16.xx.xx/documents" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
<150>Jun 05 12:24:48 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:24:48 +0000] "GET /static/img/conf_icon.png HTTP/1.1" 404 207 "https://172.16.xx.xx/documents" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
<150>Jun 05 12:24:48 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:24:48 +0000] "GET /static/img/pdf_icon.png HTTP/1.1" 404 207 "https://172.16.xx.xx/documents" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
<150>Jun 05 12:24:48 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:24:48 +0000] "GET /static/img/key_icon.png HTTP/1.1" 404 207 "https://172.16.xx.xx/documents" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
[ACCESS] 2025-06-05 12:25:00.219532 - IP: 172.16.xxx.xxx - User: admin - Visited: /documents?file=../../
[ALERT] 2025-06-05 12:25:00.248810 - IP: 172.16.xxx.xxx - User: admin - Directory traversal via /documents: ../../
<150>Jun 05 12:25:00 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:25:00 +0000] "GET /documents?file=..%2F..%2F HTTP/1.1" 200 413 "https://172.16.xx.xx/documents" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
[ACCESS] 2025-06-05 12:25:14.054258 - IP: 172.16.xxx.xxx - User: admin - Visited: /documents?file=../../home/
[ALERT] 2025-06-05 12:25:14.083551 - IP: 172.16.xxx.xxx - User: admin - Directory traversal via /documents: ../../home/
<150>Jun 05 12:25:14 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:25:14 +0000] "GET /documents?file=../../home/ HTTP/1.1" 403 67 "https://172.16.xx.xx/documents?file=..%2F..%2F" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
[ACCESS] 2025-06-05 12:25:18.065491 - IP: 172.16.xxx.xxx - User: admin - Visited: /documents?file=../../opt/
[ALERT] 2025-06-05 12:25:18.094786 - IP: 172.16.xxx.xxx - User: admin - Directory traversal via /documents: ../../opt/
<150>Jun 05 12:25:18 DESKTOP-testhost Apache[22940]: 172.16.xxx.xxx - - [05/Jun/2025:12:25:18 +0000] "GET /documents?file=../../opt/ HTTP/1.1" 403 66 "https://172.16.xx.xx/documents?file=..%2F..%2F" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"

Key difference from HRMS:

  • HTTPS makes it feel secure
  • More high-value illusion (appears privileged)
  • Built to entice credential stuffing attacks

4. rdp_listener.ps1 – RDP Honeyport

Real RDP port (3389) is exposed, but backed with a PowerShell listener that logs handshake attempts and failed auth.

5. ftp_service.py – Data Exfil Lure

Pretends to be an FTP server with shared folders named:

  • Backups
  • Config

ftp 172.16.xx.xx 21
Connected to 172.16.xx.xx.
220 220 Welcome to enterprise FTP service.
Name (172.16.xx.xx:testuser@testmac.network): root
331 Username ok, send password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
200 Active data connection established.
125 Data connection already open. Transfer starting.
drwxrwxrwx   1 owner    group           0 Apr 21 09:00 Backups
drwxrwxrwx   1 owner    group           0 Apr 21 09:00 Configs
drwxrwxrwx   1 owner    group           0 Apr 21 09:00 Documents
drwxrwxrwx   1 owner    group           0 Apr 24 11:20 Logs
-rw-rw-rw-   1 owner    group          18 Apr 17 06:28 README.txt
drwxrwxrwx   1 owner    group           0 Apr 21 09:00 Secrets
226 Transfer complete.
ftp> 

Attackers can list files but downloading always fails. Every interaction is logged.

Behavior:

  • Logs IP, attempted credentials, commands
  • Accepts weak passwords (admin:admin), then drops into a fake shell
  • Mimics real command responses before sarcastically shutting them down

Below is how the logs would be logged at our honeypot end.

[FTP] Connection from 172.16.xxx.xxx:47212
[I 2025-06-05 12:15:08] 172.16.xxx.xxx:60894-[] FTP session opened (connect)
[FTP] Connection from 172.16.xxx.xxx:60894
[I 2025-06-05 12:15:09] 172.16.xxx.xxx:47212-[] USER 'toor' failed login.
[I 2025-06-05 12:15:09] 172.16.xxx.xxx:47212-[] FTP session closed (disconnect).
[I 2025-06-05 12:15:11] 172.16.xxx.xxx:60894-[root] USER 'root' logged in.

6. telnet_fake.py – Legacy Admin Login

Old-school login prompt that just echoes back text. Most bots immediately attempt default passwords.

Logging Format

Each service logs with this unified format:

[TIMESTAMP] IP:PORT - ACTION - SERVICE_NAME - DETAILS

Example:

[2025-05-10 14:22:33] 91.230.210.45:22 - LOGIN_FAILED - ssh_service - user: admin

Logs are stored locally, forwarded to Windows Event Log, and parsed into DNIF.