
Protocolos de descoberta de tempo do Active Directory para equipes vermelhas. Extração furtiva via Kerberos, SMB, NTLM e CLDAP.

skewrun é um kit de ferramentas de descoberta de tempo do Active Directory para red teams. Ele resolve dinamicamente o horário do Controlador de Domínio via protocolos de rede (CLDAP, SMB, NTP, Kerberos, NTLM) e executa comandos via libfaketime (LD_PRELOAD), enganando o binário executado para usar o horário exato do DC.
Isso resolve o erro Kerberos KRB_AP_ERR_SKEW (Clock Skew Too Great), permitindo executar ferramentas como Impacket ou NetExec a partir de uma máquina Linux de ataque cujo relógio está fortemente dessincronizado do domínio Windows alvo, sem exigir privilégios root para alterar o horário do sistema.
A partir da v0.9.0, o Skewrun é construído com uma arquitetura biblioteca-primeiro:
ad-time: Uma crate de biblioteca Rust pura que extrai hora de protocolos AD de forma furtiva. Ela pode ser nativamente incorporada em outros implantes ou ferramentas Rust.skewrun: Um binário CLI que orquestra a biblioteca ad-time e envolve processos alvo com libfaketime.# Binário estático pré-compilado (sem necessidade do toolchain Rust)
wget https://github.com/JVBotelho/skewrun/releases/latest/download/skewrun-x86_64-linux-musl
chmod +x skewrun-x86_64-linux-musl
sudo mv skewrun-x86_64-linux-musl /usr/local/bin/skewrun
# Do crates.io
cargo install skewrun
# A partir do código fonte
git clone https://github.com/JVBotelho/skewrun && cd skewrun && cargo build --release
Nota: Você deve ter o libfaketime instalado no seu sistema (ex.: apt-get install libfaketime).
# Comportamento padrão (tenta CLDAP -> SMB -> NTP)
skewrun 10.10.10.100 -- impacket-getTGT -dc-ip 10.10.10.100 domain.local/user:pass
# Forçar métodos específicos
skewrun 10.10.10.100 -m cldap,ntlm,kerberos -- netexec smb 10.10.10.100
# Apenas mostrar o offset (útil para scripts shell)
skewrun 10.10.10.100 --print-offset
# Modo offline: fornecer um offset conhecido manualmente
skewrun --offset "+3.450s" -- impacket-getTGT ...
# Ajustar jitter entre métodos (padrão: sigma=0.4, base=8000ms; sigma=0 desabilita jitter)
skewrun 10.10.10.100 --jitter-sigma 0.2 --jitter-base-ms 5000 -- impacket-getTGT ...
use ad_time::protocols::cldap::CldapSource;
use ad_time::time_src::TimeSource;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let src = CldapSource;
let addr = "10.10.10.100:389".parse()?;
let offset_us = src.fetch(addr, Duration::from_secs(3))?;
println!("DC offset: {} µs", offset_us);
Ok(())
}
Cada módulo de protocolo (kerberos, ntlm, cldap, smb, ntp) é independente e extraível para uso em ferramentas personalizadas de red team.
O Skewrun consulta o DC para calcular o offset exato em microssegundos (DC_Time - Local_Time). Em seguida, define a variável de ambiente FAKETIME e injeta o libfaketime no comando alvo usando LD_PRELOAD.
LD_PRELOAD depende da interceptação de chamadas vinculadas dinamicamente à libc (como clock_gettime). Se você tentar usar skewrun em um binário compilado estaticamente (como muitas ferramentas Go ou Rust), o libfaketime falhará silenciosamente ao interceptar as funções de tempo. O Skewrun avisará se detectar que você está tentando executar um binário estático.
O objetivo é se misturar ao tráfego padrão do Windows e minimizar a pegada forense no DC.
Independentemente do método usado: cada socket de saída define TTL=128 e ativa Nagle
(TCP_NODELAY=0) antes do handshake TCP, correspondendo à impressão digital da pilha de rede
de um cliente Windows ao invés dos padrões Linux (TTL=64, Nagle desligado) que um observador
passivo (p0f, conn.log do Zeek) sinalizaria. Atrasos entre métodos usam jitter log-normal
alimentado por CSPRNG (não um intervalo aleatório simples) com backoff exponencial em falhas
repetidas, evitando a assinatura de temporização uniforme que uma banda fixa de jitter deixa
nos logs de conexão — veja --jitter-sigma e --jitter-base-ms acima.
cargo test # testes unitários + propriedades
cargo +nightly fuzz run fuzz_parse_krb_error # apenas Linux/macOS
Todos os analisadores de rede (parse_krb_error, parse_cldap_search_response, parse_ntlm_type2,
parse_negotiate_response, parse_ntp_timestamp) são testados para segurança contra pânico usando
proptest e testados por fuzzing no CI.
A codificação de inteiros BER é verificada quanto à minimalidade DER e correção de sinal em toda a faixa i32,
cobrindo valores negativos de etype conforme RFC 4120.
Alvos de fuzzing e corpus residem em fuzz/.
Licenciado duplamente sob MIT ou Apache 2.0, à sua escolha.
| Método | Protocolo | Porta | Notas de OPSEC (Visibilidade EDR/NDR) |
|---|
| cldap (Padrão) | CLDAP | UDP/389 | Extremamente Furtivo. Universalmente permitido. Envia uma consulta de diagnóstico LDAP rootDSE padrão (busca base objectClass=*), correspondendo à linha de base de ldapsearch, cmdlets AD do PowerShell e ferramentas de monitoramento. Dilui a lista de atributos com atributos comuns de administração e randomiza a ordem dos atributos por requisição para quebrar assinaturas NDR estáticas. |
| smb (Padrão) | SMB2 | TCP/445 | Furtivo. Extrai a hora da resposta SMB2 NEGOTIATE. Negocia SMB 3.1.1 com PREAUTH_INTEGRITY_CAPABILITIES (SHA-512), correspondendo ao comportamento de cliente Windows 10/11. |
| ntp (Padrão) | SNTP | UDP/123 | Padrão. RFC 4330 nativa. Tráfego altamente esperado de qualquer cliente. |
| ntlm | SMB2 | TCP/445 | Furtivo. Explora SMB2 SESSION_SETUP para obter um desafio NTLM Tipo 2 contendo MsvAvTimestamp. Desconecta TCP antes do Tipo 3, o que significa que nenhum Event ID 4625 (Falha de Logon) é gerado. Emula flags do Windows 10/11. |
| kerberos | Kerberos | TCP/88 | Barulhento. Envia um AS-REQ para um usuário inexistente. Codifica sname de dois componentes corretamente, rotaciona cname (erros de digitação como admnistrator), define till para a constante Windows hardcoded 20370913024805Z (não aritmética de relógio local — veja ADR-0002), randomiza nonce. Sempre gera Evento 4768/0x6 (falha de pré-autenticação para principal desconhecido) que é exportado para SIEM independentemente da política de auditoria. Pode acionar alertas de honey-account se o cname corresponder a um tripwire configurado. |