h4cking, ham radio, fire fighting and m0re

Guest WIFI With Netgear WNDAP360

The other day I read an article about some nice project using a Raspberry Pi as a WIFI access point generating a guest WIFI network. The key is generated randomly and presented as QR code to the user on a small TFT display. For protection purposes it is re-generated every 24 hours or the like (See [1]).

I tried to implement such a thing using 802.1X with predefined user and passwords placing the user into a separated VLAN. That failed because it required SSL certificates signed by an official Certification Authority which I do not have. The other option would be to integrate my own CA into the devices connecting the wireless network. So no option either.

Inspired by the Howto mentioned above I analyzed the options to let some script configure a virtual access point on my WNDAP360 by Netgear. And I found it: You can enable SSH access (unfortunately not using RSA keys), login and configure the virtual access point (VAP) with some command line options.

I could not get it to work with some shell magic and sshpass so I wrapped everything into a little Perl script running on a server placed in the management network.

#!/usr/bin/perl -w

use String::Random;
use Net::SSH2;
use Imager::QRCode;

my $host = 'hostname';
my $user = 'admin';
my $password = 'askme';
my $essid = 'SSID';
my $type = 'WPA';
my $wwwpath = '/var/www/idunno';

my $string = new String::Random;
$string->{'A'} = [ 'A'..'Z', 'a'..'z', 0..9 ];
my $secret = $string->randpattern("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");

my $ssh2 = Net::SSH2->new();

# Log into the WIFI access point and change the password for the guest WIFI
$ssh2->connect($host) or die "Unable to connect $@\n";
$ssh2->auth_password( $user, $password ) or die "Unable to login $@\n";
my $chan = $ssh2->channel();

$chan->write("interface wlan 2.4GHz\n");
$chan->write("security-profile 3\n");
$chan->write("presharedkey $secret\n");

# Generate a new QR code
my $qrcode = Imager::QRCode->new(
   size => 10,
   margin => 5,
   version => 1,
   level => 'Q',
   casesensitive => 1,
   lightcolor => Imager::Color->new(255, 255, 255),
   darkcolor => Imager::Color->new(0, 0, 0),

my $img = $qrcode->plot("WIFI:S:$essid;T:$type;P:$secret;;");
$img->write(file => "$wwwpath/qrcode.png");

my $filename = "$wwwpath/wifikey.txt";
open(my $fh, '>', $filename) or die "Could not open file '$filename' $!";
print $fh time();
print $fh "\n";
print $fh "$secret\n";
close $fh;


This emulates a SSH login and executes the commands to re-configure the WIFI key. In this case it is security profile number 3 - I use the other two for personal purposes. The image with the QR-code contains not only the key but some sort of string that can be used to configure Android devices automatically. The QR-code is placed in the web servers path. The web page is then displayd by the tablet installed to the wall in the living room.

The second part is a PHP file served by the web server. It displays the QR-code and has some java script magic to calculate the remaining time the displayed key is valid for. In my case the key is changes ervery 24 hours at 3 AM.

The source for the web page is:

$file = fopen("wifikey.txt", "r") or die("Unable to open file!");
$timestamp = trim(fgets($file));
$timestamp += 86400;
echo "<!DOCTYPE html>\n";
echo "<html>\n";
echo "<head>\n";
echo "<title>Guest WIFI Key</title>\n";
echo "<meta http-equiv=\"refresh\" content=\"3600\">\n";
echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n";
echo "<script>\n";
echo "function startTimer(display) {\n";
echo "    var end = $timestamp\n";
echo "    var start =,\n";
echo "        diff,\n";
echo "        minutes,\n";
echo "        seconds;\n";
echo "    function timer() {\n";
echo "        // get the number of seconds that have elapsed since \n";
echo "        // startTimer() was called\n";
echo "        diff = end - (( / 1000) | 0);\n";
echo "\n";
echo "        // does the same job as parseInt truncates the float\n";
echo "        hours   = (diff / 3600) | 0;\n";
echo "        minutes = ((diff - (hours * 3600)) / 60) | 0;\n";
echo "        seconds = (diff % 60) | 0;\n";
echo "\n";
echo "        hours   = hours   < 10 ? \"0\" + hours   : hours;\n";
echo "        minutes = minutes < 10 ? \"0\" + minutes : minutes;\n";
echo "        seconds = seconds < 10 ? \"0\" + seconds : seconds;\n";
echo "\n";
echo "        display.textContent = hours + \":\" + minutes + \":\" + seconds; \n";
echo "\n";
echo "        if (diff <= 0) {\n";
echo "            // add one second so that the count down starts at the full duration\n";
echo "            // example 05:00 not 04:59\n";
echo "            start = + 1000;\n";
echo "        }\n";
echo "    };\n";
echo "    // we don't want to wait a full second before the timer starts\n";
echo "    timer();\n";
echo "    setInterval(timer, 1000);\n";
echo "}\n";
echo "\n";
echo "window.onload = function () {\n";
echo "        display = document.querySelector('#time');\n";
echo "    startTimer(display);\n";
echo "};\n";
echo "</script>\n";
echo "</head>\n";
echo "<body>\n";
echo "<center><h1>Guest WIFI Key</h1></center>\n";
echo "<p><img src=\"qrcode.png\"></p>\n";
echo "<p>";
echo fgets($file);
echo "</p>\n";
echo "<div>valid for <span id=\"time\"></span></div>\n";
echo "</body>\n";
echo "</html>\n";

The Perl script is executed by a cron job and the web page automatically reloads every hour in order to display the correct key on the next day.

Apparently there is no error checking during executing the commands to change the WIFI key. So it may be possible that this solution fails :).