Merge branch 'testing'

This commit is contained in:
Stefan Kremser
2017-03-31 12:27:56 +02:00
27 changed files with 1281 additions and 1172 deletions

View File

@@ -1,5 +1,5 @@
# ESP8266 Deauther
Build your own WiFi jammer with an ESP8266.
Deauthentication attack and other hacks using an ESP8266.
![esp8266 deauther with a smartphone](https://raw.githubusercontent.com/spacehuhn/esp8266_deauther/master/screenshots/smartphone_esp_1.jpg)
@@ -24,39 +24,40 @@ Build your own WiFi jammer with an ESP8266.
Basically its a device which performs a [deauth attack](https://en.wikipedia.org/wiki/Wi-Fi_deauthentication_attack).
You select the clients you want to disconnect from their network and start the attack. As long as the attack is running, the
selected devices are unable to connect to their network.
selected devices are unable to connect to their network.
Other attacks also have been implemented, such as beacon or probe request flooding.
### How it works
The 802.11 WiFi protocol contains a so called [deauthentication frame](https://mrncciew.com/2014/10/11/802-11-mgmt-deauth-disassociation-frames/). It is used to disconnect clients safely from a wireless
The 802.11 Wi-Fi protocol contains a so called [deauthentication frame](https://mrncciew.com/2014/10/11/802-11-mgmt-deauth-disassociation-frames/). It is used to disconnect clients safely from a wireless
network.
Because these packets are unencrypted, you just need the mac address of the WiFi router and of the client device which you want to disconnect from the network. You dont need to be in the network or know the password, its enough to be in its range.
Because these management packets are unencrypted, you just need the mac address of the Wi-Fi router and of the client device which you want to disconnect from the network. You dont need to be in the network or know the password, its enough to be in its range.
### What an ESP8266 is
The [ESP8266](https://de.wikipedia.org/wiki/ESP8266) is a very cheap micro controller with build in WiFi. It contains a powerfull 160 MHz processor and you can program it
with the [Arduino IDE](https://www.arduino.cc/en/Main/Software). This makes it perfect for this project.
The [ESP8266](https://de.wikipedia.org/wiki/ESP8266) is a cheap micro controller with built-in Wi-Fi. It contains a powerfull 160 MHz processor and it can be programmed using [Arduino](https://www.arduino.cc/en/Main/Software).
You can buy these chips for under $2 from China!
### How to protect against it
With [802.11w-2009](https://en.wikipedia.org/wiki/IEEE_802.11w-2009) WiFi got an update to encrypt management frames.
So make sure your router is up to date and has management frame protection enabled. But be sure that your client device
supports it too, both ends need to have it enabled!
With [802.11w-2009](https://en.wikipedia.org/wiki/IEEE_802.11w-2009) Wi-Fi got an update to encrypt management frames.
So make sure your router is up to date and has management frame protection enabled. But note that your client device needs to
support it too, both ends need to have it enabled!
The only problem is that most devices dont use it. I tested it with different WiFi networks and devices, it worked every time! It seems that even newer devices which support frame protection dont use it by default.
The only problem is that most devices dont use it. I tested it with different Wi-Fi networks and devices, it worked every time! It seems that even newer devices which support frame protection dont use it by default.
## Disclaimer
Use it only for testing purposes on your own devices!
Use it only for testing purposes on your own devices!
I don't take any responsibility for what you do with this program.
Please check the legal regulations in your country before using it. Jamming transmitters are illegal in most countries
and this device can fall into the same category (even if its technically not the same).
My intention with this project is to draw attention to this issue.
This attack shows how vulnerable the 802.11 WiFi standard is and that it has to be fixed.
This attack shows how vulnerable the 802.11 Wi-Fi standard is and that it has to be fixed.
**A solution is already there, why dont we use it?**
## Installation
@@ -111,9 +112,9 @@ Make sure you select the right com-port, the right upload size of your ESP8266 a
**11** Scroll down and before `#endif` add following lines:
`typedef void (*freedom_outside_cb_t)(uint8 status);`
`int wifi_register_send_pkt_freedom_cb(freedom_outside_cb_t cb);`
`void wifi_unregister_send_pkt_freedom_cb(void);`
`int wifi_send_pkt_freedom(uint8 *buf, int len, bool sys_seq);`
`int Wi-Fi_register_send_pkt_freedom_cb(freedom_outside_cb_t cb);`
`void Wi-Fi_unregister_send_pkt_freedom_cb(void);`
`int Wi-Fi_send_pkt_freedom(uint8 *buf, int len, bool sys_seq);`
![screenshot of notepad, copy paste the right code](https://raw.githubusercontent.com/spacehuhn/esp8266_deauther/master/screenshots/notepad_screenshot_1.JPG)
@@ -121,9 +122,9 @@ Make sure you select the right com-port, the right upload size of your ESP8266 a
**12** Go to the SDK_fix folder of this project
**13** Copy ESP8266WiFi.cpp and ESP8266WiFi.h
**13** Copy ESP8266Wi-Fi.cpp and ESP8266Wi-Fi.h
**14** Past these files here `packages` > `esp8266` > `hardware` > `esp8266` > `2.0.0` > `libraries` > `ESP8266WiFi` > `src`
**14** Past these files here `packages` > `esp8266` > `hardware` > `esp8266` > `2.0.0` > `libraries` > `ESP8266Wi-Fi` > `src`
**15** Open `esp8266_deauther` > `esp8266_deauther.ino` in Arduino
@@ -143,7 +144,7 @@ First start your ESP8266 by giving it power.
You can use your smartphone if you have a USB OTG cable.
![esp8266 deauther with a smartphone](https://raw.githubusercontent.com/spacehuhn/esp8266_deauther/master/screenshots/smartphone_esp_2.jpg)
Scan for WiFi networks and connect to `pwned`. The password is `deauther`.
Scan for Wi-Fi networks and connect to `pwned`. The password is `deauther`.
Once connected, you can open up your browser and go to `192.168.4.1`.
You can now scan for networks...
@@ -152,7 +153,7 @@ You can now scan for networks...
scan for client devices...
![webinterface client scanner](https://raw.githubusercontent.com/spacehuhn/esp8266_deauther/master/screenshots/web_screenshot_2.JPG)
Note: While scanning the ESP8266 will shut down its access point, so you may have to go to your settings and reconnect to the WiFi network manually.
Note: While scanning the ESP8266 will shut down its access point, so you may have to go to your settings and reconnect to the Wi-Fi network manually.
...and start different attacks.
![webinterface attack menu](https://raw.githubusercontent.com/spacehuhn/esp8266_deauther/master/screenshots/web_screenshot_3.JPG)
@@ -168,14 +169,14 @@ Yes, but I will not implement this 'feature' for ethical and legal reasons.
**Can it sniff handshakes?**
The ESP8266 has a promiscuous mode in which you can sniff packets, but handshake packets are dropped and there is no other way to get them with the functions provided by the SDK.
Maybe someone will find a way around this barrier but I wasn't able to.
Maybe someone will find a way around this barrier.
**espcomm_sync failed/espcomm_open when uploading**
The ESP upload tool can't communicate with the chip, make sure the right port is selected!
You can also try out different USB ports and cables.
If this doesn't solve it you may have to install USB drivers.
Which drivers you need depends on the board, most boards use a cp2102, cp2104 or ch340.
Which drivers you need depends on the board, most boards use a cp2102 or ch340.
**AP scan doesn't work**
@@ -184,8 +185,8 @@ Try out switching the browser or open the website with another device.
**Deauth attack won't work**
If you see 0 pkts/s on the website you have made a mistake. Check if you have followed the the installation steps correctly and that the right SDK installed, it must be version 2.0.0!
If it can send packets but your target don't loose its connection then the WiFi router uses [802.11w](#how-to-protect-against-it) and it's protected against such attacks or they communicate via 5 GHz WiFi, which the ESP8266 doesn't support.
If you see 0 pkts/s on the website you've made a mistake. Check if you have followed the the installation steps correctly and that the right SDK installed, it must be version 2.0.0!
If it can send packets but your target don't loose its connection then the Wi-Fi router uses [802.11w](#how-to-protect-against-it) and it's protected against such attacks or they communicate via 5 GHz Wi-Fi, which the ESP8266 doesn't support.
###If you have other questions or problems with the ESP8266 you can also check out the official [community forum](http://www.esp8266.com/).
@@ -193,7 +194,7 @@ If it can send packets but your target don't loose its connection then the WiFi
## License
This project is licensed under the MIT License - see the [license file](LICENSE) file for details
This project is licensed under the MIT License - see the [license file](LICENSE) file for details.
## Sources and additional links
@@ -207,14 +208,15 @@ ESP8266:
packet injection with ESP8266:
* http://hackaday.com/2016/01/14/inject-packets-with-an-esp8266/
* http://bbs.espressif.com/viewtopic.php?f=7&t=1357&p=10205&hilit=wifi_pkt_freedom#p10205
* http://bbs.espressif.com/viewtopic.php?f=7&t=1357&p=10205&hilit=Wi-Fi_pkt_freedom#p10205
* https://github.com/pulkin/esp8266-injection-example
802.11w-2009: https://en.wikipedia.org/wiki/IEEE_802.11w-2009
wifi_send_pkt_freedom function limitations: http://esp32.com/viewtopic.php?f=13&t=586&p=2648&hilit=wifi_send_pkt_freedom#p2648
Wi-Fi_send_pkt_freedom function limitations: http://esp32.com/viewtopic.php?f=13&t=586&p=2648&hilit=Wi-Fi_send_pkt_freedom#p2648
esp32 esp_wifi_internal function limitations: http://esp32.com/viewtopic.php?f=13&t=586&p=2648&hilit=wifi_send_pkt_freedom#p2648
esp32 esp_Wi-Fi_internal function limitations: http://esp32.com/viewtopic.php?f=13&t=586&p=2648&hilit=Wi-Fi_send_pkt_freedom#p2648
Videos:
[![Cheap Wifi 'Jammer' Device | NodeMCU](https://img.youtube.com/vi/oQQhBdCQOTM/0.jpg)](https://www.youtube.com/watch?v=oQQhBdCQOTM)
[![Cheap Wi-Fi 'Jammer' Device | NodeMCU](https://img.youtube.com/vi/oQQhBdCQOTM/0.jpg)](https://www.youtube.com/watch?v=oQQhBdCQOTM)
[![Wifi 'Jammer' Device V1.1 | Setup Tutorial](https://img.youtube.com/vi/r5aoV5AolNo/0.jpg)](https://www.youtube.com/watch?v=r5aoV5AolNo)

View File

@@ -26,6 +26,8 @@ bool APScan::start(){
_ssid.toCharArray(names[i],33);
//data_getVendor(WiFi.BSSID(i)[0],WiFi.BSSID(i)[1],WiFi.BSSID(i)[2]).toCharArray(vendors[i],9);
if(debug){
Serial.print((String)i);
Serial.print(" - ");
_ap._print();
Serial.print(" - ");
Serial.print(channels[i]);
@@ -44,7 +46,8 @@ bool APScan::start(){
}
//for debugging the APScan crash bug
/*if(debug){
/*
if(debug){
for(int i=results;i<maxAPScanResults;i++){
Mac _ap;
_ap.set(random(255),random(255),random(255),random(255),random(255),random(255));
@@ -55,6 +58,8 @@ bool APScan::start(){
String _ssid = "test_dbeJwq3tPtJsuWtgULgShD9dxXV";
_ssid.toCharArray(names[i],33);
Serial.print((String)i);
Serial.print(" - ");
_ap._print();
Serial.print(" - ");
Serial.print(channels[i]);
@@ -68,10 +73,10 @@ bool APScan::start(){
results++;
}
}*/
}
*/
if(debug) Serial.println("scan done");
if(debug) Serial.println(getResults());
return true;
}
@@ -115,6 +120,67 @@ int APScan::getFirstTarget(){
return -1;
}
void APScan::sendResults(){
if(debug) Serial.print("sending AP scan result JSON ");
size_t _size = 10; // {"aps":[]}
for(int i=0;i<results && i<maxAPScanResults;i++){
/*
_size++; // {
_size += 5; // "i": ,
_size += String(i).length();
_size += 5; // "c": ,
_size += String(getAPChannel(i)).length();
_size += 24; // "m":"d4:21:22:da:85:f3",
_size += 8; // "ss":" ",
_size += getAPName(i).length();
_size += 5; // "r": ,
_size += String(getAPRSSI(i)).length();
_size += 6; // "e": ,
_size += 6; // "se":0
_size++; // }*/
_size += 61;
_size += String(i).length();
_size += String(getAPChannel(i)).length();
_size += getAPName(i).length();
_size += String(getAPRSSI(i)).length();
if((i!=results-1) && (i!=maxAPScanResults-1)) _size++; // ,
}
sendHeader(200, "text/json", _size);
String json;
int bufc = 0; //bufferCounter
json = "{\"aps\":[";
sendToBuffer(json);
for(int i=0;i<results && i<maxAPScanResults;i++){
if(debug) Serial.print(".");
json = "{";
json += "\"i\":"+(String)i+",";
json += "\"c\":"+(String)getAPChannel(i)+",";
json += "\"m\":\""+getAPMac(i)+"\",";
json += "\"ss\":\""+getAPName(i)+"\",";
json += "\"r\":"+(String)getAPRSSI(i)+",";
json += "\"e\":"+(String)encryption[i]+",";
//json += "\"v\":\""+getAPVendor(i)+"\",";
json += "\"se\":"+(String)getAPSelected(i);
json += "}";
if((i!=results-1) && (i!=maxAPScanResults-1)) json += ",";
sendToBuffer(json);
}
json = "]}";
sendToBuffer(json);
sendBuffer();
if(debug) Serial.println("done");
}
String APScan::getResults(){
if(debug) Serial.print("getting AP scan result JSON ");
String json = "{ \"aps\":[ ";
@@ -175,4 +241,4 @@ void APScan::select(int num){
bool APScan::isSelected(int num){
return selected[num];
}
}

View File

@@ -1,14 +1,19 @@
#ifndef APScan_h
#define APScan_h
#define maxAPScanResults 30
#define maxAPScanResults 80
#include <ESP8266WiFi.h>
#include "Mac.h"
#include "MacList.h"
#include "Settings.h"
#include <ESP8266WebServer.h>
extern String data_getVendor(uint8_t first,uint8_t second,uint8_t third);
extern ESP8266WebServer server;
extern void sendBuffer();
extern void sendToBuffer(String str);
extern void sendHeader(int code, String type, size_t _size);
extern const bool debug;
extern Settings settings;
@@ -21,6 +26,7 @@ class APScan{
String getResults();
String getResult(int i);
void select(int num);
void sendResults();
String getAPName(int num);
String getAPEncryption(int num);
@@ -38,6 +44,7 @@ class APScan{
int selectedSum;
MacList aps;
private:
int channels[maxAPScanResults];
int rssi[maxAPScanResults];
char names[maxAPScanResults][33];
@@ -47,6 +54,7 @@ class APScan{
String getEncryption(int code);
bool selected[maxAPScanResults];
};
#endif
#endif

View File

@@ -11,7 +11,7 @@ void Attack::generate(){
uint8_t _randomMacBuffer[6];
beaconAdrs._clear();
for(int i=0;i<macListLen;i++) channels[i] = random(1,12);
for(int i=0;i<macListLen;i++) channels[i] = random(1,maxChannel);
do{
getRandomVendorMac(_randomMacBuffer);
for(int i=0;i<6;i++) _randomBeaconMac.setAt(_randomMacBuffer[i],i);
@@ -136,7 +136,7 @@ void Attack::run(){
_selectedClients++;
if(settings.channelHop){
for(int j=1;j<12;j++){
for(int j=1;j<maxChannel;j++){
wifi_set_channel(j);
buildDeauth(_ap, clientScan.getClientMac(i), 0xc0, settings.deauthReason );
@@ -160,7 +160,7 @@ void Attack::run(){
_client.set(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF);
if(settings.channelHop){
for(int j=1;j<12;j++){
for(int j=1;j<maxChannel;j++){
wifi_set_channel(j);
buildDeauth(_ap, _client, 0xc0, settings.deauthReason );

View File

@@ -1,6 +1,8 @@
#ifndef Attack_h
#define Attack_h
#define maxChannel 11
#include <ESP8266WiFi.h>
extern "C" {

View File

@@ -25,9 +25,11 @@ void ClientScan::start(int _time){
curChannel = 0;
for(int i=0;i<apScan.results;i++){
if(!intInArray(apScan.getAPChannel(i),channels)){
channels[channelsNum] = apScan.getAPChannel(i);
channelsNum++;
if(apScan.isSelected(i)){
if(!intInArray(apScan.getAPChannel(i),channels)){
channels[channelsNum] = apScan.getAPChannel(i);
channelsNum++;
}
}
}

View File

@@ -1,12 +1,9 @@
#include "Mac.h"
Mac::Mac(){
adress[0] = 0x00;
adress[1] = 0x00;
adress[2] = 0x00;
adress[3] = 0x00;
adress[4] = 0x00;
adress[5] = 0x00;
for(int i=0;i<6;i++){
adress[i] = 0x00;
}
}
void Mac::set(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth, uint8_t fifth, uint8_t sixth){
@@ -23,12 +20,9 @@ void Mac::setAt(uint8_t first, int num){
}
void Mac::setMac(Mac adr){
adress[0] = adr._get(0);
adress[1] = adr._get(1);
adress[2] = adr._get(2);
adress[3] = adr._get(3);
adress[4] = adr._get(4);
adress[5] = adr._get(5);
for(int i=0;i<6;i++){
adress[i] = adr._get(i);
}
}
bool Mac::valid(){
@@ -50,17 +44,11 @@ String Mac::toString(){
return value;
}
void Mac::_print(){
Serial.print(Mac::toString());
}
void Mac::_print(){Serial.print(Mac::toString());}
void Mac::_println(){
Serial.println(Mac::toString());
}
void Mac::_println(){Serial.println(Mac::toString());}
uint8_t Mac::_get(int num){
return adress[num];
}
uint8_t Mac::_get(int num){return adress[num];}
bool Mac::compare(Mac target){
for(int i=0;i<6;i++){
@@ -69,4 +57,4 @@ bool Mac::compare(Mac target){
return true;
}

View File

@@ -17,7 +17,7 @@ void Settings::load(){
ssidHidden = (bool)EEPROM.read(ssidHiddenAdr);
if((int)EEPROM.read(apChannelAdr) >= 1 && (int)EEPROM.read(apChannelAdr) <= 11){
if((int)EEPROM.read(apChannelAdr) >= 1 && (int)EEPROM.read(apChannelAdr) <= 14){
apChannel = (int)EEPROM.read(apChannelAdr);
} else {
apChannel = 1;

File diff suppressed because one or more lines are too long

View File

@@ -8,6 +8,8 @@ extern "C" {
#include "user_interface.h"
}
ESP8266WebServer server(80);
#include <EEPROM.h>
#include "data.h"
#include "NameList.h"
@@ -21,8 +23,6 @@ extern "C" {
const bool debug = true;
/* ========== DEBUG ========== */
ESP8266WebServer server(80);
NameList nameList;
APScan apScan;
@@ -46,108 +46,21 @@ void startWifi() {
if (settings.ssid.length() < 1 || settings.ssid.length() > 32) Serial.println("WARNING: SSID length must be between 1 and 32 characters!");
}
void setup() {
void loadIndexHTML() {server.send ( 200, "text/html", data_getIndexHTML());}
void loadClientsHTML() {server.send ( 200, "text/html", data_getClientsHTML());}
void loadAttackHTML() {server.send ( 200, "text/html", data_getAttackHTML());}
void loadSettingsHTML() {server.send( 200, "text/html", data_getSettingsHTML());}
void load404() {server.send ( 404, "text/html", data_get404());}
Serial.begin(115200);
delay(2000);
void loadFunctionsJS() {server.send( 200, "text/javascript", data_getFunctionsJS());}
void loadIndexJS() {server.send ( 200, "text/javascript", data_getIndexJS());}
void loadClientsJS() {server.send ( 200, "text/javascript", data_getClientsJS());}
void loadAttackJS() {server.send ( 200, "text/javascript", data_getAttackJS());}
void loadSettingsJS() {server.send( 200, "text/html", data_getSettingsJS());}
pinMode(2, OUTPUT);
delay(50);
digitalWrite(2, HIGH);
EEPROM.begin(4096);
settings.load();
if (debug) settings.info();
nameList.load();
ssidList.load();
Serial.println("");
Serial.println("starting...");
startWifi();
attack.stopAll();
attack.generate();
/* ========== Web Server ========== */
/* HTML sites */
server.onNotFound(load404);
server.on("/", loadIndex);
server.on("/index.html", loadIndex);
server.on("/clients.html", loadClients);
server.on("/attack.html", loadAttack);
server.on("/settings.html", loadSettings);
server.on("/functions.js", loadFunctionsJS);
/* header links */
server.on ("/style.css", loadStyle);
server.on ("/manifest.json", loadManifest);
/* JSON */
server.on("/APScanResults.json", sendAPResults);
server.on("/APScan.json", startAPScan);
server.on("/APSelect.json", selectAP);
server.on("/ClientScan.json", startClientScan);
server.on("/ClientScanResults.json", sendClientResults);
server.on("/ClientScanTime.json", sendClientScanTime);
server.on("/clientSelect.json", selectClient);
server.on("/setName.json", setClientName);
server.on("/attackInfo.json", sendAttackInfo);
server.on("/attackStart.json", startAttack);
server.on("/settings.json", getSettings);
server.on("/settingsSave.json", saveSettings);
server.on("/settingsReset.json", resetSettings);
server.on("/deleteName.json", deleteName);
server.on("/clearNameList.json", clearNameList);
server.on("/editNameList.json", editClientName);
server.on("/addSSID.json", addSSID);
server.on("/cloneSSID.json", cloneSSID);
server.on("/deleteSSID.json", deleteSSID);
server.on("/randomSSID.json", randomSSID);
server.on("/clearSSID.json", clearSSID);
server.on("/resetSSID.json", resetSSID);
server.on("/saveSSID.json", saveSSID);
server.on("/restartESP.json", restartESP);
server.begin();
}
void loop() {
if (clientScan.sniffing) {
if (clientScan.stop()) startWifi();
} else {
server.handleClient();
attack.run();
}
}
void load404() {
server.send ( 200, "text/html", data_get404());
}
void loadIndex() {
server.send ( 200, "text/html", data_getIndexHTML() );
}
void loadClients() {
server.send ( 200, "text/html", data_getClientsHTML());
}
void loadAttack() {
server.send ( 200, "text/html", data_getAttackHTML() );
}
void loadFunctionsJS() {
server.send( 200, "text/javascript", data_getFunctionsJS() );
}
void loadStyle() {
server.send ( 200, "text/css", data_getStyle() );
}
void loadManifest() {
server.send ( 200, "text/css", data_getManifest() );
}
void loadSettings() {
server.send( 200, "text/html", data_getSettingsHTML() );
}
void loadStyle() {server.send ( 200, "text/css", data_getStyle());}
void loadManifest() {server.send ( 200, "text/css", data_getManifest());}
//==========AP-Scan==========
void startAPScan() {
@@ -158,12 +71,14 @@ void startAPScan() {
}
void sendAPResults() {
apScan.sendResults();
/*
if (server.hasArg("apid")) {
int apid = server.arg("apid").toInt();
server.send ( 200, "text/json", apScan.getResult(apid));
} else {
server.send ( 200, "text/json", apScan.getResults());
}
}*/
}
void selectAP() {
@@ -273,7 +188,7 @@ void saveSettings() {
}
if (server.hasArg("password")) settings.password = server.arg("password");
if (server.hasArg("apChannel")) {
if(server.arg("apChannel").toInt() >= 1 && server.arg("apChannel").toInt() <= 11){
if(server.arg("apChannel").toInt() >= 1 && server.arg("apChannel").toInt() <= 14){
settings.apChannel = server.arg("apChannel").toInt();
}
}
@@ -327,4 +242,84 @@ void editClientName() {
server.send( 200, "text/json", "true");
}
}
void setup() {
Serial.begin(115200);
delay(2000);
pinMode(2, OUTPUT);
delay(50);
digitalWrite(2, HIGH);
EEPROM.begin(4096);
settings.load();
if (debug) settings.info();
nameList.load();
ssidList.load();
Serial.println("");
Serial.println("starting...");
startWifi();
attack.stopAll();
attack.generate();
/* ========== Web Server ========== */
/* HTML sites */
server.onNotFound(load404);
server.on("/", loadIndexHTML);
server.on("/clients", loadClientsHTML);
server.on("/attack", loadAttackHTML);
server.on("/settings", loadSettingsHTML);
server.on("/js/index.js", loadIndexJS);
server.on("/js/clients.js", loadClientsJS);
server.on("/js/attack.js", loadAttackJS);
server.on("/js/settings.js", loadSettingsJS);
server.on("/js/functions.js", loadFunctionsJS);
/* header links */
server.on ("/style.css", loadStyle);
server.on ("/manifest.json", loadManifest);
/* JSON */
server.on("/APScanResults.json", sendAPResults);
server.on("/APScan.json", startAPScan);
server.on("/APSelect.json", selectAP);
server.on("/ClientScan.json", startClientScan);
server.on("/ClientScanResults.json", sendClientResults);
server.on("/ClientScanTime.json", sendClientScanTime);
server.on("/clientSelect.json", selectClient);
server.on("/setName.json", setClientName);
server.on("/attackInfo.json", sendAttackInfo);
server.on("/attackStart.json", startAttack);
server.on("/settings.json", getSettings);
server.on("/settingsSave.json", saveSettings);
server.on("/settingsReset.json", resetSettings);
server.on("/deleteName.json", deleteName);
server.on("/clearNameList.json", clearNameList);
server.on("/editNameList.json", editClientName);
server.on("/addSSID.json", addSSID);
server.on("/cloneSSID.json", cloneSSID);
server.on("/deleteSSID.json", deleteSSID);
server.on("/randomSSID.json", randomSSID);
server.on("/clearSSID.json", clearSSID);
server.on("/resetSSID.json", resetSSID);
server.on("/saveSSID.json", saveSSID);
server.on("/restartESP.json", restartESP);
server.begin();
}
void loop() {
if (clientScan.sniffing) {
if (clientScan.stop()) startWifi();
} else {
server.handleClient();
attack.run();
}
}

View File

@@ -1,171 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<script src="functions.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=0.8">
</head>
<body>
<nav>
<a href="index.html">APs</a>
<a href="clients.html">Clients</a>
<a href="attack.html">Attack</a>
<a href="settings.html">Settings</a>
<ul class="nav right">
<a href="#" onclick="restartESP()">Restart</a>
<ul>
</nav>
<div id="content">
<h1>Attack</h1>
<p class="block bold">Selected AP:</p>
<ul id="selectedAPs">
</ul>
<br />
<p class="block bold">Selected Clients:</p>
<ul id="selectedClients">
</ul>
<br />
<table>
</table>
<p class="small">
<br />
<b>deauth [deauthentication attack]:</b><br />
Sends deauthentication frames and dissociation frames to the selected client(s) in the selected WiFi access point(s).
<br />
<b>Note: </b>
If no client is selected, the packets are sent as broadcast!
<br /><br />
<b>beacon [beacon flood attack]:</b><br />
(clone:) Spams beacon frames with a similar SSID as the selected WiFi access point(s).<br />
(list:) Spams beacon frames with all SSIDs in the list below.
<br /><br />
<b>probe request [probe request flood attack]:</b><br />
Spams probe request frames with all SSIDs in the list below.<br />
Usefull to confuse and spam WiFi trackers.
</p>
<br />
<p class="block bold">SSIDs: <span id="ssidCounter">0/64</span> <button class="marginNull warnBtn" onclick="clearSSID()">clear</button> <button class="marginNull" onclick="randomSSID()">random</button> <button class="marginNull" onclick="cloneSSID()">clone</button> <button class="marginNull" onclick="addSSID()">add</button></p>
<br />
<table>
</table>
<br />
<button class="marginNull warnBtn" onclick="resetSSID()">reset</button> <button class="marginNull" onclick="saveSSID()">save</button> <p class="small" id="saved">saved</p>
</div>
</body>
<script>
var selectedAPs = document.getElementById("selectedAPs");
var selectedClients = document.getElementById("selectedClients");
var table = document.getElementsByTagName("table")[0];
var ssidList = document.getElementsByTagName("table")[1];
var saved = document.getElementById("saved");
var ssidCounter = document.getElementById("ssidCounter");
var resultInterval;
var res;
function getResults(){
getResponse("attackInfo.json",function(responseText){
res = JSON.parse(responseText);
var aps = "";
var clients = "";
var tr = "<tr><th>Attack</th><th>Status</th><th>Start/Stop</th></tr>";
for(var i=0;i<res.aps.length;i++) aps += "<li>"+res.aps[i]+"</li>";
for(var i=0;i<res.clients.length;i++) clients += "<li>"+res.clients[i]+"</li>";
selectedAPs.innerHTML = aps;
selectedClients.innerHTML = clients;
for(var i=0;i<res.attacks.length;i++){
if(res.attacks[i].running) tr += "<tr class='selected'>";
else tr += "<tr>";
tr += "<td>"+res.attacks[i].name+"</td>";
if(res.attacks[i].status == "ready") tr += "<td style='color:#1ecb1e'>"+res.attacks[i].status+"</td>";
else tr += "<td style='color:#f00'>"+res.attacks[i].status+"</td>";
if(res.attacks[i].running) tr += "<td><button class='marginNull selectedBtn' onclick='startStop("+i+")'>stop</button></td>";
else tr += "<td><button class='marginNull' onclick='startStop("+i+")'>start</button></td>";
tr += "</tr>";
}
table.innerHTML = tr;
ssidCounter.innerHTML = res.ssid.length+"/64";
var tr = "<tr><th>Name</th><th>X</th></tr>";
for(var i=0;i<res.ssid.length;i++){
tr += "<tr>";
tr += "<td>"+res.ssid[i]+"</td>";
tr += '<td><button class="marginNull warnBtn" onclick="deleteSSID('+i+')">x</button></td>';
tr += "</tr>";
}
ssidList.innerHTML = tr;
},function(){
clearInterval(resultInterval);
location.reload();
});
}
function startStop(num){
getResponse("attackStart.json?num="+num,function(responseText){
if(responseText == "true") getResults();
else alert("error");
});
}
function addSSID(){
saved.innerHTML = "";
if(res.ssid.length >= 64) alert("SSID list full :(");
else{
var _ssidName = prompt("new SSID:");
if(_ssidName != null) getResponse("addSSID.json?name="+_ssidName,getResults);
}
}
function cloneSSID(){
saved.innerHTML = "";
if(res.ssid.length >= 64) alert("SSID list full :(");
else{
var _ssidName = prompt("new SSID:");
if(_ssidName != null) getResponse("cloneSSID.json?name="+_ssidName,getResults);
}
}
function deleteSSID(num){
saved.innerHTML = "";
getResponse("deleteSSID.json?num="+num,getResults);
}
function randomSSID(){
saved.innerHTML = "";
getResponse("randomSSID.json",getResults);
}
function clearSSID(){
saved.innerHTML = "";
getResponse("clearSSID.json",getResults);
}
function saveSSID(){
saved.innerHTML = "saved";
getResponse("saveSSID.json",getResults);
}
function resetSSID(){
saved.innerHTML = "saved";
getResponse("resetSSID.json",getResults);
}
function restartESP(){
getResponse("restartESP.json", function(responseText){
if(responseText == "true") getData();
else alert("error");
});
}
getResults();
resultInterval = setInterval(getResults,3000);
</script>
</html>

View File

@@ -1,147 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<script src="functions.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=0.8">
</head>
<body>
<nav>
<a href="index.html">APs</a>
<a href="clients.html">Clients</a>
<a href="attack.html">Attack</a>
<a href="settings.html">Settings</a>
<ul class="nav right">
<a href="#" onclick="restartESP()">Restart</a>
<ul>
</nav>
<div id="content">
<h1>Scan for client devices</h1>
<label for="scanTime">Scan time:</label>
<input type="number" id="scanTime" value="10">s
<button onclick="scan()" id="startScan">start</button>
<a id="clientScanStatus">ready!</a>
<br />
<p class="warn" id="status">AP will be off while scanning!<p>
<br />
<p class="block bold" id="clientsFound">Client devices found: 0</p>
<br />
</div>
<table>
</table>
</body>
<script>
var table = document.getElementsByTagName('table')[0];
var scanBtn = document.getElementById("startScan");
var scanTime = document.getElementById("scanTime");
var clientsFound = document.getElementById("clientsFound");
var scanStatus = document.getElementById("clientScanStatus");
var res;
function compare(a,b) {
if (a.p > b.p) return -1;
if (a.p < b.p) return 1;
return 0;
}
function toggleBtn(onoff){
if(onoff){
scanBtn.style.visibility = 'visible';
}else{
scanBtn.style.visibility = 'hidden';
}
}
function getResults(reload){
if(reload === undefined){
reload = false;
}
getResponse("ClientScanResults.json",function(responseText){
res = JSON.parse(responseText);
res.clients = res.clients.sort(compare);
clientsFound.innerHTML = "Client devices found: "+res.clients.length;
var tr = '';
if(res.clients.length > 0) tr += '<tr><th>Pkts</th><th>Vendor</th><th>Name</th><th>MAC</th><th>AP</th><th>Select</th></tr>';
for(var i=0;i<res.clients.length;i++){
if(res.clients[i].s == 1) tr += '<tr class="selected">';
else tr += '<tr>';
tr += '<td>'+res.clients[i].p+'</td>';
tr += '<td>'+res.clients[i].v+'</td>';
tr += '<td>'+res.clients[i].n+' <a class="blue" onclick="changeName('+res.clients[i].i+')">edit</a></td>';
tr += '<td>'+res.clients[i].m+'</td>';
tr += '<td>'+res.clients[i].a+'</td>';
if(res.clients[i].s == 1) tr += '<td><button class="marginNull selectedBtn" onclick="select('+res.clients[i].i+')">deselect</button></td>';
else tr += '<td><button class="marginNull" onclick="select('+res.clients[i].i+')">select</button></td>';
tr += '</tr>';
}
table.innerHTML = tr;
toggleBtn(true);
scanStatus.innerHTML = "";
if(reload == true) location.reload();
},function(){
location.reload();
},6000);
}
function scan(){
getResponse("ClientScan.json?time="+scanTime.value,function(responseText){
if(responseText == "true"){
scanStatus.innerHTML = "scanning...";
toggleBtn(false);
setTimeout(function(){
scanStatus.innerHTML = "reconnecting...";
getResults(true);
},scanTime.value*1000);
}
else alert(responseText);
});
}
function select(num){
getResponse("clientSelect.json?num="+num,function(responseText){
if(responseText == "true") getResults();
else alert("error :/");
});
}
function changeName(id){
var newName = prompt("Name for "+res.clients[id].m);
if(newName != null){
getResponse("setName.json?id="+id+"&name="+newName,function(responseText){
if(responseText == "true") getResults();
else alert("error");
});
}
}
function restartESP(){
getResponse("restartESP.json", function(responseText){
if(responseText == "true") getData();
else alert("error");
});
}
getResponse("ClientScanTime.json",function(responseText){
scanTime.value = responseText;
});
getResults();
</script>
</html>

View File

@@ -1,151 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<script src="functions.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=0.8">
</head>
<body>
<nav>
<a href="index.html">APs</a>
<a href="clients.html">Clients</a>
<a href="attack.html">Attack</a>
<a href="settings.html">Settings</a>
<ul class="nav right">
<a href="#" onclick="restartESP()">Restart</a>
<ul>
</nav>
<div id="content">
<h1>Scan for WiFi access points</h1>
<button onclick="scan()" id="apScanStart">scan</button>
<a id="scanInfo">scanning...</a>
<p class="block bold" id="networksFound">Networks found: 0</p>
<p class="small">
MAC: <span id="apMAC"></span>
</p>
<br />
</div>
<table>
</table>
<p class="small" id="wpa_info">
<br />
WPA* = WPA/WPA2 auto mode
<br>
continuous scan: <button onclick="startConScan()" id="startStopScan">start</button>
</p>
</body>
<script>
var table = document.getElementsByTagName('table')[0];
var networkInfo = document.getElementById('networksFound');
var scanBtn = document.getElementById('apScanStart');
var scanInfo = document.getElementById('scanInfo');
var apMAC = document.getElementById('apMAC');
var startStopScan = document.getElementById('startStopScan');
var autoScan = false;
var canScan = true;
function toggleBtn(onoff){
if(onoff && !autoScan){
scanInfo.style.visibility = 'hidden';
scanBtn.style.visibility = 'visible';
}else{
scanInfo.style.visibility = 'visible';
scanBtn.style.visibility = 'hidden';
}
}
function compare(a,b) {
if (a.r > b.r) return -1;
if (a.r < b.r) return 1;
return 0;
}
function getEncryption(num){
if(num == 8) return "WPA*";
else if(num == 4) return "WPA2";
else if(num == 2) return "WPA";
else if(num == 7) return "none";
else if(num == 5) return "WEP";
}
function getResults(){
toggleBtn(true);
getResponse("APScanResults.json",function(responseText){
var res = JSON.parse(responseText);
res.aps = res.aps.sort(compare);
networkInfo.innerHTML = "Networks found: "+res.aps.length;
apMAC.innerHTML = "";
var tr = '';
if(res.aps.length > 0) tr += '<tr><th>Ch</th><th>SSID</th><th>RSSI</th><th>Encrypt.</th><th>Select</th></tr>';
for(var i=0;i<res.aps.length;i++){
if(res.aps[i].se == 1) tr += '<tr class="selected">';
else tr += '<tr>';
tr += '<td>'+res.aps[i].c+'</td>';
tr += '<td>'+res.aps[i].ss+'</td>';
tr += '<td>'+res.aps[i].r+' <meter value="'+res.aps[i].r+'" max="-30" min="-100" low="-80" high="-60" optimum="-50"></meter></td>';
tr += '<td>'+getEncryption(res.aps[i].e)+'</td>';
if(res.aps[i].se){
tr += '<td><button class="marginNull selectedBtn" onclick="select('+res.aps[i].i+')">deselect</button></td>';
apMAC.innerHTML = res.aps[i].m;
}
else tr += '<td><button class="marginNull" onclick="select('+res.aps[i].i+')">select</button></td>';
tr += '</tr>';
}
table.innerHTML = tr;
canScan = true;
});
}
function scan(){
canScan = false;
toggleBtn(false);
getResponse("APScan.json",function(responseText){
if(responseText == "true") getResults();
else alert("error");
toggleBtn(true);
});
}
function startConScan(){
if(autoScan){
autoScan = false;
startStopScan.innerHTML = "start";
toggleBtn(true);
}else{
autoScan = true;
startStopScan.innerHTML = "stop";
toggleBtn(false);
}
}
function select(num){
getResponse("APSelect.json?num="+num,function(responseText){
if(responseText == "true") getResults();
else alert("error");
});
}
function restartESP(){
getResponse("restartESP.json", function(responseText){
if(responseText == "true") getData();
else alert("error");
});
}
getResults();
setInterval(function(){
if(autoScan && canScan) scan();
},1000);
</script>
</html>

View File

@@ -1,222 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<script src="functions.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=0.8">
</head>
<body>
<nav>
<a href="index.html">APs</a>
<a href="clients.html">Clients</a>
<a href="attack.html">Attack</a>
<a href="settings.html">Settings</a>
<ul class="right">
<a href="#" onclick="restartESP()">Restart</a>
<ul>
</nav>
<div id="content">
<h1>Settings</h1>
<p class="block bold">WiFi</p>
<label for="ssid">SSID:</label>
<input type="text" id="ssid" minlength="1" maxlength="32">
<br />
<label for="ssidHidden">hidden:</label>
<input type="checkbox" name="ssidHidden" id="ssidHidden" value="false">
<br />
<label for="password">password:</label>
<input type="text" id="password" minlength="8" maxlength="32">
<p class="small">must have at least 8 characters! (needs restart)</p>
<br />
<label for="apChannel">channel:</label>
<input type="number" id="apChannel" min="1" max="11">
<br />
<br />
<p class="block bold">AP scan</p>
<label for="apScanHidden">scan for hidden APs:</label>
<input type="checkbox" name="apScanHidden" id="apScanHidden" value="false">
<br />
<br />
<p class="block bold">Client Scan</p>
<label for="scanTime">default scan time:</label>
<input type="number" id="scanTime" min="1" max="255">s
<br />
<br />
<p class="block bold">Attack</p>
<label for="timeout">timeout:</label>
<input type="number" id="timeout" min="-1" max="65536">s
<p class="small">0 = no timeout</p>
<label for="ssidEnc">WPA2 beacons:</label>
<input type="checkbox" name="ssidEnc" id="ssidEnc" value="false">
<br />
<label for="useLed">use LED:</label>
<input type="checkbox" name="useLed" id="useLed" value="false">
<br />
<label for="deauthReason">deauth reason:</label>
<input type="number" id="deauthReason" min="1" max="45">
<p class="small">reason codes: <a target="_blank" href="http://www.aboutcher.co.uk/2012/07/linux-wifi-deauthenticated-reason-codes/">click</a></p>
<label for="packetRate">packetrate:</label>
<input type="number" id="packetRate" min="1" max="100">pkts/s
<p class="small">only for deauth attack - may cause instability!</p>
<label for="channelHop">channel hopping:</label>
<input type="checkbox" name="channelHop" id="channelHop" value="false">
<p class="small">only for deauth attack - packetrate will be ignored!</p>
<br />
<br />
<button class="warnBtn" onclick="resetSettings()">reset</button> <button onclick="saveSettings()">save</button><p class="small" id="saved"></p><br \>
<br />
<br />
<p class="block bold" id="clientNames">Client Names</p>
<table id="nameList">
<tr><th>MAC</th><th>Vendor</th><th>Name</th><th>X</th></tr>
</table>
<br />
<button class="warnBtn" onclick="clearNameList()">clear</button></p>
</div>
<table>
</table>
</body>
<script>
var nameListTable = document.getElementById('nameList');
var ssid = document.getElementById('ssid');
var ssidHidden = document.getElementById('ssidHidden');
var password = document.getElementById('password');
var apChannel = document.getElementById('apChannel');
var apScanHidden = document.getElementById('apScanHidden');
var scanTime = document.getElementById('scanTime');
var timeout = document.getElementById('timeout');
var deauthReason = document.getElementById('deauthReason');
var packetRate = document.getElementById('packetRate');
var saved = document.getElementById('saved');
var clientNames = document.getElementById('clientNames');
var ssidEnc = document.getElementById('ssidEnc');
var useLed = document.getElementById('useLed');
var channelHop = document.getElementById('channelHop');
var res;
function getData(){
getResponse("settings.json",function(responseText){
res = JSON.parse(responseText);
ssid.value = res.ssid;
ssidHidden.checked = res.ssidHidden;
password.value = res.password;
apChannel.value = res.apChannel;
apScanHidden.checked = res.apScanHidden;
scanTime.value = res.clientScanTime;
timeout.value = res.attackTimeout;
deauthReason.value = res.deauthReason;
packetRate.value = res.attackPacketRate;
ssidEnc.checked = res.attackEncrypted;
useLed.checked = res.useLed;
channelHop.checked = res.channelHop;
clientNames.innerHTML = "Client Names "+res.nameList.length+"/50";
var tr = '<tr><th>MAC</th><th>Vendor</th><th>Name</th><th>X</th></tr>';
for(var i=0;i<res.nameList.length;i++){
tr += '<tr>';
tr += '<td>'+res.nameList[i].m+'</td>';
tr += '<td>'+res.nameList[i].v+'</td>';
tr += '<td>'+res.nameList[i].n+' <a class="blue" onclick="changeName('+i+')">edit</a></td>';
tr += '<td><button class="marginNull warnBtn" onclick="deleteName('+i+')">x</button></td>';
tr += '</tr>';
}
nameListTable.innerHTML = tr;
});
}
function changeName(id){
var newName = prompt("Name for "+res.nameList[id].m);
if(newName != null){
getResponse("editNameList.json?id="+id+"&name="+newName,function(responseText){
if(responseText == "true") getData();
else alert("error");
});
}
}
function deleteName(id){
getResponse("deleteName.json?num="+id,function(responseText){
if(responseText == "true") getData();
else alert("error");
});
}
function saveSettings(){
saved.innerHTML = "saving...";
var url = "settingsSave.json";
url += "?ssid="+ssid.value;
url += "&ssidHidden="+ssidHidden.checked;
url += "&password="+password.value;
url += "&apChannel="+apChannel.value;
url += "&apScanHidden="+apScanHidden.checked;
url += "&scanTime="+scanTime.value;
url += "&timeout="+timeout.value;
url += "&deauthReason="+deauthReason.value;
url += "&packetRate="+packetRate.value;
url += "&ssidEnc="+ssidEnc.checked;
url += "&useLed="+useLed.checked;
url += "&channelHop="+channelHop.checked;
getResponse(url, function(responseText){
if(responseText == "true"){
getData();
saved.innerHTML = "saved";
}
else alert("error");
});
}
function resetSettings(){
getResponse("settingsReset.json", function(responseText){
if(responseText == "true"){
getData();
saved.innerHTML = "saved";
}
else alert("error");
});
}
function clearNameList(){
getResponse("clearNameList.json", function(responseText){
if(responseText == "true") getData();
else alert("error");
});
}
function restartESP(){
getResponse("restartESP.json", function(responseText){
if(responseText == "true") getData();
else alert("error");
});
}
getData();
</script>
</html>

63
web_server/attack.html Normal file
View File

@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
</head>
<body>
<nav>
<a href="/">APs</a>
<a href="clients">Clients</a>
<a href="attack">Attack</a>
<a href="settings">Settings</a>
<ul class="nav right">
<a href="#" onclick="restartESP()">Restart</a>
<ul>
</nav>
<div id="content">
<h1>Attack</h1>
<p class="block bold">Selected AP:</p>
<ul id="selectedAPs">
</ul>
<br />
<p class="block bold">Selected Clients:</p>
<ul id="selectedClients">
</ul>
<br />
<table>
</table>
<p class="small">
<br />
<b>deauth [deauthentication attack]:</b><br />
Sends deauthentication frames and dissociation frames to the selected client(s) in the selected WiFi access point(s).
<br />
<b>Note: </b>
If no client is selected, the packets are sent as broadcast!
<br /><br />
<b>beacon [beacon flood attack]:</b><br />
(clone:) Spams beacon frames with a similar SSID as the selected WiFi access point(s).<br />
(list:) Spams beacon frames with all SSIDs in the list below.
<br /><br />
<b>probe request [probe request flood attack]:</b><br />
Spams probe request frames with all SSIDs in the list below.<br />
Usefull to confuse and spam WiFi trackers.
</p>
<br />
<p class="block bold">SSIDs: <span id="ssidCounter">0/64</span> <button class="marginNull warnBtn" onclick="clearSSID()">clear</button> <button class="marginNull" onclick="randomSSID()">random</button> <button class="marginNull" onclick="cloneSSID()">clone</button> <button class="marginNull" onclick="addSSID()">add</button></p>
<br />
<table>
</table>
<br />
<button class="marginNull warnBtn" onclick="resetSSID()">reset</button> <button class="marginNull" onclick="saveSSID()">save</button> <p class="small" id="saved">saved</p>
</div>
<script src="js/functions.js"></script>
<script src="js/attack.js"></script>
</body>
</html>

41
web_server/clients.html Normal file
View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
</head>
<body>
<nav>
<a href="/">APs</a>
<a href="clients">Clients</a>
<a href="attack">Attack</a>
<a href="settings">Settings</a>
<ul class="nav right">
<a href="#" onclick="restartESP()">Restart</a>
<ul>
</nav>
<div id="content">
<h1>Scan for client devices</h1>
<label for="scanTime">Scan time:</label>
<input type="number" id="scanTime" value="10">s
<button onclick="scan()" id="startScan">start</button>
<a id="clientScanStatus">ready!</a>
<br />
<p class="warn" id="status">AP will be off while scanning!<p>
<br />
<p class="block bold" id="clientsFound">Client devices found: 0</p>
<br />
</div>
<table>
</table>
<script src="js/functions.js"></script>
<script src="js/clients.js"></script>
</body>
</html>

43
web_server/index.html Normal file
View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
</head>
<body>
<nav>
<a href="/">APs</a>
<a href="clients">Clients</a>
<a href="attack">Attack</a>
<a href="settings">Settings</a>
<ul class="nav right">
<a href="#" onclick="restartESP()">Restart</a>
<ul>
</nav>
<div id="content">
<h1>Scan for WiFi access points</h1>
<button onclick="scan()" id="apScanStart">scan</button>
<a id="scanInfo">scanning...</a>
<p class="block bold" id="networksFound">Networks found: 0</p>
<p class="small">
MAC: <span id="apMAC"></span>
</p>
<br />
</div>
<table>
</table>
<p class="small" id="wpa_info">
<br />
WPA* = WPA/WPA2 auto mode
<br>
continuous scan: <button onclick="startConScan()" id="startStopScan">start</button>
</p>
<script src="js/functions.js"></script>
<script src="js/index.js"></script>
</body>
</html>

101
web_server/js/attack.js Normal file
View File

@@ -0,0 +1,101 @@
var selectedAPs = document.getElementById("selectedAPs");
var selectedClients = document.getElementById("selectedClients");
var table = document.getElementsByTagName("table")[0];
var ssidList = document.getElementsByTagName("table")[1];
var saved = document.getElementById("saved");
var ssidCounter = document.getElementById("ssidCounter");
var resultInterval;
var res;
function getResults() {
getResponse("attackInfo.json", function(responseText) {
res = JSON.parse(responseText);
var aps = "";
var clients = "";
var tr = "<tr><th>Attack</th><th>Status</th><th>Start/Stop</th></tr>";
for (var i = 0; i < res.aps.length; i++) aps += "<li>" + res.aps[i] + "</li>";
for (var i = 0; i < res.clients.length; i++) clients += "<li>" + res.clients[i] + "</li>";
selectedAPs.innerHTML = aps;
selectedClients.innerHTML = clients;
for (var i = 0; i < res.attacks.length; i++) {
if (res.attacks[i].running) tr += "<tr class='selected'>";
else tr += "<tr>";
tr += "<td>" + res.attacks[i].name + "</td>";
if (res.attacks[i].status == "ready") tr += "<td style='color:#1ecb1e'>" + res.attacks[i].status + "</td>";
else tr += "<td style='color:#f00'>" + res.attacks[i].status + "</td>";
if (res.attacks[i].running) tr += "<td><button class='marginNull selectedBtn' onclick='startStop(" + i + ")'>stop</button></td>";
else tr += "<td><button class='marginNull' onclick='startStop(" + i + ")'>start</button></td>";
tr += "</tr>";
}
table.innerHTML = tr;
ssidCounter.innerHTML = res.ssid.length + "/64";
var tr = "<tr><th>Name</th><th>X</th></tr>";
for (var i = 0; i < res.ssid.length; i++) {
tr += "<tr>";
tr += "<td>" + res.ssid[i] + "</td>";
tr += '<td><button class="marginNull warnBtn" onclick="deleteSSID(' + i + ')">x</button></td>';
tr += "</tr>";
}
ssidList.innerHTML = tr;
}, function() {
clearInterval(resultInterval);
location.reload();
});
}
function startStop(num) {
getResponse("attackStart.json?num=" + num, function(responseText) {
if (responseText == "true") getResults();
else alert("error");
});
}
function addSSID() {
saved.innerHTML = "";
if (res.ssid.length >= 64) alert("SSID list full :(");
else {
var _ssidName = prompt("new SSID:");
if (_ssidName != null) getResponse("addSSID.json?name=" + _ssidName, getResults);
}
}
function cloneSSID() {
saved.innerHTML = "";
if (res.ssid.length >= 64) alert("SSID list full :(");
else {
var _ssidName = prompt("new SSID:");
if (_ssidName != null) getResponse("cloneSSID.json?name=" + _ssidName, getResults);
}
}
function deleteSSID(num) {
saved.innerHTML = "";
getResponse("deleteSSID.json?num=" + num, getResults);
}
function randomSSID() {
saved.innerHTML = "";
getResponse("randomSSID.json", getResults);
}
function clearSSID() {
saved.innerHTML = "";
getResponse("clearSSID.json", getResults);
}
function saveSSID() {
saved.innerHTML = "saved";
getResponse("saveSSID.json", getResults);
}
function resetSSID() {
saved.innerHTML = "saved";
getResponse("resetSSID.json", getResults);
}
getResults();
resultInterval = setInterval(getResults, 3000);

98
web_server/js/clients.js Normal file
View File

@@ -0,0 +1,98 @@
var table = document.getElementsByTagName('table')[0];
var scanBtn = document.getElementById("startScan");
var scanTime = document.getElementById("scanTime");
var clientsFound = document.getElementById("clientsFound");
var scanStatus = document.getElementById("clientScanStatus");
var res;
function compare(a, b) {
if (a.p > b.p) return -1;
if (a.p < b.p) return 1;
return 0;
}
function toggleBtn(onoff) {
if (onoff) {
scanBtn.style.visibility = 'visible';
} else {
scanBtn.style.visibility = 'hidden';
}
}
function getResults(reload) {
if (reload == undefined) {
reload = false;
}
getResponse("ClientScanResults.json", function(responseText) {
res = JSON.parse(responseText);
res.clients = res.clients.sort(compare);
clientsFound.innerHTML = "Client devices found: " + res.clients.length;
var tr = '';
if (res.clients.length > 0) tr += '<tr><th>Pkts</th><th>Vendor</th><th>Name</th><th>MAC</th><th>AP</th><th>Select</th></tr>';
for (var i = 0; i < res.clients.length; i++) {
if (res.clients[i].s == 1) tr += '<tr class="selected">';
else tr += '<tr>';
tr += '<td>' + res.clients[i].p + '</td>';
tr += '<td>' + res.clients[i].v + '</td>';
tr += '<td>' + res.clients[i].n + ' <a class="blue" onclick="changeName(' + res.clients[i].i + ')">edit</a></td>';
tr += '<td>' + res.clients[i].m + '</td>';
tr += '<td>' + res.clients[i].a + '</td>';
if (res.clients[i].s == 1) tr += '<td><button class="marginNull selectedBtn" onclick="select(' + res.clients[i].i + ')">deselect</button></td>';
else tr += '<td><button class="marginNull" onclick="select(' + res.clients[i].i + ')">select</button></td>';
tr += '</tr>';
}
table.innerHTML = tr;
toggleBtn(true);
scanStatus.innerHTML = "";
if (reload == true) location.reload();
}, function() {
location.reload();
}, 6000);
}
function scan() {
getResponse("ClientScan.json?time=" + scanTime.value, function(responseText) {
if (responseText == "true") {
scanStatus.innerHTML = "scanning...";
toggleBtn(false);
setTimeout(function() {
scanStatus.innerHTML = "reconnecting...";
getResults(true);
}, scanTime.value * 1000);
}
else alert(responseText);
});
}
function select(num) {
getResponse("clientSelect.json?num=" + num, function(responseText) {
if (responseText == "true") getResults();
else alert("error :/");
});
}
function changeName(id) {
var newName = prompt("Name for " + res.clients[id].m);
if (newName != null) {
getResponse("setName.json?id=" + id + "&name=" + newName, function(responseText) {
if (responseText == "true") getResults();
else alert("error");
});
}
}
getResponse("ClientScanTime.json", function(responseText) {
scanTime.value = responseText;
});
getResults();

View File

@@ -1,21 +1,28 @@
function getResponse(adr, callback, timeoutCallback, timeout){
if(timeoutCallback === undefined) {
timeoutCallback = function(){
location.reload()
};
}
if(timeout === undefined) {
timeout = 8000;
}
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState == 4){
if(xmlhttp.status == 200) callback(xmlhttp.responseText);
else timeoutCallback();
}
};
xmlhttp.open("GET", adr, true);
xmlhttp.send();
xmlhttp.timeout = timeout;
xmlhttp.ontimeout = timeoutCallback;
function getResponse(adr, callback, timeoutCallback, timeout){
if(timeoutCallback === undefined) {
timeoutCallback = function(){
location.reload()
};
}
if(timeout === undefined) {
timeout = 8000;
}
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState == 4){
if(xmlhttp.status == 200) callback(xmlhttp.responseText);
else timeoutCallback();
}
};
xmlhttp.open("GET", adr, true);
xmlhttp.send();
xmlhttp.timeout = timeout;
xmlhttp.ontimeout = timeoutCallback;
}
function restartESP() {
getResponse("restartESP.json", function(responseText) {
if (responseText == "true") getData();
else alert("error");
});
}

99
web_server/js/index.js Normal file
View File

@@ -0,0 +1,99 @@
var table = document.getElementsByTagName('table')[0];
var networkInfo = document.getElementById('networksFound');
var scanBtn = document.getElementById('apScanStart');
var scanInfo = document.getElementById('scanInfo');
var apMAC = document.getElementById('apMAC');
var startStopScan = document.getElementById('startStopScan');
var autoScan = false;
var canScan = true;
function toggleBtn(onoff) {
if (onoff && !autoScan) {
scanInfo.style.visibility = 'hidden';
scanBtn.style.visibility = 'visible';
} else {
scanInfo.style.visibility = 'visible';
scanBtn.style.visibility = 'hidden';
}
}
function compare(a, b) {
if (a.r > b.r) return -1;
if (a.r < b.r) return 1;
return 0;
}
function getEncryption(num) {
if (num == 8) return "WPA*";
else if (num == 4) return "WPA2";
else if (num == 2) return "WPA";
else if (num == 7) return "none";
else if (num == 5) return "WEP";
}
function getResults() {
toggleBtn(true);
getResponse("APScanResults.json", function(responseText) {
var res = JSON.parse(responseText);
res.aps = res.aps.sort(compare);
networkInfo.innerHTML = "Networks found: " + res.aps.length;
apMAC.innerHTML = "";
var tr = '';
if (res.aps.length > 0) tr += '<tr><th>Ch</th><th>SSID</th><th>RSSI</th><th>Encrypt.</th><th>Select</th></tr>';
for (var i = 0; i < res.aps.length; i++) {
if (res.aps[i].se == 1) tr += '<tr class="selected">';
else tr += '<tr>';
tr += '<td>' + res.aps[i].c + '</td>';
tr += '<td>' + res.aps[i].ss + '</td>';
tr += '<td>' + res.aps[i].r + ' <meter value="' + res.aps[i].r + '" max="-30" min="-100" low="-80" high="-60" optimum="-50"></meter></td>';
tr += '<td>' + getEncryption(res.aps[i].e) + '</td>';
if (res.aps[i].se) {
tr += '<td><button class="marginNull selectedBtn" onclick="select(' + res.aps[i].i + ')">deselect</button></td>';
apMAC.innerHTML = res.aps[i].m;
}
else tr += '<td><button class="marginNull" onclick="select(' + res.aps[i].i + ')">select</button></td>';
tr += '</tr>';
}
table.innerHTML = tr;
canScan = true;
});
}
function scan() {
canScan = false;
toggleBtn(false);
getResponse("APScan.json", function(responseText) {
if (responseText == "true") getResults();
else alert("error");
toggleBtn(true);
});
}
function startConScan() {
if (autoScan) {
autoScan = false;
startStopScan.innerHTML = "start";
toggleBtn(true);
} else {
autoScan = true;
startStopScan.innerHTML = "stop";
toggleBtn(false);
}
}
function select(num) {
getResponse("APSelect.json?num=" + num, function(responseText) {
if (responseText == "true") getResults();
else alert("error");
});
}
getResults();
setInterval(function() {
if (autoScan && canScan) scan();
}, 1000);

114
web_server/js/settings.js Normal file
View File

@@ -0,0 +1,114 @@
var nameListTable = document.getElementById('nameList');
var ssid = document.getElementById('ssid');
var ssidHidden = document.getElementById('ssidHidden');
var password = document.getElementById('password');
var apChannel = document.getElementById('apChannel');
var apScanHidden = document.getElementById('apScanHidden');
var scanTime = document.getElementById('scanTime');
var timeout = document.getElementById('timeout');
var deauthReason = document.getElementById('deauthReason');
var packetRate = document.getElementById('packetRate');
var saved = document.getElementById('saved');
var clientNames = document.getElementById('clientNames');
var ssidEnc = document.getElementById('ssidEnc');
var useLed = document.getElementById('useLed');
var channelHop = document.getElementById('channelHop');
var res;
function getData() {
getResponse("settings.json", function(responseText) {
res = JSON.parse(responseText);
ssid.value = res.ssid;
ssidHidden.checked = res.ssidHidden;
password.value = res.password;
apChannel.value = res.apChannel;
apScanHidden.checked = res.apScanHidden;
scanTime.value = res.clientScanTime;
timeout.value = res.attackTimeout;
deauthReason.value = res.deauthReason;
packetRate.value = res.attackPacketRate;
ssidEnc.checked = res.attackEncrypted;
useLed.checked = res.useLed;
channelHop.checked = res.channelHop;
clientNames.innerHTML = "Client Names " + res.nameList.length + "/50";
var tr = '<tr><th>MAC</th><th>Vendor</th><th>Name</th><th>X</th></tr>';
for (var i = 0; i < res.nameList.length; i++) {
tr += '<tr>';
tr += '<td>' + res.nameList[i].m + '</td>';
tr += '<td>' + res.nameList[i].v + '</td>';
tr += '<td>' + res.nameList[i].n + ' <a class="blue" onclick="changeName(' + i + ')">edit</a></td>';
tr += '<td><button class="marginNull warnBtn" onclick="deleteName(' + i + ')">x</button></td>';
tr += '</tr>';
}
nameListTable.innerHTML = tr;
});
}
function changeName(id) {
var newName = prompt("Name for " + res.nameList[id].m);
if (newName != null) {
getResponse("editNameList.json?id=" + id + "&name=" + newName, function(responseText) {
if (responseText == "true") getData();
else alert("error");
});
}
}
function deleteName(id) {
getResponse("deleteName.json?num=" + id, function(responseText) {
if (responseText == "true") getData();
else alert("error");
});
}
function saveSettings() {
saved.innerHTML = "saving...";
var url = "settingsSave.json";
url += "?ssid=" + ssid.value;
url += "&ssidHidden=" + ssidHidden.checked;
url += "&password=" + password.value;
url += "&apChannel=" + apChannel.value;
url += "&apScanHidden=" + apScanHidden.checked;
url += "&scanTime=" + scanTime.value;
url += "&timeout=" + timeout.value;
url += "&deauthReason=" + deauthReason.value;
url += "&packetRate=" + packetRate.value;
url += "&ssidEnc=" + ssidEnc.checked;
url += "&useLed=" + useLed.checked;
url += "&channelHop=" + channelHop.checked;
getResponse(url, function(responseText) {
if (responseText == "true") {
getData();
saved.innerHTML = "saved";
}
else alert("error");
});
}
function resetSettings() {
getResponse("settingsReset.json", function(responseText) {
if (responseText == "true") {
getData();
saved.innerHTML = "saved";
}
else alert("error");
});
}
function clearNameList() {
getResponse("clearNameList.json", function(responseText) {
if (responseText == "true") getData();
else alert("error");
});
}
getData();

View File

@@ -1,38 +1,38 @@
{
"sites":[
"error404.html",
"style.css",
"functions.js",
"index.html",
"clients.html",
"attack.html",
"settings.html"
],
"json":[
"APScanResults.json",
"ClientScanResults.json",
"settings.json",
"attackInfo.json"
],
"commands":[
"APScan.json",
"APSelect.json",
"ClientScan.json",
"ClientScanTime.json",
"clientSelect.json",
"setName.json",
"attackStart.json",
"settingsSave.json",
"settingsReset.json",
"deleteName.json",
"clearNameList.json",
"editNameList.json",
"addSSID.json",
"cloneSSID.json",
"deleteSSID.json",
"randomSSID.json",
"clearSSID.json",
"resetSSID.json",
"saveSSID.json"
]
{
"sites":[
"error404.html",
"style.css",
"functions.js",
"index.html",
"clients.html",
"attack.html",
"settings.html"
],
"json":[
"APScanResults.json",
"ClientScanResults.json",
"settings.json",
"attackInfo.json"
],
"commands":[
"APScan.json",
"APSelect.json",
"ClientScan.json",
"ClientScanTime.json",
"clientSelect.json",
"setName.json",
"attackStart.json",
"settingsSave.json",
"settingsReset.json",
"deleteName.json",
"clearNameList.json",
"editNameList.json",
"addSSID.json",
"cloneSSID.json",
"deleteSSID.json",
"randomSSID.json",
"clearSSID.json",
"resetSSID.json",
"saveSSID.json"
]
}

View File

@@ -1,50 +1,50 @@
<html>
<head>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script>
function miniEsc(){
var input = $('#input').val().replace(/\r\n|\r|\n/g," ").replace( /\s\s+/g, ' ' ).replace(/"/g, '\\"' );
$('#output').val(input);
$('#info1').html($('#input').val().length);
$('#info2').html(input.length);
}
function mini(){
var input = $('#input').val().replace(/\r\n|\r|\n/g," ").replace( /\s\s+/g, ' ' );
$('#output').val(input);
$('#info1').html($('#input').val().length);
$('#info2').html(input.length);
}
String.prototype.convertToHex = function (delim) {
return this.split("").map(function(c) {
return ("0" + c.charCodeAt(0).toString(16)).slice(-2);
}).join(delim || "");
};
function byte(){
var input = $('#input').val().convertToHex(",0x");
$('#output').val("0x"+input);
$('#info1').html($('#input').val().length);
$('#info2').html(input.length);
}
function byteAndmini(){
var input = "0x"+$('#input').val().replace(/\r\n|\r|\n/g," ").replace( /\s\s+/g, ' ' ).convertToHex(",0x");;
$('#output').val(input);
$('#info1').html($('#input').val().length);
$('#info2').html(input.length);
}
</script>
</head>
<body>
<textarea id="input" rows="50" cols="100"></textarea><br />
<p id="info1"></p>
<button onclick="mini()">minify</button>
<button onclick="miniEsc()">minify + escape</button>
<button onclick="byte()">byte-ify</button>
<button onclick="byteAndmini()">minify + byte-ify</button><br />
<textarea id="output" rows="50" cols="100"></textarea><br />
<p id="info2"></p>
</body>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script>
function miniEsc(){
var input = $('#input').val().replace(/\r\n|\r|\n/g," ").replace( /\s\s+/g, ' ' ).replace(/"/g, '\\"' );
$('#output').val(input);
$('#info1').html($('#input').val().length);
$('#info2').html(input.length);
}
function mini(){
var input = $('#input').val().replace(/\r\n|\r|\n/g," ").replace( /\s\s+/g, ' ' );
$('#output').val(input);
$('#info1').html($('#input').val().length);
$('#info2').html(input.length);
}
String.prototype.convertToHex = function (delim) {
return this.split("").map(function(c) {
return ("0" + c.charCodeAt(0).toString(16)).slice(-2);
}).join(delim || "");
};
function byte(){
var input = $('#input').val().convertToHex(",0x");
$('#output').val("0x"+input);
$('#info1').html($('#input').val().length);
$('#info2').html(input.length);
}
function byteAndmini(){
var input = "0x"+$('#input').val().replace(/\r\n|\r|\n/g," ").replace( /\s\s+/g, ' ' ).convertToHex(",0x");;
$('#output').val(input);
$('#info1').html($('#input').val().length);
$('#info2').html(input.length);
}
</script>
</head>
<body>
<textarea id="input" rows="50" cols="100"></textarea><br />
<p id="info1"></p>
<button onclick="mini()">minify</button>
<button onclick="miniEsc()">minify + escape</button>
<button onclick="byte()">byte-ify</button>
<button onclick="byteAndmini()">minify + byte-ify</button><br />
<textarea id="output" rows="50" cols="100"></textarea><br />
<p id="info2"></p>
</body>
</html>

View File

@@ -1,7 +1,7 @@
**1** open minifier.html
**2** paste the html code in the upper textfield
**3** click on `minifiy + byte-ify`
**4** copy the results
**5** go to data.h and replace the array (of the changed html file) with the copied bytes
**1** open minifier.html
**2** paste the html code in the upper textfield
**3** click on `minifiy + byte-ify`
**4** copy the results
**5** go to data.h and replace the array (of the changed html file) with the copied bytes
** now upload your new sketch :)**

99
web_server/settings.html Normal file
View File

@@ -0,0 +1,99 @@
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
</head>
<body>
<nav>
<a href="/">APs</a>
<a href="clients">Clients</a>
<a href="attack">Attack</a>
<a href="settings">Settings</a>
<ul class="right">
<a href="#" onclick="restartESP()">Restart</a>
<ul>
</nav>
<div id="content">
<h1>Settings</h1>
<p class="block bold">WiFi</p>
<label for="ssid">SSID:</label>
<input type="text" id="ssid" minlength="1" maxlength="32">
<br />
<label for="ssidHidden">hidden:</label>
<input type="checkbox" name="ssidHidden" id="ssidHidden" value="false">
<br />
<label for="password">password:</label>
<input type="text" id="password" minlength="8" maxlength="32">
<p class="small">must have at least 8 characters! (needs restart)</p>
<br />
<label for="apChannel">channel:</label>
<input type="number" id="apChannel" min="1" max="14">
<br />
<br />
<p class="block bold">AP scan</p>
<label for="apScanHidden">scan for hidden APs:</label>
<input type="checkbox" name="apScanHidden" id="apScanHidden" value="false">
<br />
<br />
<p class="block bold">Client Scan</p>
<label for="scanTime">default scan time:</label>
<input type="number" id="scanTime" min="1" max="255">s
<br />
<br />
<p class="block bold">Attack</p>
<label for="timeout">timeout:</label>
<input type="number" id="timeout" min="-1" max="65536">s
<p class="small">0 = no timeout</p>
<label for="ssidEnc">WPA2 beacons:</label>
<input type="checkbox" name="ssidEnc" id="ssidEnc" value="false">
<br />
<label for="useLed">use LED:</label>
<input type="checkbox" name="useLed" id="useLed" value="false">
<br />
<label for="deauthReason">deauth reason:</label>
<input type="number" id="deauthReason" min="1" max="45">
<p class="small">reason codes: <a target="_blank" href="http://www.aboutcher.co.uk/2012/07/linux-wifi-deauthenticated-reason-codes/">click</a></p>
<label for="packetRate">packetrate:</label>
<input type="number" id="packetRate" min="1" max="100">pkts/s
<p class="small">only for deauth attack - may cause instability!</p>
<label for="channelHop">channel hopping:</label>
<input type="checkbox" name="channelHop" id="channelHop" value="false">
<p class="small">only for deauth attack - packetrate will be ignored!</p>
<br />
<br />
<button class="warnBtn" onclick="resetSettings()">reset</button> <button onclick="saveSettings()">save</button><p class="small" id="saved"></p><br \>
<br />
<br />
<p class="block bold" id="clientNames">Client Names</p>
<table id="nameList">
<tr><th>MAC</th><th>Vendor</th><th>Name</th><th>X</th></tr>
</table>
<br />
<button class="warnBtn" onclick="clearNameList()">clear</button></p>
</div>
<table>
</table>
<script src="js/functions.js"></script>
<script src="js/settings.js"></script>
</body>
</html>

View File

@@ -1,161 +1,161 @@
/* Global */
*,
body {
margin: 0;
padding: 0;
font-family: arial;
color: #432929;
}
h1 {
font-size: 22px;
margin-bottom: 0.6em;
background: #00B0FF;
color: #fff;
padding: 0.2em;
border-radius: 4px;
}
button {
background: #00B0FF;
color: #fff;
border: 1px solid #8F8F8F;
border-radius: 14px;
padding: 0.34em 0.3em;
margin-bottom: 0.6em;
}
button:hover {
color: #000;
}
label {
display: inline-block;
width: 135px;
}
input {
height: 22px;
width: 120px;
border: 1px solid #A99D9D;
border-radius: 5px;
padding: 0.2em;
margin: 2px;
}
input[type="checkbox"] {
height: 15px;
width: auto;
}
.warn {
color: #c20000;
}
.warnBtn {
background: #c20000;
color: #fff;
}
.warnBtn:hover {
background: #f00;
color: #000;
}
.selectedBtn {
background: #fff;
color: #000;
}
.selectedBtn:hover {
background: #00B0FF;
color: #fff;
}
.right {
float: right;
}
.bold {
font-weight: bold;
}
.block {
display: block;
}
.marginNull {
margin: 0
}
.blue {
color: #00B0FF
}
.small {
font-size: 14px;
color: #727272
}
/* Navigation */
nav {
background: #222;
}
nav a {
background: #222;
color: #999;
padding: 0.5em;
display: inline-block;
text-decoration: none;
}
nav a:hover {
background: #000;
color: #f0f0f0;
}
/* Content */
#content {
padding: 0.34em;
}
table {
padding: 0;
width: 100%;
max-width: 960px;
margin: 0 auto;
border-spacing: 0;
background: #222222;
}
table th {
background: #222222;
color: #f0f0f0;
}
table td {
font-size: 14px;
background: #f0f0f0;
}
table th,
table td {
text-align: center;
padding: 0.1em 0;
}
table .selected td {
background: #11a4cc;
}
/* Specific */
#saved {
display: inline;
}
#clientScanStatus {
margin-left: 1em;
}
#clientScanStart {
margin-left: 30px;
}
#scanTime {
width: 60px;
}
#apScanStatus {
margin-left: 1em;
}
#rssiBar {
width: 100px;
height: 15px;
background: #fff;
}
#rssiBar > div {
width: 52px;
height: 15px;
background: #c20000;
}
#wpa_info {
padding-left: 0.34em;
}
#saved {
display: inline;
margin-left: 20px;
/* Global */
*,
body {
margin: 0;
padding: 0;
font-family: arial;
color: #432929;
}
h1 {
font-size: 22px;
margin-bottom: 0.6em;
background: #00B0FF;
color: #fff;
padding: 0.2em;
border-radius: 4px;
}
button {
background: #00B0FF;
color: #fff;
border: 1px solid #8F8F8F;
border-radius: 14px;
padding: 0.34em 0.3em;
margin-bottom: 0.6em;
}
button:hover {
color: #000;
}
label {
display: inline-block;
width: 135px;
}
input {
height: 22px;
width: 120px;
border: 1px solid #A99D9D;
border-radius: 5px;
padding: 0.2em;
margin: 2px;
}
input[type="checkbox"] {
height: 15px;
width: auto;
}
.warn {
color: #c20000;
}
.warnBtn {
background: #c20000;
color: #fff;
}
.warnBtn:hover {
background: #f00;
color: #000;
}
.selectedBtn {
background: #fff;
color: #000;
}
.selectedBtn:hover {
background: #00B0FF;
color: #fff;
}
.right {
float: right;
}
.bold {
font-weight: bold;
}
.block {
display: block;
}
.marginNull {
margin: 0
}
.blue {
color: #00B0FF
}
.small {
font-size: 14px;
color: #727272
}
/* Navigation */
nav {
background: #222;
}
nav a {
background: #222;
color: #999;
padding: 0.5em;
display: inline-block;
text-decoration: none;
}
nav a:hover {
background: #000;
color: #f0f0f0;
}
/* Content */
#content {
padding: 0.34em;
}
table {
padding: 0;
width: 100%;
max-width: 960px;
margin: 0 auto;
border-spacing: 0;
background: #222222;
}
table th {
background: #222222;
color: #f0f0f0;
}
table td {
font-size: 14px;
background: #f0f0f0;
}
table th,
table td {
text-align: center;
padding: 0.1em 0;
}
table .selected td {
background: #11a4cc;
}
/* Specific */
#saved {
display: inline;
}
#clientScanStatus {
margin-left: 1em;
}
#clientScanStart {
margin-left: 30px;
}
#scanTime {
width: 60px;
}
#apScanStatus {
margin-left: 1em;
}
#rssiBar {
width: 100px;
height: 15px;
background: #fff;
}
#rssiBar > div {
width: 52px;
height: 15px;
background: #c20000;
}
#wpa_info {
padding-left: 0.34em;
}
#saved {
display: inline;
margin-left: 20px;
}