Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
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.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
Herramientas/GitHubGitHub/karkas66/lacuna-rs
ShellcodePost-ExplotaciónRed TeamingDesarrollo de PayloadsExplotación de Binarios
GitHubkarkas66/lacuna-rs

lacuna-rs

Crate de Rust para suplantación de pila de llamadas con ghost-frame, syscalls indirectos en tiempo de ejecución e inyección APC en Windows x64. Proporciona resolución de SSN, emisión de stubs JIT y cifrado de parámetros basado en VEH para evasión de EDR.

Ver Repositorio
181hace 1 mesAú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

lacuna-rs

Suplantación de pila de llamadas de fotogramas fantasma + llamadas al sistema indirectas en tiempo de ejecución para Windows x64 — portado a Rust.

Portado de LACUNA Chain (lacuna_chain.c) por Mohamed Alzhrani (0xmaz).

lacuna-rs es una crate Rust reutilizable que proporciona las mismas primitivas que el TTP original en C, estructurada para que pueda integrarse en cualquier proyecto Rust — como wsyscall-rs o syscalls-rs, pero con resolución de SSN en tiempo de ejecución, direccionamiento syscall;ret por función y suplantación de pila de fotogramas fantasma.


Crítico: Requisito del puntero de marco

La característica stack-spoof fallará silenciosamente si la crate consumidora no se compila con punteros de marco.

Las primitivas de pisoteo de pila en chain.rs localizan la ranura de dirección de retorno de la persona que llama mediante mov rbp, {x} en ensamblador en línea. Esto requiere que la cadena RBP esté intacta. Rust (y la mayoría de los compiladores en modo release) omiten los punteros de marco por defecto.

build.rs establece force-frame-pointers=yes para la generación de código de esta propia crate, pero Cargo no puede propagar banderas del compilador a crates descendentes. Debes agregar esto a tu propio proyecto:

root@kitploit:~
# .cargo/config.toml  (en TU crate, NO en lacuna-rs)
[build]
rustflags = ["-C", "force-frame-pointers=yes"]

Sin esto, stomp_plant() leerá basura de RBP y no hará nada (mejor caso) o corromperá la pila (peor caso). La crate no tiene forma de detectar en tiempo de ejecución si los punteros de marco están habilitados — simplemente no funcionará.

Si solo necesitas las primitivas de escaneo, resolución de SSN o inyección (sin suplantación de pila), puedes omitir la característica stack-spoof y este requisito no aplica.


Qué hace


Banderas de características

root@kitploit:~
[dependencies]
lacuna-rs = { version = "0.1", features = ["inject", "stack-spoof", "veh"] }

Cuando no hay características habilitadas, solo están disponibles las capas de escaneo/PE/NT.


Inicio rápido

Escanear regiones fantasma

root@kitploit:~
cargo run --example scan

Construir + verificar la cadena de fotogramas fantasma

root@kitploit:~
cargo run --example verify --features stack-spoof

Inyectar shellcode mediante sección + APC

Prueba de inyección Ejemplo de inyección con implante real - C2 estaba desconectado pero el Shellcode se ejecutó

root@kitploit:~
cargo run --example inject --features inject,stack-spoof,veh -- <pid> <sc.bin>

Agrega --verbose para habilitar la salida de diagnóstico de VEH (volcados de pila, impresiones de registros):

root@kitploit:~
cargo run --example inject --features inject,stack-spoof,veh -- <pid> <sc.bin> --verbose

Algoritmo de puntuación de hilos

En lugar de encolar APC a todos los hilos del proceso objetivo (lo que puede bloquear el proceso cuando demasiados hilos son alertados simultáneamente), inject_sapc utiliza un algoritmo de puntuación para seleccionar los mejores MAX_APC_THREADS (5) candidatos:

  • Tiempo de ciclo — NtQueryInformationThread(ThreadCycleTime) — indicador principal de actividad
  • Tiempo de CPU — NtQueryInformationThread(ThreadTimes) — tiempo de kernel + usuario
  • Contador de suspensión — NtQueryInformationThread(ThreadSuspendCount) — los hilos no suspendidos reciben +300 de bonificación; los suspendidos obtienen -150 por cada suspensión
  • Prioridad — NtQueryInformationThread(ThreadBasicInformation) — los hilos con prioridad 8-10 reciben +150 de bonificación; las prioridades fuera de rango reciben -100 de penalización

