0 / 11 steps complete
Advanced AI-Assisted AI Security

L10: Deepfake Detection & Media Forensics

Analyze synthetic media using Python-based deepfake detection tools, frequency analysis, and metadata forensics. Learn to identify GAN artifacts, JPEG compression inconsistencies, and audio manipulation — critical skills as synthetic media becomes a primary vector for fraud and disinformation.

Python 3 OpenCV FaceForensics++ ExifTool FFmpeg Kali Linux
Phase 1: Environment Setup & Sample Acquisition
1 Install deepfake detection dependencies

On Kali Linux, set up the detection environment:

sudo apt update && sudo apt install -y python3-pip ffmpeg exiftool libgl1
pip3 install opencv-python-headless numpy matplotlib scipy \
    Pillow imageio scikit-learn torch torchvision

mkdir ~/deepfake-lab && cd ~/deepfake-lab
mkdir samples/real samples/fake analysis reports

# Install py-feat for facial action unit analysis (optional)
pip3 install py-feat

# Verify
python3 -c "import cv2, torch, PIL; print(f'OpenCV {cv2.__version__}, PyTorch {torch.__version__}')"
2 Obtain sample images and deepfakes for analysis

Download sample media from the FaceForensics++ dataset (research use) and public deepfake samples:

cd ~/deepfake-lab

# Download FaceForensics++ sample (requires academic registration at github.com/ondyari/FaceForensics)
# Alternative: use this public deepfake detection dataset subset
wget -P samples/ \
  "https://github.com/yuezunli/celeb-deepfakeforensics/raw/master/README.md"

# Generate synthetic "deepfake" test images using StyleGAN-generated faces
# thispersondoesnotexist.com API (public faces, no real person)
python3 << 'EOF'
import requests, os, time

print("Downloading GAN-generated faces (this-person-does-not-exist.com)...")
for i in range(10):
    resp = requests.get("https://thispersondoesnotexist.com/",
                        headers={"User-Agent": "Mozilla/5.0"})
    if resp.status_code == 200:
        with open(f"samples/fake/gan_face_{i:03d}.jpg", 'wb') as f:
            f.write(resp.content)
        print(f"  Downloaded fake face {i+1}/10")
    time.sleep(2)  # Rate limit

# Real photos: download from Flickr Creative Commons (public domain faces)
print("\nNote: Add real photos to samples/real/ for comparison")
print("Source: commons.wikimedia.org/wiki/Category:Photographs_of_people")
EOF
Phase 2: Image Forensics — GAN Artifact Detection
3 Frequency domain analysis — detect GAN fingerprints

GAN-generated images have characteristic frequency artifacts visible in the DFT spectrum:

cat > ~/deepfake-lab/frequency_analysis.py << 'EOF'
import cv2, numpy as np, matplotlib.pyplot as plt
import os, glob

