Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
StealMyBitcoin-PoC — Une version publique du challenge Steal My Bitcoin | Kitploit
Outils/GitHubGitHub/killaboi/stealmybitcoin-poc
Criminalistique MémoireAnalyse des VulnérabilitésExploitationExploitation d'Applications WebCTFDéveloppement de Charges UtilesExploitation de BinairesLabs et Pratique
GitHubkillaboi/stealmybitcoin-poc

StealMyBitcoin-PoC

Une version publique du challenge Steal My Bitcoin

Voir le dépôt
2164160il y a 1 jourPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

sponsorisé avec ❤️ par hextree.io

Preuve de concept du CTF Steal My Bitcoin

Un callback PHP en direct contre le challenge Steal My Bitcoin, du début à la fin, dans un seul fichier Python utilisant uniquement la bibliothèque standard.

Python Dependencies Target Lab

Ce README explique stealmybitcoin_poc.py, une reproduction en un seul fichier Python de la séquence de requêtes utilisée lors de mon run du CTF Steal My Bitcoin. Il n'utilise que la bibliothèque standard de Python. Le script construit lui-même les requêtes, vérifie chaque fuite avant de passer à la suite, et enregistre les octets HTTP d'un nouveau run.

Cela peut probablement être fait plus simplement sans tout le débogage superflu et, comme je l'ai découvert dans ma vidéo John Hammond sur l'élévation de privilèges locale de Steam, vous pourriez probablement nettoyer ce PoC peu élégant.

[!NOTE] ⚠️ Sérieusement, nettoyez-le ou faites votre propre meilleure version qui ne soit pas chiffonnée par Sol. ⚠️

Tout ce qui suit est écrit pour le lab Docker, pas pour le challenge en direct. Le lab est un serveur local Alpine PHP 8.4.26 en CLI derrière nginx, soit la même build et le même modèle de workers que ceux sur lesquels les offsets ont été calibrés. Montez-le en une minute et toute la chaîne s'exécute de bout en bout sur votre propre machine. Le run en direct qui a réellement résolu le challenge est conservé comme note historique en bas.

  stage                 what goes out                        what comes back
  ─────────────────────────────────────────────────────────────────────────────
  0  health      GET /php-version.php                 ->  PHP 8.4.26 confirmed
  1  heap leak   249,042 B producer + 8 x   6,672 B   ->  256 B cookie
                 (OOM poisons a worker)                   └─ Zend chunk base
  2  code leak   251,557 B producer + 8 x  92,388 B   ->   16 B cookie
                 (OOM poisons a worker)                   └─ zval_ptr_dtor
  3  callback    368,812 B POST                       ->  forged HashTable runs
                 (fake HashTable + command)               └─ zif_system(cmd)
  ─────────────────────────────────────────────────────────────────────────────
  20 HTTP requests at most. No looping, no retries.

La version courte

  1. Un petit upload multipart amène PHP à consommer bien plus de mémoire que ne le suggère la taille sur le réseau.
  2. Lorsqu'il manque de mémoire en cours d'analyse de la progression d'upload de session, PHP laisse un état obsolète dans un worker qui sera ensuite réutilisé.
  3. En façonnant correctement les requêtes suivantes, des octets s'échappent via les cookies de session. Je les interprète comme une adresse de tas et une adresse de code PHP.
  4. Avec ces deux adresses, une requête finale dépose une structure PHP forgée là où le nettoyage la trouvera, et le nettoyage appelle zif_system, le gestionnaire de PHP pour system.
  5. Cette commande renvoie un nouveau marqueur de preuve et un résultat print-flag sur la connexion toujours ouverte.

Pourquoi deux adresses ? Le système d'exploitation randomise indépendamment l'emplacement du code et du tas de PHP. La fuite du tas indique au script où placer la structure forgée, la fuite du code lui indique où réside réellement la fonction qu'il veut appeler. Les adresses provenant d'un worker antérieur ou d'une exécution précédente ne sont pas des substituts sûrs, et à chaque fois que je l'ai essayé, la tentative a simplement échoué.

Sommaire

  • Le lab
  • Commencer ici
  • Ce que fait chaque requête
    • 1. Comment un petit producteur atteint la limite de mémoire
    • 2. Comment l'adresse du tas est récupérée
    • 3. Comment l'adresse du code PHP est récupérée
    • 4. Comment la requête finale atteint system
  • Lire le résultat
  • À quelle fréquence cela fonctionne réellement
  • L'exécution en direct qui l'a résolu
  • Le jargon, brièvement
  • Remerciements

Le lab

Les offsets de ce PoC sont calibrés pour Alpine PHP 8.4.26 exécutant le serveur CLI intégré avec quatre workers. Le lab reproduit exactement cela, derrière nginx, sur l'interface loopback.

[!IMPORTANT] La compilation compte plus que la chaîne de version. Une pile Debian php:8.4.26-fpm rapporte le même PHP/8.4.26 mais a une disposition binaire différente, donc l'empreinte du pointeur de code ne correspondra jamais et l'exécution s'interrompt à l'étape de fuite de code. Utilisez l'image Alpine ci-dessous.

Trois petits fichiers. Placez-les dans un répertoire vide :

public/php-version.php (le point de terminaison de santé, et la seule page dont le PoC a besoin)

<?php
header('Content-Type: text/plain');
echo 'PHP ', PHP_VERSION, "\n";
echo 'PID: ', getmypid(), "\n";

print-flag (substitut du binaire du challenge, doit être exécutable)

#!/bin/sh
printf '%s\n' 'LOCAL_DUMMY_WIF_NEVER_USE'

nginx.conf

events {}
http {
    server {
        listen 80;
        server_name _;
        location / {
            proxy_pass http://ctf-cross-php:8000;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
        }
    }
}

Puis lancez-le :

docker network create ctf-cross-net

docker run -d --name ctf-cross-php --network ctf-cross-net \
  --read-only --tmpfs /tmp:rw,size=32m,mode=1777 --memory 1g \
  -v "$PWD/public:/app:ro" \
  -v "$PWD/print-flag:/usr/local/bin/print-flag:ro" \
  -e PHP_CLI_SERVER_WORKERS=4 \
  php:8.4.26-fpm-alpine3.23 php -S 0.0.0.0:8000 -t /app

docker run -d --name ctf-cross-nginx --network ctf-cross-net \
  -p 127.0.0.1:18166:80 \
  -v "$PWD/nginx.conf:/etc/nginx/nginx.conf:ro" \
  nginx:1.27-alpine

Vérifiez-le :

$ curl -s http://127.0.0.1:18166/php-version.php
PHP 8.4.26
PID: 1

Le PID varie selon le worker qui répond, et c'est normal. La ligne de version est l'élément que le PoC vérifie.

Démontez-le avec docker rm -f ctf-cross-nginx ctf-cross-php && docker network rm ctf-cross-net.

[!TIP] Sous Windows, exécutez ces commandes docker run depuis PowerShell, ou remplacez $PWD par un chemin de lecteur complet comme C:/lab. Dans Git Bash, $PWD s'étend en un chemin MSYS tel que /tmp/..., que Docker Desktop résout à l'intérieur de sa propre VM. Les montages bind créent alors silencieusement des répertoires vides là où devraient se trouver nginx.conf et print-flag, et les conteneurs démarrent cassés.

Télécharger l’outil