mirror of https://github.com/dirtbags/moth.git
Add pflarr's new net-re puzzles
This commit is contained in:
parent
486250b252
commit
fd4a3f978e
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
|
@ -0,0 +1,2 @@
|
||||||
|
all:
|
||||||
|
cp *.png $(DESTDIR)
|
|
@ -0,0 +1,33 @@
|
||||||
|
Time to make things difficult
|
||||||
|
=============================
|
||||||
|
|
||||||
|
So far we've been using xor as a tool to combine a message with a key to obfuscate a message. There's another word for what we've been doing: Cryptography.
|
||||||
|
|
||||||
|
The xor operation can be thought of as a trivial cryptographic cypher. It can be replaced with a wide variety of other operations, some of which we'll consider in the next few exercizes.
|
||||||
|
|
||||||
|
First though, we should consider how we've been using this cypher. In cryptography terms, we've been using it in Electronic Codebook Mode (ECB). ECB mode takes the plaintext and key as inputs to an encryption method and produces a cooresponding cyphertext. Given the same plaintext and key, the same cypher text will always result. This property has been key to figuring out many of the previous puzzles, and we're about to throw it out.
|
||||||
|
|
||||||
|
![Electronic Code Book](ECB.png)
|
||||||
|
|
||||||
|
Cipher Block Chaining
|
||||||
|
=====================
|
||||||
|
|
||||||
|
![Cipher Block Chaining](CBC.png)
|
||||||
|
|
||||||
|
Cipher block chaining is the most commonly used mode when encrypting data with an encryption algorithm. Unlike ECB mode, the resulting cyphertext at each step is dependent upon all the cypher text values that preceeded it. In the first step there is no preceding cypher text to rely on, so instead an initialization vector is used. It forms, in essence a secondary key.
|
||||||
|
|
||||||
|
![Cipher Block Chaining Decryption](CBC_decrypt.png)
|
||||||
|
|
||||||
|
Other Modes
|
||||||
|
===========
|
||||||
|
|
||||||
|
There are several other modes used with crypto algorithms that we won't be covering here. The wikipedia page on block cypher modes is a good resource in this regard.
|
||||||
|
|
||||||
|
The only thing that bears mentioning in regards to the other modes is that a couple of them (Output FeedBack and Cipher Feedback) allow for encryption functions that are not reversible, as long as they are replayable. The ciphers only act as key factories for each step in the encryption process, so we only need to get them to reproduce the keys. You can essentially use any operation as the cipher in this case: addition, complicated bit logic, or even a random number generator.
|
||||||
|
|
||||||
|
The problem
|
||||||
|
===========
|
||||||
|
|
||||||
|
Decrypt the following using CBC mode with 'xor' as the encryption algorithm, 0x5Ae2 as a two byte key, and 0x3a21 as an initialization vector (You'll be encrypting to bytes at a time):
|
||||||
|
|
||||||
|
08 a6 3e 28 0b ea 32 7a 11 e8 3f 65 44
|
|
@ -0,0 +1 @@
|
||||||
|
hello crypto!
|
|
@ -0,0 +1,17 @@
|
||||||
|
msg = 'hello crypto!'
|
||||||
|
|
||||||
|
v = [0x3a, 0x21]
|
||||||
|
k = (0x5A, 0xe2)
|
||||||
|
ct = []
|
||||||
|
for i in range(0, len(msg), len(k)):
|
||||||
|
for j in range(len(k)):
|
||||||
|
if (i+j < len(msg)):
|
||||||
|
p = ord(msg[i+j])
|
||||||
|
r = (p ^ v[j]) ^ k[j]
|
||||||
|
v[j] = r
|
||||||
|
ct.append(r)
|
||||||
|
|
||||||
|
for v in ct:
|
||||||
|
print '%02x' % v,
|
||||||
|
print
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
all: exe
|
||||||
|
cp data.pcap $(DESTDIR)
|
||||||
|
|
||||||
|
exe: exe.c
|
||||||
|
gcc -o exe exe.c
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printf("The key is 'intangible'.");
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
More Cipher Block Chaining
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Decrypt the file in the attached pcap. This is like the prior problem, except this time you don't know the key or initialization vector (IV). The key and IV are each one byte long.
|
||||||
|
|
||||||
|
If you're using Windows, [file for Windows](http://gnuwin32.sourceforge.net/packages/file.htm) might be useful.
|
|
@ -0,0 +1 @@
|
||||||
|
intangible
|
|
@ -0,0 +1,28 @@
|
||||||
|
from socket import *
|
||||||
|
|
||||||
|
data = open('exe', 'rb').read()
|
||||||
|
|
||||||
|
bs = 1020
|
||||||
|
pos = 0
|
||||||
|
tid = 0x52ab
|
||||||
|
pid = 0
|
||||||
|
|
||||||
|
key = 0x52
|
||||||
|
IV = 0xe3
|
||||||
|
|
||||||
|
data = [ord(c) for c in data]
|
||||||
|
ciphertext = []
|
||||||
|
for c in data:
|
||||||
|
ct = (IV ^ c) ^ key
|
||||||
|
IV = ct
|
||||||
|
ciphertext.append(ct)
|
||||||
|
ciphertext = ''.join([chr(c) for c in ciphertext])
|
||||||
|
|
||||||
|
|
||||||
|
sock = socket(AF_INET, SOCK_DGRAM)
|
||||||
|
addr = ("127.0.0.1", 49143)
|
||||||
|
|
||||||
|
while (pos < len(data)):
|
||||||
|
sock.sendto("%04x%04x%s" % (tid, pid, data[pos:pos+bs]), addr)
|
||||||
|
pos = pos + bs
|
||||||
|
pid += 1
|
|
@ -0,0 +1,2 @@
|
||||||
|
all:
|
||||||
|
cp sbox.pcap sbox.txt $(DESTDIR)
|
|
@ -0,0 +1,34 @@
|
||||||
|
Substitution Boxes
|
||||||
|
==================
|
||||||
|
|
||||||
|
As previously mentioned, we can use operations other than XOR to 'encrypt' our data. Substitution Boxes, or S-Boxes, are used as a step in many block ciphers, including AES, Blowfish, and DES. S-Boxes are trivial to implement in both high level languages and assembly (it's just an array).
|
||||||
|
|
||||||
|
Here's an example 4 bit S-Box:
|
||||||
|
|
||||||
|
| 00 | 01 | 10 | 11
|
||||||
|
------------------------
|
||||||
|
00 | 1 | a | c | 0
|
||||||
|
------------------------
|
||||||
|
01 | b | 3 | 2 | 7
|
||||||
|
------------------------
|
||||||
|
10 | 5 | 6 | 4 | 9
|
||||||
|
------------------------
|
||||||
|
11 | 8 | f | e | d
|
||||||
|
|
||||||
|
Two input bits select the column, two input bits select the row. The resulting value is the output. To reverse the process, we must invert the S-Box such that the output values produce the input values. You would also need to know which bits of input selected the column and which selected the row.
|
||||||
|
|
||||||
|
There isn't a key involved in the S-Box, other than the arrangement of values in the box. Both the input values are data to be encoded. In some algorithms, like AES, the S-Box is a constant, and the key is involved at other steps in the process. In other cases, like BlowFish, the key is used to generate the sbox itself.
|
||||||
|
|
||||||
|
To unravel an S-Box, you really need to know what the values contained in it actually are. To get the values, you may have to have a malware reverse engineer poke around in the binary. S-Boxes vary in size, but are commonly 16x16, using the upper four bits in a character to index the row, and the lower 4 bits for the column. Look for a chunk of data 256 bytes long that doesn't contain any repeated values.
|
||||||
|
|
||||||
|
Is it likely that you'll find this method actually being used? I hope so, since it's actually solvable. Instead you may find an Sbox associated with a commonly used crypto system such as AES, which would at least tell you the algorithm being used. That's solvable too, but you'll have to acquire the actual AES key!
|
||||||
|
|
||||||
|
The problem
|
||||||
|
===========
|
||||||
|
|
||||||
|
Working with a reverse engineer, you determined that the packets in the following pcap were encrypted using an Sbox in ECB mode. You even found the arrangement of the sbox in the malware.
|
||||||
|
|
||||||
|
The Key
|
||||||
|
=======
|
||||||
|
|
||||||
|
The key is the title of the book that was sent.
|
|
@ -0,0 +1 @@
|
||||||
|
a connecticut yankee in king arthur's court
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
22d9c2417e72a6ca1707d66ec10071b73af2e709088bf060386b0a3b73823e3d397a4e05d893a830fedd2058b9b6ef6dd3ab5098e3d784970c42cb682b8fb3ad5e7085c887627f0b10b5fb9b612a83fac68ddf3786f496744590f501c9d1e4bcce7d8e6312e0922e797653518aa2a0690d33289de1cdccd23cdbac180403bf401ea3541627882c49ba6f4f780e5d66311fec9135a7dc0f0629c02fbdb29944fcd58c43af649f55ae194df9ea52e856f6c4674602e914daf89c2de5fdb1805b5934b06c261db44c895ae6ed11365cffc75f3fb8d4f157e2a97cf725aa1b23de47be9acf654b48d09eeec51a811c1321c3a5bb6a94a4757b32154af395eb7724a1
|
|
@ -0,0 +1,38 @@
|
||||||
|
from socket import *
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
|
||||||
|
data = open('twain.txt', 'rb').read()
|
||||||
|
sbox_file = open('sbox.txt', 'wb')
|
||||||
|
|
||||||
|
try:
|
||||||
|
src_port = int(sys.argv[1])
|
||||||
|
dest_ip = sys.argv[2]
|
||||||
|
dest_port = int(sys.argv[3])
|
||||||
|
except:
|
||||||
|
print "Usage: python sender.py src_port dest_ip dest_port"
|
||||||
|
|
||||||
|
random.seed(1)
|
||||||
|
sbox = []
|
||||||
|
l = range(256)
|
||||||
|
for i in range(256):
|
||||||
|
v = random.choice(l)
|
||||||
|
sbox.append(v)
|
||||||
|
l.remove(v)
|
||||||
|
sbox_file.write('%02x' % v)
|
||||||
|
sbox_file.close()
|
||||||
|
|
||||||
|
data = [ord(c) for c in data]
|
||||||
|
ciphertext = []
|
||||||
|
for c in data:
|
||||||
|
row = (c & 0xf0) >> 4
|
||||||
|
col = c & 0x0f
|
||||||
|
index = row * 16 + col
|
||||||
|
ciphertext.append(chr(sbox[index]))
|
||||||
|
ciphertext = ''.join(ciphertext)
|
||||||
|
|
||||||
|
sock = socket(AF_INET, SOCK_STREAM)
|
||||||
|
addr = ("", src_port)
|
||||||
|
sock.bind((addr))
|
||||||
|
sock.connect((dest_ip, dest_port))
|
||||||
|
sock.send(ciphertext)
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
|
@ -0,0 +1,2 @@
|
||||||
|
all:
|
||||||
|
cp data.pcap *.png $(DESTDIR)
|
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
@ -0,0 +1,40 @@
|
||||||
|
One Time Pads
|
||||||
|
=============
|
||||||
|
|
||||||
|
As mentioned in the lecture, if you know the cryptographic algorithm being used, cracking a piece of ciphertext is simply a matter of time. Sometimes that amount of time, given current computing power, is beyond the lifespan of the sun, but you could always catch a lucky break and find the key within a couple thousand years or so.
|
||||||
|
|
||||||
|
You were able to solve some of the earlier problems because the limited key length gave you relationships between various points in the message that you could exploit to find the key. If the key length had been the same length as them message, however, those relationships would not be there. It would have been impossible, literally, for you to get the message back out even though it was encrypted using only a simple xor. A one time pad is a key such that each byte of the message is encrypted with it's own, entirely independent, on byte key.
|
||||||
|
|
||||||
|
With a one time pad, you can't rely on any patterns or information in the text. That information simply no longer exists in the ciphertext without the key. The text can even be transformed into any string of the same length given a different key. You can design a key to do exactly that, for instance, turning an executable into a jpg of a kitten.
|
||||||
|
|
||||||
|
There are a few weaknesses to a one time pad that tend to prevent it from being used. First, you need to distribute the key. Doing so reduces the security of the key to the method used to distribute it. Secondly, you can only use each key once, hence the name. Lets say you encrypted message A with key K using xor, and you also encrypted message B with the same key K in the same manner. If you xor both messages together, you get:
|
||||||
|
C = (A xor K) xor (B xor K)
|
||||||
|
C = A xor B xor K xor K
|
||||||
|
C = (A xor B) xor (K xor K)
|
||||||
|
C = (A xor B) xor 0
|
||||||
|
C = A xor B
|
||||||
|
|
||||||
|
If both A and B are a human language, you can use statistical techniques and a process of elimination to separate the two messages. If both the files are images, however, it would be pretty much impossible.
|
||||||
|
|
||||||
|
Other Block Cipher Modes
|
||||||
|
========================
|
||||||
|
|
||||||
|
Cipher Block Chaining, Electronic Code Book, and Propagating Cipher Block Chaining modes all require a cipher function that has an inverse. The inverse function allows you to decrypt the ciphertext and get back the original message.
|
||||||
|
|
||||||
|
What if, however, your original message was never an input to the cipher function? You could then use your cipher function as a generator of a new key for each byte of the message, giving you a pseudo one time pad. The cipher function in this case need not be reversible. It could be a complicated chunk of boolean logic that doesn't quite have a one-to-one input to output relationship, a random number generator (the key would be the seed), or any number of other creative methods. The output of this function would simply be combined with the message using a simple reversible function such as xor.
|
||||||
|
|
||||||
|
There are several standard methods for doing this. One is to simply combine (xor) each byte of generated key with the plaintext, as is usually done with one time pads. Additionally, there are block cipher modes designed for this type of operation: the Cipher Feedback and Output Feedback modes.
|
||||||
|
![Cipher Feedback](CF.png)
|
||||||
|
![Output Feedback](OF.png)
|
||||||
|
|
||||||
|
Decryption is simply a matter of generating the same sequence and re-applying the keys to the ciphertext (thus canceling out the key).
|
||||||
|
|
||||||
|
Non-Reversible Functions
|
||||||
|
========================
|
||||||
|
|
||||||
|
Cipher Feedback and Output Feedback modes free you to use any deterministic function as your cipher, though some functions are obviously better than others. Just about any cryptographic hash function would work well. Deterministic random number generators would work well too; the key/IV would be the seed, and you could optionally re-seed the generator at each step.
|
||||||
|
|
||||||
|
The Problem
|
||||||
|
===========
|
||||||
|
|
||||||
|
After reading that wall of text, you find yourself in the possession of this pcap. Working with your reverse engineer, you discover that the communications are encrypted using Output Feedback mode with md5 as the encryption algorithm, where the first byte of each hash is xor'ed with the plaintext. The first 16 bytes of the transmission appear to be the IV/key.
|
|
@ -0,0 +1 @@
|
||||||
|
clever girl
|
|
@ -0,0 +1,41 @@
|
||||||
|
from socket import *
|
||||||
|
from hashlib import md5
|
||||||
|
import sys
|
||||||
|
|
||||||
|
plaintext = open('clever_girl.jpg', 'rb').read()
|
||||||
|
|
||||||
|
try:
|
||||||
|
src_port = int(sys.argv[1])
|
||||||
|
dest_ip = sys.argv[2]
|
||||||
|
dest_port = int(sys.argv[3])
|
||||||
|
except:
|
||||||
|
print "Usage: python sender.py src_port dest_ip dest_port"
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
key = 'why am I the key'
|
||||||
|
data = [ord(c) for c in plaintext]
|
||||||
|
ciphertext = [key]
|
||||||
|
hasher = md5(key)
|
||||||
|
for c in data:
|
||||||
|
digest = hasher.digest()
|
||||||
|
hasher.update(digest)
|
||||||
|
ciphertext.append(chr(c ^ ord(digest[0])))
|
||||||
|
ciphertext = ''.join(ciphertext)
|
||||||
|
print ciphertext[:16]
|
||||||
|
|
||||||
|
key = ciphertext[:16]
|
||||||
|
hasher = md5(key)
|
||||||
|
decrypted = []
|
||||||
|
for c in ciphertext[16:]:
|
||||||
|
digest = hasher.digest()
|
||||||
|
hasher.update(digest)
|
||||||
|
decrypted.append(chr(ord(c) ^ ord(digest[0])))
|
||||||
|
decrypted = ''.join(decrypted)
|
||||||
|
|
||||||
|
assert decrypted == plaintext
|
||||||
|
|
||||||
|
sock = socket(AF_INET, SOCK_STREAM)
|
||||||
|
addr = ("", src_port)
|
||||||
|
sock.bind((addr))
|
||||||
|
sock.connect((dest_ip, dest_port))
|
||||||
|
sock.send(ciphertext)
|
Loading…
Reference in New Issue