def analyze_frequency(image_path, label):
    img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    if img is None: return None

    # Resize to standard size
    img = cv2.resize(img, (256, 256))

    # 2D Discrete Fourier Transform
    f = np.fft.fft2(img)
    fshift = np.fft.fftshift(f)
    magnitude = 20 * np.log(np.abs(fshift) + 1)

    # GAN artifact: periodic grid pattern at specific frequencies
    # Compute radial power spectrum
    h, w = magnitude.shape
    cy, cx = h//2, w//2
    radial_power = []
    for r in range(1, min(h,w)//2):
        mask = np.zeros((h,w), bool)
        for y in range(h):
            for x in range(w):
                if abs(np.sqrt((y-cy)**2 + (x-cx)**2) - r) < 1:
                    mask[y,x] = True
        radial_power.append(magnitude[mask].mean())

    return {
        'path': image_path,
        'label': label,
        'magnitude': magnitude,
        'radial_power': np.array(radial_power),
        'mean_high_freq': np.array(radial_power[50:]).mean(),
        'std_magnitude': magnitude.std(),
    }

# Analyze samples
results = []
for f in glob.glob('samples/fake/*.jpg')[:5]:
    r = analyze_frequency(f, 'DEEPFAKE')
    if r: results.append(r)

# Plot DFT spectrums side by side
fig, axes = plt.subplots(2, len(results), figsize=(15, 6))
for i, r in enumerate(results):
    axes[0,i].imshow(cv2.imread(r['path'])[:,:,::-1])
    axes[0,i].set_title(f"{r['label']}", fontsize=8)
    axes[0,i].axis('off')
    axes[1,i].imshow(r['magnitude'], cmap='hot')
    axes[1,i].set_title(f"DFT (σ={r['std_magnitude']:.1f})", fontsize=8)
    axes[1,i].axis('off')
plt.suptitle('Frequency Domain Analysis — GAN Artifact Detection')
plt.tight_layout()
plt.savefig('analysis/frequency_analysis.png', dpi=150)
print("Saved frequency_analysis.png")

for r in results:
    print(f"\n{os.path.basename(r['path'])}")
    print(f"  Mean high-freq power: {r['mean_high_freq']:.2f}")
    print(f"  DFT std deviation:    {r['std_magnitude']:.2f}")
    print(f"  GAN artifacts: {'LIKELY' if r['std_magnitude'] > 80 else 'UNCLEAR'}")
EOF
python3 frequency_analysis.py
4 Error Level Analysis (ELA) for manipulation detection

ELA reveals image regions with different compression histories — common in spliced or manipulated photos:

cat > ~/deepfake-lab/ela_analysis.py << 'EOF' import cv2, numpy as np, matplotlib.pyplot as plt import io from PIL import Image def ela(image_path, quality=95, scale=20): """Error Level Analysis — detect re-saved JPEG regions.""" original = Image.open(image_path).convert('RGB') # Re-save at known quality level buffer = io.BytesIO() original.save(buffer, 'JPEG', quality=quality) buffer.seek(0) recompressed = Image.open(buffer).convert('RGB') # Compute pixel-wise difference orig_arr = np.array(original, dtype=float) recomp_arr = np.array(recompressed, dtype=float) ela_arr = np.abs(orig_arr - recomp_arr) * scale ela_arr = np.clip(ela_arr, 0, 255).astype(np.uint8) # Metrics ela_mean = ela_arr.mean() ela_std = ela_arr.std() return ela_arr, ela_mean, ela_std import glob, os fig, axes = plt.subplots(2, 5, figsize=(15,6)) for i, path in enumerate(glob.glob('samples/fake/*.jpg')[:5]): ela_img, ela_mean, ela_std = ela(path) img = cv2.imread(path)[:,:,::-1] axes[0,i].imshow(img); axes[0,i].axis('off') axes[0,i].set_title(os.path.basename(path)[:15], fontsize=7) axes[1,i].imshow(ela_img); axes[1,i].axis('off') axes[1,i].set_title(f"ELA μ={ela_mean:.1f} σ={ela_std:.1f}", fontsize=7) plt.suptitle('Error Level Analysis (ELA)') plt.tight_layout() plt.savefig('analysis/ela_results.png', dpi=150) print("ELA analysis complete — saved ela_results.png") # Interpretation guide print("\nInterpretation:") print(" Uniform ELA = consistent compression history (real or fully GAN-generated)") print(" Bright patches in ELA = regions with different compression = SPLICED/MANIPULATED") print(" GAN images: often uniform ELA (no splicing) but unusual DFT spectrum") EOF python3 ela_analysis.py
Phase 3: Facial Inconsistency & Metadata Analysis
5 Detect facial inconsistencies with OpenCV

GAN-generated faces often have artifacts around eyes, teeth, hair edges, and accessories:

cat > ~/deepfake-lab/face_analysis.py << 'EOF'
import cv2, numpy as np, matplotlib.pyplot as plt, glob, os

face_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + 'haarcascade_eye.xml')