Los hilos completamente inactivos (cero ciclos y cero tiempo de CPU) se omiten por completo. Los candidatos restantes se ordenan por puntuación (mayor primero, desempate por ciclos) y se truncan a los 5 primeros.


Uso como biblioteca

Básico: escaneo + resolución de SSN

root@kitploit:~
use lacuna::{scan, nt, win::get_module};

let ntdll = get_module(b"ntdll.dll\0");

// Escanear regiones fantasma
let mut ghosts = [scan::Ghost::default(); 512];
let n = scan::scan_ghosts(ntdll, &[b"NtAllocateVirtualMemory\0"], &mut ghosts);
println!("{} regiones fantasma en ntdll", n);

// Resolver SSN + syscall;ret para una función específica
let (ssn, syscall_ret) = nt::resolve(ntdll, b"NtOpenProcess\0");
println!("NtOpenProcess: ssn={:#x}, syscall;ret={:#x}", ssn, syscall_ret);

Emitir un stub de syscall indirecto para cualquier API de NT

root@kitploit:~
use lacuna::{nt, stub, win::{get_module, HMODULE}};

let ntdll: HMODULE = get_module(b"ntdll.dll\0");

// Resolver SSN + la dirección syscall;ret de la propia función
let (ssn, syscall_ret) = nt::resolve(ntdll, b"NtAllocateVirtualMemory\0");
assert!(ssn != nt::SSN_INVALID && syscall_ret != 0);

// Emitir JIT un stub: mov r10,rcx; mov eax,SSN; jmp [syscall;ret]
// (o jmp [ghost_gadget] -> JMP [RBX] -> syscall;ret si se llamó a build_chain)
let stub = stub::make_stub(ssn, syscall_ret).expect("falló la asignación del stub");

// Convertir al tipo de puntero de función correspondiente y llamar
let alloc_vm: unsafe extern "system" fn(
    win::HANDLE, *mut win::PVOID, usize, *mut usize, win::ULONG, win::ULONG,
) -> win::NTSTATUS = unsafe { core::mem::transmute(stub.as_fn()) };

Construir la cadena de fotogramas fantasma + registrar VEH

root@kitploit:~
// Escanear ntdll/kernelbase/wow64/win32u en busca de regiones fantasma y construir
// la pila de llamadas falsa de seis capas. Establece G_GHOST_GADGET para que los stubs
// direccionen a través de JMP [RBX] en una DLL firmada.
lacuna::chain::build_chain();

// Registrar manejadores VEH (cifrado de parámetros + guardián de cadena)
let _veh = lacuna::veh::VehGuard::register().expect("Falló el registro de VEH");

// Opcional: habilitar diagnóstico detallado en tiempo de ejecución
lacuna::veh::set_verbose(true);

Envolver una syscall sensible con suplantación de pila + cifrado de parámetros

root@kitploit:~
use lacuna::win::HANDLE;
use core::ptr;

let mut h_proc: HANDLE = ptr::null_mut();
let key: u64 = 0xCAFE_1337;

// Plantar fotogramas fantasma (reemplaza las direcciones de retorno con fantasmas de DLL firmada)
lacuna::chain::stomp_plant();

// Armar DR0 en syscall;ret — VEH descifrará XOR los parámetros en el límite
lacuna::veh::pcrypt_arm(key, syscall_ret, true);

// Llamar a través del stub indirecto — RIP aterriza dentro de ntdll en la entrada del kernel
let _status = unsafe { open_proc(/* parámetros cifrados con XOR */) };

// Siempre desarmar antes de cualquier llamada no protegida
lacuna::veh::pcrypt_disarm();
lacuna::chain::stomp_restore();

OPSEC: Qué ocultar detrás de los fotogramas fantasma

No toda llamada a la API necesita el tratamiento completo de LACUNA. El principio clave es: ocultar las llamadas que un EDR correlacionaría como actividad de inyección o post-explotación.

Deben estar detrás de syscalls indirectos + fotogramas fantasma + cifrado de parámetros

Estas son las syscalls NT "joya de la corona" que los EDR enganchan y correlacionan:

