Skip to content

Commit

Permalink
Solves issues for binary arguments
Browse files Browse the repository at this point in the history
Issue: #3, #2, #1

Implements a Readme with a POC, solver binary arguments error created by
a space and is ready for merge with master

Signed-off-by: Phineas09 <[email protected]>
  • Loading branch information
Phineas09 committed Dec 20, 2022
1 parent ecbfeb0 commit 15d7e1f
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 44 deletions.
132 changes: 130 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,131 @@
# vulnerability_analytics
# `vulnerability_analytics`

TODO: Define requirements, examples and explanations.
---

- [Description](#description)
- [Limitations](#limitations)
- [How It Works](#how-it-works)
- [Setup](#setup)
- [Usage](#usage)
- [As a CLI Tool](#as-a-cli-tool)
- [Test Binary Crash](#test-binary-crash)
- [Main Help](#main-module-help)
- [Get Help](#get-command-help)
- [As a Python Module](#as-a-python-module)

---

## Description

`vulnerability_analytics` is the CRS module that explores a identified crash in order to gether more data in order to be used further by the CRS.

### Limitations

- ELF format
- Limited vulnerability support - See [Here](https://github.com/angr/rex/blob/3628781a044bc414a484ebb5ffd54061fc3a3cf8/rex/vulnerability.py)

## How It Works

This module executes the following steps for each given binary:
1. Creates a new Docker image containing that binary, this image will have a tag of form `{binaryName}:crash`
2. Sets up the EntryPoint of that new image to be the executable with user provided arguments (from the CLI).
3. Uses rex and archr in order to trace execution of the binary inside the Docker container.
4. Returns a JSON containing information at the moment of the crash like stackPointer, coreRegisters, crashType, etc.

## Setup

We highly encourage you to use docker as this module has lots of dependencies and it helps to keep your machine as clean as possible.

1. Ensure that you have `docker` installed.
2. Build the Docker image from the project root: `sudo docker build . -t vulnerability_analytics:1.0.0-alpha -f ./docker/Dockerfile`
3. Start a new container and give it access to the docker engine running on your machine: `sudo docker run -it -v /var/run/docker.sock:/var/run/docker.sock vulnerability_analytics:1.0.0-alpha bash`
4. Run the module using poetry: `/root/.local/bin/poetry vulnerability_analytics get --help`

**`Note:`** You can change the `$PATH` variable in order to acces poetry faster by running `export PATH="/root/.local/bin:$PATH"`

## Usage

### As a CLI Tool

#### Test Binary Crash

```bash
➜ /root/.local/bin/poetry run python vulnerability_analytics get --binary-path=./test_binary/source.bin --binary-arguments="--string,santa"

INFO | 2022-12-20 02:52:50,013 | rex.Crash | Filtering memory writes.
INFO | 2022-12-20 02:52:50,019 | rex.Crash | Triaging the crash.
INFO | 2022-12-20 02:52:50,023 | rex.Crash | Identifying bad_bytes
INFO | 2022-12-20 02:52:50,032 | crs.vulnerability_analytics | []
Press Enter to continue...
{"crashTypes": [], "functionAddress": "0x401136", "basicBlockAddress": "0x401136", "returnAddress": "0x500060", "stackPointer": "0x4000800ed8", "jumpKind": "Ijk_Call", "coreRegisters": {"r15": 274886537280, "r14": 0, "r13": 4198710, "r12": 274886299080, "rbp": 274886298800, "rbx": 0, "r11": 274886540016, "r10": 274886297952, "r9": 274886324288, "r8": 274888769296, "rax": 0, "rcx": 0, "rdx": 8, "rsi": 4202500, "rdi": 274886299495, "orig_rax": 0, "rip": 4198802, "cs": 51, "eflags": 582, "rsp": 274886298768, "ss": 43, "fs_base": 0, "gs_base": 0, "ds": 0, "es": 0, "fs": 0, "gs": 0}}
```

```bash
➜ /root/.local/bin/poetry run python vulnerability_analytics get --binary-path=./test_binary/hammer_controller.bin --binary-arguments="himinbjörg_is_home,loki_sucks_ass" --binary-input="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"

INFO | 2022-12-20 03:00:01,956 | rex.Crash | Preconstraining file stream <rex.preconstrained_file_stream.SimPreconstrainedFileStream object at 0x7f3d6040ee80> upon the first read().
WARNING | 2022-12-20 03:00:06,500 | angr.engines.successors | Exit state has over 256 possible solutions. Likely unconstrained; skipping. <BV64 aeg_input_default_5f_95_8 .. aeg_input_default_5e_94_8 .. aeg_input_default_5d_93_8 .. aeg_input_default_5c_92_8 .. aeg_input_default_5b_91_8 .. aeg_input_default_5a_90_8 .. aeg_input_default_59_89_8 .. aeg_input_default_58_88_8>
INFO | 2022-12-20 03:00:06,513 | rex.Crash | Filtering memory writes.
INFO | 2022-12-20 03:00:06,539 | rex.Crash | Triaging the crash.
INFO | 2022-12-20 03:00:06,548 | rex.Crash | detected ip overwrite vulnerability
INFO | 2022-12-20 03:00:06,551 | rex.Crash | Identifying bad_bytes
INFO | 2022-12-20 03:00:07,478 | crs.vulnerability_analytics | ['ip_overwrite']
Press Enter to continue...
{"crashTypes": ["ip_overwrite"], "functionAddress": "0x400086adc0", "basicBlockAddress": "0x400086adc0", "returnAddress": "0x40059a", "stackPointer": "0x4000800ed8", "jumpKind": "Ijk_Call", "coreRegisters": {"r15": 274886537280, "r14": 0, "r13": 4195927, "r12": 274886299032, "rbp": 4702111234474983745, "rbx": 0, "r11": 0, "r10": 0, "r9": 2147483647, "r8": 65535, "rax": 0, "rcx": 1, "rdx": 0, "rsi": 4196442, "rdi": 274886297280, "orig_rax": 0, "rip": 4702111234474983745, "cs": 51, "eflags": 518, "rsp": 274886298768, "ss": 43, "fs_base": 0, "gs_base": 0, "ds": 0, "es": 0, "fs": 0, "gs": 0}}
```

#### Main Module Help

```bash
➜ poetry run vulnerability_analytics

Usage: vulnerability_analytics [OPTIONS] COMMAND [ARGS]...

Displays information about a binary crash for given input.

Options:
--help Show this message and exit.

Commands:
get Displays information about the crash.
```

#### Get Command Help

```bash
➜ poetry run vulnerability_analytics get --help

Usage: vulnerability_analytics get [OPTIONS]

Displays information about the crash.

Options:
--binary-path TEXT Desired binary path. [required]
--binary-arguments TEXT Arguments provided to the binary, each argument
must be separated by comma and have no spaces if
none are required.
--binary-input TEXT Input to be provided to the binary after the
execution has started.
--base64 Marks that binary arguments and inpus are encoded
in base64.
--help Show this message and exit.
```
### As a Python Module
```python
from vulnerability_analytics.rex_api.DockerBuilder import DockerBuilder
from vulnerability_analytics.rex_api.VunlerabilityAnalysis import VulnerabilityAnalysis

image = DockerBuilder()

# image.build_custom_image(path_to_the_binary_to_be_analyzed, image_tag, binary_args - given at startup)
image.build_custom_image(
'{binary_path}', '{binary_name}' + ":crash", ['arg1', 'arg2'])

# VulnerabilityAnalysis(previouslt built image, provided_input_after_binary_has_started in bytes)
analyzer = VulnerabilityAnalysis(
image, bytes("{input}", 'utf-8')).start()

print(analyzer.export2JSON())
```
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ version = "0.1.0"

[tool.poetry.dependencies]
python = "^3.8"
angr = "^9.2.24"
angr = "9.2.24" # "^9.2.24"
docker = "^5.0.3"
click = "^8.1.3"
rich = "^12.5.1"
Expand Down
64 changes: 40 additions & 24 deletions vulnerability_analytics/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,65 @@

from array import array
import os
import base64 as b64
import click
from vulnerability_analytics.rex_api.DockerBuilder import *
from vulnerability_analytics.rex_api.VunlerabilityAnalysis import *
from archr.analyzers.rr import *

from vulnerability_analytics.rex_api.DockerBuilder import DockerBuilder
from vulnerability_analytics.rex_api.VunlerabilityAnalysis import VulnerabilityAnalysis

@click.group("cli")
def cli() -> None:
"""Displays information about a binary crash for given input."""

@cli.command("get", help="Displays information about the crash.")
@click.option("--binary-path", required=True, type=str, help="Desired binary path.")
@click.option("--binary-arguments", type=str)
@click.option("--binary-input", type=str)
@click.option("--binary-arguments", type=str, help="Arguments provided to the binary, each argument must be separated by comma and have no spaces if none are required.")
@click.option("--binary-input", type=str, help="Input to be provided to the binary after the execution has started.")
@click.option("--base64", is_flag=True, show_default=True, default=False, help="Marks that binary arguments and inpus are encoded in base64.")
def get(
binary_path: str = "",
binary_arguments: array = [],
binary_input: str = ""
binary_input: str = "",
base64: bool = False
) -> None:

image = DockerBuilder()
try:

print(binary_path, binary_arguments, binary_input, base64)

if binary_input is None and binary_arguments is None:
print('You must provide at least one type of argument.')
exit(0)

if binary_input is None:
binary_input = "Dummy binary_input"

# Maybe encode the crash_input into base64?
if(base64):
# Must have string
binary_arguments = b64.b64decode(binary_arguments).decode('utf-8')
binary_input = b64.b64decode(binary_input) # We will have bytes
else:
binary_input = bytes(binary_input, 'utf-8') # Must have bytes

binary_arguments_parsed = binary_arguments.split(',')
if binary_arguments is not None:
binary_arguments = binary_arguments.replace(" ", "")
binary_arguments_parsed = binary_arguments.split(',')

binary_name = os.path.basename(binary_path).split('.')[0]
binary_name = os.path.basename(binary_path).split('.')[0]

print(binary_path, binary_arguments, binary_input)
image = DockerBuilder()

# Parse other_arguments
image.build_custom_image(
binary_path, binary_name + ":crash", binary_arguments_parsed)

# print(binary_name)
# print(binary_arguments)
out_string = binary_name + ";" + binary_arguments
# print(out_string.replace(" ", ""))
# exit(0)
# Parse other_arguments
image.build_custom_image(
binary_path, binary_name + ":crash", binary_arguments_parsed)
analyzer = VulnerabilityAnalysis(
image, binary_input).start()
input("Press Enter to continue...")
print(analyzer.export2JSON())

analyzer = VulnerabilityAnalysis(
image, bytes(binary_input, 'utf-8')).start()
input("Press Enter to continue...")
print(analyzer.export2JSON())
# Unknown error handler
except Exception as exception:
print('Some error occured: ' + str(exception))


def main() -> None:
Expand Down
5 changes: 0 additions & 5 deletions vulnerability_analytics/res/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
FROM ubuntu:latest

COPY libc.so.6.binary /libc.so.6
COPY ld-linux-x86-64.so.2.binary /ld-linux-x86-64.so.2



Binary file not shown.
Binary file removed vulnerability_analytics/res/libc.so.6.binary
Binary file not shown.
16 changes: 4 additions & 12 deletions vulnerability_analytics/rex_api/DockerBuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ def build_custom_image(self, target_path: str, image_tag: str, *argv: str) -> No
# print(type(image))
self.image = image

# print(self.tmp_dockerfile)
# import time
# time.sleep(100000)

return image

def __prepare_dockerfile(self, target_path: str, argument_list: typing.List) -> None:
Expand All @@ -57,7 +53,9 @@ def __prepare_dockerfile(self, target_path: str, argument_list: typing.List) ->
shutil.copy2(target_path, self.tmp_docker_folder.name)

# Create the ENTRYPOINT command using the followint template
entry_point = 'ENTRYPOINT ["/ld-linux-x86-64.so.2", "--library-path", "/", ' + '"' + os.path.basename(target_path) + '"'
# entry_point = 'ENTRYPOINT ["/ld-linux-x86-64.so.2", "--library-path", "/", ' + '"' + os.path.basename(target_path) + '"'
entry_point = 'ENTRYPOINT ["' + os.path.basename(target_path) + '"'

# If we have some additional arguments, add them

if(len(argument_list)):
Expand All @@ -67,14 +65,8 @@ def __prepare_dockerfile(self, target_path: str, argument_list: typing.List) ->
# Then close the statement
entry_point = entry_point + ']'


# Open temp file in append mode and add copy command (used to copy the binary) and the ENTRYPOINT command
with open(self.tmp_dockerfile, 'a') as dockerfile:
dockerfile.write("\nCOPY {} /\n".format(os.path.basename(target_path)))
dockerfile.write("\n" + entry_point + "\n")







0 comments on commit 15d7e1f

Please sign in to comment.