This post appeared originally in our sysadvent series and has been moved here following the discontinuation of the sysadvent microsite

A firewall activity plot for showing port access. The temptation was just a bit too great to ignore, so I chose to see it as a canvas for artwork. All I should need to do is to convert a PNG image to series of nmap commands, easy right?

Plot size

First off, lets determine the actual size of the canvas – I mean plot. The graph area is 1040x417 pixels, but since the graph is logarithmic, the bottom part is not really usable, so I decide to limit the height to 237 pixels, which corresponds to port 80 and up. I need to find the logarithmic boundaries as well as the logarithmic value per vertical pixel, or “Y”:

Highest port available: log(65535) = 4.81647330376525
Lowest port I will use:  log(80) = 1.90308998699194

Delta Y(port) = 65535-80 = 65455
Delta Y(log) = 4.81647330376525 - 1.90308998699194 = 2.91338331677331
Logarithmic value per pixel Y is: 2.91338331677331 / 65455 = 0.012292756610858

The coordinate system of a a PNG image starts at top left corner, so all port values have to be calculated from the top. A pixels Y value will therefor be calculated as:

Bottom margin (1.90308998699194) + ( height value (2.91338331677331) - ( pixel(Y) * 0.012292756610858 ) )
Use this as power to find the port:  Port = 10^value

Although this may seem like high precision, the initial values are based on eyeballing, so we need to allow for some stretching in the final result.

It’s now easy to map each column in an image to ports, so the next step is to find the horizontal resolution. The plot is 1040 pixels wide, which spans exactly 24 hours, or 86400 seconds:

86400 sec / 1040 pixel = 83.0769230769231 sec/pixel

So I need to tickle the firewall every 83 seconds to get nice pixel values. The decimals can be ignored here, as the final script will introduce some delays anyway. After all, I’m drawing on a firewall, not trying to rescue Matt Damon from Mars…

Monochrome image

With the canvas in place, the next step is to prepare a monochrome PNG file from an image. The easiest type is an image with just 1 indexed colour. I created mine using Gimp, but I would think any decent image program would work.

Begin with creating a new image with the maximal usable size: 1040 x 237 pixels with transparent background. Now just draw what you want, using just one colour. Take care to use the top of the image as reference line. Then change the image mode to 1-bit “Indexed” (black and white). The transparent background should remain, and the single colour be white. Finally, crop the image to remove unneeded margins on the sides and bottom, but keep the top. Export as PNG.

Now, I used python to convert the image to a shell script. The code is not elegant, but it does the job.

#!/usr/bin/python

from PIL import Image
import math

inimage = "demo.png"  # Hard coded filename
lowest = 1.90308998699194
deltay = 2.91338331677331
valuey = 0.012292756610858
target = "target.example.org"

im = Image.open(inimage) # Use im to find image size
pix = im.load()          # Use pix to address individual pixels

print("#!/bin/bash")
for x in range(im.size[0]):
  ports=[]
  for y in range(im.size[1]):
    if pix[x,y]==1:
      ports.append(str(int(math.pow(10,(lowest+(deltay-(y*valuey)))))))
  if ports:
    print("nmap -4 -Pn -p {:s} {:s}".format(','.join(ports),target))
  print("sleep 82")     # nmap use around 1 second, so sleep the remaining 82.

Running the script

Now, run the python script and output to a shell script, i.e. drawplot.sh. Normally, nmap needs to be run as root, so sudo access is useful. Next to consider is when to run the script. I found that on the target the plot is only updated once approx 20 minutes past every hour. If the image is less than 40 pixels wide, it can fit within one hour, and by starting it, say 22 minutes past the hour, the full image can appear at once. Of course, if the image is wider, it will cover more than one hour, and other start time concerns may be taken into account instead.

$ sudo at 16:22
at> drawplot.sh

All is left is to wait and see the results …

More colours

I know that IPv4 TCP-packets becomes green plots. The other colours are light blue for IPv4 UDP, red for IPv6 TCP, and white for IPv6 UDP. Not the greatest palette for artwork, and I’m no artist, but thought I would make a little something in the holiday spirit.

I’ll create a PNG image with four indexed colours on a transparent background, making sure the colourmap is arranged correctly, and make a small addition to the python script. One thing to remember is that each column will consist of 0 to 4 nmap commands, so we cannot just sleep a constant number of seconds between each. Adding a frequency variable, a column counter, and calculating seconds using date, should even out the delays, averaging to one column every 83 seconds. Again, I’m not trying to make the script elegant, it just has to work:

#!/usr/bin/python

from PIL import Image
import math

inimage = "xmastree.png"  # Hard coded filename
lowest = 1.90308998699194
deltay = 2.91338331677331
valuey = 0.012292756610858
target = "target.example.org"

im = Image.open(inimage) # Use im to find image size
pix = im.load()          # Use pix to address individual pixels

nmap_opts = [ None, "", "-sU", "-6", "-6 -sU" ]
print("#!/bin/bash")
print("FREQ=83")
print("START=$( date +%s )")
print("COLS=0")
for x in range(im.size[0]):
  ports = [[],[],[],[],[]]
  for y in range(im.size[1]):
    port="{:d}".format(int(math.pow(10,(lowest+(deltay-(y*valuey))))))
    colour = pix[x,y]
    if colour >=1 and colour <=4:
      ports[colour].append(port)

  for colour in range(1,5):
    if ports[colour]:
      print("nmap {:s} -Pn -p {:s} {:s}".format(nmap_opts[colour],','.join(ports[colour]),target))
  print("let COLS++")
  print("sleep $(( ${START} + ${COLS} * ${FREQ} - $( date +%s ) ))")

As before, run the python script and output to shell script xmastree.sh, and start that at a selected time…

Xmas tree
A suitable 4-colour Christmas greeting

Containerized Development Environment

Do you spend days or weeks setting up your development environment just the way you like it when you get a new computer? Is your home directory a mess of dotfiles and metadata that you’re reluctant to clean up just in case they do something useful? Do you avoid trying new versions of software because of the effort to roll back software and settings if the new version doesn’t work?

Take control over your local development environment with containerization and Dev-Env-as-Code!

... [continue reading]

Ansible-runner

Published on February 27, 2024

Portable Java shell scripts with Java 21

Published on February 21, 2024