Para cada una de estas:

  1. Resolver con nt::resolve(ntdll, b"NtXxx\0")
  2. Emitir un stub con stub::make_stub(ssn, syscall_ret)
  3. Cifrar con XOR los parámetros sensibles con veh::pcrypt_arm(key, syscall_ret, true)
  4. Envolver la llamada entre chain::stomp_plant() y chain::stomp_restore()

Pueden ser directas (sin necesidad de fotogramas fantasma)

Principios clave

  1. Resolver cualquier syscall de NT con nt::resolve(ntdll, b"NtXxx\0") — devuelve (SSN, syscall_ret_VA).
  2. Emitir un stub JIT con stub::make_stub(ssn, syscall_ret) — devuelve un puntero de función invocable.
  3. Convertir el stub al tipo extern "system" fn(...) apropiado mediante core::mem::transmute.
  4. (Solo para llamadas sensibles) Construir la cadena de fotogramas fantasma con chain::build_chain() y llamar a chain::stomp_plant() / chain::stomp_restore() alrededor de la llamada.
  5. (Solo para llamadas sensibles) Armar el cifrado de parámetros con veh::pcrypt_arm(key, syscall_ret, true) antes de la llamada, y veh::pcrypt_disarm() después.
  6. Siempre desarmar el VEH antes de hacer llamadas no protegidas — un golpe de DR0 accidental en una syscall desarmada bloqueará el proceso.

El stub maneja automáticamente la secuencia mov r10, rcx / mov eax, SSN / jmp [syscall;ret]. Si se registró un gadget fantasma, el stub direcciona a través de JMP [RBX] para redirección de ejecución de doble uso + marco puente sin artefactos.


Cobertura de verificación del artículo técnico

Las 9 contribuciones clave del artículo técnico de LACUNA Chain están representadas en el código:

Cobertura de superficie de detección


¿Por qué no usar simplemente syscalls-rs?

syscalls-rs y wsyscall-rs generan una tabla de SSN en tiempo de compilación a partir de la ntdll de una versión específica de Windows. Si la máquina objetivo ejecuta una versión diferente, los SSN son incorrectos y las syscalls fallan (o activan heurísticas de EDR).

lacuna-rs resuelve los SSN en tiempo de ejecución leyendo directamente los stubs de ntdll, y apunta a la instrucción syscall;ret de la propia función de modo que RIP está dentro de ntdll en la entrada del kernel — derrotando las heurísticas de "desajuste de SSN" y "syscall indirecto desde memoria no respaldada" de los EDR.

Además, lacuna-rs proporciona la cadena de suplantación de pila de fotogramas fantasma que syscalls-rs no ofrece.


Requisitos de compilación

  • Objetivo: x86_64-pc-windows-msvc (o x86_64-pc-windows-gnu)
  • Edición de Rust: 2021
  • Dependencias: litcrypt2 (ofuscación de cadenas en tiempo de compilación)

Configuración de puntero de marco (solo para stack-spoof)

La característica stack-spoof requiere punteros de marco. build.rs establece force-frame-pointers=yes automáticamente para esta crate, pero las crates consumidoras también deben configurarlo en su .cargo/config.toml:

root@kitploit:~
# .cargo/config.toml (en la crate consumidora)
[build]
rustflags = ["-C", "force-frame-pointers=yes"]

Ofuscación de cadenas con litcrypt2

Todos los literales de cadena en la crate y los ejemplos están envueltos en macros lc!(), que los cifran en tiempo de compilación y los descifran en tiempo de ejecución. La clave de cifrado se lee de la variable de entorno LITCRYPT_ENCRYPT_KEY; si no está configurada, litcrypt2 genera automáticamente una clave aleatoria.

Para fijar una clave para compilaciones reproducibles:

root@kitploit:~
set LITCRYPT_ENCRYPT_KEY=tu-clave-secreta
cargo build

Arquitectura

