Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion APs/config/mgt/hostapd_wpe_tls.eap_user.tmp
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,4 @@

# Existing user identities
"${IDENTITY_MGT_TLS}" TLS
"${USER_GLOBALMANAGER}" TLS
"${IDENTITY_MGT_PHISHING}" TLS
9 changes: 2 additions & 7 deletions APs/config/owe/hostapd_owe.conf.tmp
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,12 @@ channel=$CHANNEL_OWE
# SSID ------------------------------------------------------------------
ssid=$ESSID_OWE

ieee80211n=1
ieee80211ac=1

auth_algs=1
wpa=2
wpa_key_mgmt=OWE

wpa_pairwise=CCMP
rsn_pairwise=CCMP

ieee80211w=2
owe_groups=19

ctrl_interface=/run/hostapd-$WLAN_OWE
ctrl_interface_group=0
ctrl_interface_group=0
229 changes: 195 additions & 34 deletions APs/mac80211_hwsim/dragondrain.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,100 @@
#!/bin/bash
set -euo pipefail

patch_FILE="${1:-mac80211_hwsim.c}"
usage() {
cat <<'EOF'
Usage:
bash dragondrain.sh [mac80211_hwsim.c]
bash dragondrain.sh --file mac80211_hwsim.c [--simulate-dos|--detect-only]

Environment tuning (all optional):
PATCH_AUTH_THRESHOLD auth req/sec threshold (default: 30)
PATCH_SAE_AUTH_THRESHOLD SAE auth req/sec threshold (default: 8)
PATCH_ASSOC_THRESHOLD assoc+reassoc req/sec threshold (default: 20)
PATCH_TOTAL_THRESHOLD total auth+assoc req/sec threshold (default: 40)
PATCH_DETECT_WINDOWS consecutive flood windows before trigger (default: 3)
PATCH_QUIET_WINDOWS quiet windows before reset (default: 6)
PATCH_BLOCK_SECONDS DoS block duration when detected (default: 30)
PATCH_SIMULATE_DOS 1 enables periodic simulated DoS (default: 0)
PATCH_SIM_INTERVAL_SECONDS interval between simulations (default: 90)
PATCH_SIM_BLOCK_SECONDS simulated DoS duration (default: 15)
EOF
}

require_posint() {
local name="$1"
local value="$2"
if ! [[ "$value" =~ ^[0-9]+$ ]] || [[ "$value" -lt 1 ]]; then
echo "[-] $name must be a positive integer, got: $value" >&2
exit 1
fi
}

patch_FILE="mac80211_hwsim.c"
patch_FILE_SET=0

while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h)
usage
exit 0
;;
--file)
patch_FILE="${2:-}"
if [[ -z "$patch_FILE" ]]; then
echo "[-] --file requires a path" >&2
exit 1
fi
patch_FILE_SET=1
shift 2
;;
--simulate-dos)
PATCH_SIMULATE_DOS=1
shift
;;
--detect-only)
PATCH_SIMULATE_DOS=0
shift
;;
*)
if [[ "$patch_FILE_SET" -eq 0 && "$1" != --* ]]; then
patch_FILE="$1"
patch_FILE_SET=1
shift
else
echo "[-] Unknown argument: $1" >&2
usage
exit 1
fi
;;
esac
done

PATCH_AUTH_THRESHOLD="${PATCH_AUTH_THRESHOLD:-30}"
PATCH_SAE_AUTH_THRESHOLD="${PATCH_SAE_AUTH_THRESHOLD:-8}"
PATCH_ASSOC_THRESHOLD="${PATCH_ASSOC_THRESHOLD:-20}"
PATCH_TOTAL_THRESHOLD="${PATCH_TOTAL_THRESHOLD:-40}"
PATCH_DETECT_WINDOWS="${PATCH_DETECT_WINDOWS:-3}"
PATCH_QUIET_WINDOWS="${PATCH_QUIET_WINDOWS:-6}"
PATCH_BLOCK_SECONDS="${PATCH_BLOCK_SECONDS:-30}"
PATCH_SIMULATE_DOS="${PATCH_SIMULATE_DOS:-0}"
PATCH_SIM_INTERVAL_SECONDS="${PATCH_SIM_INTERVAL_SECONDS:-90}"
PATCH_SIM_BLOCK_SECONDS="${PATCH_SIM_BLOCK_SECONDS:-15}"

