cryptohack general

Encodings

ASCII

ASCII is a 7-bit encoding standard which allows the representation of text using the integers 0-127.

1
2
3
arr = [99, 114, 121, 112, 116, 111, 123, 65, 83, 67, 73, 73, 95, 112, 114, 49, 110, 116, 52, 98, 108, 51, 125]
result = "".join(chr(x) for x in arr)
print(result)
1
2
❯ python encoding.py
crypto{ASCII_pr1nt4bl3}
crypto{ASCII_pr1nt4bl3}

Hex

Hexadecimal can be used to represent ASCII strings. First each letter is converted to an ordinal number according to the ASCII table. Then the decimal numbers are converted to base-16 numbers.

1
2
3
a = 0x63727970746f7b596f755f77696c6c5f62655f776f726b696e675f776974685f6865785f737472696e67735f615f6c6f747d
result = bytes.fromhex(hex(a)[2:]).decode()
print(result)
1
2
❯ python hex.py
crypto{You_will_be_working_with_hex_strings_a_lot}
crypto{You_will_be_working_with_hex_strings_a_lot}

Base64

Base64 allows us to represent binary data as an ASCII string using an alphabet of 64 characters.

1
2
3
4
import base64
h = '72bca9b68fc16ac7beeb8f849dca1d8a783e8acf9679bf9269f7bf'
a = base64.b64encode(bytes.fromhex(h))
print(a.decode())
1
crypto/Base+64+Encoding+is+Web+Safe/
crypto/Base+64+Encoding+is+Web+Safe/

Bytes and Big Integers

Cryptosystems like RSA works on numbers, but messages are made up of characters. The most common way is to take the ordinal bytes of the message, convert them into hexadecimal, and concatenate.

1
2
3
4
from Crypto.Util.number import *
enc = "11515195063862318899931685488813747395775516287289682636499965282714637259206269"
result = long_to_bytes(int(enc)).decode()
print(result)
1
crypto{3nc0d1n6_4ll_7h3_w4y_d0wn}
crypto{3nc0d1n6_4ll_7h3_w4y_d0wn}

Encoding Challenge

Automating the decoding process for 100 levels.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import base64
import codecs
import json
from Crypto.Util.number import *
from pwn import *

def base64_decode(encoded):
return base64.b64decode(encoded.encode()).decode()

def rot13_decode(encoded):
return codecs.decode(encoded, "rot13")

def hex_decode(encoded):
return bytes.fromhex(encoded).decode()

def bigint_decode(encoded):
return bytes.fromhex(encoded[2:]).decode()

def utf8_decode(encoded):
return "".join([chr(x) for x in encoded])

r = remote("socket.cryptohack.org", 13377, level="debug")

def json_recv():
return json.loads(r.recvline().decode())

def json_send(hsh):
r.sendline(json.dumps(hsh).encode())

def decode(type, encoded):
match type:
case "base64": return base64_decode(encoded)
case "rot13": return rot13_decode(encoded)
case "hex": return hex_decode(encoded)
case "bigint": return bigint_decode(encoded)
case "utf-8": return utf8_decode(encoded)
case _: return None

while True:
received = json_recv()
if "flag" in received:
print(f"Flag: {received['flag']}")
break
to_send = {"decoded": decode(received["type"], received["encoded"])}
json_send(to_send)
crypto{3nc0d3_d3c0d3_3nc0d3}

XOR

XOR Starter

XOR each character of the string label with the integer 13.

1
2
3
4
5
from pwn import xor
a = "label"
b = 13
result = xor(a.encode(), b).decode()
print(f"crypto{{{result}}}")
crypto{aloha}

XOR Properties

Using XOR properties: Commutative, Associative, Identity, and Self-Inverse.

1
2
3
4
5
6
7
8
9
10
from pwn import xor
KEY1 = "a6c8b6733c9b22de7bc0253266a3867df55acde8635e19c73313"
KEY2_KEY1 = "37dcb292030faa90d07eec17e3b1c6d8daf94c35d4c9191a5e1e"
KEY2_KEY3 = "c1545756687e7573db23aa1c3452a098b71a7fbf0fddddde5fc1"
FLAG_K1_K2_K3 = "04ee9855208a2cd59091d04767ae47963170d1660df7f56f5faf"

# FLAG ^ K1 ^ K2 ^ K3 ^ K1 ^ (K2 ^ K3) = FLAG
k1_k2_k3 = xor(bytes.fromhex(KEY1), bytes.fromhex(KEY2_KEY3))
FLAG = xor(bytes.fromhex(FLAG_K1_K2_K3), k1_k2_k3)
print(FLAG.decode())
crypto{x0r_i5_ass0c1at1v3}

Favourite byte

XOR with a single secret byte.

1
2
3
4
5
6
from pwn import xor
data = bytes.fromhex("73626960647f6b206821204f21254f7d694f7624662065622127234f726927756d")
for i in range(256):
res = xor(data, i)
if b"crypto" in res:
print(res.decode())
crypto{0x10_15_my_f4v0ur173_by7e}

You either know, XOR you don't