root@kitploit:~
lacuna-rs/
├── Cargo.toml
├── build.rs              # Establece force-frame-pointers para stack-spoof
├── src/
│   ├── lib.rs            # Raíz de la crate + reexportaciones + scan_all() + configuración de litcrypt2
│   ├── win.rs            # Enlaces FFI de Win32/NT (compatible con no_std)
│   ├── pe.rs             # Análisis de secciones PE + exportaciones
│   ├── scan.rs           # Escaneo de regiones fantasma .pdata + gadgets
│   ├── nt.rs             # Resolución de SSN + direccionamiento syscall;ret
│   ├── stub.rs           # Emisión de stub de syscall indirecto JIT
│   ├── veh.rs            # Cifrado de parámetros con VEH + breakpoint hardware
│   ├── chain.rs          # Cadena de fotogramas fantasma LACUNA + pisoteo
│   └── inject.rs         # Inyección APC basada en secciones
├── examples/
│   ├── scan.rs           # lacuna.exe scan
│   ├── verify.rs         # lacuna.exe verify
│   └── inject.rs         # lacuna.exe inject <pid> <sc.bin>
└── .cargo/
    └── config.toml       # force-frame-pointers + objetivo windows-msvc

Créditos

  • Investigación original e implementación en C: Mohamed Alzhrani (0xmaz) — LACUNA Chain
  • Técnica: Ghost Frames: Forging Plausible Call Stacks from .pdata Lacunae
  • Lógica de búsqueda del mejor hilo APC: Adaptada de FrankensteinAPCInjection por S12cybersecurity
  • Ofuscación de cadenas: litcrypt2 v0.1.3

Divulgación completa

Este proyecto fue asistido activamente por un chatbot de IA. Tuve la oportunidad de probar un modelo de Código Abierto más nuevo y muy capaz (GLM-5.2 de Z.ai (Zhipu AI)) y quería darle una tarea desafiante. Lecciones aprendidas: Funcionó bien si el humano sabe cómo deben ser los resultados. Si no... solo basura.

Licencia

MIT

