ad-time 1.1.0

Active Directory time discovery protocols for red teams.
Documentation
# Skewrun

[![CI](https://github.com/JVBotelho/skewrun/actions/workflows/ci.yml/badge.svg)](https://github.com/JVBotelho/skewrun/actions/workflows/ci.yml)
[![Crates.io](https://img.shields.io/crates/v/skewrun.svg)](https://crates.io/crates/skewrun)

`skewrun` is an Active Directory time discovery toolkit for red teams. It dynamically resolves the Domain Controller's time via network protocols (CLDAP, SMB, NTP, Kerberos, NTLM) and executes commands via `libfaketime` (`LD_PRELOAD`), tricking the executed binary into using the exact DC time.

This solves the Kerberos `KRB_AP_ERR_SKEW` (Clock Skew Too Great) error, allowing you to run tools like Impacket or NetExec from a Linux attack machine whose clock is heavily desynchronized from the target Windows domain, **without requiring root privileges to change the system time**.

## Architecture: Library-First

Starting with `v0.9.0`, Skewrun is built as a library-first architecture:
- **`ad-time`**: A pure Rust library crate that extracts time from AD protocols stealthily. It can be natively embedded into other Rust implants or tools.
- **`skewrun`**: A CLI binary that orchestrates the `ad-time` library and wraps target processes with `libfaketime`.

## Installation

```bash
# Pre-built static binary (no Rust toolchain required)
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

# From crates.io
cargo install skewrun

# From source
git clone https://github.com/JVBotelho/skewrun && cd skewrun && cargo build --release
```

*Note: You must have `libfaketime` installed on your system (e.g., `apt-get install libfaketime`).*

## Usage

```bash
# Default behavior (tries CLDAP -> SMB -> NTP)
skewrun 10.10.10.100 -- impacket-getTGT -dc-ip 10.10.10.100 domain.local/user:pass

# Force specific methods
skewrun 10.10.10.100 -m cldap,ntlm,kerberos -- netexec smb 10.10.10.100

# Just print the offset (useful for shell scripting)
skewrun 10.10.10.100 --print-offset

# Offline mode: supply a known offset manually
skewrun --offset "+3.450s" -- impacket-getTGT ...
```

## Using as a library

```rust
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(())
}
```

Each protocol module (`kerberos`, `ntlm`, `cldap`, `smb`, `ntp`) is independent and extractable for use in custom red team tooling.

## How It Works

Skewrun queries the DC to calculate the exact microsecond offset `(DC_Time - Local_Time)`. It then sets the `FAKETIME` environment variable and injects `libfaketime` into the target command using `LD_PRELOAD`.

### FAKETIME limitations (Static Binaries)
`LD_PRELOAD` relies on intercepting `libc` dynamically linked calls (like `clock_gettime`). If you attempt to use `skewrun` on a **statically compiled binary** (such as many Go or Rust tools), `libfaketime` will silently fail to hook the time functions. Skewrun will warn you if it detects you are attempting to run a static binary.

## Forensic Noise & Evasion

The goal is to blend in with standard Windows wire traffic and minimize forensic footprint on the DC. 

| Method | Protocol | Port | OPSEC Notes (EDR/NDR Visibility) |
|--------|----------|------|-----------------------------------|
| **cldap** (Default) | CLDAP | UDP/389 | **Extremely Stealthy**. Universally allowed. Sends a standard LDAP `rootDSE` diagnostic query (`objectClass=*` base search), matching the baseline of `ldapsearch`, PowerShell AD cmdlets, and monitoring tools. Dilutes the attribute list with common admin attrs and randomizes `timeLimit` to break static NDR signatures. |
| **smb** (Default) | SMB2 | TCP/445 | **Stealthy**. Extracts time from the `SMB2 NEGOTIATE` response. Negotiates SMB 3.1.1 with `PREAUTH_INTEGRITY_CAPABILITIES` (SHA-512), matching Windows 10/11 client behavior. |
| **ntp** (Default) | SNTP | UDP/123 | **Standard**. Native RFC 4330. Highly expected traffic from any client. |
| **ntlm** | SMB2 | TCP/445 | **Stealthy**. Exploits `SMB2 SESSION_SETUP` to get an NTLM Type 2 Challenge containing `MsvAvTimestamp`. Disconnects TCP before Type 3, meaning **no Event ID 4625 (Logon Failure) is generated**. Emulates Windows 10/11 flags. |
| **kerberos** | Kerberos | TCP/88 | **Loud**. Sends an `AS-REQ` for a non-existent user. Encodes proper two-component `sname`, rotates `cname` (typos like *admnistrator*), sets `till` to the Windows hardcoded constant `20370913024805Z` (not local clock arithmetic — see ADR-0002), randomizes `nonce`. Always generates Event 4768/0x6 (pre-authentication failure for unknown principal) which is exported to SIEM regardless of audit policy. May trigger honey-account alerts if the `cname` matches a configured tripwire. |

## Testing

```bash
cargo test                                            # unit + property tests
cargo +nightly fuzz run fuzz_parse_krb_error          # Linux/macOS only
```

All network-facing parsers (`parse_krb_error`, `parse_cldap_search_response`, `parse_ntlm_type2`,
`parse_negotiate_response`, `parse_ntp_timestamp`) are property-tested for panic safety using
[proptest](https://github.com/proptest-rs/proptest) and fuzz-tested in CI.
BER integer encoding is verified for DER minimality and sign correctness across the full `i32` range,
covering negative etype values per RFC 4120.
Fuzz targets and corpus live in `fuzz/`.

## License

Dual-licensed under [MIT](LICENSE-MIT) or [Apache 2.0](LICENSE-APACHE) at your option.