import gdb import subprocess import sys import re def debug(a,b=""): print(a,b)
def get_libc_path(): recv_data = gdb.execute("vmmap",to_string=True) lines = recv_data.split("\n") for line in lines: if 'libc' in line: string=line.split()[-1] string = string[:-4] return string return None
def get_gadget_info(library): """ 该函数作用是获取当前 libc 的 one_gadget信息 -l2除外 将每一组的 one_gadget 信息(地址和条件)放到一个元组里面,作为返回列表的一个元素
参数: library(str): 当前程序所依赖的 libc 库路径
返回值: gadgets_info(list): 列表中的元素是元组(装有一组 one_gadget 信息),后续进行条件判断时只需要对该列表进行遍历
""" result = subprocess.run(["one_gadget", library], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if (result.returncode != 0): print("Error: ", result.stderr.decode().strip()) sys.exit(result.returncode) gadgets = result.stdout.decode().strip().split("\n\n") gadgets_info = [] for gadget in gadgets: address_line, constraints_line = gadget.strip().split("\nconstraints:") address = address_line.strip().split()[0] constraints = constraints_line.strip().split('\n') gadgets_info.append((address, constraints)) return gadgets_info
def get_register_value(register): return gdb.parse_and_eval('$'+register)
def get_address_permissions(address): recv_data = gdb.execute("vmmap "+str(address),to_string=True) if "There are no mappings for specified address or module" in recv_data: return False recv_data=recv_data.split('\n')[1] recv_data=recv_data.strip().split() permissions=recv_data[3] if 'w' in permissions: return True else: return False
def is_got_address_of_libc(string): """ 该函数用来判断目标地址是否为 got_address_of_libc(这个地址是 libc 中具有 rw 权限的首地址)
参数: string(str):传入的为 one_gadget 需要判断是否为 got_address_of_libc的字符串 例如: "ebx is the GOT address of libc"
返回值: (bool):如果目标地址为 got_address_of_libc 那么返回 True ,反之返回 False """ match=re.findall('(\w+) is the GOT address of libc',string) register_value=get_register_value(match[0]) if int(register_value) < 0: register_value=register_value+(1<<32) recv_data = gdb.execute("vmmap",to_string=True) lines = recv_data.split("\n") for line in lines: if "rw" in line and "libc" in line: got_address_of_libc=line.split()[0][5:] if register_value == int(got_address_of_libc,16): return True else: return False
def is_writeable_check(string): ''' 这个函数的作用是来判断one_gadget中某个地址是否具有可写的权限
0xebcf5 execve("/bin/sh", r10, rdx) constraints: address rbp-0x78 is writable [r10] == NULL || r10 == NULL [rdx] == NULL || rdx == NULL 例如上面one_gadget的第一个条件 address rbp-0x78 is writable 该函数将自己获取rbp-0x78 (如果是单个寄存器也可以进行判断 比如判断rsi当前地址是否具有写的权限) 的地址判断其是否为一个具有写权限的地址
参数: string(str):该参数是one_gadget关于某个地址是否为可写的条件字符串 例如“address rbp-0x78 is writable” 返回值: (bool): 判断目标地址是否具有可写权限 如果具有写权限则返回True 反之返回False ''' result = re.findall(r'(\w+)\s*([+-])\s*(\w+)', string) if not result: result = re.findall(r'\b\w+\b', string) result = result[1] if len(result)==1:
result=result[0] register=result[0] operator=result[1] operand=result[2] if operator == '-': calc_value=int(get_register_value(register))-int(operand,16) if operator == '+': calc_value=int(get_register_value(register))+int(operand,16) if calc_value == 0: return False return get_address_permissions(calc_value) if len(result)==3: register_value=get_register_value(result) if register_value==0: return False return get_address_permissions(register_value)
def get_register_value_ptr(register): """ 该函数作用获取寄存器所指向的值
参数: register(str):被访问的寄存器名称
返回值: (int):如果寄存器值为0或者寄存器的值为非法地址则返回-1 否则返回寄存器所指向的值 """ address = gdb.parse_and_eval("$" + register) if address == 0: return -1 try: if register[0] == 'r': value = gdb.selected_inferior().read_memory(address, 8) if register[0] == 'e': value = gdb.selected_inferior().read_memory(address, 4) except gdb.MemoryError: return -1 return int.from_bytes(value, byteorder='little')
def condition_equal_A(condition_list): """rsp & 0xf == 0""" condition_list=condition_list[0] register,operator,operand=condition_list[0],condition_list[1],condition_list[2] if operator == '&': calc_value=int(get_register_value(register)) & int(operand,16) if calc_value == 0: return True else: return False
def condition_equal_B(condition_list): """rcx == NULL""" register=condition_list[0] calc_value=int(get_register_value(register)) if calc_value == 0: return True else: return False
def condition_equal_C(condition_list): """(u16)[rbp] == NULL""" condition_list=condition_list[0] condition=condition_list[0] register=condition_list[1] if condition == "u16": calc_value=int(get_register_value_ptr(register)) if calc_value == -1: return False calc_value=calc_value & 0xffff if calc_value == 0: return True else: return False
def condition_equal_D(condition_list): """[[rbp-0x70]] == NULL""" condition_list=re.findall(r"\[(\w+)([+-])(\w+)", condition_list[0]) condition_list=condition_list[0] register,operator,operand=condition_list[0],condition_list[1],condition_list[2] if operator == '-': calc_value = int(get_register_value(register)) - int(operand,16) if calc_value == 0 - int(operand,16): return False if operator == '+': calc_value = int(get_register_value(register)) + int(operand,16) if calc_value == 0 + int(operand,16): return False if calc_value == 0: return False try: if register[0] == 'r': calc_value = gdb.selected_inferior().read_memory(calc_value, 8) calc_value = int.from_bytes(calc_value, byteorder='little') if register[1] == 'e': calc_value = gdb.selected_inferior().read_memory(calc_value, 4) calc_value = int.from_bytes(calc_value, byteorder='little') except gdb.MemoryError: return False if calc_value ==0: return False try: if register[0] == 'r': calc_value = gdb.selected_inferior().read_memory(calc_value, 8) calc_value=int.from_bytes(calc_value, byteorder='little') if register[1] == 'e': calc_value = gdb.selected_inferior().read_memory(calc_value, 4) calc_value=int.from_bytes(calc_value, byteorder='little')
except gdb.MemoryError: return False if calc_value ==0: return True
def condition_equal_E(condition_list): """[r10] == NULL""" register=condition_list[0] calc_value =get_register_value_ptr(register) if calc_value == -1: return False if calc_value == 0: return True else: return False
def condition_equal_F(condition_list): """[esp+0x3c] == NULL""" condition_list=condition_list[0] register,operator,operand=condition_list[0],condition_list[1],condition_list[2] if operator == "+": calc_value=int(get_register_value(register) + int(operand,16)) if operator == "-": calc_value=int(get_register_value(register) - int(operand,16)) if calc_value == 0: return False try: if register[0] == 'r': calc_value = gdb.selected_inferior().read_memory(calc_value,8) calc_value = int.from_bytes(calc_value, byteorder='little') if register[0] == 'e': calc_value = gdb.selected_inferior().read_memory(calc_value,4) calc_value = int.from_bytes(calc_value, byteorder='little') except gdb.MemoryError: return False if calc_value == 0: return True else: return False
def equal_judgement(string): """ 该函数来处理 one_gadget 中的等式判断,我目前发现 one_gadget中的等式一共有六种类型(如下)
"rsp & 0xf == 0", "rcx == NULL", "(u16)[rbp] == NULL", "[[rbp-0x70]] == NULL", "[r10] == NULL", "[esp+0x3c] == NULL"
我通过正则表达式来识别出这六种情况,并将他们的关键信息匹配出来,再调用其对应的处理函数
参数: string(str): 等式判断条件的字符串,例如 "[esp+0x34] == NULL"
返回值: (bool): 如果等式成立则返回 True 不成立则返回 False
""" judgement=[] match=[] judgement = re.findall(r"(\w+)\s*(\&)\s*(\w+)", string) if judgement: return condition_equal_A(judgement)
judgement = re.findall(r'\((\w+)\)\[(\w+)\]', string) if judgement: return condition_equal_C(judgement)
judgement = re.findall(r'\[(.*?)\]', string) if judgement: if len(judgement[0].split('[')) > 1: return condition_equal_D(judgement) else: match = re.findall(r'(\w+)\s*([+-])\s*(.*)', judgement[0]) if match: return condition_equal_F(match)
judgement = re.findall(r"(\w+) == NULL", string) if judgement: return condition_equal_B(judgement)
judgement = re.findall(r"\[(\w+)\] == NULL",string) if judgement: return condition_equal_E(judgement)
def check(constraints): """ 该函数作用是将传入的每一组 one_gadget 按照条件进行分类,然后调用更具体的函数进行处理 目前我考虑了 one_gadget 的三种情况,分别是 is writeable 和 is got address of libc 以及对等式的判断 如果使用 l2 参数的话,会有更多的情况,我认为它们概率极小并且条件过于繁多,所以目前没有对它们进行判断 如果之后有条件没有考虑到需要添加的,或者想处理 l2 的 one_gadget 则在这里添加新的函数
参数: constraints(list):存储的是一组的 one_gadget 所有信息
返回值: (bool)如果当前这组 one_gadget 的所有条件都成立返回 True 反之有一个条件没有满足就返回 False
""" result = [] result1 = [] for constraint in constraints: if "is writable" in constraint: result.append(is_writeable_check(constraint)) continue if "is the GOT address of libc" in constraint: result.append(is_got_address_of_libc(constraint)) continue
if "||" in constraint: for i in constraint.split('||'): if "== 0" in i or "== NULL" in i: result1.append(equal_judgement(i)) result.append(any(result1)) result1 = [] continue
if "== 0" in constraint or "== NULL" in constraint: result.append(equal_judgement(constraint)) continue
print('one_gadget--->', result, constraints) return all(result)
def check_gadget_cmd(): """ 该函数为 check_gadget 命令的主函数 该命令实现了对当前位置概率略高的 one_gadget 能否生效做了判断
命令使用方法: 如果你想判断劫持执行流的这个位置是否有 one_gadget 能够生效,那么使用 gdb 调试到劫持执行流的地址处 使用该命令就可以看到是否有 one_gadget 能用了 目前只能判断概率较高的 one_gadget 能否使用,无法对 -l2 显示出来的 one_gadget 进行判断
check_gadget_cmd() 函数无参且无返回值 """
libc_path=get_libc_path() if libc_path == None: print("\033[1;31m"+"Program is a static link!"+"\033[0m\n") return all_gadget_info=get_gadget_info(libc_path) for i in range(len(all_gadget_info)): gadget_address,gadget_constraints=all_gadget_info[i][0],all_gadget_info[i][1] result=check(gadget_constraints) if result: print("\n\033[1;31m"+"="*120+"\033[0m") print("\033[1;31m"+"Successful one_gadget"+"\033[0m","\033[1;31m") print("\033[1;31m"+"gadget_address------->"+"\033[0m\t\t","\033[1;32m"+gadget_address+"\033[0m","\033[1;31m") print("\033[1;31m"+"gadget_info---------->"+"\033[0m\t\t","\033[1;32m"+str(gadget_constraints)+"\033[0m") print("\033[1;31m"+"="*120+"\033[0m\n")
gdb.execute("define check_gadget\n\tpython check_gadget_cmd()\nend")
|