Thanks to visit codestin.com
Credit goes to lib.rs

#sudo #privileges #uac #admin #escalation

privesc

Small utility library for multi-platform privilege escalation

6 stable releases

Uses new Rust 2024

1.2.0 Dec 25, 2025
1.1.2 Dec 25, 2025
1.1.1 Dec 24, 2025
1.0.1 Dec 24, 2025
1.0.0 Dec 23, 2025

#197 in Authentication

50 downloads per month
Used in quincy-gui

MIT license

51KB
764 lines

privesc

Crates.io Documentation Build status License: MIT Matrix

Cross-platform privilege escalation for Rust.

Run commands with elevated privileges on macOS, Linux, and Windows.

Usage

There is explicit validation for the program parameter to ensure that it is an absolute path to an executable file.

This is done in order to ensure that the target executable has not been tampered with or is not a symlink to a malicious binary (PATH hijacking etc.)

use privesc::PrivilegedCommand;

fn main() -> privesc::Result<()> {
    let output = PrivilegedCommand::new("/usr/bin/cat")
        .arg("/etc/shadow")
        .run()?;

    if output.success() {
        if let Some(content) = output.stdout_str() {
            println!("{content}");
        }
    }

    Ok(())
}

With all options:

use privesc::PrivilegedCommand;

let output = PrivilegedCommand::new("/usr/bin/cat")
    .args(["/etc/shadow", "/etc/passwd"])
    .gui(true)
    .prompt("Reading protected files")
    .run()?;

Spawning without blocking

Use spawn() to start a privileged process and continue working while it runs:

use privesc::PrivilegedCommand;

fn main() -> privesc::Result<()> {
    let mut child = PrivilegedCommand::new("/usr/bin/long-task")
        .spawn()?;

    if let Some(id) = child.id() {
        println!("Process started with ID: {id}");
    }

    // Do other work while the process runs...

    // Check if done without blocking
    if let Some(status) = child.try_wait()? {
        println!("Already finished: {status}");
    }

    // Block until completion
    let output = child.wait()?;
    println!("Exit status: {}", output.status);

    Ok(())
}

Platform Behavior

Platform GUI mode CLI mode Output capture
macOS AppleScript dialog sudo Yes
Linux pkexec sudo Yes
Windows UAC prompt UAC prompt No

macOS

CLI mode (sudo): Arguments are passed directly via Rust's Command::args(), which uses execve under the hood. No shell is involved — arguments are passed as-is to the target program regardless of special characters.

GUI mode (osascript): Arguments flow through two stages:

  1. Rust → osascript: Uses Command::args() (no shell, safe)
  2. AppleScript → target: Uses quoted form of to escape each argument before passing to do shell script

AppleScript's quoted form of wraps arguments in single quotes and escapes embedded single quotes as '\''. This prevents shell interpretation of $, backticks, spaces, and other metacharacters.

Linux

CLI mode (sudo): Arguments are passed directly via Rust's Command::args(), which uses execve under the hood. No shell is involved — arguments are passed as-is to the target program regardless of special characters.

GUI mode (pkexec): Same as CLI mode (sudo)

Windows

CLI mode (UAC via ShellExecuteExW): The Windows API takes arguments as a single string, not an array. This library implements custom escaping following Windows command-line parsing conventions:

  • Regular executables: Arguments are escaped per CommandLineToArgvW rules (quotes, backslashes)
  • Batch files (.bat/.cmd): Additional escaping prevents %VAR% environment variable expansion and related injection vectors (addresses CVE-2024-24576 class vulnerabilities)

GUI mode (runas): Same as CLI mode (ShellExecuteExW)

Dependencies

~0.2–29MB
~472K SLoC