require_posint PATCH_AUTH_THRESHOLD "$PATCH_AUTH_THRESHOLD"
require_posint PATCH_SAE_AUTH_THRESHOLD "$PATCH_SAE_AUTH_THRESHOLD"
require_posint PATCH_ASSOC_THRESHOLD "$PATCH_ASSOC_THRESHOLD"
require_posint PATCH_TOTAL_THRESHOLD "$PATCH_TOTAL_THRESHOLD"
require_posint PATCH_DETECT_WINDOWS "$PATCH_DETECT_WINDOWS"
require_posint PATCH_QUIET_WINDOWS "$PATCH_QUIET_WINDOWS"
require_posint PATCH_BLOCK_SECONDS "$PATCH_BLOCK_SECONDS"
require_posint PATCH_SIM_INTERVAL_SECONDS "$PATCH_SIM_INTERVAL_SECONDS"
require_posint PATCH_SIM_BLOCK_SECONDS "$PATCH_SIM_BLOCK_SECONDS"

if [[ "$PATCH_SIMULATE_DOS" != "0" && "$PATCH_SIMULATE_DOS" != "1" ]]; then
echo "[-] PATCH_SIMULATE_DOS must be 0 or 1" >&2
exit 1
fi

if [[ ! -f "$patch_FILE" ]]; then
echo "[-] File not found: $patch_FILE" >&2
Expand All @@ -14,6 +107,8 @@ patch_HELPERS_MARK="/* [HWSIM-PATCH] kick helpers */"
patch_APFILTER_MARK="/* [HWSIM-PATCH] ap dest filter */"
patch_RX_MARK_BEGIN="/* [HWSIM-PATCH-RX] begin */"

echo "[i] Tuning: auth=$PATCH_AUTH_THRESHOLD sae_auth=$PATCH_SAE_AUTH_THRESHOLD assoc=$PATCH_ASSOC_THRESHOLD total=$PATCH_TOTAL_THRESHOLD detect_windows=$PATCH_DETECT_WINDOWS quiet_windows=$PATCH_QUIET_WINDOWS block_s=$PATCH_BLOCK_SECONDS simulate_dos=$PATCH_SIMULATE_DOS sim_interval_s=$PATCH_SIM_INTERVAL_SECONDS sim_block_s=$PATCH_SIM_BLOCK_SECONDS"

# 1) Ensure required headers
if ! grep -q '<linux/workqueue.h>' "$patch_FILE"; then
perl -i -pe 'BEGIN{$patch_done=0} if(!$patch_done && /^#include\b/){ print "#include <linux/workqueue.h>\n"; $patch_done=1 }' "$patch_FILE"
Expand All @@ -32,13 +127,17 @@ fi
# 2) Add patch_ fields to struct mac80211_hwsim_data
if ! grep -q "patch_attack_triggered" "$patch_FILE"; then
sed -i '/struct mac80211_hwsim_data[[:space:]]*{/a\
\t/* Patch flood detection (receiver only) */\
\t/* Patch flood detection and DoS simulation */\
\tbool patch_attack_triggered;\
\tint patch_auth_counter;\
\tint patch_sae_auth_counter;\
\tint patch_assoc_counter;\
\tunsigned long patch_last_jiffies;\
\tint patch_flood_streak;\
\tint patch_quiet_streak;\
\tunsigned long patch_block_until_jiffies;\
\tunsigned long patch_simulate_next_jiffies;\
\tunsigned long patch_simulate_until_jiffies;\
' "$patch_FILE"
echo "[+] Added patch_ fields to struct mac80211_hwsim_data"
else
Expand All @@ -50,6 +149,10 @@ if ! grep -qF "$patch_HELPERS_MARK" "$patch_FILE"; then
patch_HELPERS_CONTENT=$(cat <<'PATCH_EOF'

/* [HWSIM-PATCH] kick helpers */
#ifndef WLAN_AUTH_SAE
#define WLAN_AUTH_SAE 3
#endif

static struct work_struct patch_kick_work;
static struct ieee80211_hw *patch_kick_hw;
static bool patch_kick_inited;
Expand Down Expand Up @@ -132,7 +235,7 @@ static bool patch_is_for_local_ap(struct ieee80211_hw *patch_hw,
#endif

ieee80211_iterate_active_interfaces_atomic(patch_hw, patch_iter_flags,
patch_ap_match_iter, &patch_ctx);
patch_ap_match_iter, &patch_ctx);
return patch_ctx.patch_match;
}
PATCH_EOF
Expand All @@ -148,86 +251,144 @@ else
echo "[=] patch_ AP destination filter helpers already present"
fi

