u/FrozenSuricats

[Showcase] RASP.Net follow-up: Sink Detection and benchmarks with real backends
▲ 5 r/dotnet

[Showcase] RASP.Net follow-up: Sink Detection and benchmarks with real backends

TL;DR: 5 months in, RASP.Net moved from WAF-style request inspection to true Sink protection (SQL, SSRF, Path Traversal, etc.) via IL-rewriting and MonoMod. Benchmarks against real backends show the overhead is statistically invisible. Looking for feedback on the architecture.

So, 5 months ago i did my first post here talking about the RASP.Net project (and its unreal/crazy fast hot-path overhead) from there I've finished the XSS detection engine (and suddenly it wasn't crazy fast anymore), removed reflection from the hot-path using source generators instead to check nested props.

And now I've finished the beginning of the sink protection, moving it from the HTTP/gRPC messages to the sinks to stop being a "WAF in process", also ventured more into c++ for the taint tracking that's still v1, the entrypoint scan still exists, they are lighter and used to start the taint and context for the sinks

Here are the sink coverage:

  • EF Core IDbCommandInterceptor for SQL
  • DelegatingHandler plus SocketsHttpHandler.ConnectCallback for SSRF
  • System.Text.Json type-info modifier for deserialization. Works under AOT and trimming.
  • MonoMod runtime patching for FileStream/File.* (path traversal) and Process.Start (command injection), since the BCL has no hook for these. Opt-in, and it'll ship as a separate package, i don't think it should end up arriving as a dependency nobody asked.
  • A native ICorProfilerCallback profiler rewriting IL at JIT time for taint tracking, same idea from Contrast and Datadog. v1 only propagates taint through String.Concat(string, string) and it's Windows-only. I kept the scope tiny on purpose for now.

We also have a per-request AsyncLocal context flowing from entrypoint to sink so when an alert is raised it also can tells which method and caller produced it

Now to the numbers, last time i reported a 4.5ns delta, it was one SearchValues inspection over a no-op interceptor; The comparable number today is the full source-generated scan of a message: ~109ns clean, vs 1120ns for the old reflection path, but for the sink pivot I've improved the methodology so here is what the guards cost under load:

Sink RASP off (p50 / p99) RASP on (p50 / p99)
Path Traversal (FileStream) 851 µs / 1349 µs 858 µs / 1404 µs
Command Injection (Process.Start) 60.76 ms / 108.22 ms 61.50 ms / 104.59 ms
SQL (EF Core → Postgres) 7.76 ms / 16.97 ms 7.50 ms / 16.50 ms
SSRF (HttpClient) 299 µs / 756 µs 306 µs / 914 µs

As i said in the title the setup is real: real Kestrel host, real backends (Postgres via Testcontainers, real subprocess, real outbound HTTP), 25 concurrent workers for 20s per endpoint.
These numbers represent the latencies with the entire sink wired or totally absent
A file open, a process spawn, a DB round trip or an outbound HTTP call costs so much more than the inspection that the guard never shows up in the percentiles.

One thing i wasn't expecting is that the SSRF DNS check fires roughly once per pooled connection rather than once per request, i was building an opt-in DNS cache for it and ended up removing it

So here are some questions to hopefully start some conversation in the comments and give me feedback and/or ideas

  • Would you allow MonoMod patching of FileStream in a production process, even opt-in? where you would draw that line?
  • Taint tracking that only covers String.Concat(string, string)is an useful foundation, or just security theater until the rest are in?
  • Anything in the benchmark setup that would make you distrust the "no measurable overhead" claim?

Our next milestone is NuGet packaging (net8.0;net10.0, MonoMod split off, MinVer for versioning).
Repo: https://github.com/JVBotelho/RASP.Net
ADRs 006/007 have the full reasoning if you want to tear those apart too.
Everything here is under the v1.1.0 tag

u/FrozenSuricats — 1 day ago
▲ 22 r/redteamsec+1 crossposts

skewrun – bypass Kerberos clock skew (KRB_AP_ERR_SKEW) without root or touching the system clock (Rust, v1.1.0)

I'm the author, sharing this here because it's a narrow, annoying problem most of us have hit on an engagement: the attack box's clock drifts from the DC's, Kerberos throws KRB_AP_ERR_SKEW, and the usual fix (ntpdate or rdate against the DC) needs root and clobbers your whole system clock, not just Kerberos. Other processes, logs, TLS all shift too, and on a VM the hypervisor often stomps your fix a few seconds later unless you also disable host time sync.

skewrun finds the DC's real time by talking to it directly (CLDAP, SMB, NTP, Kerberos, or NTLM), then wraps your target command with libfaketime via LD_PRELOAD, so only that process sees the corrected time. The machine's own clock never changes.

Since v0.9.0 it's split into a pure Rust library (ad-time) and a CLI (skewrun) built on top, so the protocol sources are usable standalone in other tooling.

One thing from this release that might be useful to others doing packet-level OPSEC work: the Kerberos AS-REQ till field isn't computed by Windows, it's a hardcoded constant (20370913024805Z, Sept 13 2037) since at least Server 2003, confirmed via a Heimdal KDC bug report with real captures and the leaked Windows Server 2003 source. Computing now + jitter instead of hardcoding that value is a detectable difference. Full writeup and two more similar decisions (SMB dialect list, CLDAP baseline) are in the repo's ADRs.

Install: cargo install skewrun (or a static musl binary in releases, no toolchain needed)
Repo: https://github.com/JVBotelho/skewrun

Feedback and issues welcome, especially if anyone has actually captured traffic to confirm or refute the till finding, that part's still on my list.

github.com
u/FrozenSuricats — 9 hours ago