-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
98 changed files
with
102,351 additions
and
6 deletions.
There are no files selected for viewing
37 changes: 37 additions & 0 deletions
37
rgbCTF2020/Cryptography/AdequateEncryptionStandard/adequate_encryption_standard.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Adequate Encryption Standard | ||
## Challenge Description | ||
I wrote my own AES! Can you break it? | ||
|
||
hQWYogqLXUO+rePyWkNlBlaAX47/2dCeLFMLrmPKcYRLYZgFuqRC7EtwX4DRtG31XY4az+yOvJJ/pwWR0/J9gg== | ||
|
||
File: adequate_encryption_standard.py | ||
|
||
## Solution | ||
|
||
The file `adequate_encryption_standard.py` contains an encryption method which resembles | ||
AES. The encryption method contains a bug in the round function. | ||
|
||
``` | ||
for _ in range(ROUNDS): | ||
block = enc_sub(block) | ||
block = enc_perm(block) | ||
block = bytearray(block) | ||
for i in range(len(block)): | ||
block[i] ^= key[idx] | ||
``` | ||
|
||
After every round of regular AES, the cipher state is XOR-ed with a 16-byte round key. | ||
However, in this case `key[idx]` uses the wrong loop variable, and its value does | ||
not change over the encryption of an entire block. Thus we can brute force the possible | ||
values for `key[idx]` for each block of the provided ciphertext. | ||
|
||
We write the corresponding decryption method for the cipher, test block decryption via | ||
brute force for every block, and recover the flag. | ||
|
||
[Solution](decode.py) | ||
|
||
## Flag | ||
```rgbCTF{brut3_f0rc3_is_4LW4YS_th3_4nsw3r(but_with_0ptimiz4ti0ns)}``` | ||
|
||
### Author | ||
[keegan](https://twitter.com/inf_0_) |
109 changes: 109 additions & 0 deletions
109
rgbCTF2020/Cryptography/AdequateEncryptionStandard/decode.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import base64 | ||
from adequate_encryption_standard import * | ||
|
||
|
||
pbox_i = [pbox.index(i) for i in range(64)] | ||
|
||
def dec_perm(in_bytes: bytes) -> bytes: | ||
num = int.from_bytes(in_bytes, 'big') | ||
binary = bin(num)[2:].rjust(BLOCK_SIZE * 8, '0') | ||
permuted = ''.join([binary[pbox_i[i]] for i in range(BLOCK_SIZE * 8)]) | ||
out = bytes([int(permuted[i:i + 8], 2) for i in range(0, BLOCK_SIZE * 8, 8)]) | ||
return out | ||
|
||
def dec_sub(in_bytes: bytes) -> bytes: | ||
return bytes([sbox.index(b) for b in in_bytes]) | ||
|
||
def encrypt(plain: bytes, key: bytes) -> bytes: | ||
blocks = to_blocks(plain) | ||
out = bytearray() | ||
key = expand_key(key, len(blocks)) | ||
for idx, block in enumerate(blocks): | ||
block = pad(block) | ||
assert len(block) == BLOCK_SIZE | ||
for _ in range(ROUNDS): | ||
block = enc_sub(block) | ||
block = enc_perm(block) | ||
block = bytearray(block) | ||
for i in range(len(block)): | ||
block[i] ^= key[idx] | ||
out.extend(block) | ||
return bytes(out) | ||
|
||
def decrypt(cipher: bytes, key: bytes) -> bytes: | ||
blocks = to_blocks(cipher) | ||
out = bytearray() | ||
key = expand_key(key, len(blocks)) | ||
for idx, block in enumerate(blocks): | ||
for _ in range(ROUNDS): | ||
block = bytearray(block) | ||
for i in range(len(block)): | ||
block[i] ^= key[idx] | ||
block = bytes(block) | ||
assert len(block) == BLOCK_SIZE | ||
block = dec_perm(block) | ||
block = dec_sub(block) | ||
out.extend(block) | ||
return bytes(out) | ||
|
||
def decrypt_ks(cipher: bytes, keystream: bytes) -> bytes: | ||
blocks = to_blocks(cipher) | ||
out = bytearray() | ||
key = keystream | ||
for idx, block in enumerate(blocks): | ||
for _ in range(ROUNDS): | ||
block = bytearray(block) | ||
for i in range(len(block)): | ||
block[i] ^= key[idx] | ||
block = bytes(block) | ||
assert len(block) == BLOCK_SIZE | ||
block = dec_perm(block) | ||
block = dec_sub(block) | ||
out.extend(block) | ||
return bytes(out) | ||
|
||
def score(pt): | ||
score = 0 | ||
for p in pt: | ||
if p < 128: | ||
score += 1 | ||
return score | ||
|
||
def hack_block(ct): | ||
assert len(ct) == BLOCK_SIZE | ||
|
||
ks = b"\xe4" | ||
for i in range(256): | ||
ks = bytes(bytearray([i])) | ||
|
||
pt = decrypt_ks(ct, ks) | ||
if score(pt) == 8: | ||
print(hex(i), pt) | ||
|
||
def hack(ct, i): | ||
blocks = to_blocks(ct) | ||
|
||
print(f"Block {i}") | ||
hack_block(ct[i*BLOCK_SIZE:(i+1)*BLOCK_SIZE]) | ||
print() | ||
|
||
|
||
def main(): | ||
ciphertext = "hQWYogqLXUO+rePyWkNlBlaAX47/2dCeLFMLrmPKcYRLYZgFuqRC7EtwX4DRtG31XY4az+yOvJJ/pwWR0/J9gg==" | ||
ct = base64.b64decode(ciphertext) | ||
hack(ct, 0) | ||
hack(ct, 1) | ||
hack(ct, 2) | ||
hack(ct, 3) | ||
hack(ct, 4) | ||
hack(ct, 5) | ||
hack(ct, 6) | ||
hack(ct, 7) | ||
|
||
ks = bytearray([0x9f, 0xc1, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]) | ||
print(decrypt_ks(ct, ks)) | ||
|
||
|
||
|
||
if __name__ == "__main__": | ||
main() |
12,786 changes: 12,786 additions & 0 deletions
12,786
rgbCTF2020/Cryptography/GrabYourJisho/grab your jisho.txt
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Grab your jisho | ||
## Challenge Description | ||
|
||
これは文字化けか?それとも暗号…? | ||
|
||
File: grab your jisho | ||
|
||
## Solution | ||
|
||
We are provided with a 2MB text file with characters from the Unicode CJK blocks. | ||
The text is not recognized by web translation services, so we conclude the flag must be found some other way. We find the following string resembling a URL: | ||
|
||
```昒鏽霱彊://鱓攫襵.扺譸醸叏褖𠆢夳鯁曵.蕐屩形/尤甠賿䵷/_/_/_/_/_____``` | ||
|
||
It seems like individual characters correspond directly to individual A-Z letters. | ||
For example, "昒鏽霱彊://鱓攫襵." probably decodes to "http://www." This tells us we | ||
are dealing with some sort of substitution cipher. However, it is not one-to-one | ||
substitution, since otherwise we would expect each of the "t"s or "w"s to match the | ||
others. This tells us that either we are working with a many-to-one substitution cipher | ||
or perhaps a Vigenere cipher. With so much ciphertext, we can just treat it as a | ||
many-to-one cipher, where one decoded Latin alphabet letter can be represented by | ||
one of many CJK characters. | ||
|
||
We start by identifying the most frequently appearing character and all of the words | ||
it appears in. We use a dictionary of English words and their frequency to determine | ||
the likelihood that the CJK character is an "A," "B," "C," and so on. For example, if we | ||
are considering the character "躄," and we see start three letter words like "躄呵芀" or | ||
"躄亟玊" or words with apostrophes like "齃劅魂'躄," we might speculate that the character | ||
decodes to a "T" or a "S," since words with those letters in the same position are common. | ||
|
||
If one candidate letter is significantly more likely than all the others, we make the | ||
substitution in the text. We repeat until we have recovered a large portion of the text. | ||
This reveals that the plaintext is taken from the book "Myths & Legends of Japan." | ||
We find the flag added to this text, except there are some unique characters which only | ||
appear once in the flag and cannot be decoded using frequency analysis. | ||
|
||
```RGBCTF讞鸞鸚鱺YOMINIKUI鸝钁厵鬱``` | ||
|
||
"Yominikui" translates to "hard to read" or "illegible," so we know we are at least on the | ||
right track. At this point, we look at the relationship between the CJK characters and Latin | ||
characters we've decoded already. As a sample, observe the following: | ||
|
||
```A:一 B:儿 C:亍 Z:髗``` | ||
|
||
The number of strokes in a character appears to correspond to the letter of the alphabet. | ||
In fact, jisho.org helpfully reports the number of strokes used to write a character. | ||
We input the 8 remaining characters to the site and find they take 27, 30, 28, 30, 30, | ||
28, 30, and 29 strokes respectively. Since these values are greater than 26, which | ||
corresponds to Z, we assume that these characters decode to the ASCII characters directly following z. This way the flag has the correct format, and we can understand the remaining characters in the flag. | ||
|
||
## Flag | ||
```rgbctf{~|~yominikui~|~}``` | ||
|
||
### Author | ||
[keegan](https://twitter.com/inf_0_) |
Oops, something went wrong.