Radicale Security Testing: Default Credentials, CalDAV, CardDAV, and Contact/Calendar Data

Radicale is a widely deployed lightweight self-hosted CalDAV and CardDAV server — it syncs contacts, calendars, and tasks between devices without third-party cloud services. Key assessment areas: Radicale defaults to no authentication in some configurations making all contact and calendar data publicly accessible; the htpasswd credential file supports weak hash formats including plaintext and MD5; all data is stored as plaintext vCard (.vcf) and iCalendar (.ics) files on the server filesystem; and the WebDAV protocol exposes a PROPFIND method for directory enumeration. This guide covers systematic Radicale security assessment.

Table of Contents

  1. Authentication Configuration and No-Auth Testing
  2. CalDAV and CardDAV Data Enumeration
  3. Filesystem Contact and Calendar Data Extraction
  4. Radicale Security Hardening

Authentication Configuration and No-Auth Testing

# Radicale — authentication configuration and no-auth testing
RADICALE_URL="https://cal.example.com"

# Test if Radicale is running without authentication
# A PROPFIND to root reveals collections without credentials
curl -s -X PROPFIND "${RADICALE_URL}/" \
  -H "Depth: 1" \
  -H "Content-Type: text/xml" \
  -d '' \
  -o /dev/null -w "%{http_code}" 2>/dev/null
# 207 without credentials = auth=none configured (unauthenticated access)
# 401 = auth required (htpasswd or remote-user)

# Enumerate all users and their collections
curl -s -X PROPFIND "${RADICALE_URL}/" \
  -H "Depth: 1" \
  -H "Content-Type: text/xml" \
  -d '' \
  2>/dev/null | grep -oP '[^<]+' | sed 's|<[^>]*>||g'

# htpasswd credential testing — common weak passwords
cat /etc/radicale/htpasswd 2>/dev/null | head -10
# Format: username:$apr1$SALT$HASH (MD5) or username:PLAINTEXT
# MD5 format is crackable with hashcat -m 1600

# Read Radicale config to check auth type
cat /etc/radicale/config 2>/dev/null | grep -A5 '\[auth\]'
# type = none — no authentication
# type = htpasswd — file-based auth
# htpasswd_encryption = md5 or plain — weak formats

CalDAV and CardDAV Data Enumeration

# Radicale CalDAV/CardDAV data enumeration
RADICALE_URL="https://cal.example.com"
USER="user"
PASS="password"

# Enumerate all calendars for a user
curl -s -X PROPFIND "${RADICALE_URL}/${USER}/" \
  -u "${USER}:${PASS}" \
  -H "Depth: 1" \
  -H "Content-Type: text/xml" \
  -d '' \
  2>/dev/null | python3 -c "
import sys, re
data = sys.stdin.read()
hrefs = re.findall(r'<[^:]*:href>([^<]+)', data)
names = re.findall(r'<[^:]*:displayname>([^<]+)', data)
print('Collections:')
for h in hrefs:
    print(f'  {h}')
" 2>/dev/null

# Download all vCard contacts
curl -s "${RADICALE_URL}/${USER}/contacts/" \
  -u "${USER}:${PASS}" \
  -X REPORT \
  -H "Content-Type: text/xml" \
  -d '' \
  2>/dev/null | python3 -c "
import sys, re
data = sys.stdin.read()
# Extract vCard data
vcards = re.findall(r'BEGIN:VCARD.*?END:VCARD', data, re.DOTALL)
print(f'Contacts: {len(vcards)}')
for vc in vcards[:5]:
    fn = re.search(r'FN:(.*)', vc)
    tel = re.findall(r'TEL[^:]*:(.*)', vc)
    email = re.findall(r'EMAIL[^:]*:(.*)', vc)
    print(f'  Name: {fn.group(1) if fn else \"?\"}')
    print(f'  Phone: {tel[:2]}')
    print(f'  Email: {email[:2]}')
" 2>/dev/null

# Download all calendar events
curl -s "${RADICALE_URL}/${USER}/calendar/" \
  -u "${USER}:${PASS}" \
  -X REPORT \
  -H "Content-Type: text/xml" \
  -d '' \
  2>/dev/null | python3 -c "
