The C.O.P (Cult of Pickles) have started up a new web store to sell their merch. We believe that the funds are being used to carry out illicit pickle-based propaganda operations! Investigate the site and try and find a way into their operation!
The database doesn't hold records in standard way, it stores them as base64 pickle data.
The view product endpoint is vulnerable to SQLi
There's no sanitization, so we can dump the tables. There's one huge problem problem tho, the database is SQLite and it doesn't contain the flag and nor can we achieve RCE via SQLite.
Python-Pickle-RCE-Exploit is a famous Python exploit. We can take advantage of base64/pickle usage and send a malicious request.
First let's test locally:
C.O.P-1.png
Same payload doesn't work remotely, probably because curl doesn't exist.
I wasn't able to make application talk to outside using different methods via http. We could hijack the module
The trickiest part was making __reduce__ method work for the Item class, the SimpleNamespace trick worked as a workaround.
from flask import g
from application import app
from sqlite3 import dbapi2 as sqlite3
import base64, pickle
def connect_db():
return sqlite3.connect('cop.db', isolation_level=None)
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = connect_db()
db.row_factory = sqlite3.Row
return db
def query_db(query, args=(), one=False):
with app.app.app_context():
cur = get_db().execute(query, args)
rv = [dict((cur.description[idx][0], value) \
for idx, value in enumerate(row)) for row in cur.fetchall()]
return (next(iter(rv[0].values())) if rv else None) if one else rv
class Item:
def __init__(self, name, description, price, image):
self.name = name
self.description = description
self.image = image
self.price = price
def migrate_db():
items = [
Item('Pickle Shirt', 'Get our new pickle shirt!', '23', '/static/images/pickle_shirt.jpg'),
Item('Pickle Shirt 2', 'Get our (second) new pickle shirt!', '27', '/static/images/pickle_shirt2.jpg'),
Item('Dill Pickle Jar', 'Literally just a pickle', '1337', '/static/images/pickle.jpg'),
Item('Branston Pickle', 'Does this even fit on our store?!?!', '7.30', '/static/images/branston_pickle.jpg')
]
with open('schema.sql', mode='r') as f:
shop = map(lambda x: base64.b64encode(pickle.dumps(x)).decode(), items)
get_db().cursor().executescript(f.read().format(*list(shop)))
from application.database import query_db
class shop(object):
@staticmethod
def select_by_id(product_id):
return query_db(f"SELECT data FROM products WHERE id='{product_id}'", one=True)
@staticmethod
def all_products():
return query_db('SELECT * FROM products')
from flask import Blueprint, render_template
from application.models import shop
web = Blueprint('web', __name__)
@web.route('/')
def index():
return render_template('index.html', products=shop.all_products())
@web.route('/view/<product_id>')
def product_details(product_id):
return render_template('item.html', product=shop.select_by_id(product_id))
DROP TABLE IF EXISTS products;
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data TEXT NOT NULL,
created_at NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO products (data) VALUES ("{0}"), ("{1}"), ("{2}"), ("{3}");