Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
StealMyBitcoin-PoC — Una versión pública del desafío Steal My Bitcoin | Kitploit
Herramientas/GitHubGitHub/killaboi/stealmybitcoin-poc
Forensia de MemoriaAnálisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebCTFDesarrollo de PayloadsExplotación de BinariosLabs y Práctica
GitHubkillaboi/stealmybitcoin-poc

StealMyBitcoin-PoC

Una versión pública del desafío Steal My Bitcoin

Ver Repositorio
2164160hace 1 díaAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

patrocinado con ❤️ por hextree.io

Prueba de concepto del CTF Steal My Bitcoin

Un callback PHP en vivo contra el desafío Steal My Bitcoin, de principio a fin, en un único archivo Python de biblioteca estándar.

Python Dependencies Target Lab

Este README explica stealmybitcoin_poc.py, una reproducción en Python de un solo archivo de la secuencia de peticiones usada en mi ejecución del CTF Steal My Bitcoin. Solo utiliza la biblioteca estándar de Python. El script construye las peticiones por sí mismo, verifica cada filtración antes de continuar y registra los bytes HTTP de una nueva ejecución.

Esto probablemente se pueda hacer más fácilmente sin toda la parafernalia extra de depuración y, como descubrí en mi vídeo de John Hammond sobre la Escalada de Privilegios Locales de Steam, probablemente podrías limpiar esta fea PoC.

[!NOTE] ⚠️ Sinceramente, por favor límpiala o haz tu propia versión mejor que no esté destrozada por Sol. ⚠️

Todo lo que sigue está escrito contra el laboratorio Docker, no contra el desafío en vivo. El laboratorio es un servidor CLI local de Alpine PHP 8.4.26 detrás de nginx, que es la misma compilación y modelo de workers sobre el que se calibraron los offsets. Levántalo en un minuto y toda la cadena se ejecuta de principio a fin en tu propia máquina. La ejecución en vivo que realmente resolvió el desafío se conserva como nota histórica al final.

  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 versión corta

  1. Una pequeña carga multipart hace que PHP consuma mucha más memoria de lo que sugiere el tamaño en la red.
  2. Cuando se queda sin memoria a mitad del análisis del progreso de carga de la sesión, PHP deja un estado obsoleto en un worker que luego se reutiliza.
  3. Si se moldean correctamente las solicitudes de seguimiento, los bytes se filtran a través de las cookies de sesión. Yo los interpreto como una dirección del heap y una dirección del código de PHP.
  4. Con ambas direcciones, una solicitud final coloca una estructura PHP falsificada donde la limpieza la encontrará, y la limpieza llama a zif_system, el manejador de PHP para system.
  5. Ese comando devuelve un nuevo marcador de prueba y un resultado de print-flag a través de la conexión que sigue abierta.

¿Por qué dos direcciones? El sistema operativo aleatoriza dónde se ubican el código y el heap de PHP, de forma independiente. La fuga del heap le indica al script dónde colocar la estructura falsificada, y la fuga del código le indica dónde vive realmente la función que quiere llamar. Las direcciones de un worker anterior o de una ejecución anterior no son sustitutos seguros, y cada vez que lo intenté, el intento simplemente falló.

Contenido

  • El laboratorio
  • Empieza aquí
  • Qué hace cada solicitud
    • 1. Cómo un pequeño productor alcanza el límite de memoria
    • 2. Cómo se recupera la dirección del heap
    • 3. Cómo se recupera la dirección del código de PHP
    • 4. Cómo la solicitud final llega a system
  • Lectura del resultado
  • Con qué frecuencia funciona realmente
  • La ejecución en vivo que lo resolvió
  • Jerga, brevemente
  • Agradecimientos

El laboratorio

Los offsets de esta PoC están calibrados para Alpine PHP 8.4.26 ejecutando el servidor CLI integrado con cuatro workers. El laboratorio reproduce exactamente eso, detrás de nginx, en loopback.

[!IMPORTANT] La compilación importa más que la cadena de versión. Una pila de Debian php:8.4.26-fpm reporta el mismo PHP/8.4.26 pero tiene un diseño de binario diferente, por lo que la huella del puntero de código nunca coincidirá y la ejecución se aborta en la etapa de fuga de código. Usa la imagen de Alpine a continuación.

Tres archivos pequeños. Colócalos en un directorio vacío:

public/php-version.php (el endpoint de salud, y la única página que la PoC necesita)

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

print-flag (sustituto del binario del reto, debe ser ejecutable)

#!/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;
        }
    }
}

Luego actívalo:

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

Compruébalo:

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

El PID varía según qué worker responda, y eso está bien. La línea de versión es la parte que verifica el PoC.

Desmontarlo con docker rm -f ctf-cross-nginx ctf-cross-php && docker network rm ctf-cross-net.

[!TIP] En Windows, ejecute esos comandos docker run desde PowerShell, o reemplace $PWD con una ruta de unidad completa como C:/lab. En Git Bash $PWD se expande a una ruta de MSYS como /tmp/..., que Docker Desktop resuelve dentro de su propia VM. Los bind mounts entonces crean silenciosamente directorios vacíos donde deberían estar nginx.conf y print-flag, y los contenedores arrancan rotos.

Descargar herramienta