Exemple #1
0
def main():
    parser = argparse.ArgumentParser(description="Follow a concrete trace")
    parser.add_argument("-f",
                        "--explore_from",
                        help="Value of PC from which to explore symbolically",
                        type=str)
    parser.add_argument(
        "-t",
        "--explore_to",
        type=str,
        default=sys.maxsize,
        help=
        "Value of PC until which to explore symbolically. (Probably don't want this set)",
    )
    parser.add_argument("--verbose",
                        "-v",
                        action="count",
                        default=0,
                        help="Increase verbosity")
    parser.add_argument(
        "cmd",
        type=str,
        nargs="+",
        help=
        'Program and arguments. Use "--" to separate script arguments from target arguments',
    )
    args = parser.parse_args(sys.argv[1:])

    range = None
    if args.explore_from:
        range = (args.explore_from, args.explore_to)

    # Create a concrete Manticore and record it
    m1 = Manticore.linux(args.cmd[0], args.cmd[1:])
    t = ExtendedTracer()
    r = TraceReceiver(t)
    m1.verbosity(args.verbose)
    m1.register_plugin(t)
    m1.register_plugin(r)
    m1.run(procs=1)

    time.sleep(3)

    # Create a symbolic Manticore and follow last trace
    symbolic_args = ["+" * len(arg) for arg in args.cmd[1:]]
    m2 = Manticore.linux(args.cmd[0], symbolic_args)
    f = Follower(r.trace)
    if range:
        f.add_symbolic_range(*range)
    m2.verbosity(args.verbose)
    m2.register_plugin(f)
    m2.run()
Exemple #2
0
    def test_thumb_mode_entrypoint(self):
        # thumb_mode_entrypoint is a binary with only one instruction
        #   0x1000: add.w   r0, r1, r2
        # which is a Thumb instruction, so the entrypoint is set to 0x1001
        m = Manticore.linux(
            os.path.join(os.path.dirname(__file__), "binaries",
                         "thumb_mode_entrypoint"))
        m.context["success"] = False

        @m.init
        def init(m, ready_states):
            for state in ready_states:
                state.platform.current.regfile.write("R0", 0)
                state.platform.current.regfile.write("R1", 0x1234)
                state.platform.current.regfile.write("R2", 0x5678)

        @m.hook(0x1001)
        def pre(state):
            # If the wrong PC value was used by the loader (0x1001 instead of 0x1000),
            # the wrong instruction bytes will have been fetched from memory
            state.abandon()

        @m.hook(0x1004)
        def post(state):
            # If the wrong execution mode was set by the loader, the wrong instruction
            # will have been executed, so the register value will be incorrect
            with m.locked_context() as ctx:
                ctx["success"] = state.cpu.regfile.read("R0") == 0x68AC
            state.abandon()

        m.run()
        self.assertTrue(m.context["success"])
Exemple #3
0
def symbolic_run_get_cons(trace):
    """
    Execute a symbolic run that follows a concrete run; return constraints generated
    and the stdin data produced
    """
    # mem: has no concurrency support. Manticore should be 'Single' process
    m2 = Manticore.linux(prog, workspace_url="mem:")
    f = Follower(trace)
    set_verbosity(VERBOSITY)
    m2.register_plugin(f)

    def on_term_testcase(mm, state, err):
        with m2.locked_context() as ctx:
            readdata = []
            for name, fd, data in state.platform.syscall_trace:
                if name in ("_receive", "_read") and fd == 0:
                    readdata.append(data)
            ctx["readdata"] = readdata
            ctx["constraints"] = list(state.constraints.constraints)

    m2.subscribe("will_terminate_state", on_term_testcase)

    m2.run()

    constraints = m2.context["constraints"]
    datas = m2.context["readdata"]

    return constraints, datas
Exemple #4
0
def symbolic_run_get_cons(trace):
    '''
    Execute a symbolic run that follows a concrete run; return constraints generated
    and the stdin data produced
    '''

    m2 = Manticore.linux(prog, workspace_url='mem:')
    f = Follower(trace)
    m2.verbosity(VERBOSITY)
    m2.register_plugin(f)

    def on_term_testcase(mcore, state, stateid, err):
        with m2.locked_context() as ctx:
            readdata = []
            for name, fd, data in state.platform.syscall_trace:
                if name in ('_receive', '_read') and fd == 0:
                    readdata.append(data)
            ctx['readdata'] = readdata
            ctx['constraints'] = list(state.constraints.constraints)

    m2.subscribe('will_terminate_state', on_term_testcase)

    m2.run()

    constraints = m2.context['constraints']
    datas = m2.context['readdata']

    return constraints, datas
Exemple #5
0
    def test_thumb_mode_entrypoint(self):
        # thumb_mode_entrypoint is a binary with only one instruction
        #   0x1000: add.w   r0, r1, r2
        # which is a Thumb instruction, so the entrypoint is set to 0x1001
        m = Manticore.linux(
            os.path.join(os.path.dirname(__file__), 'binaries',
                         'thumb_mode_entrypoint'))
        m.success = False

        @m.init
        def init(state):
            state.cpu.regfile.write('R0', 0)
            state.cpu.regfile.write('R1', 0x1234)
            state.cpu.regfile.write('R2', 0x5678)

        @m.hook(0x1001)
        def pre(state):
            # If the wrong PC value was used by the loader (0x1001 instead of 0x1000),
            # the wrong instruction bytes will have been fetched from memory
            state.abandon()

        @m.hook(0x1004)
        def post(state):
            # If the wrong execution mode was set by the loader, the wrong instruction
            # will have been executed, so the register value will be incorrect
            m.success = state.cpu.regfile.read('R0') == 0x68ac
            state.abandon()

        m.run()
        self.assertTrue(m.success)
Exemple #6
0
def concrete_run_get_trace(inp):
    m1 = Manticore.linux(prog, concrete_start=inp, workspace_url='mem:')
    t = ExtendedTracer()
    r = TraceReceiver(t)
    m1.verbosity(VERBOSITY)
    m1.register_plugin(t)
    m1.register_plugin(r)
    m1.run(procs=1)
    return r.trace
Exemple #7
0
def concrete_run_get_trace(inp):

    consts = config.get_group("core")
    consts.mprocessing = consts.mprocessing.single

    m1 = Manticore.linux(prog, concrete_start=inp, workspace_url="mem:")
    t = ExtendedTracer()
    # r = TraceReceiver(t)
    set_verbosity(VERBOSITY)
    m1.register_plugin(t)
    # m1.register_plugin(r)
    m1.run()
    for st in m1.all_states:
        return t.get_trace(st)
Exemple #8
0
    def test_symbolic_argv_envp(self):

        dirname = os.path.dirname(__file__)
        self.m = Manticore.linux(os.path.join(dirname, 'binaries', 'arguments_linux_amd64'), argv=['+'],
                                 envp={'TEST': '+'})

        for state in self.m.all_states:
            ptr = state.cpu.read_int(state.cpu.RSP + (8 * 2))  # get argv[1]
            mem = state.cpu.read_bytes(ptr, 2)
            self.assertTrue(issymbolic(mem[0]))
            self.assertEqual(mem[1], b'\0')

            ptr = state.cpu.read_int(state.cpu.RSP + (8 * 4))  # get envp[0]
            mem = state.cpu.read_bytes(ptr, 7)
            self.assertEqual(b''.join(mem[:5]), b'TEST=')
            self.assertEqual(mem[6], b'\0')
            self.assertTrue(issymbolic(mem[5]))
Exemple #9
0
    def test_symbolic_argv_envp(self) -> None:
        dirname = os.path.dirname(__file__)
        self.m = Manticore.linux(
            os.path.join(dirname, "binaries", "arguments_linux_amd64"),
            argv=["+"],
            envp={"TEST": "+"},
        )

        for state in self.m.all_states:
            ptr = state.cpu.read_int(state.cpu.RSP + (8 * 2))  # get argv[1]
            mem = state.cpu.read_bytes(ptr, 2)
            self.assertTrue(issymbolic(mem[0]))
            self.assertEqual(mem[1], b"\0")

            ptr = state.cpu.read_int(state.cpu.RSP + (8 * 4))  # get envp[0]
            mem = state.cpu.read_bytes(ptr, 7)
            self.assertEqual(b"".join(mem[:5]), b"TEST=")
            self.assertEqual(mem[6], b"\0")
            self.assertTrue(issymbolic(mem[5]))
Exemple #10
0
def main():
    parser = argparse.ArgumentParser(prog="sandshrew")

    # required arg group for help display
    required = parser.add_argument_group("required arguments")
    required.add_argument("-t",
                          "--test",
                          dest="test",
                          required=True,
                          help="Target binary for sandshrew analysis")

    # constraint configuration
    parser.add_argument(
        "-c",
        "--constraint",
        dest="constraint",
        required=False,
        help=
        "Constraint to apply to symbolic input. Includes ascii, alpha, num, or alphanum",
    )

    # debugging options
    parser.add_argument(
        "--debug",
        dest="debug",
        action="store_true",
        required=False,
        help="If set, turns on debugging output for sandshrew",
    )
    parser.add_argument(
        "--trace",
        dest="trace",
        action="store_true",
        required=False,
        help="If set, trace instruction recording will be outputted to logger",
    )

    # other configuration settings
    parser.add_argument(
        "--cmpsym",
        dest="cmp_sym",
        default="__strcmp_ssse3",
        required=False,
        help=
        "Overrides comparison function used to test for equivalence (default is strcmp)",
    )

    # parse or print help
    args = parser.parse_args()
    if args is None:
        parser.print_help()
        return 0

    # initialize verbosity
    if args.debug:
        logging.basicConfig(level=logging.DEBUG)

    # check binary arch support for x86_64
    if not binary_arch(args.test):
        raise NotImplementedError(
            "sandshrew only supports x86_64 binary concretization")

    # initialize Manticore
    m = Manticore.linux(args.test, ["+" * BUFFER_SIZE])
    m.verbosity(2)

    # initialize mcore context manager
    m.context["syms"] = binary_symbols(args.test)
    m.context["exec_flag"] = False
    m.context["argv1"] = None

    logging.debug(f"Functions for concretization: {m.context['syms']}")

    # add record trace hook throughout execution
    m.context["trace"] = []

    # initialize state by checking and storing symbolic argv
    @m.init
    def init(m, ready_states):

        logging.debug(f"Checking for symbolic ARGV")

        # determine argv[1] from state.input_symbols by label name
        argv1 = next(sym for sym in next(ready_states).input_symbols
                     if sym.name == "ARGV1")
        if argv1 is None:
            raise RuntimeException(
                "ARGV was not provided and/or made symbolic")

        # store argv1 in global state
        with m.locked_context() as context:
            context["argv1"] = argv1

    # store a trace counter, and output if arg was set
    @m.hook(None)
    def record(state):
        pc = state.cpu.PC
        if args.trace:
            print(f"{hex(pc)}")
        with m.locked_context() as context:
            context["trace"] += [pc]

    for sym in m.context["syms"]:

        @m.hook(m.resolve("SANDSHREW_" + sym))
        def concrete_checker(state):
            """
            initial checker hook for SANDSHREW_sym that checks for the presence of symbolic input.
            If so, an unconstrained hook is attached to the memory location to restore symbolic state after concretization
            """
            cpu = state.cpu

            with m.locked_context() as context:
                logging.debug(
                    f"Entering target function SANDSHREW_{sym} at {hex(state.cpu.PC)}"
                )

                # check if RSI, the assumed input arg, is symbolic
                data = cpu.read_int(cpu.RSI)
                if issymbolic(data):
                    logging.debug(
                        f"Symbolic input parameter to function {sym}() detected"
                    )

                    # store instruction after `call SANDSHREW_*`
                    return_pc = context["trace"][-1] + 5

                    # attach a hook to the return_pc, as this is where we will perform concolic execution
                    @m.hook(return_pc)
                    def unconstrain_hook(state):
                        """
                        unconstrain_hook writes unconstrained symbolic data to the memory location of the output.
                        """
                        with m.locked_context() as context:

                            # output param is RDI, symbolicate RAX
                            context["return_addr"] = cpu.RAX
                            logging.debug(
                                f"Writing unconstrained buffer to output memory location"
                            )

                            # initialize unconstrained symbolic input
                            return_buf = state.new_symbolic_buffer(BUFFER_SIZE)

                            # apply charset constraints based on user input
                            for i in range(BUFFER_SIZE):

                                if args.constraint == "alpha":
                                    state.constrain(
                                        operators.OR(
                                            operators.AND(
                                                ord("A") <= return_buf[i],
                                                return_buf[i] <= ord("Z")),
                                            operators.AND(
                                                ord("a") <= return_buf[i],
                                                return_buf[i] <= ord("z")),
                                        ))

                                elif args.constraint == "num":
                                    state.constrain(
                                        operators.AND(
                                            ord("0") <= return_buf[i],
                                            return_buf[i] <= ord("9")))

                                elif args.constraint == "alphanum":
                                    raise NotImplementedError(
                                        "alphanum constraint set not yet implemented"
                                    )

                                elif args.constraint == "ascii":
                                    state.constrain(
                                        operators.AND(
                                            ord(" ") <= return_buf[i],
                                            return_buf[i] <= ord("}")))

                            # write to address
                            state.cpu.write_bytes(context["return_addr"],
                                                  return_buf)

        @m.hook(m.resolve(sym))
        def concolic_hook(state):
            """
            hook used in order to concretize the execution of a `call <sym>` instruction
            """
            cpu = state.cpu

            with m.locked_context() as context:

                # store `call sym` instruction and ret val instruction
                call_pc = context["trace"][-1]

                # we are currently in the function prologue of `sym`. Let's go back to `call sym`.
                state.cpu.PC = call_pc

                # use the fallback emulator to concretely execute call instruction.
                logging.debug(
                    f"Concretely executing `call <{sym}>` at {hex(call_pc)}")
                state.cpu.decode_instruction(state.cpu.PC)
                emu = UnicornEmulator(state.cpu)
                emu.emulate(state.cpu.instruction)

                logging.debug("Continuing with Manticore symbolic execution")

    # TODO(alan): resolve ifunc (different archs use different optimized implementations)
    @m.hook(m.resolve(args.cmp_sym))
    def cmp_model(state):
        """
        used in order to invoke Manticore function model for strcmp and/or other comparison operation
        calls. While a developer can write a test case using a crypto library's built in
        constant-time comparison operation, it is preferable to use strcmp().
        """
        logging.debug("Invoking model for comparsion call")
        state.invoke_model(strcmp)

    @m.hook(m.resolve("abort"))
    def fail_state(state):
        """
        hook attached at fail state signified by abort call, which indicates that an edge case
        input is provided and the abort() call is made
        """

        logging.debug("Entering edge case path")

        # solve for the symbolic argv input
        with m.locked_context() as context:
            solution = state.solve_one(context["return_addr"], BUFFER_SIZE)
            print(f"Solution found: {solution}")

            # write solution to individual test case to workspace
            rand_str = lambda n: "".join(
                [random.choice(string.ascii_lowercase) for i in range(n)])
            with open(m.workspace + "/" + "sandshrew_" + rand_str(4),
                      "w") as fd:
                fd.write(str(solution))

        m.terminate()

    # run manticore
    m.run()
    print(
        f"Total instructions: {len(m.context['trace'])}\nLast instruction: {hex(m.context['trace'][-1])}"
    )
    return 0
Exemple #11
0
 def setUp(self):
     self.m = Manticore.linux(self.BIN_PATH)