Initial public portfolio release
Sanitized version of red team infrastructure automation platform. Operational content (implant pipelines, lures, credential capture) replaced with documented stubs. Architecture and infrastructure automation code intact.
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple Email Tracking Server
|
||||
|
||||
A minimal Flask application that serves transparent tracking pixels
|
||||
and logs email opens with metadata.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from flask import Flask, request, send_file, render_template_string
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('/root/Tools/tracker/data/tracker.log'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
|
||||
# Create Flask app
|
||||
app = Flask(__name__)
|
||||
|
||||
# Directory to store tracking data
|
||||
DATA_DIR = '/root/Tools/tracker/data'
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
|
||||
# Path to 1x1 transparent pixel
|
||||
PIXEL_PATH = os.path.join(DATA_DIR, 'pixel.png')
|
||||
|
||||
# Create 1x1 transparent PNG if it doesn't exist
|
||||
if not os.path.exists(PIXEL_PATH):
|
||||
from PIL import Image
|
||||
img = Image.new('RGBA', (1, 1), color=(0, 0, 0, 0))
|
||||
img.save(PIXEL_PATH)
|
||||
|
||||
# Path to tracking data
|
||||
TRACKING_DATA_PATH = os.path.join(DATA_DIR, 'tracking_data.json')
|
||||
|
||||
def load_tracking_data():
|
||||
"""Load existing tracking data from JSON file"""
|
||||
if os.path.exists(TRACKING_DATA_PATH):
|
||||
try:
|
||||
with open(TRACKING_DATA_PATH, 'r') as f:
|
||||
return json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
logging.error("Error loading tracking data, starting fresh")
|
||||
return {}
|
||||
|
||||
def save_tracking_data(data):
|
||||
"""Save tracking data to JSON file"""
|
||||
with open(TRACKING_DATA_PATH, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
@app.route('/pixel/<tracking_id>.png')
|
||||
def tracking_pixel(tracking_id):
|
||||
"""Serve a tracking pixel and log the request"""
|
||||
# Get client information
|
||||
user_agent = request.headers.get('User-Agent', 'Unknown')
|
||||
ip_address = request.remote_addr
|
||||
timestamp = datetime.now().isoformat()
|
||||
referer = request.headers.get('Referer', 'Unknown')
|
||||
|
||||
# Log the tracking event
|
||||
logging.info(f"Pixel loaded - ID: {tracking_id}, IP: {ip_address}")
|
||||
|
||||
# Add tracking event to data
|
||||
tracking_data = load_tracking_data()
|
||||
|
||||
if tracking_id not in tracking_data:
|
||||
tracking_data[tracking_id] = []
|
||||
|
||||
tracking_data[tracking_id].append({
|
||||
'timestamp': timestamp,
|
||||
'ip_address': ip_address,
|
||||
'user_agent': user_agent,
|
||||
'referer': referer
|
||||
})
|
||||
|
||||
save_tracking_data(tracking_data)
|
||||
|
||||
# Return the 1x1 transparent pixel
|
||||
return send_file(PIXEL_PATH, mimetype='image/png')
|
||||
|
||||
@app.route('/')
|
||||
def dashboard():
|
||||
"""Display tracking statistics dashboard"""
|
||||
tracking_data = load_tracking_data()
|
||||
|
||||
# Prepare data for the dashboard
|
||||
stats = []
|
||||
for tracking_id, events in tracking_data.items():
|
||||
stats.append({
|
||||
'id': tracking_id,
|
||||
'views': len(events),
|
||||
'last_view': events[-1]['timestamp'] if events else 'Never',
|
||||
'unique_ips': len(set(e['ip_address'] for e in events))
|
||||
})
|
||||
|
||||
# Sort by most views
|
||||
stats.sort(key=lambda x: x['views'], reverse=True)
|
||||
|
||||
# Simple HTML dashboard template
|
||||
template = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Email Tracking Dashboard - {{ tracker_domain }}</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
|
||||
h1 { color: #333; }
|
||||
table { border-collapse: collapse; width: 100%; }
|
||||
th, td { text-align: left; padding: 8px; border-bottom: 1px solid #ddd; }
|
||||
tr:hover { background-color: #f5f5f5; }
|
||||
th { background-color: #4CAF50; color: white; }
|
||||
.container { max-width: 800px; margin: 0 auto; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Email Tracking Dashboard</h1>
|
||||
<p>To track email opens, add this HTML to your emails:</p>
|
||||
<pre><img src="https://{{ redirector_domain }}/px/YOUR_TRACKING_ID.png" height="1" width="1" /></pre>
|
||||
<p>Alternatively, use this URL with your C2 server directly:</p>
|
||||
<pre><img src="https://{{ tracker_domain }}/pixel/YOUR_TRACKING_ID.png" height="1" width="1" /></pre>
|
||||
|
||||
<h2>Tracking Statistics</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Tracking ID</th>
|
||||
<th>Views</th>
|
||||
<th>Unique IPs</th>
|
||||
<th>Last View</th>
|
||||
<th>Details</th>
|
||||
</tr>
|
||||
{% for stat in stats %}
|
||||
<tr>
|
||||
<td>{{ stat.id }}</td>
|
||||
<td>{{ stat.views }}</td>
|
||||
<td>{{ stat.unique_ips }}</td>
|
||||
<td>{{ stat.last_view }}</td>
|
||||
<td><a href="/details/{{ stat.id }}">View Details</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
return render_template_string(
|
||||
template,
|
||||
stats=stats,
|
||||
tracker_domain="{{ tracker_domain }}",
|
||||
redirector_domain="{{ redirector_subdomain }}.{{ domain }}"
|
||||
)
|
||||
|
||||
@app.route('/details/<tracking_id>')
|
||||
def tracking_details(tracking_id):
|
||||
"""Display detailed tracking information for a specific ID"""
|
||||
tracking_data = load_tracking_data()
|
||||
|
||||
if tracking_id not in tracking_data:
|
||||
return f"No data found for tracking ID: {tracking_id}", 404
|
||||
|
||||
events = tracking_data[tracking_id]
|
||||
|
||||
# Simple HTML template for details
|
||||
template = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tracking Details: {{ tracking_id }}</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
|
||||
h1, h2 { color: #333; }
|
||||
table { border-collapse: collapse; width: 100%; }
|
||||
th, td { text-align: left; padding: 8px; border-bottom: 1px solid #ddd; }
|
||||
tr:hover { background-color: #f5f5f5; }
|
||||
th { background-color: #4CAF50; color: white; }
|
||||
.container { max-width: 800px; margin: 0 auto; }
|
||||
.back { margin-bottom: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="back"><a href="/">« Back to Dashboard</a></div>
|
||||
<h1>Tracking Details: {{ tracking_id }}</h1>
|
||||
<p>Total views: {{ events|length }}</p>
|
||||
|
||||
<h2>Events</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>IP Address</th>
|
||||
<th>User Agent</th>
|
||||
<th>Referer</th>
|
||||
</tr>
|
||||
{% for event in events %}
|
||||
<tr>
|
||||
<td>{{ event.timestamp }}</td>
|
||||
<td>{{ event.ip_address }}</td>
|
||||
<td>{{ event.user_agent }}</td>
|
||||
<td>{{ event.referer }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
return render_template_string(template, tracking_id=tracking_id, events=events)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||
@@ -0,0 +1,33 @@
|
||||
# templates/tracker-config.j2
|
||||
"""
|
||||
Email Tracker Configuration
|
||||
"""
|
||||
|
||||
# Basic Configuration
|
||||
SECRET_KEY = '{{ lookup("password", "/dev/null chars=ascii_letters,digits length=32") }}'
|
||||
DEBUG = False
|
||||
|
||||
# Database settings
|
||||
DB_NAME = 'tracker.db'
|
||||
|
||||
# Domain configuration
|
||||
DOMAIN = '{{ tracker_domain }}'
|
||||
USE_HTTPS = {{ tracker_setup_ssl | default(true) | lower }}
|
||||
|
||||
# Tracking pixel path
|
||||
TRACKING_PATH = 'pixel.png'
|
||||
|
||||
# API Keys
|
||||
{% if tracker_ipinfo_token is defined and tracker_ipinfo_token != "" %}
|
||||
IPINFO_TOKEN = '{{ tracker_ipinfo_token }}'
|
||||
{% else %}
|
||||
IPINFO_TOKEN = None
|
||||
{% endif %}
|
||||
|
||||
# Notification settings
|
||||
ENABLE_NOTIFICATIONS = False
|
||||
NOTIFICATION_EMAIL = '{{ tracker_email | default("admin@" + domain) }}'
|
||||
|
||||
# Logger configuration
|
||||
LOG_LEVEL = 'INFO'
|
||||
LOG_FILE = '/var/log/tracker.log'
|
||||
@@ -0,0 +1,52 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name {{ tracker_domain }};
|
||||
|
||||
# Redirect to HTTPS if SSL is enabled
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name {{ tracker_domain }};
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /etc/letsencrypt/live/{{ tracker_domain }}/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/{{ tracker_domain }}/privkey.pem;
|
||||
|
||||
# Proxy to tracker app
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Specific location for tracking pixel
|
||||
location ~ ^/pixel/(.+)\.png$ {
|
||||
proxy_pass http://127.0.0.1:5000/pixel/$1.png;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# Cache control - don't cache tracking pixels
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
||||
expires off;
|
||||
}
|
||||
|
||||
# Security headers
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "no-referrer" always;
|
||||
|
||||
{% if zero_logs | default(true) %}
|
||||
# Disable logging for privacy
|
||||
access_log off;
|
||||
error_log /dev/null crit;
|
||||
{% endif %}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
[Unit]
|
||||
Description=Email Tracking Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=tracker
|
||||
Group=tracker
|
||||
WorkingDirectory=/root/Tools/tracker
|
||||
ExecStart=/root/Tools/tracker/venv/bin/python /root/Tools/tracker/simple_email_tracker.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
# Security settings
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
NoNewPrivileges=true
|
||||
|
||||
# Hide process information
|
||||
StandardOutput=null
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user