BookStack Security Testing: Default Credentials, Image Upload SSRF, API Token Exposure, and Content Export

BookStack is a widely deployed open-source PHP-based documentation platform organizing content in a book/chapter/page hierarchy, used by SMEs and enterprises as an internal knowledge base and IT documentation system. Its security profile has several well-known characteristics: BookStack ships with default credentials documented in the official installation guide — admin@admin.com / password — which are frequently unchanged in self-hosted deployments particularly when installed from one-click deployment scripts; BookStack's image upload pipeline processes images with PHP's GD library and optionally ImageMagick — ImageMagick-based deployments are historically vulnerable to SSRF via MVG format coercions (ImageTragick class vulnerabilities) when policy.xml restrictions are not configured; BookStack provides a REST API authenticated with per-user API tokens created in user account settings — admin API tokens allow creating, reading, and deleting all books, chapters, and pages in the instance; the page content export feature generates PDFs using wkhtmltopdf or Puppeteer which fetch all referenced resources including external URLs in page content — attacker-controlled image URLs or CSS imports in page content can trigger SSRF through the export engine; and BookStack's Wysiwyg editor uses TinyMCE with server-side HTML sanitization — historical versions had insufficient sanitization allowing stored XSS through code block or raw HTML insertion. This guide covers systematic BookStack security assessment.

Table of Contents

  1. Default Credential Testing
  2. REST API Token Enumeration
  3. Image Upload SSRF Testing
  4. Export Pipeline SSRF
  5. BookStack Security Hardening

Default Credential Testing

# BookStack default credentials: admin@admin.com / password
# Documented in official install guide — commonly unchanged in quick installs

BOOKSTACK_URL="https://docs.example.com"

# Test default credentials against the BookStack login endpoint
RESPONSE=$(curl -s -c /tmp/bs_cookies.txt \
  "${BOOKSTACK_URL}/login" 2>/dev/null)
CSRF_TOKEN=$(echo "$RESPONSE" | grep -oP 'name="_token" value="\K[^"]+' | head -1)

LOGIN_RESPONSE=$(curl -s -X POST "${BOOKSTACK_URL}/login" \
  -c /tmp/bs_cookies.txt -b /tmp/bs_cookies.txt \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "_token=${CSRF_TOKEN}&email=admin%40admin.com&password=password" \
  -w "\n%{http_code}" 2>/dev/null)
CODE=$(echo "$LOGIN_RESPONSE" | tail -1)

if [ "$CODE" == "302" ] || [ "$CODE" == "200" ]; then
  # Check if we landed on the dashboard (not back to login)
  DASHBOARD=$(curl -s "${BOOKSTACK_URL}/" \
    -b /tmp/bs_cookies.txt 2>/dev/null | grep -c "Your Recent Pages\|Your Recent Books")
  if [ "$DASHBOARD" -gt 0 ]; then
    echo "DEFAULT CREDENTIALS WORK: admin@admin.com/password — full admin access"
  fi
fi

REST API Token Enumeration

# BookStack REST API — authenticated with token_id:token_secret
# API tokens are created in user Settings → API Tokens
# An admin's API token gives full read/write access to all content

BOOKSTACK_URL="https://docs.example.com"
TOKEN_ID="your-token-id"
TOKEN_SECRET="your-token-secret"

# List all books (demonstrates read scope of token)
curl -s "${BOOKSTACK_URL}/api/books" \
  -H "Authorization: Token ${TOKEN_ID}:${TOKEN_SECRET}" \
  2>/dev/null | python3 -c "
import json,sys
d = json.load(sys.stdin)
books = d.get('data',[])
print(f'Books accessible ({d.get(\"total\",0)} total):')
for b in books[:10]:
    print(f\"  [{b['id']}] {b['name']} — {b.get('description','no desc')[:50]}\")
" 2>/dev/null

# List all pages — may include sensitive internal documentation
curl -s "${BOOKSTACK_URL}/api/pages?count=100" \
  -H "Authorization: Token ${TOKEN_ID}:${TOKEN_SECRET}" \
  2>/dev/null | python3 -c "
import json,sys
d = json.load(sys.stdin)
pages = d.get('data',[])
print(f'Pages: {len(pages)} / {d.get(\"total\",0)}')
for p in pages[:10]:
    print(f\"  [{p['id']}] {p['name']} (book_id={p.get('book_id','?')})\")
" 2>/dev/null

# Read specific page content (may contain passwords, API keys, internal URLs)
PAGE_ID=1
curl -s "${BOOKSTACK_URL}/api/pages/${PAGE_ID}" \
  -H "Authorization: Token ${TOKEN_ID}:${TOKEN_SECRET}" \
  2>/dev/null | python3 -c "
import json,sys
d = json.load(sys.stdin)
print(f'Page: {d.get(\"name\",\"?\")}')
print(f'HTML content (first 200 chars): {d.get(\"html\",\"\")[:200]}')
" 2>/dev/null

BookStack Security Hardening

BookStack Security Hardening Checklist:
Security TestMethodRisk
Default admin@admin.com/password credentials unchangedPOST /login with default credentials — full admin access to all documentation including sensitive contentCritical
REST API token with admin scopeGET /api/pages with admin token — reads all pages including content with embedded credentials and internal URLsCritical
ImageMagick SSRF via MVG/MSL file uploadUpload image with MVG header containing url: delegate — triggers server-side HTTP request to attacker URLHigh
PDF export SSRF via page content URLsCreate page with external resource URL — export to PDF triggers wkhtmltopdf fetch of attacker-controlled URLHigh
Stored XSS via raw HTML in page contentInsert script tag via HTML editor or API page creation — executes in context of all users who view the pageHigh
Unauthenticated page access on public instancesGET /books or /pages without login — public BookStack instances expose all documentation without authenticationHigh

Automate BookStack Security Testing

Ironimo tests BookStack deployments for default admin@admin.com/password credentials unchanged from installation defaults, REST API token access providing full book and page content enumeration including sensitive documentation, ImageMagick MVG/MSL SSRF via the image upload endpoint on deployments using ImageMagick without policy.xml restrictions, PDF export pipeline SSRF via page content URLs fetched by wkhtmltopdf, stored XSS via raw HTML insertion in page content, and unauthenticated access to all documentation on publicly accessible BookStack instances.

Start free scan