Descargar herramienta
PrimitivaFunción en CMódulo en Rust
Análisis de secciones PE + exportacionespe_section(), pe_export()pe
Escaneo de región fantasma .pdatascan_ghosts(), best_ghost()scan
Descubrimiento de gadget fantasma (jmp [rbx])scan_ghost_gadgets()scan
Buscador de hueco NOP en win32uwin32u_nop_gap()scan
Buscador de anclaje BYOUD-MFfind_mf_target()scan
Resolución de SSN (Hell's Gate / Halo's Gate)resolve_ssn()nt
Localizador de syscall;ret por funciónfind_func_syscall()nt
Emisión de stub de syscall indirecto JITalloc_stub()stub
Redirección de stub hacia gadget fantasma(en alloc_stub())stub
Cifrado de parámetros con VEH + breakpoint hardwareparam_encrypt_veh(), pcrypt_arm()veh
VEH guardián de cadenachain_veh()veh
Construcción de cadena LACUNAbuild_chain()chain
Pisoteo de pila (BYOUD-RT)stomp_plant(), stomp_restore()chain
Caminante de cadena (verificación)lacuna_walk_chain()chain
Inyección APC basada en seccionesdo_inject_sapc()inject
CaracterísticaDescripción¿Requiere punteros de marco?
syscalls (por defecto)Resolución de SSN + emisión de stub JITNo
inject (por defecto)Inyección APC basada en secciones (inject::inject_sapc)No
vehCifrado de parámetros con VEH + breakpoint hardwareNo
stack-spoofCadena de fotogramas fantasma LACUNA + pisoteo de pilaSí
no-stdModo no_std (experimental)No
LlamadaPor qué es sensible
NtOpenProcessAbre un manejador a otro proceso — primer paso de la inyección
NtCreateSection + NtMapViewOfSection (remoto)Firma de inyección basada en secciones
NtWriteVirtualMemoryEscritura entre procesos — indicador clásico de inyección
NtCreateThreadExCreación de hilo remoto — primitiva de inyección de mayor señal
NtQueueApcThreadInyección APC — alta señal
NtProtectVirtualMemoryCambios de permisos RWX — indicador de preparación de shellcode
NtAllocateVirtualMemory (remoto)Asignación remota — preludio de inyección
NtSetInformationThreadOcultación de hilo (HideFromDebugger) — indicador de evasión
LlamadaPor qué es segura
NtQueryInformationThreadSolo consulta, rara vez enganchado, sin escritura entre procesos
NtDelayExecutionDormir — benigno, usado por todas las aplicaciones
NtCloseCierre de manejador — benigno, extremadamente común
GetModuleHandleA / GetProcAddressResolución de módulos — no es una syscall, no se puede enganchar con hooks de userland de EDR
CreateToolhelp32Snapshot / Thread32First / Thread32NextEnumeración de hilos — kernel32, no syscall de ntdll
OpenThread / CloseHandleOperaciones estándar de manejador — kernel32
GetThreadContext / SetThreadContextNecesario para DR0 — kernel32, solo proceso propio
  • Todas las cadenas de salida deben estar envueltas en lc!() — la crate lo exige, y los diagnósticos de VEH están controlados por set_verbose(true) que por defecto está desactivado.
  • #Concepto del artículoUbicación en el códigoDescripción
    1BYOUD-Gap (modificación .pdata cero)chain.rs — construcción de cadena de fotogramas fantasmaExplota huecos entre entradas RUNTIME_FUNCTION; el unwinder los trata como fotogramas hoja (RSP += 8)
    2Ataque de Ventana APC ETW-Tiinject.rs — drenaje alertable de NtDelayExecutionControla cuándo se dispara la instantánea de pila APC de ETW-Ti manipulando el estado alertable del hilo
    3Cifrado de parámetros en contexto BYOUDveh.rs — pcrypt_arm(), param_encrypt_veh()Cifra con XOR los parámetros de syscall en el escenario; descifra dentro de un VEH de breakpoint hardware en la instrucción syscall
    4Cadena de huecos NOP en Win32u + Gadget Fantasmascan.rs — win32u_nop_gap(), scan_ghost_gadgets()1,242 huecos NOP en win32u.dll proporcionan fotogramas hoja en lista blanca; gadget fantasma JMP [RBX] en ntdll+0xFC47B
    5Proximidad semántica de fotogramas fantasma en kernelbasechain.rs — capa L2_kbase cerca de VirtualProtectFantasma de 238 bytes que termina en el punto de entrada de VirtualProtect — indistinguible de un sitio de retorno real de VP
    6BYOUD-MF (Teletransporte RSP de Fotograma de Máquina)chain.rs — struct MachFrame, scan.rs — find_mf_target()Explota UWOP_PUSH_MACHFRAME (código de operación 10) en los despachadores KiUser* para teletransporte arbitrario de RSP en un solo fotograma
    7BYOUD-RT (Cálculo de RSP en tiempo de ejecución)chain.rs — teb_stack_base(), teb_stack_limit()Lee TEB.StackBase (GS:[0x08]) en el momento de la llamada para calcular la distancia exacta del fotograma — no se necesita precalibración
    8Proximidad de fotograma fantasma en wow64.dllchain.rs — capa L1_wow64, scan.rs — apunta a Wow64PrepareForExceptionFantasma de 91 bytes que termina en la entrada de Wow64PrepareForException — cuarta capa semántica para la cadena
    9Cadena LACUNA de seis capaschain.rs — struct LacunaStack (L1-L5 + MachFrame)Cadena completa: KiUserExceptionDispatcher -> wow64 -> kernelbase -> ntdll -> win32u -> RtlUserThreadStart
    Capa de detecciónEstadoImplementación
    Verificación de módulo de origenEVADIDOTodos los fotogramas en ntdll / kernelbase / wow64 / win32u
    Corrección del recorrido de unwindEVADIDOTodos los fotogramas lacuna son hoja -> RSP+8 válido
    Escaneo forense de .pdataEVADIDOModificación cero; los huecos son preexistentes
    Pila sombra CETEVADIDOCadena puramente hoja; no se consulta la pila sombra
    Análisis semántico de fotogramasEVADIDOAdyacencia a WoW64 exception + VirtualProtect
    Exención de regla de Win32uEVADIDOLa capa 4 está explícitamente excluida por todas las reglas
    ETW-Ti STACKWALKEVADIDOEl ataque de ventana APC controla el tiempo de la instantánea
    Inspección de parámetrosEVADIDODescifrado VEH con breakpoint hardware
    Callbacks del kernelPARCIALLas operaciones de manejador aún disparan ObRegisterCallbacks