Exfiltrating data without a network connection

If you have access to a cloud server or a hosted platform which you could only interact with via some web view, but desperately need the data off it, fret not. Even if the system has no way of communicating via the network in or out, you can still get what you want. All you need is a shell prompt and a GUI.

The idea is simple:

  • Convert data to an image
  • Display the image
  • Take a snapshot
  • Convert image back to data


  • This method is good for data that can fit on your screen, say 1024x800px. Using the safest one px to one bit encoding that’s 100k of data. If you have more data you could split it multiple images, or try higher density encoding (grayscale or even rgb) but this has a serious risk of corruption shall any images be lossy compressed in the pipe. Alternatively you could split the data into 100k chunks and transfer it separately.
  • The system you’re exfilling data from just needs a shell and a way to display an image. A browser or imagemagick would suffice.

Here are the technical details. I assume 1000px is a good horizontal size, that fits on most screens.

  1. On the target system, run the following in bash.

    t=file.name s=$(stat -c%s $t) # size h=$((s/10008+8)) # image height r=$((h1000/8-s)) # remaining filler

  2. Make it into a portable bitmap image. Here I am using class P4 (black and white). The spaces and the semicolons are important.

    { echo -e “P4\n1000 $h\n”; cat $t; printf ‘\0%.0s’ $(eval “echo {1..$r}”); } > $t.pbm

The subshell trickery is to add a header and pad the tail to make it a correct pbm.

  1. Display it at 100% resolution with no distortions or overlaps. With imagemagick simply do

display $t.pbm or convert to png and display with something else

  1. Do a screenshot or, if you have an HTML5 canvas dump it like this (inside the browser JS prompt)

    var c=document.getElementById(“alpha”); var d=c.toDataURL(“image/png”); var w=window.open(‘about:blank’,’image from canvas’); w.document.write(“ img src=’“+d+”’ alt=’from canvas’/ “);

Carefully crop the resulting image back to the original size (1000x$h) either with gimp (easiest) or with imagemagick.

  1. Convert back to data

convert can.png can.pbm

and trim the header and the tail out

dd if=can.pbm of=result bs=1 skip=13 count=$((87012-13-90))

In this example 87012 is the size of the image, 13 is the header length, 90 is the remainder ($r - tail padding) less one byte


  1. To do the full grayscale PGM use this format. It is risky though since any image compression, or even a color shift will make your data bad

    h=$((s/1000+1) r=$((h*1000-s)) { echo -e “P5\n1000 $h\n255\n”; cat $t; printf ‘\0%.0s’ $(eval “echo {1..$r}”); } > $t.pgm; (note the additional 255 header)

  2. You can do a 4 bit grayscale, which is less likely to be affected by brightness/contrast/gamma shift, but trickier, since you need to modify source data. Split bytes in half and shifting left to magnify the difference, then convert to the pgm, substituting 255 in the header for 16

    h=$((s/10002+2) r=$((h1000/2-s))