T03 · Remote Payload Retrieval and Execution
- Location
SKILL.md:24- Finding
Unverified Mutable Remote Installer Executed Directly by Bash
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This recovery skill mostly matches its stated purpose, but it gives agents and installers broad system-changing authority with unsafe remote installation and under-disclosed automatic repairs.
Review before installing. Prefer the bundled `scripts/sos.sh` over the GitHub pipe-to-bash path, avoid running `sos auto` as root on production or managed hosts, and use the read-only diagnostics first unless you are comfortable with service restarts, process kills, DNS changes, and temporary-file cleanup.
SKILL.md:24Unverified Mutable Remote Installer Executed Directly by Bash
scripts/install.sh:10Installer Downloads, Installs, and Executes an Unverified Remote Executable
scripts/sos.sh:121Automatic Recovery Deletes Unrelated Files Across the Shared Temporary Directory
scripts/sos.sh:507Autofix Replaces System DNS Configuration Without Administrator Confirmation
scripts/sos.sh:943Rollback Performs Global npm Installation from Insufficiently Validated Version Input
The description presents the skill primarily as a recovery/management utility for SOS, focused on installing, running, diagnosing, and fixing OpenClaw bot issues. The supplied code chunk instead performs a narrow and materially different action: uninstalling the sos command from the system. While uninstall might loosely fall under 'manage,' it is not stated in the declared purpose, and none of the recovery, diagnosis, autofix, rollback, network check, or Telegram test behaviors are present in this code. Therefore this code chunk does not accurately represent the declared description.
Piping curl output directly into bash executes network-delivered code immediately without giving the operator or agent a chance to inspect it. In this skill's context, the command may run with elevated privileges during emergency recovery, making compromise especially damaging and increasing the blast radius to full host takeover.
Alternative — install from GitHub:
curl -fsSL https://raw.githubusercontent.com/clawsos/claw-sos/main/install.sh | bash
Piping curl output directly into bash is a classic unsafe command-chaining pattern because it immediately executes remote content without validation. In this script's context, it can lead to arbitrary code execution and privileged system modification if the fetched installer or its source is compromised.
# ═══════════════════════════════════════════════════════════════
# Claw-SOS Installer
# ═══════════════════════════════════════════════════════════════
# Usage: curl -fsSL https://raw.githubusercontent.com/clawsos/claw-sos/main/install.sh | bash
# Or: wget -qO- https://raw.githubusercontent.com/clawsos/claw-sos/main/install.sh | bash
set -euo pipefail
Piping wget output into bash is equally dangerous because it combines network retrieval and code execution into one step with no review or verification. This creates a simple path for supply-chain compromise to become immediate local execution.
# Claw-SOS Installer
# ═══════════════════════════════════════════════════════════════
# Usage: curl -fsSL https://raw.githubusercontent.com/clawsos/claw-sos/main/install.sh | bash
# Or: wget -qO- https://raw.githubusercontent.com/clawsos/claw-sos/main/install.sh | bash
set -euo pipefail
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.
if [[ -z "$chat_id" ]]; then
# Try to get from recent updates
chat_id=$(timeout 5 curl -sf "https://api.telegram.org/bot${token}/getUpdates?limit=1" 2>/dev/null \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(d['result'][-1]['message']['chat']['id'])" 2>/dev/null || echo "")
if [[ -z "$chat_id" ]]; then
echo -e "${RED}✗ Can't auto-detect chat ID. Please enter manually.${NC}"
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.
local msg="🔧 SOS Test Message | $timestamp | This confirms Telegram delivery is working."
echo -e "\n Sending test message..."
local result=$(timeout 10 curl -sf -X POST "https://api.telegram.org/bot${token}/sendMessage" \
-d "chat_id=${chat_id}" -d "text=${msg}" 2>/dev/null)
if echo "$result" | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['ok']" 2>/dev/null; then
Autofix silently overwrites /etc/resolv.conf with hard-coded public resolvers after a failed DNS check, without an immediate confirmation prompt at the moment of change. On production systems this can break managed DNS setups, bypass enterprise controls, or disrupt networking further, while making a destructive system change during an automated recovery flow.
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.
echo -e "${GREEN}Backed up:${NC}"
echo " Config → $BACKUP_DIR/pre-update-${TIMESTAMP}.json"
echo " Version → $CURRENT_VER"
ls -t $BACKUP_DIR/pre-update-*.json 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null
ls -t $BACKUP_DIR/pre-update-*.version 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null
echo -e "${GREEN}Old backups cleaned (keeping last 5).${NC}"
log "INFO" "Manual backup created: pre-update-${TIMESTAMP}"
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.
echo -e "${GREEN}Backed up:${NC}"
echo " Config → $BACKUP_DIR/pre-update-${TIMESTAMP}.json"
echo " Version → $CURRENT_VER"
ls -t $BACKUP_DIR/pre-update-*.json 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null
ls -t $BACKUP_DIR/pre-update-*.version 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null
echo -e "${GREEN}Old backups cleaned (keeping last 5).${NC}"
log "INFO" "Manual backup created: pre-update-${TIMESTAMP}"
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
fi
if [[ -f "${INSTALL_DIR}/${SCRIPT_NAME}" ]]; then
$SUDO rm -f "${INSTALL_DIR}/${SCRIPT_NAME}"
echo -e "${GREEN}✓ Removed ${INSTALL_DIR}/${SCRIPT_NAME}${NC}"
echo -e " Log file kept at: ~/.openclaw/backups/sos.log"
echo -e " Config backups kept at: ~/.openclaw/backups/"
The skill clearly instructs use of shell commands including local installation, remote SSH/SCP actions, and execution of a recovery script, but it declares no explicit tool scope or permissions. That omission weakens policy enforcement and increases the chance an agent could invoke impactful shell operations without clear authorization boundaries.
The trigger phrases are broad and overlap with common support language like 'bot is down,' 'not responding,' and 'emergency fix.' In an agent environment, that can cause over-invocation of a high-impact recovery skill, leading to unnecessary SSH actions, restarts, rollback, or destructive remediation in situations that only needed diagnosis.
Installing a command into /usr/local/bin creates persistence by adding a new executable into a standard path that may be invoked later by users or automation. In this skill's emergency-recovery context, such persistence is somewhat expected, but it still increases risk because the installed artifact is network-fetched and intended for repeated future use.
echo -e "${CYAN}╚═══════════════════════════════════════╝${NC}"
echo ""
# Check if running as root or can write to install dir
if [[ -w "$INSTALL_DIR" ]]; then
SUDO=""
elif command -v sudo &>/dev/null; then
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
SUDO=""
elif command -v sudo &>/dev/null; then
SUDO="sudo"
echo -e "${YELLOW}Need sudo to install to ${INSTALL_DIR}${NC}"
else
echo -e "${RED}Error: Can't write to ${INSTALL_DIR} and sudo not available.${NC}"
echo -e "${YELLOW}Try: INSTALL_DIR=~/.local/bin bash <(curl -fsSL ...)${NC}"
This line creates the installation directory with sudo when needed, contributing to privileged filesystem modification. In the context of an installer that later places a remotely fetched executable there, this privileged directory creation helps enable persistence in a trusted execution path.
# Allow custom install dir
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
mkdir -p "$INSTALL_DIR" 2>/dev/null || $SUDO mkdir -p "$INSTALL_DIR"
# Detect OS
OS="$(uname -s)"
This command fetches remote content and writes it directly to an executable location, potentially under sudo. That creates a straightforward supply-chain and remote-code-execution risk because mutable network content is being trusted as a local command without verification.
# Download
echo -ne " Downloading sos.sh... "
if command -v curl &>/dev/null; then
$SUDO curl -fsSL "$RAW_URL" -o "${INSTALL_DIR}/${SCRIPT_NAME}"
elif command -v wget &>/dev/null; then
$SUDO wget -qO "${INSTALL_DIR}/${SCRIPT_NAME}" "$RAW_URL"
else
The installer downloads a remote script from GitHub and writes it directly into a system executable path, potentially using sudo, without any integrity verification or interactive confirmation. If the remote content, branch, repository, or network path is compromised, the user can silently install attacker-controlled code with elevated privileges.
This wget variant has the same security issue as the curl path: it writes unverified remote content into a trusted local executable path, potentially with elevated privileges. An attacker who can influence the fetched content gains code execution and persistence when the installed command is later run.
if command -v curl &>/dev/null; then
$SUDO curl -fsSL "$RAW_URL" -o "${INSTALL_DIR}/${SCRIPT_NAME}"
elif command -v wget &>/dev/null; then
$SUDO wget -qO "${INSTALL_DIR}/${SCRIPT_NAME}" "$RAW_URL"
else
echo -e "${RED}Error: curl or wget required${NC}"
exit 1
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
local svc_type=$(detect_service)
if [[ "$action" == "start" && "$svc_type" == "none" ]]; then
if [[ "$OS_TYPE" == "macos" ]]; then
# Check for plist even if not currently loaded
if ls ~/Library/LaunchAgents/*openclaw* &>/dev/null 2>&1 || ls /Library/LaunchDaemons/*openclaw* &>/dev/null 2>&1; then
svc_type="launchctl"
fi
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
local svc_type=$(detect_service)
if [[ "$action" == "start" && "$svc_type" == "none" ]]; then
if [[ "$OS_TYPE" == "macos" ]]; then
# Check for plist even if not currently loaded
if ls ~/Library/LaunchAgents/*openclaw* &>/dev/null 2>&1 || ls /Library/LaunchDaemons/*openclaw* &>/dev/null 2>&1; then
svc_type="launchctl"
fi
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
local svc_type=$(detect_service)
if [[ "$action" == "start" && "$svc_type" == "none" ]]; then
if [[ "$OS_TYPE" == "macos" ]]; then
# Check for plist even if not currently loaded
if ls ~/Library/LaunchAgents/*openclaw* &>/dev/null 2>&1 || ls /Library/LaunchDaemons/*openclaw* &>/dev/null 2>&1; then
svc_type="launchctl"
fi
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
local svc_type=$(detect_service)
if [[ "$action" == "start" && "$svc_type" == "none" ]]; then
if [[ "$OS_TYPE" == "macos" ]]; then
# Check for plist even if not currently loaded
if ls ~/Library/LaunchAgents/*openclaw* &>/dev/null 2>&1 || ls /Library/LaunchDaemons/*openclaw* &>/dev/null 2>&1; then
svc_type="launchctl"
fi
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
local svc_type=$(detect_service)
if [[ "$action" == "start" && "$svc_type" == "none" ]]; then
if [[ "$OS_TYPE" == "macos" ]]; then
# Check for plist even if not currently loaded
if ls ~/Library/LaunchAgents/*openclaw* &>/dev/null 2>&1 || ls /Library/LaunchDaemons/*openclaw* &>/dev/null 2>&1; then
svc_type="launchctl"
fi
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
local svc_type=$(detect_service)
if [[ "$action" == "start" && "$svc_type" == "none" ]]; then
if [[ "$OS_TYPE" == "macos" ]]; then
# Check for plist even if not currently loaded
if ls ~/Library/LaunchAgents/*openclaw* &>/dev/null 2>&1 || ls /Library/LaunchDaemons/*openclaw* &>/dev/null 2>&1; then
svc_type="launchctl"
fi
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
local svc_type=$(detect_service)
if [[ "$action" == "start" && "$svc_type" == "none" ]]; then
if [[ "$OS_TYPE" == "macos" ]]; then
# Check for plist even if not currently loaded
if ls ~/Library/LaunchAgents/*openclaw* &>/dev/null 2>&1 || ls /Library/LaunchDaemons/*openclaw* &>/dev/null 2>&1; then
svc_type="launchctl"
fi
No suspicious patterns detected.