import sys, re
data = sys.stdin.read()
events = re.findall(r'BEGIN:VEVENT.*?END:VEVENT', data, re.DOTALL)
print(f'Calendar events: {len(events)}')
for ev in events[:5]:
    summary = re.search(r'SUMMARY:(.*)', ev)
    dtstart = re.search(r'DTSTART[^:]*:(.*)', ev)
    location = re.search(r'LOCATION:(.*)', ev)
    attendees = re.findall(r'ATTENDEE[^:]*:mailto:(.*)', ev)
    print(f'  Event: {summary.group(1) if summary else \"?\"}')
    print(f'  Start: {dtstart.group(1) if dtstart else \"?\"}')
    print(f'  Location: {location.group(1) if location else \"\"}')
    print(f'  Attendees: {attendees[:3]}')
" 2>/dev/null

Filesystem Contact and Calendar Data Extraction

# Radicale filesystem data extraction

# Radicale stores all data as plaintext files
COLLECTIONS="/var/lib/radicale/collections"
# Or: /config/radicale/ (Docker) / ~/.config/radicale/collections/

ls "${COLLECTIONS}/" 2>/dev/null
# Directories named after usernames or paths

# Read all vCard contact files
find "${COLLECTIONS}" -name "*.vcf" 2>/dev/null | while read vcf; do
  echo "=== $vcf ==="
  cat "$vcf" | grep -E "^(FN|TEL|EMAIL|ADR|ORG|BDAY):" | head -10
done

# Read all iCalendar event files
find "${COLLECTIONS}" -name "*.ics" 2>/dev/null | while read ics; do
  echo "=== $ics ==="
  cat "$ics" | grep -E "^(SUMMARY|DTSTART|DTEND|LOCATION|ATTENDEE|DESCRIPTION):" | head -10
done

# Docker Radicale configuration
docker inspect radicale 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for c in d:
    for e in c.get('Config',{}).get('Env',[]):
        print(e)
    for m in c.get('Mounts',[]):
        print(f'Volume: {m.get(\"Source\")} -> {m.get(\"Destination\")}')
" 2>/dev/null

# If using htpasswd — check hash format and crack
cat /etc/radicale/htpasswd 2>/dev/null
# $apr1$ = MD5-APR (hashcat -m 1600)
# {SHA} prefix = SHA1 base64 (hashcat -m 101)
# No prefix = plaintext

# Crack MD5-APR htpasswd
# hashcat -m 1600 /etc/radicale/htpasswd /usr/share/wordlists/rockyou.txt

Radicale Security Hardening

Radicale Security Hardening Checklist:
Security TestMethodRisk
Unauthenticated PROPFIND collection enumerationPROPFIND / with Depth: 1 and no credentials — if auth=none, complete list of all user collections; discovery of all users and their calendars/address books without authenticationCritical
vCard contact data extractionREPORT with addressbook-query — all contacts with names, phone numbers, email addresses, home addresses, and birthdays; complete personal address book extraction without database accessHigh
iCalendar event extractionREPORT with calendar-query — all calendar events with titles, times, locations, attendee email addresses, and private notes/descriptions; complete schedule and meeting history extractionHigh
htpasswd MD5 hash offline crackingRead /etc/radicale/htpasswd — MD5-APR hashes (hashcat -m 1600) are crackable with rockyou.txt in minutes for common passwords; SHA1 hashes even faster; bcrypt significantly more resistantHigh
Filesystem plaintext .vcf and .ics file accessRead /var/lib/radicale/collections/ — all contact and calendar data in plaintext files; no encryption at rest; file system access gives complete data for all usersHigh

Automate Radicale Security Testing

Ironimo tests Radicale deployments for unauthenticated PROPFIND collection enumeration (auth=none detection), vCard contact data extraction with phone number and address harvesting, iCalendar event attendee email enumeration, htpasswd MD5/SHA1 hash format identification and cracking resistance assessment, filesystem .vcf and .ics plaintext file access, per-user collection isolation boundary testing, HTTP Basic Auth over plain HTTP credential transmission detection, rights module owner_only bypass testing, and Docker RADICALE_CONFIG authentication configuration assessment.

Start free scan