mirror of
https://github.com/autistic-symposium/sec-pentesting-toolkit.git
synced 2025-06-03 06:01:44 -04:00
Add old writeups
This commit is contained in:
parent
06365916d8
commit
1b774c9add
89 changed files with 4052 additions and 688 deletions
|
@ -0,0 +1,293 @@
|
|||
# CSAW CTF 2014 - Cryptography 200 - Psifer School
|
||||
|
||||
|
||||
The problem starts with the following text:
|
||||
|
||||
> There's no heartbleed here. Why don't we use these ciphers?
|
||||
>
|
||||
> nc 54.209.5.48 12345
|
||||
>
|
||||
> Written by psifertex
|
||||
|
||||
|
||||
------
|
||||
|
||||
## Stage One: Caesar Cipher
|
||||
|
||||
|
||||
#### Connecting to the Server
|
||||
|
||||
We start typing the **netcat** command in the terminal:
|
||||
|
||||
```sh
|
||||
nc 54.209.5.48 12345
|
||||
```
|
||||
|
||||
We get the following message back:
|
||||
|
||||
> Welcome to psifer school v0.002
|
||||
>
|
||||
> Your exam begins now. You have 10 seconds, work fast.
|
||||
>
|
||||
> Here is your first psifer text, a famous ancient roman would be proud if you solve it.
|
||||
>
|
||||
> psifer text: **wkh dqvzhu wr wklv vwdjh lv vxshuvlpsoh**
|
||||
>
|
||||
>Time's up. Try again later.
|
||||
|
||||
This text gives a cipher ``` wkh dqvzhu wr wklv vwdjh lv vxshuvlpsoh``` and the hint *a famous ancient roman would be proud*. That's all we need to decipher it!
|
||||
|
||||
|
||||
#### Frequency Analysis
|
||||
The famous roman is **Caesar**, and [his cryptographic scheme] is one of the simplest possible. This cipher is also known as **rotation cipher**, because all we do is rotating the letters by some value (the **key**). A modern version of it is called **ROT13**, meaning **rotation by 13 places**. This is a simple letter substitution cipher which replaces each letter with the 13th letter after it in the alphabet. In this case, we say that the *key is 13*.
|
||||
|
||||
In our problem, we don't know the key. However, there is a method to circumvent it: we can count how many times each letter appears in the text and then we use some previous knowledge about the frequency of each letter in the English words. For example, in the English language, *e*, *t*, *a*, *o*, and *n* are frequent letters while *z* or *v* is not. This means that we can analyze the frequency of each character to determine what's the most probable rotation key.
|
||||
|
||||
To count the frequency of characters in our cipher, we write a snippet that creates a counter [dictionary (hash table)] with all the (lowercase) characters as the dictionary's keys. Note that we could have used Python's [Counter() data-structure] as well. We then iterate through each character in the message, counting their frequency, and returning a sorted list of these values:
|
||||
|
||||
|
||||
```python
|
||||
import string
|
||||
|
||||
def frequency(msg):
|
||||
# Compute the word frequencies
|
||||
dict_freq = dict([(c,0) for c in string.lowercase])
|
||||
diff = 0.0
|
||||
for c in msg.lower():
|
||||
if 'a'<= c <= 'z':
|
||||
diff += 1
|
||||
dict_freq[c] += 1
|
||||
list_freq = dict_freq.items()
|
||||
list_freq.sort()
|
||||
return [b / diff for (a, b) in list_freq]
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
#### Deciphering the Cipher
|
||||
|
||||
|
||||
|
||||
Using a [well-known table of word frequency values], we write a snippet that does the following:
|
||||
|
||||
1. First, for each of the 26 letters, we subtract its known frequency value from the frequency obtained from our message.
|
||||
2. Second, we find what is the minimum value from those subtractions. The closest value is the most probable value for the rotation key.
|
||||
|
||||
|
||||
```python
|
||||
def delta(freq_word, freq_eng):
|
||||
# zip together the value from the text and the value from FREQ
|
||||
diff = 0.0
|
||||
for a, b in zip(freq_word, freq_eng):
|
||||
diff += abs(a - b)
|
||||
return diff
|
||||
|
||||
def decipher(msg):
|
||||
# Decipher by frequency
|
||||
min_delta, best_rotation = 20, 0.0
|
||||
freq = frequency(msg)
|
||||
for key in range(26):
|
||||
d = delta(freq[key:] + freq[:key], FREQ_ENGLISH)
|
||||
if d < min_delta:
|
||||
min_delta = d
|
||||
best_rotation = key
|
||||
return cipher(msg, -best_rotation)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
Once we have the key, we just plug it back to the cipher algorithm, inverting the rotation to the other side, with ```cipher(msg, -best_rotation)```. In this cipher function, we iterate through all the character in the message, checking whether it's a letter or a special character. If it is the former case we perform the following operations:
|
||||
|
||||
1. We start getting the integer representing the [Unicode] code point of the character.
|
||||
2. To get its position in the alphabet and we subtract it from the Unicode value of *a*, given by **ord('a')** (this is 97).
|
||||
3. We add the key value to it to get the (absolute) shift position.
|
||||
4. Now we need to remember that this cipher is a ring, *i.e*, adding more stuff should always lead to a *spot* within the 26 letters in the alphabet. That's why we apply an [module] operation to this number to get the *relative* position in the letter's table.
|
||||
5. Finally, we just need the value of the shift to the Unicode of *a* to get the position of the character in the cipher.
|
||||
6. Remember we are using *-key*, so instead of making a new cipher, we are using the same steps to rotate the cipher to the other side to recover the message.
|
||||
|
||||
```python
|
||||
def cipher(msg, key):
|
||||
# Make the cipher
|
||||
dec = ''
|
||||
for c in msg.lower():
|
||||
if 'a' <= c <= 'z':
|
||||
dec += chr(ord('a') + (ord(c) - ord('a') + key) % 26)
|
||||
else:
|
||||
dec += c
|
||||
return dec
|
||||
```
|
||||
|
||||
Bingo! The snippets above lead us to our first answer in this problem:
|
||||
|
||||
> the answer to this stage is **supersimple**
|
||||
|
||||
Netcating several times can return other similar answers such as **hopeyouautomate** or **easypeesy** or **notveryhard**. They are all correct.
|
||||
|
||||
|
||||
|
||||
#### Automating the Response
|
||||
|
||||
To advance forward, we need to send one of the above answers to the socket. However, we only **have 10 seconds** to do this! It's clear that we need to automate this problem with a script.
|
||||
|
||||
We can do this in many ways. In Python, for example, we can use the libraries [telnetlib] or [socket] or even writing our [own netcat script]. We will use the former for this exploit. Let us create a telnet connection with:
|
||||
|
||||
```python
|
||||
from telnetlib import Telnet
|
||||
|
||||
PORT = 12345
|
||||
HOST = '54.209.5.48'
|
||||
|
||||
tn = Telnet(HOST ,PORT)
|
||||
```
|
||||
|
||||
In this case, socket reading can be done with ```tn.read_until(b'psifer text: ')```, which reads until a given string is encountered, or ```tn.read_all()```, which reads all data until EOF.
|
||||
|
||||
To write a string to the socket we do ```tn.write(mystring.encode() + b'\n')```. Here, the method [encode()] returns an encoded version of the string, *i.e* a translation of a sequence of bytes to a Unicode string.
|
||||
|
||||
|
||||
As a side note, if we had decided to use the [socket] library to create a *TCP socket*, the process would be easy as well:
|
||||
|
||||
```python
|
||||
s = socket(AF_INET, SOCK_STREAM)
|
||||
s.connect(HOST)
|
||||
```
|
||||
|
||||
Here ```socket.AF_UNIX, socket.AF_INET, socket.AF_INET6``` are constants that represent the address (and protocol) families. The constants ```socket.SOCK_STREAM, socket.SOCK_DGRAM, socket.SOCK_RAW, socket.SOCK_RDM, socket.SOCK_SEQPACKET```represent the socket types.
|
||||
|
||||
To read the socket stream we would use commands such as ```s.recv(2048)``` and for writing, we could use ```s.sendall(answer)```.
|
||||
|
||||
|
||||
|
||||
#### Decrypting and Sending the Answer
|
||||
Now, back to our problem. After creating the telnet connection, we read whatever comes in:
|
||||
```python
|
||||
tn.read_until(b'psifer text: ')
|
||||
```
|
||||
|
||||
We decode and decrypt the text, and then encode it again:
|
||||
```python
|
||||
msg_in1 = tn.read_until(b'\n').decode().strip()
|
||||
dec_msg_in1 = decipher(msg_in1)
|
||||
answer1 = dec_msg_in1.split()[-1].encode() + b'\n'
|
||||
```
|
||||
|
||||
Finally, we send our answer to the telnet session (the same answer obtained before):
|
||||
```python
|
||||
tn.write(answer1)
|
||||
```
|
||||
|
||||
-----------------------------------------
|
||||
|
||||
## Stage Two: Offset with Special Characters
|
||||
|
||||
The second stage starts with the following message:
|
||||
|
||||
|
||||
> Congratulations, you have solved stage 1. You have 9 seconds left.
|
||||
>
|
||||
> Now it's time for something slightly more difficult. Hint, everybody knows it's
|
||||
> not length that matters.
|
||||
|
||||
Together with the hint *length doesn't matter*, we get the following cipher (translated as a Python string variable because of the special characters):
|
||||
|
||||
```I'lcslraooh o rga tehhywvf.retFtelh mao ae af ostloh lusr bTsfnr, epawlltddaheoo aneviedr ose rtyyng etn aini ft oooey hgbifecmoswuut!oa eeg ar rr h.u t. hylcg io we ph ftooriysneirdriIa utyco gfl oostif sp u"+'""'+"flcnb roh tprn.o h```
|
||||
|
||||
|
||||
To crack this cipher we need to deal with special characters to find the rotation shift. We proceed with the following steps:
|
||||
|
||||
1. We start looping over the length of our message, where for each iteration we create a blank list with the size of the message. This is a bit *space-expensive* and it should be optimized if we needed to scale for larger problems. It's fine for our current problem.
|
||||
|
||||
2. We start a second loop, which will tell us about the shifts. This loop iterates again in the length of the message, this time adding the current character to the list we've created before and updated a pointer to the pacing value given in the first loop. Notice that we have a loop inside another, so this solution has *O(n^2) runtime* and it also should be optimized for larger problems.
|
||||
|
||||
3. Inside this second loop, we check whether the pacing pointer is larger than the length of the message, and if this is the case, we register it in a shift counter. The former pointer receives the value of this shift. This is the end of the second loop.
|
||||
|
||||
4. Back to the first loop, we add all the characters so far from our list into the message string. But when should we stop doing this? Until we make sure that had a rotation that produces real words. I tried a few common words, and 'you' worked just fine!
|
||||
|
||||
|
||||
```python
|
||||
def solve2(msg):
|
||||
# Shift cypher, but dealing with special characters
|
||||
for j in range(2, len(msg)):
|
||||
|
||||
dec_msg = ['0'] * len(msg)
|
||||
idec_msg, shift = 0, 0
|
||||
|
||||
for i in range(len(msg)):
|
||||
dec_msg[idec_msg] = msg[i]
|
||||
idec_msg += j
|
||||
|
||||
if idec_msg > len(msg) - 1:
|
||||
shift += 1
|
||||
idec_msg = shift
|
||||
dec_msg = "".join(dec_msg)
|
||||
|
||||
if "you" not in dec_msg: continue
|
||||
return dec_msg
|
||||
```
|
||||
|
||||
After decoding this stage's cipher we get the key for the next stage, which is then sent back through the socket:
|
||||
|
||||
> I hope you don't have a problem with this challenge. It should be fairly straight forward if you have done lots of basic crypto. The magic phrase for your efforts is "**not not wrong**". For your efforts, you will get another challenge!
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
## Stage Three: Vigenere Cipher
|
||||
|
||||
The next message lets us know that we are close to the end:
|
||||
|
||||
> Congratulations, you have solved stage 2. You have 9 seconds left.
|
||||
> Last one.
|
||||
|
||||
And comes with the following cipher:
|
||||
```
|
||||
MVJJN BQXKF NCEPZ WWVSH YFCSV JEEBB UVRMX HKPIE PMMVZ FOPME ZQIIU EUZZW CGHMV BKBTZ BBHVR MVTQP ENXRM HIRNB WTGDZ CFEDS TKBBW HBFDI KILCM MUUPX WUNIN PWPFJ IEZTP MVQBX ACVKN AEMPV KQXAB ZMDUD ILISV NHKBJ FCIMW HTUVR MNNGU KIFED STLLX XAOUN YVEGV BEXEI BHJNI GHXFI FQFYV VXZFE FXFFH OBVXR MVNLT NHUYY FEZWD GBKEL SGFLM LXBFO NEIOS MZHML XAJUX EIKWH YNAIK SOFLF EEKPI XLSDB PNGHV XHFON MSFOL VMNVX HIRNB XBGTF FOEUZ FZMAS NZEGL HFTPM PDNWM DVKCG WHAFE OKWXF ZIBRQ XCSJI FIMVJ EAFEK MIRXT PBHUC YEEFP MZNMP XZBDV EMMHM VFTQU ABISA EWOMZ NMPXZ BDVPL HGFWF XISSX RMPLB HFRML RHKJU IGXPO OKNHQ TYFKB BWAOS UYKXA OOZNG IXRTK IUIBT ZFOOI LCMMY WEECU FZLMF DMVWK CIHPT BTPES OXYLC HIQII UEUZZ RFKIT RZYUO IMVFT IWITB ENCEP UFFVT XVBUI KNAVH IHYCM MYWUY YETLA PJNHJ MVFGF TMGHF ONBWL HBKCV EMSBT BHJMV FCYOI EGJDH HXTAB JIVLB GUKBX JNBOP NAMGU JJNAE MRFGY GHBBH FHPLB QIIUG HHALV SRSNU FKNAE MDPVG FMZVU SYXBT QUCSM LXFJX BMSYT TVNMS LIDTY LWY
|
||||
```
|
||||
|
||||
This is a **[Vigenere Cipher]**, which is basically several Caesar ciphers in sequence, with different shift values, given by a key-word. Finding these shifts when we don't know the key can be done by writing the alphabet 26 times in different rows. In this case, each alphabet is shifted cyclically to the left compared to the previous alphabet (26 Caesar ciphers).
|
||||
|
||||
Although we could use some [online Vigenere cracker] to extract the flag from this text, we will instead write a code. We use Python's library [pygenere], which has the methods ```crack_message()``` to decipher the message and ```crack_codeword()``` to find the key (useful because we don't have the key). We then send our cipher to the following function:
|
||||
|
||||
```python
|
||||
def solve3(msg):
|
||||
key = VigCrack(msg).crack_codeword()
|
||||
dec_msg = VigCrack(msg).crack_message()
|
||||
dec_msg = dec_msg.replace(" ", "")
|
||||
return key, dec_msg
|
||||
```
|
||||
|
||||
This will give us the **key = TOBRUTE** and the deciphered text. After fixing the spaces between the words, we get:
|
||||
|
||||
```
|
||||
THIS TIME WE WILL GIVE YOU MORE PLAINTEXT TO WORK WITH YOU WILL PROBABLY FIND THAT HAVING EXTRA CONTENT THAT IS ASCII MAKES THIS ONE MORE SOLVABLE IT WOULD BE SOLVABLE WITHOUT THAT BUT WE WILL MAKE SURE TO GIVE LOTS OF TEXT JUST TO MAKE SURE THAT WE CAN HANDLE IT I WONDER HOW MUCH WILL BE REQUIRED LETS PUT THE MAGIC PHRASE FOR THE NEXT LEVEL IN THE MIDDLE RIGHT HERE NORMALWORD OK NOW MORE TEXT TO MAKE SURE THAT IT IS SOLVABLE I SHOULD PROBABLY JUST PUT IN SOME NURSERY RHYME OR SOMETHING MARY HADA LITTLE LAMB LITTLE LAMB LITTLE LAMB MARY HADA LITTLE LAMB WHOSE FLEEZE WAS WHITE AS SNOW I DONT WANT TO MAKE THIS HARDER THAN IT NEEDS TO BE IF YOU VE SOLVED A LOT OF SIMPLE CRYPTO CHALLENGES YOU PROBABLY ALREADY HAVE THE CODE AND WILL BREEZE RIGHT THROUGH IT IF IT HELPS MOST OF THE PLAINTEXT IS STATIC AT EACH OF THE LEVELS I M NOT A MASOCHIST THE FUNNY THING IS THAT DEPENDING ON WHICH RANDOMKEY YOU GET THAT POEM MIGHT BE EXACTLY THE RIGHT OFFSET TO SUCCESSFULLY MOUNT AN ATTACK WE LL SEE LITTLE BIT MORE LITTLE BIT MORE THERE,
|
||||
```
|
||||
Reading it carefully give us the last answer for the flag: **NORMALWORD**. Sweet!
|
||||
|
||||
|
||||
|
||||
|
||||
## Final Words
|
||||
|
||||
If you like this solution, take a look at my [exploit for this problem].
|
||||
|
||||
**Hack all the things!**
|
||||
|
||||
[his cryptographic scheme]: http://en.wikipedia.org/wiki/Caesar_cipher
|
||||
[exploit for this problem]: https://github.com/bt3gl/CTFs-Gray-Hacker-and-PenTesting/tree/master/CTFs_and_WarGames/2014-CSAW-CTF/cryptography/crypto-200
|
||||
[scripts from other authors]:https://github.com/bt3gl/CTFs-and-Hacking-Scripts-and-Tutorials/tree/master/2014-CSAW-CTF/cryptography/crypto-200/from_the_net
|
||||
[well-known table of word frequency values]: http://en.wikipedia.org/wiki/Letter_frequency
|
||||
[telnetlib]: https://docs.python.org/2/library/telnetlib.html
|
||||
[socket]: https://docs.python.org/2/library/socket.html
|
||||
[own netcat script]: https://github.com/bt3gl/CTFs-and-Hacking-Scripts-and-Tutorials/blob/master/Tutorials/Useful_Scripts/netcat.py
|
||||
[pygenere]: http://smurfoncrack.com/pygenere/pygenere.php
|
||||
[Vigenere Cipher]: http://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher
|
||||
[online Vigenere cracker]: http://smurfoncrack.com/pygenere/
|
||||
[dictionary (hash table)]: https://docs.python.org/2/tutorial/datastructures.html#dictionaries
|
||||
[Counter() data-structure]: https://docs.python.org/2/library/collections.html#collections.Counter
|
||||
[ord()]: https://docs.python.org/2/library/functions.html#ord
|
||||
[module]: http://en.wikipedia.org/wiki/Modulo_operation
|
||||
[Unicode]: http://en.wikipedia.org/wiki/Unicode
|
||||
[encode()]: https://docs.python.org/2/library/stdtypes.html#str.encode
|
|
@ -0,0 +1,87 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
|
||||
__author__ = "bt3gl"
|
||||
|
||||
|
||||
import string
|
||||
|
||||
|
||||
FREQ_ENGLISH = [0.0749, 0.0129, 0.0354, 0.0362, 0.1400, 0.0218, 0.0174, 0.0422, 0.0665, 0.0027, 0.0047, 0.0357,0.0339, 0.0674, 0.0737, 0.0243, 0.0026, 0.0614, 0.0695, 0.0985, 0.0300, 0.0116, 0.0169, 0.0028, 0.0164, 0.0004]
|
||||
|
||||
|
||||
|
||||
def delta(freq_word, freq_eng):
|
||||
# zip together the value from the text and the value from FREQ_EdiffGlist_freqISH
|
||||
diff = 0.0
|
||||
for a, b in zip(freq_word, freq_eng):
|
||||
diff += abs(a - b)
|
||||
return diff
|
||||
|
||||
|
||||
|
||||
def cipher(msg, key):
|
||||
# Make the cipher
|
||||
dec = ''
|
||||
for c in msg.lower():
|
||||
if 'a' <= c <= 'z':
|
||||
dec += chr(ord('a') + (ord(c) - ord('a') + key) % 26)
|
||||
else:
|
||||
dec += c
|
||||
return dec
|
||||
|
||||
|
||||
|
||||
def frequency(msg):
|
||||
# Compute the word frequencies
|
||||
dict_freq = dict([(c,0) for c in string.lowercase])
|
||||
diff = 0.0
|
||||
for c in msg.lower():
|
||||
if 'a'<= c <= 'z':
|
||||
diff += 1
|
||||
dict_freq[c] += 1
|
||||
list_freq = dict_freq.items()
|
||||
list_freq.sort()
|
||||
return [b / diff for (a, b) in list_freq]
|
||||
|
||||
|
||||
|
||||
def decipher(msg):
|
||||
# Decipher by frequency
|
||||
min_delta, best_rotation = 20, 0.0
|
||||
freq = frequency(msg)
|
||||
for k in range(26):
|
||||
d = delta(freq[k:] + freq[:k], FREQ_ENGLISH)
|
||||
if d < min_delta:
|
||||
min_delta = d
|
||||
best_rotation = k
|
||||
return cipher(msg, -best_rotation)
|
||||
|
||||
|
||||
|
||||
def decipher_simple(msg):
|
||||
# very smart way of solving using translate and maketrans methods
|
||||
diff = (ord('t') - ord(s[0])) % 26
|
||||
x = string.ascii_lowercase
|
||||
x = x[diff:] + x[:diff]
|
||||
ans = string.translate(s,string.maketrans(string.ascii_lowercase,x))
|
||||
return ans
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# creating a cipher
|
||||
key = 13
|
||||
text = 'hacker school is awesome!'
|
||||
cipe = cipher(text, key)
|
||||
print "Cipher: " + cipe
|
||||
|
||||
# decyphering
|
||||
with open('cipher.txt', 'r') as f:
|
||||
cip = f.readlines()
|
||||
cip = cip[0].strip()
|
||||
cipe = decipher(cip)
|
||||
print "Decipher: " + cipe
|
||||
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
<?php
|
||||
class Vigenere {
|
||||
public static $mVigTable;
|
||||
public static $mKey;
|
||||
public static $mMod;
|
||||
final public function __construct($rKey = false, $rVigTable = false)
|
||||
{
|
||||
self::$mVigTable = $rVigTable ? $rVigTable : 'abcdefghijklmnopqrstuvwxyz';
|
||||
self::$mMod = strlen(self::$mVigTable);
|
||||
self::$mKey = $rKey ? $rKey : self::generateKey();
|
||||
}
|
||||
final public function getKey()
|
||||
{
|
||||
return self::$mKey;
|
||||
}
|
||||
private function getVignereIndex($iPos)
|
||||
{
|
||||
return strpos(self::$mVigTable, $iPos); // todo: catch chars not in table
|
||||
}
|
||||
private function getVignereChar($rVignereIndex)
|
||||
{
|
||||
(int)$iVignereIndex = $rVignereIndex;
|
||||
$iVignereIndex=$iVignereIndex>=0 ? $iVignereIndex : strlen(self::$mVigTable)+$iVignereIndex;
|
||||
return self::$mVigTable{$iVignereIndex};
|
||||
}
|
||||
final public function generateKey()
|
||||
{
|
||||
self::$mKey = '';
|
||||
for ($i = 0; $i < self::$mMod; $i++) {
|
||||
self::$mKey .= self::$mVigTable{rand(0, self::$mMod)};
|
||||
}
|
||||
return self::$mKey;
|
||||
}
|
||||
final public function encrypt($rText, $rKey)
|
||||
{
|
||||
$sEncryptValue = '';
|
||||
$sKey = (String)$rKey;
|
||||
$sText = (String)$rText;
|
||||
|
||||
for($i = 0; $i < strlen($sText); $i++)
|
||||
{
|
||||
(int)$iText = self::getVignereIndex($sText[$i]);
|
||||
(int)$iKey = self::getVignereIndex(self::charAt($sKey, $i));
|
||||
|
||||
$iEncryptIndex = ($iText+$iKey)%(self::$mMod);
|
||||
$iEncryptIndex = self::EnsureKeyValid($iEncryptIndex);
|
||||
$sEncryptValue .= self::getVignereChar($iEncryptIndex);
|
||||
}
|
||||
return $sEncryptValue;
|
||||
}
|
||||
final public function encrypt2($rText, $rKey, $rMod)
|
||||
{
|
||||
(String)$sEncryptValue = '';
|
||||
$sKey = (String)$rKey;
|
||||
$sText = (String)$rText;
|
||||
$iMod = (int)$rMod;
|
||||
|
||||
for($i = 0; $i < strlen($sText); $i++)
|
||||
{
|
||||
(int)$iText = self::getVignereIndex($sText[$i]);
|
||||
(int)$iKey = self::getVignereIndex(self::charAt($sKey, $i));
|
||||
|
||||
$iEncryptIndex = ($iText+$iKey)%($iMod);
|
||||
$iEncryptIndex = self::EnsureKeyValid($iEncryptIndex);
|
||||
$sEncryptValue .= self::getVignereChar($iEncryptIndex);
|
||||
}
|
||||
return $sEncryptValue;
|
||||
}
|
||||
final public function decrypt($rText, $rKey)
|
||||
{
|
||||
(String)$sDecryptValue = '';
|
||||
$sKey = (String)$rKey;
|
||||
$sText = (String)$rText;
|
||||
|
||||
for($i = 0; $i < strlen($sText); $i++)
|
||||
{
|
||||
(int)$iText = self::getVignereIndex($sText[$i]);
|
||||
(int)$iKey = self::getVignereIndex(self::charAt($sKey, $i));
|
||||
|
||||
$iDecryptIndex = ($iText-$iKey)%(self::$mMod);
|
||||
$iDecryptIndex = self::EnsureKeyValid($iDecryptIndex);
|
||||
|
||||
$sDecryptValue .= self::getVignereChar($iDecryptIndex);
|
||||
}
|
||||
return $sDecryptValue;
|
||||
}
|
||||
final public function decrypt2($rText, $rKey, $rMod)
|
||||
{
|
||||
(String)$sDecryptValue = '';
|
||||
$sKey = (String)$rKey;
|
||||
$sText = (String)$rText;
|
||||
$iMod = (int)$rMod;
|
||||
|
||||
for($i = 0; $i < strlen($sText); $i++)
|
||||
{
|
||||
(int)$iText = self::getVignereIndex($sText[$i]);
|
||||
(int)$iKey = self::getVignereIndex(self::charAt($sKey, $i));
|
||||
|
||||
|
||||
$iDecryptIndex = ($iText-$iKey)%($iMod);
|
||||
$iDecryptIndex = self::EnsureKeyValid($iDecryptIndex);
|
||||
|
||||
$sDecryptValue .= self::getVignereChar($iDecryptIndex);
|
||||
}
|
||||
return $sDecryptValue;
|
||||
}
|
||||
private function charAt($rStr, $rPos)
|
||||
{
|
||||
$iPos = (int)$rPos%strlen((String)$rStr);
|
||||
|
||||
return $rStr{$iPos};
|
||||
}
|
||||
|
||||
private function EnsureKeyValid($rIndex)
|
||||
{
|
||||
$iIndex = (int)$rIndex;
|
||||
return ($iIndex >= 0) ? $iIndex : (strlen(self::$mVigTable)+$iIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function decrypt_final($key,$text){
|
||||
$text = str_replace(" ","", strtolower($text));
|
||||
$key = $key;
|
||||
$plain= new Vigenere($key);
|
||||
$plain= $plain->decrypt($text,$key);
|
||||
return strtoupper($plain);
|
||||
}
|
||||
|
||||
function str_rot($s, $n = 13) {
|
||||
$n = (int)$n % 26;
|
||||
if (!$n) return $s;
|
||||
for ($i = 0, $l = strlen($s); $i < $l; $i++) {
|
||||
$c = ord($s[$i]);
|
||||
if ($c >= 97 && $c <= 122) {
|
||||
$s[$i] = chr(($c - 71 + $n) % 26 + 97);
|
||||
} else if ($c >= 65 && $c <= 90) {
|
||||
$s[$i] = chr(($c - 39 + $n) % 26 + 65);
|
||||
}
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
function inStr ($needle, $haystack) { // instr("test","testtest");
|
||||
$needlechars = strlen($needle);
|
||||
$i = 0;
|
||||
for($i=0; $i < strlen($haystack); $i++)
|
||||
{
|
||||
if(substr($haystack, $i, $needlechars) == $needle)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$usenet = fsockopen("54.209.5.48", "12345", $errno, $errstr, 3);
|
||||
first:
|
||||
while (!feof($usenet)) {
|
||||
$kp = fgets($usenet, 128);
|
||||
if(inStr("psifer text:", $kp)){
|
||||
$kp = explode("psifer text: ", $kp);
|
||||
$kp = $kp[1];
|
||||
$kp = explode("\n", $kp);
|
||||
$kp = $kp[0];
|
||||
for($i=0;$i<25;$i++){
|
||||
$temp = str_rot($kp, $i) . "\n";
|
||||
if(instr("the answer to this stage is" , $temp)){
|
||||
$temp = explode("the answer to this stage is " , $temp);
|
||||
$temp = $temp[1];
|
||||
goto second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
second:
|
||||
fputs($usenet, $temp);
|
||||
while (!feof($usenet)) {
|
||||
$kp = fgets($usenet, 260);
|
||||
if(inStr("psifer text:", $kp)){
|
||||
$kp = explode("psifer text: ", $kp);
|
||||
$kp = $kp[1];
|
||||
$possible_flag = array("easiest answer", "more answers here", "winning for the win", "tired of making up bogus answers", "not not wrong");
|
||||
$possible_guess= rand(0,2);
|
||||
fputs($usenet, $possible_flag[$possible_guess] . "\n");
|
||||
goto third;
|
||||
}
|
||||
}
|
||||
third:
|
||||
while (!feof($usenet)) {
|
||||
if(!$usenet) die("error.");
|
||||
$kp = fgets($usenet, 8192);
|
||||
if(inStr("psifer text:", $kp)){
|
||||
$kp = explode("psifer text: ", $kp);
|
||||
$kp = $kp[1];
|
||||
/*
|
||||
echo @decrypt_final("force",$kp) . "\n----\n";
|
||||
echo @decrypt_final("tobrute",$kp) . "\n----\n";
|
||||
echo @decrypt_final("dictionary",$kp) . "\n----\n";
|
||||
echo @decrypt_final("diary",$kp) . "\n----\n";
|
||||
*/
|
||||
if(inStr("HISTIMEWEWILLGIVEYOU",@decrypt_final("force",$kp))){
|
||||
$temp = @decrypt_final("force",$kp);
|
||||
$temp = explode("DLERIGHTHERE",$temp);
|
||||
$temp = $temp[1];
|
||||
$temp = explode("OKNOW",$temp);
|
||||
$temp = $temp[0];
|
||||
fputs($usenet, $temp);
|
||||
}elseif(inStr("HISTIMEWEWILLGIVEYOU",@decrypt_final("tobrute",$kp))){
|
||||
$temp = @decrypt_final("tobrute",$kp);
|
||||
$temp = explode("DLERIGHTHERE",$temp);
|
||||
$temp = $temp[1];
|
||||
$temp = explode("OKNOW",$temp);
|
||||
$temp = $temp[0];
|
||||
fputs($usenet, $temp);
|
||||
}elseif(inStr("HISTIMEWEWILLGIVEYOU",@decrypt_final("dictionary",$kp))){
|
||||
$temp = @decrypt_final("tobrute",$kp);
|
||||
$temp = explode("DLERIGHTHERE",$temp);
|
||||
$temp = $temp[1];
|
||||
$temp = explode("OKNOW",$temp);
|
||||
$temp = $temp[0];
|
||||
fputs($usenet, $temp);
|
||||
}elseif(inStr("HISTIMEWEWILLGIVEYOU",@decrypt_final("diary",$kp))){
|
||||
$temp = @decrypt_final("diary",$kp);
|
||||
$temp = explode("DLERIGHTHERE",$temp);
|
||||
$temp = $temp[1];
|
||||
$temp = explode("OKNOW",$temp);
|
||||
$temp = $temp[0];
|
||||
fputs($usenet, $temp);
|
||||
}
|
||||
fputs($usenet,"\n");
|
||||
$kp = fgets($usenet, 8192);
|
||||
echo $kp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,168 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
# By Black-ID
|
||||
#@Go Back
|
||||
#CSAW 2014
|
||||
|
||||
|
||||
use IO::Socket;
|
||||
use POSIX;
|
||||
use warnings;
|
||||
no warnings;
|
||||
|
||||
my $server = "54.209.5.48";
|
||||
my $port = 12345;
|
||||
|
||||
# Create new Socket
|
||||
my $sock = new IO::Socket::INET(PeerAddr => $server,PeerPort => $port,Proto => 'tcp') or die "Can't connect\n";
|
||||
|
||||
# Log on to CSAW server.
|
||||
|
||||
sub caesarCipher {
|
||||
$strText = $_[0];
|
||||
$iShiftValue = $_[1];
|
||||
|
||||
my $strCipherText = "";
|
||||
@strTextArray = split(//, $strText);
|
||||
|
||||
$iShift = $iShiftValue % 26;
|
||||
if ($iShift < 0) {
|
||||
$iShift += 26;
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
while ($i < length($strText)) {
|
||||
$c = uc($strTextArray[$i]);
|
||||
if ( ($c ge 'A') && ($c le 'Z') ) {
|
||||
if ( chr(ord($c) + $iShift) gt 'Z') {
|
||||
$strCipherText .= chr(ord($c) + $iShift - 26);
|
||||
} else {
|
||||
$strCipherText .= chr(ord($c) + $iShift);
|
||||
}
|
||||
} else {
|
||||
$strCipherText .= " ";
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
return lc($strCipherText);
|
||||
}
|
||||
|
||||
sub scytaleCipher {
|
||||
$strText = $_[0];
|
||||
$iShiftValue = $_[1];
|
||||
|
||||
my $strCipherText = "";
|
||||
@strTextArray = split //, $strText;
|
||||
$M = length($strText);
|
||||
$iShift = floor($M/$iShiftValue);
|
||||
|
||||
if ($iShift*$iShiftValue < $M) {
|
||||
$iShift++;
|
||||
}
|
||||
for($a=0;$a<$iShiftValue;$a++) {
|
||||
for($i=0;$i<$iShift;$i++) {
|
||||
$strCipherText .= $strTextArray[$a+$i*$iShiftValue];
|
||||
}
|
||||
}
|
||||
return $strCipherText;
|
||||
}
|
||||
sub vigenerebrute{
|
||||
$strChiper = $_[0];
|
||||
$strCipherText = "";
|
||||
$a = 0;
|
||||
for($a=0;$a<10;$a++) {
|
||||
@chars = split(//, "abcdefghijklmnopqrstuvwxyz");
|
||||
#print $chars[0];
|
||||
for($i=0;$i<26;$i++) {
|
||||
$output = decrypt_string($strChiper,$chars[$i]);
|
||||
@out = split(//, $output);
|
||||
@cmp = split(//, "thistimewewillgive");
|
||||
#print $out[$i];
|
||||
if($out[$a] eq $cmp[$a]) {
|
||||
$strCipherText .= $chars[$i];
|
||||
}
|
||||
}
|
||||
$output = decrypt_string($strChiper,$strCipherText);
|
||||
if($output =~ /thistimewewillgive(.*)/){
|
||||
last;
|
||||
}
|
||||
}
|
||||
if ($output =~ /righthere(.*)oknow/){
|
||||
return $1;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
sub decrypt_string{
|
||||
my ($ciphertext, $key) = @_;
|
||||
my $plaintext;
|
||||
$key = $key x (length($ciphertext) / length($key) + 1);
|
||||
for( my $i=0; $i<length($ciphertext); $i++ ){
|
||||
$plaintext .=
|
||||
decrypt_letter( (substr($ciphertext,$i,1)), (substr($key,$i,1)));
|
||||
}
|
||||
return $plaintext;
|
||||
}
|
||||
sub decrypt_letter{
|
||||
my ($cipher, $row) = @_;
|
||||
my $plain;
|
||||
$row = ord(lc($row)) - ord('a');
|
||||
$cipher = ord(lc($cipher)) - ord('a');
|
||||
$plain = ($cipher - $row) % 26;
|
||||
|
||||
$plain = chr($plain + ord('a'));
|
||||
|
||||
return lc($plain);
|
||||
}
|
||||
|
||||
$sock->recv($data,1024);
|
||||
print $data;
|
||||
$sock->recv($data,1024);
|
||||
print $data;
|
||||
if($data =~ m/psifer text: (.+)$/g){
|
||||
$cipher = $1;
|
||||
print "\nEncoded: $cipher\n";
|
||||
for($a=0;$a<=26;$a++) {
|
||||
|
||||
$st = caesarCipher($cipher, $a);
|
||||
if ($st =~ /the answer(.*)/){
|
||||
print "\nDecoded: $st\n";
|
||||
$ccipher = substr $st, 28;
|
||||
$sock->send($ccipher."\n");
|
||||
print "\nKey: $ccipher\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
$sock->recv($data,1024);
|
||||
print $data;
|
||||
if($data =~ /psifer text: (.*)/){
|
||||
$cipher = $1;
|
||||
print "\nEncoded: $cipher\n";
|
||||
for($a=1;$a<=300;$a++) {
|
||||
$st = scytaleCipher ($cipher,$a);
|
||||
if ($st =~ /I hope you(.*)/){
|
||||
print "[$a] => $st\n";
|
||||
if($st =~ /"(.+?)"/){
|
||||
$key = $1;
|
||||
print "\nkey is : $key\n";
|
||||
$sock->send($key."\n");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$sock->recv($data,1024);
|
||||
print $data;
|
||||
if($data =~ /psifer text: (.*)/){
|
||||
$cipher = $1;
|
||||
$cipher =~ s/\s+//g;
|
||||
print "\nEncoded: $cipher\n";
|
||||
$key = vigenerebrute($cipher);
|
||||
print "\nkey is : $key\n";
|
||||
$sock->send($key."\n");
|
||||
$sock->recv($data,1024);
|
||||
print $data;
|
||||
$sock->recv($data,1024);
|
||||
print $data;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
|
||||
__author__ = "bt3gl"
|
||||
|
||||
|
||||
import sys
|
||||
from telnetlib import Telnet
|
||||
from caesarCipher import decipher, decipher_simple
|
||||
from pygenere import VigCrack
|
||||
|
||||
|
||||
|
||||
def solve1(msg):
|
||||
# Caesar Cipher
|
||||
# Solve the first cypher and encode back to unicode:
|
||||
# the method encode() returns an encoded version of the string
|
||||
# Both method work fine.
|
||||
# One is by freq. analysis or the other by rotating
|
||||
dec_msg = decipher(msg)
|
||||
#dec_msg = decipher_simple(msg)
|
||||
dec_msg = dec_msg.split()[-1]
|
||||
return dec_msg
|
||||
|
||||
|
||||
|
||||
|
||||
def solve2(msg):
|
||||
msg="I'lcslraooh o rga tehhywvf.retFtelh mao ae af ostloh lusr bTsfnr, epawlltddaheoo aneviedr ose rtyyng etn aini ft oooey hgbifecmoswuut!oa eeg ar rr h.u t. hylcg io we ph ftooriysneirdriIa utyco gfl oostif sp u"+'""'+"flcnb roh tprn.o h"
|
||||
# Shift cypher, but dealing with special characters
|
||||
for j in range(2, len(msg)):
|
||||
|
||||
dec_msg = ['0'] * len(msg)
|
||||
idec_msg, shift = 0, 0
|
||||
|
||||
for i in range(len(msg)):
|
||||
dec_msg[idec_msg] = msg[i]
|
||||
idec_msg += j
|
||||
|
||||
if idec_msg > len(msg) - 1:
|
||||
shift += 1
|
||||
idec_msg = shift
|
||||
dec_msg = "".join(dec_msg)
|
||||
|
||||
if "you" not in dec_msg: continue
|
||||
return dec_msg
|
||||
|
||||
|
||||
|
||||
|
||||
def solve3(msg):
|
||||
# Vigenere Cypher
|
||||
key = VigCrack(msg).crack_codeword()
|
||||
dec_msg = VigCrack(msg).crack_message()
|
||||
dec_msg = dec_msg.replace(" ", "")
|
||||
return key, dec_msg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
"""
|
||||
PORT = 12345
|
||||
HOST = '54.209.5.48'
|
||||
tn = Telnet(HOST ,PORT)
|
||||
|
||||
|
||||
'''
|
||||
SOLVING STAGE 1 - CEASAR CIPHER
|
||||
'''
|
||||
|
||||
tn.read_until(b'psifer text: ')
|
||||
msg_in1 = tn.read_until(b'\n').dec_msg().strip()
|
||||
|
||||
answer1 = solve1(msg_in1)
|
||||
|
||||
tn.write(answer1.encode() + b'\n')
|
||||
|
||||
print 'Message stage 1: ' + msg_in1
|
||||
print
|
||||
print 'Answer stage 1: ' + answer1
|
||||
print
|
||||
print
|
||||
|
||||
|
||||
|
||||
'''
|
||||
SOLVING STAGE 2 - SPECIAL CHARS
|
||||
'''
|
||||
msg_in2 = tn.read_all().dec_msg()
|
||||
msg_in2 = (msg_in2.split(':')[1]).split("Time")[0]
|
||||
|
||||
answer2 = solve2(msg_in2)
|
||||
|
||||
tn.write(answer2.encode() + b'\n')
|
||||
|
||||
print 'Message stage 2: ' + msg_in2
|
||||
print
|
||||
print 'Answer stage 2: ' + answer2
|
||||
print
|
||||
print
|
||||
|
||||
|
||||
'''
|
||||
SOLVING STAGE 3 - VINEGERE
|
||||
'''
|
||||
msg_in3 = tn.read_all().dec_msg()
|
||||
msg_in3 = (msg_in3.split(':')[1]).split("Time")[0]
|
||||
|
||||
|
||||
key, answer3 = solve3(msg_in3)
|
||||
tn.write(answer3.encode() + b'\n')
|
||||
|
||||
print 'Message stage 3: ' + msg_in3
|
||||
print
|
||||
print 'Answer stage 3: ' + answer3
|
||||
print '(key: ' + key + ')'
|
||||
|
||||
"""
|
||||
print solve2(msg='a')
|
|
@ -0,0 +1,477 @@
|
|||
# PyGenere v 0.3
|
||||
#
|
||||
# Release Date: 2007-02-16
|
||||
# Author: Simon Liu <webmonkey at smurfoncrack dot com>
|
||||
# URL: http://smurfoncrack.com/pygenere
|
||||
# History and License at end of file
|
||||
|
||||
|
||||
r"""
|
||||
This library implements the Caesar and Vigenere ciphers, allowing a piece of
|
||||
plaintext to be encoded using a numeric rotation or an alphabetic keyword,
|
||||
and also decoded if the key/rotation is known.
|
||||
|
||||
In case the key is not known, methods are provided that analyze the ciphertext
|
||||
and attempt to find the original key and decode the message: these work using
|
||||
character frequency analysis. English, French, German, Italian, Portuguese,
|
||||
and Spanish texts are currently supported. Results are generally accurate if
|
||||
the length of the plaintext is long compared to the length of the key used to
|
||||
encipher it.
|
||||
|
||||
Example usage:
|
||||
|
||||
>>> from pygenere import *
|
||||
>>> plaintext = 'Attack at dawn.'
|
||||
>>> key = 3
|
||||
>>> ciphertext = Caesar(plaintext).encipher(key)
|
||||
>>> ciphertext
|
||||
'Dwwdfn dw gdzq.'
|
||||
>>> Vigenere(ciphertext).decipher('D') # A=0, B=1, C=2, D=3, etc.
|
||||
'Attack at dawn.'
|
||||
|
||||
The 'Attack at dawn.' message is too short for the automatic Vigenere decoder
|
||||
to work properly. A way around this is to concatenate copies of the message
|
||||
to itself, increasing the amount of text to analyze:
|
||||
|
||||
>>> VigCrack(ciphertext*5).crack_codeword(1)
|
||||
'D'
|
||||
>>> VigCrack(ciphertext*5).crack_message()
|
||||
'Attack at dawn.Attack at dawn.Attack at dawn.Attack at dawn.Attack at dawn.'
|
||||
|
||||
The crack_message() and crack_codeword() methods in the VigCrack class take 0,
|
||||
1 or 2 arguments. For more information, see the docstrings for those methods.
|
||||
|
||||
Note that this method (repeating the ciphertext) does not always work, but can
|
||||
sometimes be of use, as in the case of the example above.
|
||||
|
||||
Both the encipher() and decipher() methods for Vigenere and Caesar objects
|
||||
return a cipher object of the same type. This makes method chaining possible:
|
||||
|
||||
>>> codeword = 'King'
|
||||
>>> Vigenere(plaintext).encipher(codeword).decipher(codeword)
|
||||
'Attack at dawn.'
|
||||
>>> Caesar(plaintext).encipher(3).decipher(2).decipher(1)
|
||||
'Attack at dawn.'
|
||||
|
||||
Note:
|
||||
|
||||
1. Non-alphabetic input (e.g. " " and "." above) is left as is.
|
||||
2. The case of the input (plaintext/ciphertext) is preserved.
|
||||
3. The case of the key doesn't matter, e.g. 'king', 'KING', and 'KiNg' are
|
||||
identical keys.
|
||||
|
||||
Since each cipher is a subclass of the built-in str class, any cipher object
|
||||
can be treated as a string. For instance:
|
||||
|
||||
>>> Vigenere(plaintext).replace(' ', '').lower()
|
||||
'attackatdawn.'
|
||||
|
||||
However, since Python 2.1 and below don't seem to support subclasses of
|
||||
the str class, Python 2.2 or newer is required to use this library.
|
||||
|
||||
By default, PyGenere assumes that the original plaintext message was written
|
||||
in English, and thus English character frequencies are used for analysis.
|
||||
To change the language, the set_language() method is used. For example, the
|
||||
following code shows a short French string, encrypted with the keyword
|
||||
'FR', decoded. Without setting the language first, an incorrect result is
|
||||
obtained:
|
||||
|
||||
>>> text = 'Non, je ne veux pas coucher avec vous ce soir'
|
||||
>>> encrypted = Vigenere(text).encipher('FR')
|
||||
>>> print VigCrack(encrypted).set_language('FR').crack_codeword(2)
|
||||
FR
|
||||
>>> print VigCrack(encrypted).crack_codeword(2)
|
||||
FS
|
||||
|
||||
This isn't always the case: two languages may have similar enough character
|
||||
frequency distributions that decoding sometimes works correctly even when the
|
||||
language setting is incorrect.
|
||||
|
||||
Currently, PyGenere's language options other than English are DE (German),
|
||||
ES (Spanish), FR (French), IT (Italian), and PT (Portuguese).
|
||||
"""
|
||||
|
||||
|
||||
class Caesar(str):
|
||||
|
||||
"""An implementation of the Caesar cipher."""
|
||||
|
||||
def encipher(self, shift):
|
||||
"""Encipher input (plaintext) using the Caesar cipher and return it
|
||||
(ciphertext)."""
|
||||
ciphertext = []
|
||||
for p in self:
|
||||
if p.isalpha():
|
||||
ciphertext.append(chr((ord(p) - ord('Aa'[int(p.islower())]) +
|
||||
shift) % 26 + ord('Aa'[int(p.islower())])))
|
||||
else:
|
||||
ciphertext.append(p)
|
||||
return Caesar(''.join(ciphertext))
|
||||
|
||||
def decipher(self, shift):
|
||||
"""Decipher input (ciphertext) using the Caesar cipher and return it
|
||||
(plaintext)."""
|
||||
return self.encipher(-shift)
|
||||
|
||||
|
||||
class Vigenere(str):
|
||||
|
||||
"""An implementation of the Vigenere cipher."""
|
||||
|
||||
def encipher(self, key):
|
||||
"""Encipher input (plaintext) using the Vigenere cipher and return
|
||||
it (ciphertext)."""
|
||||
ciphertext = []
|
||||
k = 0
|
||||
n = len(key)
|
||||
for i in range(len(self)):
|
||||
p = self[i]
|
||||
if p.isalpha():
|
||||
ciphertext.append(chr((ord(p) + ord(
|
||||
(key[k % n].upper(), key[k % n].lower())[int(p.islower())]
|
||||
) - 2*ord('Aa'[int(p.islower())])) % 26 +
|
||||
ord('Aa'[int(p.islower())])))
|
||||
k += 1
|
||||
else:
|
||||
ciphertext.append(p)
|
||||
return Vigenere(''.join(ciphertext))
|
||||
|
||||
def decipher(self, key):
|
||||
"""Decipher input (ciphertext) using the Vigenere cipher and return
|
||||
it (plaintext)."""
|
||||
plaintext = []
|
||||
k = 0
|
||||
n = len(key)
|
||||
for i in range(len(self)):
|
||||
c = self[i]
|
||||
if c.isalpha():
|
||||
plaintext.append(chr((ord(c) - ord(
|
||||
(key[k % n].upper(), key[k % n].lower())[int(c.islower())]
|
||||
)) % 26 + ord('Aa'[int(c.islower())])))
|
||||
k += 1
|
||||
else:
|
||||
plaintext.append(c)
|
||||
return Vigenere(''.join(plaintext))
|
||||
|
||||
|
||||
class InputError(Exception):
|
||||
|
||||
"""This class is only used for throwing exceptions if the user supplies
|
||||
invalid input (e.g. ciphertext is an empty string)."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class VigCrack(Vigenere):
|
||||
|
||||
"""
|
||||
VigCrack objects have methods to break Vigenere-encoded texts when the
|
||||
original key is unknown.
|
||||
|
||||
The technique used is based on the one described in:
|
||||
|
||||
http://www.stonehill.edu/compsci/Shai_papers/RSA.pdf
|
||||
(pages 9-10)
|
||||
|
||||
Character frequencies taken from:
|
||||
http://www.csm.astate.edu/~rossa/datasec/frequency.html (English)
|
||||
http://www.characterfrequency.com/ (French, Italian, Portuguese, Spanish)
|
||||
http://www.santacruzpl.org/readyref/files/g-l/ltfrqger.shtml (German)
|
||||
"""
|
||||
|
||||
# Unless otherwise specified, test for codewords between (and including)
|
||||
# these two lengths:
|
||||
__default_min_codeword_length = 5
|
||||
__default_max_codeword_length = 9
|
||||
|
||||
# The following are language-specific data on character frequencies.
|
||||
# Kappa is the "index of coincidence" described in the cryptography paper
|
||||
# (link above).
|
||||
__english_data = {
|
||||
'A':8.167, 'B':1.492, 'C':2.782, 'D':4.253, 'E':12.702,
|
||||
'F':2.228, 'G':2.015, 'H':6.094, 'I':6.996, 'J':0.153,
|
||||
'K':0.772, 'L':4.025, 'M':2.406, 'N':6.749, 'O':7.507,
|
||||
'P':1.929, 'Q':0.095, 'R':5.987, 'S':6.327, 'T':9.056,
|
||||
'U':2.758, 'V':0.978, 'W':2.360, 'X':0.150, 'Y':1.974,
|
||||
'Z':0.074, 'max_val':12.702, 'kappa':0.0667
|
||||
}
|
||||
|
||||
__french_data = {
|
||||
'A':8.11, 'B':0.903, 'C':3.49, 'D':4.27, 'E':17.22,
|
||||
'F':1.14, 'G':1.09, 'H':0.769, 'I':7.44, 'J':0.339,
|
||||
'K':0.097, 'L':5.53, 'M':2.89, 'N':7.46, 'O':5.38,
|
||||
'P':3.02, 'Q':0.999, 'R':7.05, 'S':8.04, 'T':6.99,
|
||||
'U':5.65, 'V':1.30, 'W':0.039, 'X':0.435, 'Y':0.271,
|
||||
'Z':0.098, 'max_val':17.22, 'kappa':0.0746
|
||||
}
|
||||
|
||||
__german_data = {
|
||||
'A':6.506, 'B':2.566, 'C':2.837, 'D':5.414, 'E':16.693,
|
||||
'F':2.044, 'G':3.647, 'H':4.064, 'I':7.812, 'J':0.191,
|
||||
'K':1.879, 'L':2.825, 'M':3.005, 'N':9.905, 'O':2.285,
|
||||
'P':0.944, 'Q':0.055, 'R':6.539, 'S':6.765, 'T':6.742,
|
||||
'U':3.703, 'V':1.069, 'W':1.396, 'X':0.022, 'Y':0.032,
|
||||
'Z':1.002, 'max_val':16.693, 'kappa':0.0767
|
||||
}
|
||||
|
||||
__italian_data = {
|
||||
'A':11.30, 'B':0.975, 'C':4.35, 'D':3.80, 'E':11.24,
|
||||
'F':1.09, 'G':1.73, 'H':1.02, 'I':11.57, 'J':0.035,
|
||||
'K':0.078, 'L':6.40, 'M':2.66, 'N':7.29, 'O':9.11,
|
||||
'P':2.89, 'Q':0.391, 'R':6.68, 'S':5.11, 'T':6.76,
|
||||
'U':3.18, 'V':1.52, 'W':0.00, 'X':0.024, 'Y':0.048,
|
||||
'Z':0.958, 'max_val':11.57, 'kappa':0.0733
|
||||
}
|
||||
|
||||
__portuguese_data = {
|
||||
'A':13.89, 'B':0.980, 'C':4.18, 'D':5.24, 'E':12.72,
|
||||
'F':1.01, 'G':1.17, 'H':0.905, 'I':6.70, 'J':0.317,
|
||||
'K':0.0174, 'L':2.76, 'M':4.54, 'N':5.37, 'O':10.90,
|
||||
'P':2.74, 'Q':1.06, 'R':6.67, 'S':7.90, 'T':4.63,
|
||||
'U':4.05, 'V':1.55, 'W':0.0104, 'X':0.272, 'Y':0.0165,
|
||||
'Z':0.400, 'max_val':13.89, 'kappa':0.0745
|
||||
}
|
||||
|
||||
__spanish_data = {
|
||||
'A':12.09, 'B':1.21, 'C':4.20, 'D':4.65, 'E':13.89,
|
||||
'F':0.642, 'G':1.11, 'H':1.13, 'I':6.38, 'J':0.461,
|
||||
'K':0.038, 'L':5.19, 'M':2.86, 'N':7.23, 'O':9.58,
|
||||
'P':2.74, 'Q':1.37, 'R':6.14, 'S':7.43, 'T':4.49,
|
||||
'U':4.53, 'V':1.05, 'W':0.011, 'X':0.124, 'Y':1.14,
|
||||
'Z':0.324, 'max_val':13.89, 'kappa':0.0766
|
||||
}
|
||||
|
||||
# The default language is set to English.
|
||||
__lang = 'EN'
|
||||
__lang_data = __english_data
|
||||
|
||||
# This method sets the lang (__lang) attribute of a VigCrack object.
|
||||
def set_language(self, language):
|
||||
self.__lang = language.upper()
|
||||
if self.__lang == 'DE':
|
||||
self.__lang_data = self.__german_data
|
||||
elif self.__lang == 'ES':
|
||||
self.__lang_data = self.__spanish_data
|
||||
elif self.__lang == 'FR':
|
||||
self.__lang_data = self.__french_data
|
||||
elif self.__lang == 'IT':
|
||||
self.__lang_data = self.__italian_data
|
||||
elif self.__lang == 'PT':
|
||||
self.__lang_data = self.__portuguese_data
|
||||
else:
|
||||
self.__lang = 'EN'
|
||||
return self
|
||||
|
||||
# Rotate text n places to the right, wrapping around at the end.
|
||||
def __rotate_right(self, n):
|
||||
cutting_point = len(self) - (n % len(self))
|
||||
return self[cutting_point:] + self[:cutting_point]
|
||||
|
||||
# Get every nth char from a piece of text, from a given starting position.
|
||||
def __get_every_nth_char(self, start, n):
|
||||
accumulator = []
|
||||
for i in range(len(self)):
|
||||
if (i % n) == start:
|
||||
accumulator.append(self[i])
|
||||
return VigCrack(''.join(accumulator)).set_language(self.__lang)
|
||||
|
||||
# Build a dictionary containing the number of occurrences of each char.
|
||||
def __count_char_freqs(self):
|
||||
dictionary = {}
|
||||
self = self.upper()
|
||||
for char in self:
|
||||
if char.isalpha():
|
||||
dictionary[char] = dictionary.get(char, 0) + 1
|
||||
return dictionary
|
||||
|
||||
# Scale the dictionary so that it can be compared with __lang_data.
|
||||
def __scale(self, dictionary):
|
||||
v = dictionary.values()
|
||||
v.sort()
|
||||
max_val = v[-1]
|
||||
scaling_factor = self.__lang_data['max_val']/max_val
|
||||
for (k, v) in dictionary.items():
|
||||
dictionary[k] = v*scaling_factor
|
||||
return dictionary
|
||||
|
||||
# The residual error is the difference between a char's frequency in
|
||||
# __lang_data and its frequency in the scaled dictionary from above.
|
||||
# The error is then squared to remove a possible negative value.
|
||||
def __sum_residuals_squared(self, dictionary):
|
||||
sum = 0
|
||||
for (k, v) in dictionary.items():
|
||||
sum += (v - self.__lang_data[k])**2
|
||||
return sum
|
||||
|
||||
# Find the Caesar shift that brings the ciphertext closest to the
|
||||
# character distribution of the plaintext's language.
|
||||
def __find_best_caesar_shift(self):
|
||||
best = 0
|
||||
smallest_sum = -1
|
||||
# Find the residual sum for each shift.
|
||||
for shift in range(26):
|
||||
encoded_text = Caesar(self).encipher(shift)
|
||||
vigcrack_obj = VigCrack(encoded_text).set_language(self.__lang)
|
||||
char_freqs = vigcrack_obj.__count_char_freqs()
|
||||
scaled = vigcrack_obj.__scale(char_freqs)
|
||||
current_sum = vigcrack_obj.__sum_residuals_squared(scaled)
|
||||
# Keep track of the shift with the lowest residual sum.
|
||||
# If there's a tie, the smallest shift wins.
|
||||
if smallest_sum == -1:
|
||||
smallest_sum = current_sum
|
||||
if current_sum < smallest_sum:
|
||||
best = shift
|
||||
smallest_sum = current_sum
|
||||
return best
|
||||
|
||||
def __find_codeword_length(self, min_length, max_length):
|
||||
codeword_length = min_length
|
||||
kappas = []
|
||||
# Put the kappa value for each codeword length tested into an array.
|
||||
for i in range(min_length, max_length + 1):
|
||||
temp = self.__rotate_right(i)
|
||||
coincidences = 0
|
||||
for j in range(len(self)):
|
||||
if temp[j] == self[j]:
|
||||
coincidences += 1
|
||||
kappas.append(float(coincidences)/len(self))
|
||||
# Find out which value of kappa is closest to the kappa of the
|
||||
# plaintext's language. If there's a tie, the shortest codeword wins.
|
||||
smallest_squared_diff = -1
|
||||
for i in range((max_length + 1) - min_length):
|
||||
current_squared_diff = (self.__lang_data['kappa'] - kappas[i])**2
|
||||
if smallest_squared_diff == -1:
|
||||
smallest_squared_diff = current_squared_diff
|
||||
if current_squared_diff < smallest_squared_diff:
|
||||
codeword_length = min_length + i
|
||||
smallest_squared_diff = current_squared_diff
|
||||
return codeword_length
|
||||
|
||||
def __find_codeword(self, min_length, max_length):
|
||||
# Strip away invalid chars.
|
||||
accumulator = []
|
||||
for char in self:
|
||||
if char.isalpha():
|
||||
accumulator.append(char)
|
||||
alpha_only = VigCrack(''.join(accumulator)).set_language(self.__lang)
|
||||
codeword_length = alpha_only.__find_codeword_length(min_length,
|
||||
max_length)
|
||||
# Build the codeword by finding one character at a time.
|
||||
codeword = []
|
||||
for i in range(codeword_length):
|
||||
temp = alpha_only.__get_every_nth_char(i, codeword_length)
|
||||
shift = temp.__find_best_caesar_shift()
|
||||
if shift == 0:
|
||||
codeword.append('A')
|
||||
else:
|
||||
codeword.append(chr(ord('A') + (26 - shift)))
|
||||
return VigCrack(''.join(codeword)).set_language(self.__lang)
|
||||
|
||||
def __parse_args(self, *arg_list):
|
||||
if len(arg_list) == 0: # Use default values for codeword length.
|
||||
min_length = self.__default_min_codeword_length
|
||||
max_length = self.__default_max_codeword_length
|
||||
elif len(arg_list) == 1: # Exact codeword length specified by user.
|
||||
min_length = max_length = int(arg_list[0])
|
||||
else: # min_length and max_length given by user.
|
||||
min_length = int(arg_list[0])
|
||||
max_length = int(arg_list[1])
|
||||
# Check for input errors.
|
||||
if min_length == max_length:
|
||||
if min_length < 1:
|
||||
raise InputError('Codeword length is too small')
|
||||
else:
|
||||
if min_length < 1:
|
||||
raise InputError('min_length is too small')
|
||||
if max_length < 1:
|
||||
raise InputError('max_length is too small')
|
||||
if max_length < min_length:
|
||||
raise InputError('max_length cannot be shorter than min_length')
|
||||
if len(self) == 0:
|
||||
raise InputError('Ciphertext is empty')
|
||||
if len(self) < max_length:
|
||||
raise InputError('Ciphertext is too short')
|
||||
# Check that the ciphertext contains at least one valid character.
|
||||
has_valid_char = False
|
||||
for char in self:
|
||||
if char.isalpha():
|
||||
has_valid_char = True
|
||||
break
|
||||
if not has_valid_char:
|
||||
raise InputError('No valid characters in ciphertext')
|
||||
# If everything's all right, return the min_length and max_length.
|
||||
return [min_length, max_length]
|
||||
|
||||
def crack_codeword(self, *arg_list):
|
||||
"""
|
||||
Try to find the codeword that encrypted the ciphertext object.
|
||||
If no arguments are supplied, codewords between the default minimum
|
||||
length and the default maximum length are tried.
|
||||
If one integer argument is supplied, only codewords with that length
|
||||
will be tried.
|
||||
If two integer arguments are given then the first argument is treated
|
||||
as a minimum codeword length, and the second argument is treated as a
|
||||
maximum codeword length, to try.
|
||||
"""
|
||||
array = self.__parse_args(*arg_list)
|
||||
return self.__find_codeword(array[0], array[1])
|
||||
|
||||
def crack_message(self, *arg_list):
|
||||
"""
|
||||
Try to decode the ciphertext object.
|
||||
This method accepts arguments in the same way as the crack_codeword()
|
||||
method.
|
||||
"""
|
||||
codeword = self.crack_codeword(*arg_list)
|
||||
return self.decipher(codeword)
|
||||
|
||||
|
||||
# History
|
||||
# -------
|
||||
#
|
||||
# 2007-02-16: v 0.3. Minor (mostly cosmetic) modifications to make the code
|
||||
# more compliant with the Python Style Guide
|
||||
# (http://www.python.org/dev/peps/pep-0008/).
|
||||
#
|
||||
# 2006-06-11: v 0.2. Language support added for German (DE), Spanish (ES),
|
||||
# French (FR), Italian (IT), and Portuguese (PT).
|
||||
#
|
||||
# 2006-04-29: v 0.1. First release.
|
||||
#
|
||||
#
|
||||
#
|
||||
# License
|
||||
# -------
|
||||
#
|
||||
# Copyright (c) 2006, Simon Liu <webmonkey at smurfoncrack dot com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# This library incorporates code from the PyCipher project on SourceForge.net
|
||||
# (http://sourceforge.net/projects/pycipher/). The original copyright notice
|
||||
# is preserved below as required; these modifications are released under the
|
||||
# same terms.
|
||||
#
|
||||
#
|
||||
# Copyright (c) 2005, Aggelos Orfanakos <csst0266atcsdotuoidotgr>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
Loading…
Add table
Add a link
Reference in a new issue