Created
June 3, 2020 13:23
-
-
Save gteissier/8dfd606e2fafa4ed2495fe748c8a2a19 to your computer and use it in GitHub Desktop.
gdb Python script that adds root-single command to elevate a process given by the name of its executable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gdb | |
import re | |
from collections import namedtuple | |
DETAILS = { | |
# API 24 playstore | |
# API 25 playstore | |
'3.10.0+': (0xC0887D20, 0xC092138C, 316, 0xC0A7754C, 4), | |
# API 26 playstore | |
'3.18.56+': (0xC09F262A, 0xC0AD4208, 380, 0xC0C3428C, 4), | |
# API 27 playstore | |
'3.18.91+': (0xC09FC478, 0xC0AEE208, 380, 0xC0C4F28C, 4), | |
# API 28 playstore | |
'4.4.124+': (0xFFFFFFFF80BF5613, 0xFFFFFFFF80E0FBD8, 688, 0xFFFFFFFF8118CFD0, 8), | |
# API 29 playstore | |
'4.14.112+': (0xFFFFFFFF8100E970, 0xFFFFFFFF81415868, 680, 0xFFFFFFFF8169CDC8, 8), | |
} | |
Task = namedtuple('Task', 'addr real_cred comm') | |
class Rooter: | |
def __init__(self): | |
for version in DETAILS: | |
(version_addr, init_task_tasks, tasks_to_comm, selinux_enforcing, address_size) = DETAILS[version] | |
version_str = self.cstring(version_addr) | |
if version_str.startswith(version): | |
print('[+] kernel version confirmed to be %s' % version) | |
self.version = version | |
self.init_task_tasks = init_task_tasks | |
self.tasks_to_comm = tasks_to_comm | |
self.selinux_enforcing = selinux_enforcing | |
self.address_size = address_size | |
if self.address_size == 4: self.dereference_fmt = 'w' | |
elif self.address_size == 8: self.dereference_fmt = 'g' | |
return | |
raise Exception("kernel version has not been successfully identified") | |
def cstring(self, addr): | |
output = gdb.execute('x/s 0x%x' % (addr), to_string=True) | |
if 'error' in output: return '' | |
m = re.search(r'"(.*)"', output) | |
if not m: return '' | |
return m.group(1) | |
def dereference(self, addr): | |
output = gdb.execute('x/%sx 0x%x' % (self.dereference_fmt, addr), to_string=True) | |
if 'error' in output: return -1 | |
m = re.search(r'\t(0x[a-fA-F0-9]+)', output) | |
if not m: return -1 | |
return int(m.group(1), 0) | |
def get_tasks(self): | |
if self.tasks_to_comm == 0: | |
current_task = self.init_task_tasks | |
output = gdb.execute('find 0x%x,+1024,"swapper/0"' % (current_task), to_string=True) | |
print('[+] output = %r' % output) | |
m = re.search(r'(0x[a-fA-F0-9]+)\n', output, re.S) | |
if not m: | |
print('[!] there shall be something wrong, "swapper/0" could not be found near init_task address') | |
return [] | |
found_comm = int(m.group(1), 0) | |
found_distance = found_comm - self.init_task_tasks | |
print('[+] computed tasks_to_comm: 0x%x - 0x%x = %d' % (found_comm, self.init_task_tasks, found_distance)) | |
self.tasks_to_comm = found_distance | |
tasks = [] | |
current_task = self.init_task_tasks | |
while True: | |
comm = self.cstring(current_task + self.tasks_to_comm) | |
real_cred = self.dereference(current_task + self.tasks_to_comm - 2*self.address_size) | |
next_task = self.dereference(current_task) | |
tasks.append(Task(current_task, real_cred, comm)) | |
if next_task == self.init_task_tasks: | |
break | |
current_task = next_task | |
return tasks | |
def change_uids(self, task, uid): | |
for i in range(9): | |
uid_addr = task.real_cred + 4 + 4*i | |
gdb.execute('set *(unsigned int *) 0x%x = 0' % uid_addr) | |
def change_capabilities(self, task, caps): | |
for i in range(3): | |
cap_addr = task.real_cred + 0x30 + 8*i | |
gdb.execute('set *(unsigned long *) 0x%x = 0x%x' % (cap_addr, caps)) | |
def disable_selinux(self): | |
gdb.execute('set *(unsigned int *) 0x%x = 0' % (self.selinux_enforcing)) | |
class RootSingle(gdb.Command): | |
def __init__(self): | |
super().__init__('root-single', gdb.COMMAND_USER) | |
def invoke(self, args, tty): | |
args = gdb.string_to_argv(args) | |
if len(args) == 1: | |
name = args[0] | |
else: | |
print('usage: root-single <process name to elevate>') | |
return | |
rooter = Rooter() | |
tasks = rooter.get_tasks() | |
found_task = False | |
for task in tasks: | |
if name in task.comm: | |
print('[+] found matching task at 0x%x' % (task.addr)) | |
rooter.change_uids(task, 0) | |
print('[+] changed uids to 0') | |
rooter.change_capabilities(task, 0x3fffffffff) | |
print('[+] changed caps to 0x3fffffffff') | |
rooter.disable_selinux() | |
print('[+] changed selinux to permissive') | |
found_task = True | |
if not found_task: | |
print('[!] no task was found containing name "%s", please double check the task exists' % name) | |
RootSingle() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment