question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Strange behavior with constraints on symbolic command-line argument

See original GitHub issue

I have this very simple C program with an obvious buffer-overflow on the stack:

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
    char buf[32];
    strcpy(buf,argv[1]);
    printf("Input:%s\n",buf);
    return 0;
}

I’ve compiled this with -fno-stack-protector and -z execstack.

I can smash the stack on this and gain control of rip with the input string python -c 'from struct import pack;print("A"*40 + str(pack("<Q", 0x424242424242), "utf-8"))'. I can also verify in gdb that rip gets set to 0x424242424242.

However, I cannot solve for this rip value in angr. But I am able to solve for rbp.

This is my code:

    proj = angr.Project("./csbo", exclude_sim_procedures_list=["strcpy"])

    # Make command-line arg symbolic
    num_input_chars = 64
    sym_commandline_arg = claripy.BVS("argv1", 8 * num_input_chars)

    state = proj.factory.entry_state(
        args=["csbo", sym_commandline_arg],
        add_options={
            angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY,
            angr.options.CONSTRAINT_TRACKING_IN_SOLVER
        }
    )

    # Looking for unconstrained paths
    sm = proj.factory.simulation_manager(state, save_unconstrained=True)

    # Step execution until we find an unconstrained path
    while sm.active and not sm.unconstrained:
        sm.step()

    # Make a copy of the state
    if not sm.unconstrained:
        raise Exception("Did not find any unconstrained paths!")

    s = sm.unconstrained[0].copy()

    pattern = 0x4142434445464748
    s.add_constraints(s.regs.rbp == pattern)
    assert s.satisfiable()

This works and I am also able to solve for a value of the command-line arg that will satisfy the constraints. However, when I add s.add_constraints(s.regs.rip == 0x0000424242424242), the constraints are no longer satisfiable. Why is that? There is clearly an input that can achieve that, so why is the solver not able to find it?

I notice that when I print out rip, I get <BV64 0x0 .. argv1_39_512[167:160] .. argv1_39_512[175:168] .. argv1_39_512[183:176] .. argv1_39_512[191:184]>, which shows that there are clearly bits that are influenced by the input.

I’m still learning how to use angr, so my apologies if I have missed something obvious.

EDIT: I noticed that when I use the value 0x0000000042424242 in the rip constraint, it works. Is there some implicit range for the rip that angr uses?

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:14 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
rhelmotcommented, Jun 6, 2019

Yep, that’s what I did. Here’s my script:

#!/usr/bin/env python

def main():
    import angr
    import claripy

    # !!! This is on a binary that has no stack protection and stack is executable !!!
    # Tell angr to go through strcpy
    proj = angr.Project("./csbo", exclude_sim_procedures_list=["strcpy"])

    input_buf_size = 32

    # Make command-line arg symbolic
    input_length = input_buf_size * 2
    argv1 = claripy.BVS("argv1", 8 * input_length)

    options = {
        angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
        angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS,
        angr.options.CONSTRAINT_TRACKING_IN_SOLVER
    }
    options.update(angr.options.unicorn)
    state = proj.factory.full_init_state(
        args=["csbo", argv1],
        add_options=options,
        remove_options={
            angr.options.LAZY_SOLVES,
            angr.options.SYMBOLIC_MEMORY_NO_SINGLEVALUE_OPTIMIZATIONS
        }
    )

    constraints = []

    # I didn't use argv1.chop(8) because I only want the non-null constraints on rbp
    # and lower 6 bytes of rip.
    for i in range(0, input_buf_size + 8 + 6):
        byte = argv1.get_byte(i)
        state.solver.add(byte != 0)
        constraints.append(byte != 0)
    state.libc.buf_symbolic_bytes = input_length + 1

    # Looking for unconstrained paths
    sm = proj.factory.simulation_manager(state, save_unconstrained=True)

    while True:
        # Step execution until we find unconstrained paths
        while sm.active and not sm.unconstrained:
            sm.step()

        print(sm)

        # Make a copy of the state
        if not sm.unconstrained:
            raise Exception("Uh oh! Did not find any unconstrained paths!")
        s = sm.unconstrained.pop().copy()

        rbp_pattern = 0x4142434445464748
        rip_pattern = 0x00007fffffffd831
        s.add_constraints(s.regs.rbp == rbp_pattern)
        s.add_constraints(s.regs.rip == rip_pattern)

        #good = s.satisfiable(extra_constraints=constraints)
        good = s.satisfiable()
        if good:
            break

    print("solution rip", hex(s.solver.eval(s.regs.rip)))
    print("solution rbp", hex(s.solver.eval(s.regs.rbp)))
    solution = s.solver.eval(argv1, cast_to=bytes)
    print("Command-line arg to hijack rip:", solution)
    print("Hijacked-value to be placed at offset:", solution.index(rip_pattern.to_bytes(8, 'little')))

if __name__ == '__main__':
    main()
1reaction
rhelmotcommented, Jun 6, 2019

I found that the difference between pypi and master for this testcase is caused by https://github.com/angr/claripy/commit/4ed61924880af1ea8fb778047d896ec0156412a6. This fixes a bug which caused inconsistent results to come out of the solver. There should be a new pypi release fairly soon.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Whitebox Fuzz Testing in Production - CiteSeerX
Starting with a well-formed input, whitebox fuzzing consists of symbolically executing the program under test dynamically, gathering constraints on inputs from ...
Read more >
fixlog - The Netlib
Fix memory allocation bug that bit when a constraint was partially dropped (or a variable partially fixed) before, e.g., solve; option linelim 1;...
Read more >
MultiOtter: Multiprocess Symbolic Execution
One key point for multiprocess symbolic execution is that all processes must share the same constraints on symbolic values.
Read more >
Using LD, the GNU linker - Invocation
This is to prevent gaps between symbols due to alignment constraints. This option disables that sorting. -split-by-reloc count: Trys to creates extra sections ......
Read more >
A User's Guide to gringo, clasp, clingo, and iclingo
3.1.1 Normal Programs and Integrity Constraints . ... tion) or with flag --help (to see the available command line options). For instance,.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found