# 5) RX wrapper (receiver-only flood detection)
# 5) RX wrapper (receiver-only flood detection + optional DoS simulation)
if grep -qF "$patch_RX_MARK_BEGIN" "$patch_FILE"; then
echo "[=] RX wrapper already present"
else
patch_RX_WRAPPER=$(cat <<'PATCH_EOF'
patch_RX_WRAPPER=$(cat <<PATCH_EOF
do {
/* [HWSIM-PATCH-RX] begin */
struct ieee80211_hw *patch_hw = data->hw;
struct sk_buff *patch_skb = skb;
struct mac80211_hwsim_data *patch_p = patch_hw->priv;
struct ieee80211_hdr *patch_hdr;
struct ieee80211_mgmt *patch_mgmt;
u16 patch_fc;
u16 patch_auth_alg = 0;
unsigned long patch_now = jiffies;

if (patch_skb && patch_skb->len >= 2) {
bool patch_mgmt_req;
bool patch_is_sae_auth = false;
bool patch_flood;
int patch_total;
bool patch_attack_active;
bool patch_simulation_active;

if (patch_skb && patch_skb->len >= 24) {
patch_hdr = (struct ieee80211_hdr *)patch_skb->data;
patch_mgmt = (struct ieee80211_mgmt *)patch_skb->data;
patch_fc = le16_to_cpu(patch_hdr->frame_control);
patch_mgmt_req = ieee80211_is_auth(patch_fc) ||
ieee80211_is_assoc_req(patch_fc) ||
ieee80211_is_reassoc_req(patch_fc);
if (ieee80211_is_auth(patch_fc) && patch_skb->len >= 26) {
patch_auth_alg = le16_to_cpu(patch_mgmt->u.auth.auth_alg);
patch_is_sae_auth = patch_auth_alg == WLAN_AUTH_SAE;
}

/* Receiver only:
* If addr1 is broadcast/multicast, use addr3 (BSSID) to identify the AP.
* If addr1 is unicast, match either addr1 or addr3 against the AP vif.
*/
* If addr1 is broadcast/multicast, use addr3 (BSSID) to identify the AP.
* If addr1 is unicast, match either addr1 or addr3 against the AP vif.
*/
if (is_multicast_ether_addr(patch_hdr->addr1)) {
if (!patch_is_for_local_ap(patch_hw, NULL, patch_hdr->addr3))
goto patch_pass;
} else {
if (!patch_is_for_local_ap(patch_hw, patch_hdr->addr1, patch_hdr->addr3))
goto patch_pass;
}
if (patch_p->patch_attack_triggered &&
time_before(patch_now, patch_p->patch_block_until_jiffies)) {
if (ieee80211_is_auth(patch_fc) ||
ieee80211_is_assoc_req(patch_fc) ||
ieee80211_is_reassoc_req(patch_fc)) {
dev_kfree_skb_any(patch_skb);
patch_skb = NULL;

if (${PATCH_SIMULATE_DOS}) {
if (!patch_p->patch_simulate_next_jiffies)
patch_p->patch_simulate_next_jiffies = patch_now + ${PATCH_SIM_INTERVAL_SECONDS} * HZ;

if (time_after_eq(patch_now, patch_p->patch_simulate_next_jiffies)) {
patch_p->patch_simulate_until_jiffies = patch_now + ${PATCH_SIM_BLOCK_SECONDS} * HZ;
patch_p->patch_simulate_next_jiffies = patch_now + ${PATCH_SIM_INTERVAL_SECONDS} * HZ;

if (!patch_kick_inited) {
INIT_WORK(&patch_kick_work, patch_kick_workfn);
patch_kick_inited = true;
}
patch_kick_hw = patch_hw;
ieee80211_queue_work(patch_hw, &patch_kick_work);
pr_info("[HWSIM-PATCH][%s] Simulated DoS for %ds\n",
wiphy_name(patch_hw->wiphy), ${PATCH_SIM_BLOCK_SECONDS});
}
}

if (patch_skb && ieee80211_is_auth(patch_fc)) {
patch_attack_active = patch_p->patch_attack_triggered &&
time_before(patch_now, patch_p->patch_block_until_jiffies);
patch_simulation_active = ${PATCH_SIMULATE_DOS} &&
time_before(patch_now, patch_p->patch_simulate_until_jiffies);

if ((patch_attack_active && patch_mgmt_req) ||
(patch_simulation_active && (patch_mgmt_req || ieee80211_is_data(patch_fc)))) {
dev_kfree_skb_any(patch_skb);
patch_skb = NULL;
}

if (patch_skb && patch_mgmt_req) {
if (time_before(patch_now, patch_p->patch_last_jiffies + HZ)) {
patch_p->patch_auth_counter++;
if (ieee80211_is_auth(patch_fc)) {
patch_p->patch_auth_counter++;
if (patch_is_sae_auth)
patch_p->patch_sae_auth_counter++;
} else {
patch_p->patch_assoc_counter++;
}
} else {
if (patch_p->patch_auth_counter > 20) {
patch_total = patch_p->patch_auth_counter + patch_p->patch_assoc_counter;
patch_flood = patch_p->patch_auth_counter >= ${PATCH_AUTH_THRESHOLD} ||
patch_p->patch_sae_auth_counter >= ${PATCH_SAE_AUTH_THRESHOLD} ||
patch_p->patch_assoc_counter >= ${PATCH_ASSOC_THRESHOLD} ||
patch_total >= ${PATCH_TOTAL_THRESHOLD};

if (patch_flood) {
patch_p->patch_flood_streak++;
patch_p->patch_quiet_streak = 0;
pr_info("[HWSIM-PATCH][%s] Flood window (%d/5)\n",
wiphy_name(patch_hw->wiphy), patch_p->patch_flood_streak);
pr_info("[HWSIM-PATCH][%s] Flood window auth=%d sae_auth=%d assoc=%d total=%d (streak=%d/%d)\n",
wiphy_name(patch_hw->wiphy),
patch_p->patch_auth_counter,
patch_p->patch_sae_auth_counter,
patch_p->patch_assoc_counter,
patch_total,
patch_p->patch_flood_streak,
${PATCH_DETECT_WINDOWS});
} else {
patch_p->patch_flood_streak = 0;
if (patch_p->patch_flood_streak > 0)
patch_p->patch_flood_streak--;
if (patch_p->patch_attack_triggered)
patch_p->patch_quiet_streak++;
else
patch_p->patch_quiet_streak = 0;
}

if (patch_p->patch_flood_streak >= 5 &&
!patch_p->patch_attack_triggered) {
if (patch_p->patch_flood_streak >= ${PATCH_DETECT_WINDOWS} && !patch_attack_active) {
patch_p->patch_attack_triggered = true;
patch_p->patch_block_until_jiffies = patch_now + 30 * HZ;

pr_info("[HWSIM-PATCH][%s] Flood detected -> restarting hw to drop all stations\n",
wiphy_name(patch_hw->wiphy));
patch_p->patch_block_until_jiffies = patch_now + ${PATCH_BLOCK_SECONDS} * HZ;
patch_p->patch_quiet_streak = 0;

if (!patch_kick_inited) {
INIT_WORK(&patch_kick_work, patch_kick_workfn);
patch_kick_inited = true;
}
patch_kick_hw = patch_hw;
ieee80211_queue_work(patch_hw, &patch_kick_work);
pr_info("[HWSIM-PATCH][%s] DragonDrain detected -> DoS mode for %ds\n",
wiphy_name(patch_hw->wiphy), ${PATCH_BLOCK_SECONDS});
}

if (patch_p->patch_attack_triggered &&
patch_p->patch_quiet_streak >= 10) {
patch_p->patch_quiet_streak >= ${PATCH_QUIET_WINDOWS}) {
patch_p->patch_attack_triggered = false;
pr_info("[HWSIM-PATCH][%s] Quiet 10s -> mode RESET\n",
patch_p->patch_flood_streak = 0;
patch_p->patch_quiet_streak = 0;
patch_p->patch_sae_auth_counter = 0;
pr_info("[HWSIM-PATCH][%s] Quiet windows reached -> reset detection state\n",
wiphy_name(patch_hw->wiphy));
}

patch_p->patch_auth_counter = 1;
patch_p->patch_auth_counter = ieee80211_is_auth(patch_fc) ? 1 : 0;
patch_p->patch_sae_auth_counter = (ieee80211_is_auth(patch_fc) && patch_is_sae_auth) ? 1 : 0;
patch_p->patch_assoc_counter = ieee80211_is_auth(patch_fc) ? 0 : 1;
patch_p->patch_last_jiffies = patch_now;
}
}
Expand Down
7 changes: 4 additions & 3 deletions APs/mac80211_hwsim/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ sudo apt install linux-headers-$(uname -r) -y || true
sudo apt-get install -y gcc-12 g++-12 build-essential || true

### ---- Download the code and parche ----------------------------------
rm -f mac80211_hwsim.c mac80211_hwsim.h mac80211_hwsim.c.bak
bash patch80211.sh

bash dragondrain.sh
PATCH_SAE_AUTH_THRESHOLD=4 PATCH_DETECT_WINDOWS=2 bash dragondrain.sh --simulate-dos

TARGET_VERSION_ERROR="2.4-WiFiChallengeLab-version"
TARGET_VERSION_ERROR="2.4.1-WiFiChallengeLab-version"
TARGET_VERSION=$(grep -oP 'MODULE_VERSION\("([^"]+)"\)' mac80211_hwsim.c | grep -oP '(?<=")[^"]+(?=")' || echo $TARGET_VERSION_ERROR)

### ---- Compile and install
Expand Down Expand Up @@ -49,7 +50,7 @@ ${ALT_MODNAME}-objs := mac80211_hwsim.o
EOF

echo "==> Building ${ALT_MODNAME}.ko …"
make -s -C "/lib/modules/${KVER}/build" M="${BUILD_DIR}" modules
SKIP_BTF=1 make -s -C "/lib/modules/${KVER}/build" M="${BUILD_DIR}" modules

# verify version of freshly‑built binary
NEW_VER="$(modver "./${ALT_MODNAME}.ko")"
Expand Down
2 changes: 1 addition & 1 deletion APs/mac80211_hwsim/patch80211.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ CFILE="${DEST}/mac80211_hwsim.c"

# MODULE_VERSION
if ! grep -q 'WiFiChallengeLab-version' "$CFILE"; then
perl -0777 -i -pe 's/MODULE_LICENSE\("GPL"\);\n/MODULE_LICENSE("GPL");\nMODULE_VERSION("2.4-WiFiChallengeLab-version");\n/s' "$CFILE"
perl -0777 -i -pe 's/MODULE_LICENSE\("GPL"\);\n/MODULE_LICENSE("GPL");\nMODULE_VERSION("2.4.1-WiFiChallengeLab-version");\n/s' "$CFILE"
echo " • MODULE_VERSION added"
else
echo " • MODULE_VERSION already present"
Expand Down
Loading