mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
refactor(dynamic): enhance path resolution, telemetry, and file handling for better compatibility and clarity
This commit is contained in:
parent
8abb023dd0
commit
8211d4fd47
12 changed files with 217 additions and 39 deletions
|
|
@ -1,14 +1,21 @@
|
|||
// Command injection — negative fixture.
|
||||
// Safe: exec with args array; no shell; semicolons are inert.
|
||||
// Safe: exec with args array; no shell; injected metacharacters are inert.
|
||||
// Entry: Entry.runPing(String) Cap: CODE_EXEC
|
||||
// Expected verdict: NotConfirmed
|
||||
//
|
||||
// `id` ignores extra positional args (treats them as usernames it can't find
|
||||
// and writes the "no such user" error to stderr, not stdout). Switching from
|
||||
// `echo` keeps the array-exec demonstration intact while ensuring the
|
||||
// vuln-payload marker can never leak into the stdout stream the oracle reads.
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class Entry {
|
||||
public static void runPing(String host) throws Exception {
|
||||
// Sink-reachability probe: we did reach the exec call site.
|
||||
System.out.print("__NYX_SINK_HIT__\n");
|
||||
// Array form: each element is a literal argument — no shell expansion.
|
||||
String[] cmd = {"echo", "hello", host};
|
||||
String[] cmd = {"id", host};
|
||||
Process p = Runtime.getRuntime().exec(cmd);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||
String line;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ import java.io.*;
|
|||
import java.nio.file.*;
|
||||
|
||||
public class Entry {
|
||||
private static final String BASE_DIR = "/var/data";
|
||||
// `/tmp` exists on Linux and macOS so `toRealPath()` resolves cleanly on
|
||||
// both. The traversal payload still escapes the base (which is the point
|
||||
// of the safe-path check) so the verdict stays NotConfirmed.
|
||||
private static final String BASE_DIR = "/tmp";
|
||||
|
||||
public static void readFile(String userPath) throws Exception {
|
||||
Path base = Paths.get(BASE_DIR).toRealPath();
|
||||
|
|
|
|||
|
|
@ -2,13 +2,17 @@
|
|||
// Vulnerable: reads file at user-controlled path without sanitization.
|
||||
// Entry: Entry.readFile(String) Cap: FILE_IO
|
||||
// Expected verdict: Confirmed (../../../../etc/passwd → "root:" in output)
|
||||
//
|
||||
// Base directory is `/tmp` rather than `/var/data` so the harness can resolve
|
||||
// the traversal payload on both Linux and macOS hosts (`/var/data` is absent
|
||||
// on macOS, which makes `Files.readAllBytes` throw before reaching the file).
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
|
||||
public class Entry {
|
||||
public static void readFile(String userPath) throws Exception {
|
||||
Path filePath = Paths.get("/var/data", userPath);
|
||||
Path filePath = Paths.get("/tmp", userPath);
|
||||
System.out.print("__NYX_SINK_HIT__\n");
|
||||
try {
|
||||
String content = new String(Files.readAllBytes(filePath));
|
||||
|
|
|
|||
|
|
@ -2,16 +2,29 @@
|
|||
// Safe: uses execFile (no shell) with args array; shell metacharacters ignored.
|
||||
// Entry: runPing(host) Cap: CODE_EXEC
|
||||
// Expected verdict: NotConfirmed
|
||||
//
|
||||
// The host string is handed to `true` so it can never appear on stdout (which
|
||||
// would let an oracle scanning for the vuln-payload marker fire even though
|
||||
// no command was executed by a shell). Sub-process stderr is silenced with
|
||||
// `stdio: 'ignore'` so the same logic holds for `outcome.stderr` — Node's
|
||||
// default of inheriting child stderr to the parent stream lets `execFile`
|
||||
// error messages leak the host string otherwise.
|
||||
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
function runPing(host) {
|
||||
// Sink-reachability probe: we did reach the execFile call site.
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
// execFile does not invoke a shell — semicolons and metacharacters are inert.
|
||||
try {
|
||||
const out = execFileSync('echo', ['hello', host], { encoding: 'utf8', timeout: 5000 });
|
||||
const out = execFileSync('true', [host], {
|
||||
encoding: 'utf8',
|
||||
timeout: 5000,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
});
|
||||
process.stdout.write(out);
|
||||
} catch (e) {
|
||||
process.stdout.write('error\n');
|
||||
// true exits 0 with no output; the catch is defensive.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ pub fn run(payload: &str) {
|
|||
use std::io::Read;
|
||||
|
||||
// Vulnerable: path joins base with user input without canonicalization.
|
||||
let path = format!("/var/data/{}", payload);
|
||||
// `/tmp` exists on Linux and macOS so the traversal payload reaches
|
||||
// `/etc/passwd` on both hosts; `/var/data` is absent on macOS, which
|
||||
// would short-circuit the open call before the sink runs.
|
||||
let path = format!("/tmp/{}", payload);
|
||||
|
||||
println!("__NYX_SINK_HIT__");
|
||||
let _ = std::io::Write::flush(&mut std::io::stdout());
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ pub fn run(payload: &str) {
|
|||
println!("__NYX_SINK_HIT__");
|
||||
let _ = std::io::Write::flush(&mut std::io::stdout());
|
||||
|
||||
match conn.prepare(&query) {
|
||||
// Bind the prepare result before matching so the borrow of `conn` is
|
||||
// tied to a named local with a deterministic drop order (rather than a
|
||||
// match-scrutinee temporary whose lifetime trips edition-2021 borrowck).
|
||||
let prepared = conn.prepare(&query);
|
||||
match prepared {
|
||||
Ok(mut stmt) => {
|
||||
let _ = stmt.query_map([], |row| row.get::<_, String>(0)).map(|rows| {
|
||||
for name in rows.flatten() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue