Edit on GitHub

#  Script examples

This page provides example scripts to get familiar with Scalpel’s Python library. They are designed for real use cases.

#  Table of content

#  GZIP-ed API

Let’s assume you encountered an API using a custom protocol that gzips multiple form-data fields.

Quick-and-dirty Scalpel script to directly edit the unzipped data and find hidden secrets:

from pyscalpel import Request, Response, logger
import gzip


def unzip_bytes(data):
    try:
        # Create a GzipFile object with the input data
        with gzip.GzipFile(fileobj=data) as gz_file:
            # Read the uncompressed data
            uncompressed_data = gz_file.read()
        return uncompressed_data
    except OSError as e:
        logger.error(f"Error: Failed to unzip the data - {e}")


def req_edit_in_fs(req: Request) -> bytes | None:
    gz = req.multipart_form["fs"].content

    # Decode utf-16 and re-encoding to get rid of null bytes in the editor
    content = gzip.decompress(gz).decode("utf-16le").encode("latin-1")
    return content


def req_edit_out_fs(req: Request, text: bytes) -> Request | None:
    data = text.decode("latin-1").encode("utf-16le")
    content = gzip.compress(data, mtime=0)
    req.multipart_form["fs"].content = content
    return req


def req_edit_in_filetosend(req: Request) -> bytes | None:
    gz = req.multipart_form["filetosend"].content
    content = gzip.decompress(gz)
    return content


def req_edit_out_filetosend(req: Request, text: bytes) -> Request | None:
    data = text
    content = gzip.compress(data, mtime=0)
    req.multipart_form["filetosend"].content = content
    return req


def res_edit_in(res: Response) -> bytes | None:
    gz = res.content
    if not gz:
        return

    content = gzip.decompress(gz)
    content.decode("utf-16le").encode("utf-8")
    return content


def res_edit_out(res: Response, text: bytes) -> Response | None:
    res.content = text
    return res

#  Cryptography using a session as a secret

In this case, the client encrypted its form data using a session token obtained upon authentication.

This script demonstrates that Scalpel can be easily used to deal with stateful behaviors:

💡 Find a mock API to test this case in Scalpel’s GitHub repository: test/server.js.

from pyscalpel import Request, Response, Flow
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto.Util.Padding import pad, unpad
from base64 import b64encode, b64decode


session: bytes = b""


def match(flow: Flow) -> bool:
    return flow.path_is("/encrypt-session*") and bool(
        session or flow.request.method != "POST"
    )


def get_cipher(secret: bytes, iv=bytes(16)):
    hasher = SHA256.new()
    hasher.update(secret)
    derived_aes_key = hasher.digest()[:32]
    cipher = AES.new(derived_aes_key, AES.MODE_CBC, iv)
    return cipher


def decrypt(secret: bytes, data: bytes) -> bytes:
    data = b64decode(data)
    cipher = get_cipher(secret)
    decrypted = cipher.decrypt(data)
    return unpad(decrypted, AES.block_size)


def encrypt(secret: bytes, data: bytes) -> bytes:
    cipher = get_cipher(secret)
    padded_data = pad(data, AES.block_size)
    encrypted = cipher.encrypt(padded_data)
    return b64encode(encrypted)


def response(res: Response) -> Response | None:
    if res.request.method == "GET":
        global session
        session = res.content or b""
        return


def req_edit_in_encrypted(req: Request) -> bytes:
    secret = session
    encrypted = req.form[b"encrypted"]
    if not encrypted:
        return b""

    return decrypt(secret, encrypted)


def req_edit_out_encrypted(req: Request, text: bytes) -> Request:
    secret = session
    req.form[b"encrypted"] = encrypt(secret, text)
    return req


def res_edit_in_encrypted(res: Response) -> bytes:
    secret = session
    encrypted = res.content

    if not encrypted:
        return b""

    return decrypt(secret, encrypted)


def res_edit_out_encrypted(res: Response, text: bytes) -> Response:
    secret = session
    res.content = encrypt(secret, text)
    return res

If you encountered an interesting case, feel free to contact us to add it!