Brute-forcing the key using the known flag format crypto{.

1
2
3
4
5
from pwn import xor
ciphertext = bytes.fromhex("0e0b213f26041e480b26217f27342e175d0e070a3c5b103e2526217f27342e175d0e077e263451150104")
key_part = xor(ciphertext[:7], b"crypto{") # b'myXORke'
key = b"myXORkey"
print(xor(ciphertext, key).decode())
crypto{1f_y0u_Kn0w_En0uGH_y0u_Kn0w_1t_4ll}

Lemur XOR

Visual XOR between the RGB bytes of two images.

1
2
3
4
5
6
from PIL import Image
import numpy as np
img_flag = np.array(Image.open("./flag.png"))
img_lemur = np.array(Image.open("./lemur.png"))
result = np.bitwise_xor(img_flag, img_lemur)
Image.fromarray(result).save("./result.png")
crypto{X0Rly_n0t!}

Mathematics

Greatest Common Divisor

Using Euclid's Algorithm to find the GCD.

1
2
3
4
5
6
def gcd(a, b):
while b:
a, b = b, a % b
return a

print(gcd(66528, 52920))
1512

Extended GCD

Finding integers \(u, v\) such that \(a \cdot u + b \cdot v = \gcd(a, b)\).

1
2
3
4
5
6
7
8
9
10
11
def extended_gcd(a, b):
if a == 0:
return b, 0, 1
gcd, x1, y1 = extended_gcd(b % a, a)
x = y1 - (b // a) * x1
y = x1
return gcd, x, y

p, q = 26513, 32321
g, u, v = extended_gcd(p, q)
print(min(u, v))
-8404

Modular Arithmetic 1

Finding the remainder of \(a\) divided by \(m\).

1
2
3
x = 11 % 6
y = 8146798528947 % 17
print(min(x, y))
2

Modular Arithmetic 2

Fermat's Little Theorem: \(a^{p-1} \equiv 1 \pmod{p}\) if \(p\) is prime and \(p \nmid a\).

1
2
p = 65537
print(pow(273246787654, 65536, 65537))
1

Modular Arithmetic 3

Finding the multiplicative inverse \(d = 3^{-1} \pmod{13}\).

1
2
# Using Fermat's Little Theorem: 3^(13-2) % 13
print(pow(3, 11, 13))
9

Data Formats

Privacy-Enhanced Mail?

Extracting the private key \(d\) from a PEM-formatted RSA key.

1
2
3
4
from Crypto.PublicKey import RSA
with open("privacy_enhanced_mail.pem", "r") as f:
key = RSA.import_key(f.read())
print(key.d)
15682700288056331364787171045819973654991149949197959929860861228180021707316851924456205543665565810892674190059831330231436970914474774562714945620519144389785158908994181951348846017432506464163564960993784254153395406799101314760033445065193429592512349952020982932218524462341002102063435489318813316464511621736943938440710470694912336237680219746204595128959161800595216366237538296447335375818871952520026993102148328897083547184286493241191505953601668858941129790966909236941127851370202421135897091086763569884760099112291072056970636380417349019579768748054760104838790424708988260443926906673795975104689

CERTainly not

Finding the modulus of a DER-encoded x509 RSA certificate.

1
2
3
4
from Crypto.PublicKey import RSA
with open("2048b-rsa-example-cert.der", "rb") as f:
key = RSA.import_key(f.read())
print(key.n)
22825373692019530804306212864609512775374171823993708516509897631547513634635856375624003737068034549047677999310941837454378829351398302382629658264078775456838626207507725494030600516872852306191255492926495965536379271875310457319107936020730050476235278671528265817571433919561175665096171189758406136453987966255236963782666066962654678464950075923060327358691356632908606498231755963567382339010985222623205586923466405809217426670333410014429905146941652293366212903733630083016398810887356019977409467374742266276267137547021576874204809506045914964491063393800499167416471949021995447722415959979785959569497

SSH Keys

Extracting the modulus \(n\) from an SSH public key.

1
2
3
4
from Crypto.PublicKey import RSA
with open("bruce_rsa.pub", "rb") as f:
key = RSA.import_key(f.read())
print(key.n)
3931406272922523448436194599820093016241472658151801552845094518579507815990600459669259603645261532927611152984942840889898756532060894857045175300145765800633499005451738872081381267004069865557395638550041114206143085403607234109293286336393552756893984605214352988705258638979454736514997314223669075900783806715398880310695945945147755132919037973889075191785977797861557228678159538882153544717797100401096435062359474129755625453831882490603560134477043235433202708948615234536984715872113343812760102812323180391544496030163653046931414723851374554873036582282389904838597668286543337426581680817796038711228401443244655162199302352017964997866677317161014083116730535875521286631858102768961098851209400973899393964931605067856005410998631842673030901078008408649613538143799959803685041566964514489809211962984534322348394428010908984318940411698961150731204316670646676976361958828528229837610795843145048243492909

Transparency

Using subfinder to find subdomains and locating the flag on one of them.

1
2
3
4
5
6
❯ subfinder -d cryptohack.org
...
thetransparencyflagishere.cryptohack.org

❯ curl https://thetransparencyflagishere.cryptohack.org
crypto{thx_redpwn_for_inspiration}
crypto{thx_redpwn_for_inspiration}