mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-15 20:05:13 +02:00
[pitboss/grind] deferred session-0008 (20260516T052512Z-20f8)
This commit is contained in:
parent
f053665a83
commit
1ef650dc48
5 changed files with 292 additions and 11 deletions
|
|
@ -22,7 +22,7 @@
|
|||
//! tracks the history of incompatible corpus changes; bumping it invalidates
|
||||
//! all `dynamic_verdict_cache` entries whose spec touched the changed cap.
|
||||
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
use crate::dynamic::oracle::{ProbePredicate, SignalSet};
|
||||
use crate::labels::Cap;
|
||||
|
||||
/// Re-exported canonical [`Oracle`] type.
|
||||
|
|
@ -45,7 +45,8 @@ pub use crate::dynamic::oracle::Oracle;
|
|||
/// | 2 | 2025-12-15 | SSRF OOB-variant added; oracle semantics tightened |
|
||||
/// | 3 | 2026-05-12 | Migrated to `CuratedPayload`; provenance + fixture_paths enforced; SSRF OOB-nonce slot added |
|
||||
/// | 4 | 2026-05-14 | Phase 07: `benign_control` paired refs + benign payloads added to SQLI / CMDI / SSRF (file-scheme) |
|
||||
pub const CORPUS_VERSION: u32 = 4;
|
||||
/// | 5 | 2026-05-16 | FMT_STRING SinkCrash payload + benign control (Phase 08 unrelated-crash acceptance fixture) |
|
||||
pub const CORPUS_VERSION: u32 = 5;
|
||||
|
||||
/// Where a payload originated.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
@ -137,11 +138,11 @@ pub type Payload = CuratedPayload;
|
|||
/// | FILE_IO | yes | path traversal + benign control |
|
||||
/// | SSRF | yes | file:// scheme + OOB nonce slot |
|
||||
/// | HTML_ESCAPE | yes | XSS script marker + benign control |
|
||||
/// | FMT_STRING | yes | SinkCrash + benign control (Phase 08) |
|
||||
/// | ENV_VAR | no | source-only cap; no sink oracle |
|
||||
/// | SHELL_ESCAPE | no | sanitizer cap; no sink oracle |
|
||||
/// | URL_ENCODE | no | sanitizer cap; no sink oracle |
|
||||
/// | JSON_PARSE | no | no reliable oracle |
|
||||
/// | FMT_STRING | no | no reliable oracle |
|
||||
/// | DESERIALIZE | no | no reliable oracle |
|
||||
/// | CRYPTO | no | no reliable oracle |
|
||||
/// | UNAUTHORIZED_ID | no | auth bypass; no oracle |
|
||||
|
|
@ -160,13 +161,13 @@ const CORPUS_SUPPORTED: u32 = Cap::SQL_QUERY.bits()
|
|||
| Cap::CODE_EXEC.bits()
|
||||
| Cap::FILE_IO.bits()
|
||||
| Cap::SSRF.bits()
|
||||
| Cap::HTML_ESCAPE.bits();
|
||||
| Cap::HTML_ESCAPE.bits()
|
||||
| Cap::FMT_STRING.bits();
|
||||
|
||||
const CORPUS_UNSUPPORTED: u32 = Cap::ENV_VAR.bits()
|
||||
| Cap::SHELL_ESCAPE.bits()
|
||||
| Cap::URL_ENCODE.bits()
|
||||
| Cap::JSON_PARSE.bits()
|
||||
| Cap::FMT_STRING.bits()
|
||||
| Cap::DESERIALIZE.bits()
|
||||
| Cap::CRYPTO.bits()
|
||||
| Cap::UNAUTHORIZED_ID.bits()
|
||||
|
|
@ -201,6 +202,9 @@ pub fn payloads_for(cap: Cap) -> &'static [CuratedPayload] {
|
|||
if cap.contains(Cap::HTML_ESCAPE) {
|
||||
return XSS;
|
||||
}
|
||||
if cap.contains(Cap::FMT_STRING) {
|
||||
return FMT_STRING;
|
||||
}
|
||||
&[]
|
||||
}
|
||||
|
||||
|
|
@ -298,13 +302,14 @@ mod tests {
|
|||
assert!(!payloads_for(Cap::FILE_IO).is_empty());
|
||||
assert!(!payloads_for(Cap::SSRF).is_empty());
|
||||
assert!(!payloads_for(Cap::HTML_ESCAPE).is_empty());
|
||||
assert!(!payloads_for(Cap::FMT_STRING).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsupported_caps_return_empty() {
|
||||
let unsupported = [
|
||||
Cap::ENV_VAR, Cap::SHELL_ESCAPE, Cap::URL_ENCODE, Cap::JSON_PARSE,
|
||||
Cap::FMT_STRING, Cap::DESERIALIZE, Cap::CRYPTO, Cap::UNAUTHORIZED_ID,
|
||||
Cap::DESERIALIZE, Cap::CRYPTO, Cap::UNAUTHORIZED_ID,
|
||||
Cap::DATA_EXFIL, Cap::LDAP_INJECTION, Cap::XPATH_INJECTION,
|
||||
Cap::HEADER_INJECTION, Cap::OPEN_REDIRECT, Cap::SSTI, Cap::XXE,
|
||||
Cap::PROTOTYPE_POLLUTION,
|
||||
|
|
@ -329,12 +334,36 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn vuln_payloads_not_benign() {
|
||||
for cap in [Cap::SQL_QUERY, Cap::CODE_EXEC, Cap::FILE_IO, Cap::HTML_ESCAPE] {
|
||||
for cap in [
|
||||
Cap::SQL_QUERY, Cap::CODE_EXEC, Cap::FILE_IO, Cap::HTML_ESCAPE,
|
||||
Cap::FMT_STRING,
|
||||
] {
|
||||
let has_vuln = payloads_for(cap).iter().any(|p| !p.is_benign);
|
||||
assert!(has_vuln, "{cap:?} must have at least one vuln (non-benign) payload");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fmt_string_has_sink_crash_oracle_and_benign_control() {
|
||||
let payloads = payloads_for(Cap::FMT_STRING);
|
||||
let vuln = payloads
|
||||
.iter()
|
||||
.find(|p| !p.is_benign)
|
||||
.expect("FMT_STRING must have a vuln payload");
|
||||
assert!(
|
||||
matches!(vuln.oracle, Oracle::SinkCrash { .. }),
|
||||
"FMT_STRING vuln payload oracle must be SinkCrash (Phase 08)"
|
||||
);
|
||||
let bref = vuln
|
||||
.benign_control
|
||||
.expect("FMT_STRING vuln must reference a benign control");
|
||||
assert!(
|
||||
resolve_benign_control(vuln, Cap::FMT_STRING).is_some(),
|
||||
"FMT_STRING benign-control label '{}' must resolve",
|
||||
bref.label,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marker_uniqueness_sqli() {
|
||||
for p in SQLI {
|
||||
|
|
@ -345,7 +374,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn all_payloads_have_fixture_paths() {
|
||||
let caps = [Cap::SQL_QUERY, Cap::CODE_EXEC, Cap::FILE_IO, Cap::SSRF, Cap::HTML_ESCAPE];
|
||||
let caps = [
|
||||
Cap::SQL_QUERY, Cap::CODE_EXEC, Cap::FILE_IO, Cap::SSRF,
|
||||
Cap::HTML_ESCAPE, Cap::FMT_STRING,
|
||||
];
|
||||
for cap in caps {
|
||||
for p in payloads_for(cap) {
|
||||
assert!(
|
||||
|
|
@ -359,7 +391,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn all_payloads_have_valid_since_corpus_version() {
|
||||
let caps = [Cap::SQL_QUERY, Cap::CODE_EXEC, Cap::FILE_IO, Cap::SSRF, Cap::HTML_ESCAPE];
|
||||
let caps = [
|
||||
Cap::SQL_QUERY, Cap::CODE_EXEC, Cap::FILE_IO, Cap::SSRF,
|
||||
Cap::HTML_ESCAPE, Cap::FMT_STRING,
|
||||
];
|
||||
for cap in caps {
|
||||
for p in payloads_for(cap) {
|
||||
assert!(
|
||||
|
|
@ -442,7 +477,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn benign_entries_are_terminal() {
|
||||
let caps = [Cap::SQL_QUERY, Cap::CODE_EXEC, Cap::FILE_IO, Cap::SSRF, Cap::HTML_ESCAPE];
|
||||
let caps = [
|
||||
Cap::SQL_QUERY, Cap::CODE_EXEC, Cap::FILE_IO, Cap::SSRF,
|
||||
Cap::HTML_ESCAPE, Cap::FMT_STRING,
|
||||
];
|
||||
for cap in caps {
|
||||
for p in payloads_for(cap).iter().filter(|p| p.is_benign) {
|
||||
assert!(
|
||||
|
|
@ -666,3 +704,50 @@ const XSS: &[CuratedPayload] = &[
|
|||
benign_control: None,
|
||||
},
|
||||
];
|
||||
|
||||
// ── Format string (Phase 08 SinkCrash variant) ────────────────────────────────
|
||||
// The vuln payload confirms via a sink-site Crash probe rather than an
|
||||
// output marker. The bytes themselves are not load-bearing — the
|
||||
// detection contract is "process aborts inside the entry call AFTER the
|
||||
// crash-guard installs". Fixtures choose how to crash on the payload;
|
||||
// the canonical example is a printf-family sink that interprets `%n`
|
||||
// against a controlled destination pointer on a guard-compiled binary.
|
||||
//
|
||||
// The benign control must reach the same entry without crashing; its
|
||||
// bytes carry `NYX_BENIGN` so fixture code can short-circuit before
|
||||
// the fault path.
|
||||
const FMT_STRING: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"%n%n%n%n%n%n%n%n%n%n",
|
||||
label: "fmt-string-percent-n-crash",
|
||||
oracle: Oracle::SinkCrash {
|
||||
signals: SignalSet::all(),
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 5,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &[
|
||||
"tests/dynamic_fixtures/c/free_fn/sink_fault.c",
|
||||
"tests/dynamic_fixtures/c/free_fn/setup_fault.c",
|
||||
],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: Some(PayloadRef { label: "fmt-string-benign" }),
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"benign_safe_fmt_NYX_BENIGN",
|
||||
label: "fmt-string-benign",
|
||||
oracle: Oracle::SinkCrash {
|
||||
signals: SignalSet::all(),
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 5,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/c/free_fn/sink_fault.c"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ pub const NYX_VERSION: &str = env!("CARGO_PKG_VERSION");
|
|||
/// [`crate::dynamic::corpus::CORPUS_VERSION`]; the compile-time assertion
|
||||
/// below + the [`corpus_version_const_matches_corpus_module`] runtime test
|
||||
/// jointly guard drift.
|
||||
pub const CORPUS_VERSION: &str = "4";
|
||||
pub const CORPUS_VERSION: &str = "5";
|
||||
|
||||
/// Compile-time guard that pins [`CORPUS_VERSION`] (this module) to the
|
||||
/// textual form of [`crate::dynamic::corpus::CORPUS_VERSION`]. Bumping the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue