RFCLand
Categories: forensics
2020-07-24
by not_really
All RFCs are created equal, right?
Author: DrDinosaur
Files: challenge.pcap
All RFCs are created equal, right?
Author: DrDinosaur
Files: challenge.pcap
If we browse through the pcap in wireshark, we’ll see lots of base64 being sent and some responses coming back. We should probably first make a list of all that base64 stuff. We’ll export all data like so:
tshark -Tfields -e data -r challenge.pcap | xxd -r -p > out.txt
Essentially, we use -Tfields to print out a list of certain values, -e data to print only data, and -r to read from pcap. The output is in hex though, so we use xxd -r -p to read from the pipe and convert back to text. Then we output that to out.txt.
If we look inside the file it’s going to be a little hard to read since there won’t be any line breaks.
Since the important stuff is in curly braces, we can write a script to only text inside of the {}.
text = open("out.txt", "r").read()
outText = ""
charsUntilStartReading = -1
for c in text:
if c == "{":
charsUntilStartReading = 10 # skip {"data": "
elif c == "}":
charsUntilStartReading = -1
outText = outText[:-1] # remove "
outText += "\n"
if charsUntilStartReading == 0:
outText += c
if charsUntilStartReading > 0:
charsUntilStartReading -= 1
open("outfmt.txt", "w").write(outText)
We have lots of base64 so let’s export all of it to files so we can look at it in a hex editor.
import base64
text = open("outfmt.txt", "r").read()
i = 0
for line in text.splitlines():
open(f"outline_{str(i).zfill(3)}.dat", "w").write(base64.b64decode(line))
i += 1
Hmm… we seem to get a Invalid base64-encoded string
error. Let’s try catch. What could go wrong?
import base64
text = open("outfmt.txt", "r").read()
i = 0
for line in text.splitlines():
try:
open(f"outline_{str(i).zfill(3)}.dat", "w").write(base64.b64decode(line))
except:
print(f"rip {i}")
i += 1
Okay… all of them failed. Let’s try adding a = or two. (Yes, I am not proud of this code but it works)
import base64
text = open("outfmt.txt", "r").read()
i = 0
for line in text.splitlines():
fileName = f"outline_{str(i).zfill(3)}.dat"
decoded = ""
passed = False
try:
decoded = base64.b64decode(line)
print(f"pass {i}")
passed = True
except:
try:
decoded = base64.b64decode(line + "=")
print(f"pass {i}")
passed = True
except:
try:
decoded = base64.b64decode(line + "==")
print(f"pass {i}")
passed = True
except:
print(f"rip {i}")
if passed:
open(fileName, "wb").write(decoded)
i += 1
Let’s use the same trick as Raymonds and print out the headers.
for file in *; do echo $file; xxd $file | head -1; done
There’s too many to list here, but there seem to be more jpeg headers in the first half than in the second. Seems like we need to combine them somehow. Because jpegs are special, you don’t need the whole file to get an idea of what the picture looks like. Let’s take a look by renaming everything to jpeg with for file in *.dat; do mv "$file" "${file%.dat}.jpg"; done
or ren *.dat *.jpg
on windows.
Hmm, 95 looks like it has text. Let’s see what it says.
It’s the flag! But we aren’t able to see the whole image because there’s still more to the file missing. It must be one of those other non jpeg header files we saw, right? Let’s test that by combining all the other base64 lines to this one image.
import base64
text = open("outfmt.txt", "r").read()
firstPart = text.splitlines()[95]
i = 0
for line in text.splitlines():
fileName = f"outline_{str(i).zfill(3)}.jpg"
line = firstPart + line
decoded = ""
passed = False
try:
decoded = base64.b64decode(line)
print(f"pass {i}")
passed = True
except:
try:
decoded = base64.b64decode(line + "=")
print(f"pass {i}")
passed = True
except:
try:
decoded = base64.b64decode(line + "==")
print(f"pass {i}")
passed = True
except:
print(f"rip {i}")
if passed:
open(fileName, "wb").write(decoded)
i += 1
Only one of these has more of the message and that’s 106.
Okay, let’s do it one more time.
import base64
text = open("outfmt.txt", "r").read()
firstPart = text.splitlines()[95] + text.splitlines()[106]
i = 0
for line in text.splitlines():
fileName = f"pcapout/outline_{str(i).zfill(3)}.jpg"
line = firstPart + line
decoded = ""
passed = False
try:
decoded = base64.b64decode(line)
print(f"pass {i}")
passed = True
except:
try:
decoded = base64.b64decode(line + "=")
print(f"pass {i}")
passed = True
except:
try:
decoded = base64.b64decode(line + "==")
print(f"pass {i}")
passed = True
except:
print(f"rip {i}")
if passed:
open(fileName, "wb").write(decoded)
i += 1
Nice, looks like 223 has it.
It turns out that this was not the intended way to solve this problem. Let’s go back to wireshark with this newfound knowledge and look at 95, 106, and 223. (Packet no. is 7*index+4, so 669, 746, and 1565)
It’s actually nowhere in the tcp messages but instead in the ipv4 header. Scroll up, open Internet Protocol Version 4
, and then Flags
. You’ll notice that they all have a reserved bit set (known as the “evil” bit). You can filter by ip.flags.rb == 1
or ip.flags == 0x8000
. We can see the 5 packets that send our valid image. Let’s try that for a Full Ultra HD:tm: image.
import base64
text = open("outfmt.txt", "r").read()
base = (text.splitlines()[95] +
text.splitlines()[106] +
text.splitlines()[223] +
text.splitlines()[244] +
text.splitlines()[245])
open("out.jpg", "wb").write(base64.b64decode(base))
Okay, maybe it’s not that quality, but it’s better than that potato image we the first time.
In case you’re interested, here’s the rfc: https://www.ietf.org/rfc/rfc3514.txt