def analyze_face(image_path):
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(gray, 1.1, 5, minSize=(100,100))
    if len(faces) == 0:
        return {'face_detected': False}

    x, y, w, h = faces[0]
    face_roi = gray[y:y+h, x:x+w]
    face_color = img[y:y+h, x:x+w]

    # Detect eyes in face region
    eyes = eye_cascade.detectMultiScale(face_roi, 1.1, 5)

    # Laplacian for blur detection (deepfakes often have uneven sharpness)
    laplacian = cv2.Laplacian(face_roi, cv2.CV_64F)
    sharpness = laplacian.var()

    # Symmetry analysis (deepfakes can have L-R asymmetry artifacts)
    left_half = face_roi[:, :w//2]
    right_half = cv2.flip(face_roi[:, w//2:], 1)
    if left_half.shape == right_half.shape:
        symmetry_score = cv2.matchTemplate(
            left_half.astype(float), right_half.astype(float),
            cv2.TM_CCOEFF_NORMED)[0][0]
    else:
        symmetry_score = 0

    return {
        'face_detected': True,
        'face_size': (w, h),
        'eyes_detected': len(eyes),
        'sharpness': sharpness,
        'symmetry_score': float(symmetry_score),
        'face_img': face_color,
    }

results = []
for path in glob.glob('samples/fake/*.jpg')[:5]:
    r = analyze_face(path)
    r['path'] = path
    results.append(r)
    if r['face_detected']:
        print(f"\n{os.path.basename(path)}")
        print(f"  Eyes detected:  {r['eyes_detected']} (expect 2)")
        print(f"  Sharpness:      {r['sharpness']:.1f}")
        print(f"  L-R Symmetry:   {r['symmetry_score']:.3f}")
        if r['eyes_detected'] != 2:
            print(f"  [!] Unusual eye count — possible GAN artifact")
        if r['symmetry_score'] < 0.7:
            print(f"  [!] Low facial symmetry — possible manipulation")
EOF
python3 face_analysis.py
6 Metadata forensics with ExifTool

Genuine photos from cameras contain rich EXIF data. GAN-generated images have none or suspicious metadata:

for img in samples/fake/*.jpg; do echo "=== $(basename $img) ===" exiftool "$img" | grep -E "File Type|Image Size|Camera Model|GPS|Software|Create Date|Modify Date|Color Space|Focal Length" echo "" done # Key red flags in EXIF metadata: # - No Camera Model / Make → likely GAN-generated or screenshot # - Software field contains "GIMP", "Photoshop", "Stable Diffusion", etc. # - Create Date = Modify Date exactly → no camera processing # - GPS coordinates absent → untraceable origin # - Color Space = sRGB only (cameras use Adobe RGB or camera-specific profiles) # Compare with a known real photo exiftool samples/real/reference_photo.jpg 2>/dev/null || \ echo "Add a real JPEG to samples/real/ for comparison"
Phase 4: Video Deepfake & Audio Analysis
7 Extract and analyze frames from deepfake video
cat > ~/deepfake-lab/video_analysis.py << 'EOF' import cv2, numpy as np, subprocess, os def analyze_video(video_path, output_dir='analysis/frames'): os.makedirs(output_dir, exist_ok=True) cap = cv2.VideoCapture(video_path) fps = cap.get(cv2.CAP_PROP_FPS) total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) print(f"Video: {os.path.basename(video_path)}") print(f" FPS: {fps:.1f}, Total frames: {total}") face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') frame_results = [] for frame_idx in range(0, total, max(1, total//20)): # Sample 20 frames cap.set(cv2.CAP_PROP_POS_FRAMES, frame_idx) ret, frame = cap.read() if not ret: break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.1, 5) laplacian = cv2.Laplacian(gray, cv2.CV_64F).var() frame_results.append({ 'frame': frame_idx, 'faces': len(faces), 'sharpness': laplacian, }) cap.release() # Detect temporal inconsistencies sharpness = [r['sharpness'] for r in frame_results] sharpness_std = np.std(sharpness) print(f" Sharpness variance across frames: {sharpness_std:.1f}") if sharpness_std > 500: print(f" [!] HIGH sharpness variance — possible face-swap seams") face_counts = [r['faces'] for r in frame_results] if max(face_counts, default=0) == 0: print(f" [!] No faces detected — may need different cascade") return frame_results # Usage (provide a sample video) # analyze_video('samples/deepfake_sample.mp4') print("Video analysis module ready. Provide a video file path.") print("Download FaceForensics++ samples or use: youtube-dl [public deepfake sample]") EOF # Extract key video metadata with ffprobe ffprobe -v quiet -print_format json -show_streams -show_format \ samples/deepfake_video.mp4 2>/dev/null || \ echo "Add a video to samples/ to analyze. FFprobe ready." # Look for encoding inconsistencies # Real cameras: consistent keyframe intervals, single codec # Deepfakes: often re-encoded multiple times, irregular keyframes
8 Audio forensics — detect voice cloning artifacts

Cloned voices show artifacts in the spectrogram — unnatural pitch, formant discontinuities:

pip3 install librosa soundfile cat > ~/deepfake-lab/audio_analysis.py << 'EOF' import librosa, librosa.display, numpy as np, matplotlib.pyplot as plt def analyze_audio(audio_path): y, sr = librosa.load(audio_path, sr=None, mono=True) print(f"\nAudio: {audio_path}") print(f" Duration: {len(y)/sr:.2f}s, Sample rate: {sr}Hz") # Mel spectrogram (visual fingerprint of voice) S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128, fmax=8000) S_dB = librosa.power_to_db(S, ref=np.max) # Spectral features spec_centroid = librosa.feature.spectral_centroid(y=y, sr=sr)[0] spec_rolloff = librosa.feature.spectral_rolloff(y=y, sr=sr, roll_percent=0.85)[0] zero_crossing = librosa.feature.zero_crossing_rate(y)[0] mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13) print(f" Spectral centroid mean: {spec_centroid.mean():.1f}Hz") print(f" Spectral rolloff mean: {spec_rolloff.mean():.1f}Hz") print(f" Zero crossing rate: {zero_crossing.mean():.4f}") # Plot fig, axes = plt.subplots(2, 1, figsize=(12, 6)) librosa.display.specshow(S_dB, x_axis='time', y_axis='mel', sr=sr, fmax=8000, ax=axes[0]) axes[0].set_title('Mel Spectrogram (Voice Fingerprint)') axes[0].colorbar(format='%+2.0f dB') librosa.display.specshow(mfcc, x_axis='time', ax=axes[1]) axes[1].set_title('MFCC Features (look for discontinuities)') axes[1].colorbar() plt.tight_layout() plt.savefig('analysis/audio_spectrogram.png', dpi=150) # Cloning artifacts: sudden spectral jumps, unnatural pauses centroid_diff = np.diff(spec_centroid) jumps = (np.abs(centroid_diff) > centroid_diff.std() * 3).sum() print(f" Spectral discontinuities: {jumps}") if jumps > len(centroid_diff) * 0.05: print(f" [!] HIGH discontinuity rate — possible voice clone splice") print("Audio analysis module ready.") print("Provide an MP3/WAV file path to analyze_audio()") EOF python3 audio_analysis.py
9 Build a composite deepfake scoring rubric

Combine all signals into a structured assessment framework:

Detection MethodIndicatorWeightScore
EXIF MetadataNo camera make/modelHIGH
ELA AnalysisInconsistent compressionHIGH
DFT SpectrumGAN grid artifactsMEDIUM
Facial AnalysisEye count, symmetryMEDIUM
Audio SpectrogramSplice discontinuitiesHIGH
Video TemporalSharpness varianceMEDIUM
Phase 5: Reporting & Threat Landscape
10 Assess deepfake threat scenarios for organizations

Document the real-world threat landscape and organizational risks:

Threat ScenarioVectorExample
CEO Fraud / BECVoice clone in video callCFO instructed to wire $25M
Political DisinformationFace-swap videoFake speech of official
Fake EvidenceImage manipulationPlanted in legal/HR case
Identity FraudGAN-generated ID photoBypasses KYC system
Synthetic PhishingCloned voice/videoIT helpdesk impersonation
11 Document findings and produce forensic media report

Lab Findings

MetricValue
Images analyzed
EXIF anomalies found
ELA inconsistencies
DFT artifacts detected
Facial anomalies
Composite verdict

Next: Lab L11 — Vulnerability Triage with AI (Nessus)

Prioritize vulnerabilities from Nessus scans using AI-powered risk scoring and context.

Start L11 →
AI Media Forensics Analyst

Enter your Anthropic API key to activate the AI analyst:

Quick Prompts: