b01lers
Dream Stealing
Challenge Description:
I’ve managed to steal some secrets from their subconscious, can you figure out anything from this?
MD5: bb416c708f242b0c70d6f2c07d646d9f
ciphertext.txt:
So, in this challenge, I’m given an MD5 hash, ciphertext that needs to be decrypted, a factor of N, a public key, and a modulus. I think that once I find a supposed flag, I run an MD5 hash on it and check if it matches the MD5 hash I was originally given to determine if it was correct or not. From my understanding, this challenge requires me to essentially apply the hash function backwards to the ciphertext. When I run “ciphertext%public_key”, I get 47292.
UPDATE
Writeup: https://aptx1337.github.io/posts/ctf/b01lers/crypto_01_dream_Stealing.html
Turns out I was I was applying the inverse of the hash function incorrectly. This is apparently an RSA function, meaning that the decryption formula is: pow(c, d) = pow(pow(m, e), d) = m (mod m),
with c = ciphertext, d = private key exponent, m = the integer representing the padded plaintext, n = modulus.
So, in order to obtain the plaintext, I would need to:
- Get the other factor of the modulus, by dividing the modulus by the given n factor.
- Calculate the prime factors (p - 1)*(q - 1)
- Get the modular inverse of the public key
’’’ from Crypto.Util.number import long_to_bytes
e = 65537 c = 75665489286663825011389014693118717144564492910496517817351278852753259053052732535663285501814281678158913989615919776491777945945627147232073116295758400365665526264438202825171012874266519752207522580833300789271016065464767771248100896706714555420620455039240658817899104768781122292162714745754316687483 n = 98570307780590287344989641660271563150943084591122129236101184963953890610515286342182643236514124325672053304374355281945455993001454145469449640602102808287018619896494144221889411960418829067000944408910977857246549239617540588105788633268030690222998939690024329717050066864773464183557939988832150357227
p = 9695477612097814143634685975895486365012211256067236988184151482923787800058653259439240377630508988251817608592320391742708529901158658812320088090921919 q = 10166627341555233885462189686170129966199363862865327417835599922534140147190891310884780246710738772334481095318744300242272851264697786771596673112818133 #calculate this by dividing N by p, just some basic maths ;).
phi = (p-1)*(q-1) z = pow(e, -1, phi) m = pow(c, z, n) ‘’’
Shared Dreaming
Challenge Description:
It’s not just about depth you know, you need the simplest version of the idea in order for it to grow naturally in a subject’s mind; it’s a very subtle art.
MD5: 0d9787a4fb3e48d2b6a8c7cb1a7db618
shareddreaming.txt:
So, in this challenge, I’m given an MD5 hash, ciphertext (with the operations used to obtain it), 3 hints giving more operations used in this cipher, and the length of the flag through RandByte. So, if I can xor the ciphertext with a3 and RandByte, I can get the flag. To do this, I first need to get a3. Looking at the hints, I see that xor-ing Hint 2 and Hint 1 would give me a1. If I xor Hint 3 with a1, I can get a3.
So, xor-ing Hint 1 with Hint 2 gives me: a1 = 6012799428683443997689587680337673480971720162848260648052236293921950878883876866182340141784603077585618289040815411545017412217024358641774352396510770 (in decimal form). a3 = 12969117837359611590158531524137144983698950013035435361651744234441525282625193054901967784556715455348067253045411962885728614821895623458135072145344009.
Now, I need to figure out what RandByte is.
’’’ def KPA (ct): known = ord(‘f’) pad = ct[0] ^ known pt = ‘’ for ch in ct: pt += chr(pad ^ ch) return pt
hint1 = 0x8ba4c4dfce33fd6101cf5c56997531c024a10f1dc323eb7fe3841ac389747fb90e3418f90011ef2610fa3636cd6cf0002d19faa30d39161fbd45cc58abff6a84
hint2 = 0xf969375145322aba697ce9b4e00aa88e81ffe5c306b1b98148f33c4581b2ac39bc95f13b27c39f2311a590b7e27cdbdb7599f615acd70c45378e44fb319b8cb6
hint3 = 0x855249b385f7b1d9923f71feb3bdee1032963ab51aa7b9d89a20c08c381e77890aa8849702d8791f8e636e833928ba6ea44c5f261983b7e29bd82e44b77fe03b
a1 = hint1 ^ hint2
a3 = hint3 ^ a1
ct = ‘0’ + str(hex(a3))[2:]
print(“a1:”, hex(a1)) print(“a3:”, hex(a3)) pt = KPA(bytearray.fromhex(ct))
print(pt) ‘’’
Update
Writeup: https://github.com/t3rmin0x/CTF-Writeups/tree/master/b01lersCTF_bootcamp/Crypto/Shared%20Dreaming#shared-dreaming
So it turns out that I was one step away from solving the challenge. I needed to use the ciphertext hex to xor against a3, and then pass that into the line where I’m defining ct before putting it into KPA. The program should look like:
’’’ def KPA(ct): known = ord(‘f’) # from flag format = known byte pad = ct[0] ^ known # 1st byte ^ known byte
pt = ''
for ch in ct:
pt += chr(pad ^ ch)
return pt
a1a2a3a4 = 0x8ba4c4dfce33fd6101cf5c56997531c024a10f1dc323eb7fe3841ac389747fb90e3418f90011ef2610fa3636cd6cf0002d19faa30d39161fbd45cc58abff6a84 a2a3a4 = 0xf969375145322aba697ce9b4e00aa88e81ffe5c306b1b98148f33c4581b2ac39bc95f13b27c39f2311a590b7e27cdbdb7599f615acd70c45378e44fb319b8cb6 a1a3 = 0x855249b385f7b1d9923f71feb3bdee1032963ab51aa7b9d89a20c08c381e77890aa8849702d8791f8e636e833928ba6ea44c5f261983b7e29bd82e44b77fe03b flaga3Rb = 0xf694bc3d12a0673aead8fc4fdf964f5ec0c1d938e722bf333000f300088ead0dec1e7e03720331098068c13a066ca9bca89850a8ee67feb8471af5f47b4c0f13
a1 = a1a2a3a4 ^ a2a3a4 a3 = a1 ^ a1a3 flagRb = flaga3Rb ^ a3
ct = ‘0’ + str(hex(flagRb))[2:] # padding is important pt = KPA(bytearray.fromhex(ct))
print(pt) ‘’’
Totem
Challenge Description:
Is this a dream or not? Use your totem to find out. Flag format: ctf{}.
MD5: 64570ca140218e167da3995184bc89a4
totem-template.py:
When I run the netcat command: “nc chal.ctf.b01lers.com 2008”, a response looking like the picture below appears, allowing for an input given by the user. totem-template.py indicates that I need to implement the decryption of various ciphers given by a function name. Namely, the Baconian, rot13, atbash, and base64 decoding. I implemented the cipher decryption for each, and when I run the program, I receive an EOF error at the moment (for rot13). There may be an issue with my implementation.
’’’
You can install these packages to help w/ solving unless you have others in mind
i.e. python3 -m pip install {name of package}
from pwn import * import codecs from base64 import b64decode from string import ascii_lowercase
HOST = ‘chal.ctf.b01lers.com’ PORT = 2008
lookup_table = {‘A’ : ‘Z’, ‘B’ : ‘Y’, ‘C’ : ‘X’, ‘D’ : ‘W’, ‘E’ : ‘V’, ‘F’ : ‘U’, ‘G’ : ‘T’, ‘H’ : ‘S’, ‘I’ : ‘R’, ‘J’ : ‘Q’, ‘K’ : ‘P’, ‘L’ : ‘O’, ‘M’ : ‘N’, ‘N’ : ‘M’, ‘O’ : ‘L’, ‘P’ : ‘K’, ‘Q’ : ‘J’, ‘R’ : ‘I’, ‘S’ : ‘H’, ‘T’ : ‘G’, ‘U’ : ‘F’, ‘V’ : ‘E’, ‘W’ : ‘D’, ‘X’ : ‘C’, ‘Y’ : ‘B’, ‘Z’ : ‘A’}
Dictionary to lookup the index of alphabets
dict1 = {‘A’ : 1, ‘B’ : 2, ‘C’ : 3, ‘D’ : 4, ‘E’ : 5, ‘F’ : 6, ‘G’ : 7, ‘H’ : 8, ‘I’ : 9, ‘J’ : 10, ‘K’ : 11, ‘L’ : 12, ‘M’ : 13, ‘N’ : 14, ‘O’ : 15, ‘P’ : 16, ‘Q’ : 17, ‘R’ : 18, ‘S’ : 19, ‘T’ : 20, ‘U’ : 21, ‘V’ : 22, ‘W’ : 23, ‘X’ : 24, ‘Y’ : 25, ‘Z’ : 26}
Dictionary to lookup alphabets
corresponding to the index after shift
dict2 = {0 : ‘Z’, 1 : ‘A’, 2 : ‘B’, 3 : ‘C’, 4 : ‘D’, 5 : ‘E’, 6 : ‘F’, 7 : ‘G’, 8 : ‘H’, 9 : ‘I’, 10 : ‘J’, 11 : ‘K’, 12 : ‘L’, 13 : ‘M’, 14 : ‘N’, 15 : ‘O’, 16 : ‘P’, 17 : ‘Q’, 18 : ‘R’, 19 : ‘S’, 20 : ‘T’, 21 : ‘U’, 22 : ‘V’, 23 : ‘W’, 24 : ‘X’, 25 : ‘Y’}
lookup = {‘A’:’aaaaa’, ‘B’:’aaaab’, ‘C’:’aaaba’, ‘D’:’aaabb’, ‘E’:’aabaa’, ‘F’:’aabab’, ‘G’:’aabba’, ‘H’:’aabbb’, ‘I’:’abaaa’, ‘J’:’abaab’, ‘K’:’ababa’, ‘L’:’ababb’, ‘M’:’abbaa’, ‘N’:’abbab’, ‘O’:’abbba’, ‘P’:’abbbb’, ‘Q’:’baaaa’, ‘R’:’baaab’, ‘S’:’baaba’, ‘T’:’baabb’, ‘U’:’babaa’, ‘V’:’babab’, ‘W’:’babba’, ‘X’:’babbb’, ‘Y’:’bbaaa’, ‘Z’:’bbaab’} r = remote(HOST,PORT)
def bacon(message): decipher = ‘’ i = 0
# emulating a do-while loop
while True :
# condition to run decryption till
# the last set of ciphertext
if(i < len(message)-4):
# extracting a set of ciphertext
# from the message
substr = message[i:i + 5]
# checking for space as the first
# character of the substring
if(substr[0] != ' '):
'''
This statement gets us the key(plaintext) using the values(ciphertext)
Just the reverse of what we were doing in encrypt function
'''
decipher += list(lookup.keys())[list(lookup.values()).index(substr)]
i += 5 # to get the next set of ciphertext
else:
# adds space
decipher += ' '
i += 1 # index next to the space
else:
break # emulating a do-while loop
return decipher
def rot13(message):
decipher = ‘’
for letter in message:
# checks for space
if(letter != ‘ ‘):
print(“letter:”, letter)
letter = letter.capitalize()
# looks up the dictionary and
# subtracts the shift to the index
num = ( dict1[letter] - 13 + 26) % 26
print(“num:”, num)
# looks up the second dictionary for the
# shifted alphabets and adds them
decipher += dict2[num]
else:
# adds space
decipher += ‘ ‘
return decipher
def atbash(message): cipher = ‘’ for letter in message: # checks for space if(letter != ‘ ‘): print(“letter:”, letter) letter = letter.capitalize() #adds the corresponding letter from the lookup_table cipher += lookup_table[letter] else: # adds space cipher += ‘ ‘
return cipher
def Base64(s): message_bytes = base64.b64decode(s) message = message_bytes.decode(‘ascii’) return message
if name == ‘main’:
count = 0
while True:
r.recvuntil(‘Method: ‘)
method = r.recvuntil(‘\n’).strip()
r.recvuntil(‘Ciphertext: ‘)
argument = r.recvuntil(‘\n’).strip()
result = globals()[method.decode()](argument.decode()) # :)
r.recv()
r.sendline(result.encode())
count += 1
if count == 1000:
print(r.recv())
exit(0) '''
UPDATE
Writeup: https://ctftime.org/writeup/23979 Looking at the writeup for this challenge, and I’m still not entirely sure why my implementation was incorrect, but the writeup’s rot13 approaches it a little differently, but correctly and a little more efficiently. I can’t really test the program again because the server isn’t still running, but I think once I fixed this rot13, it would produce the flag.
Updated rot13:
’’’ def rot13(s): chars = “abcdefghijklmnopqrstuvwxyz” trans = chars[13:]+chars[:13] rot_char = lambda c: trans[chars.find(c)] if chars.find(c)>-1 else c return ‘‘.join( rot_char(c) for c in s )
lookup_table = {‘a’ : ‘z’, ‘b’ : ‘y’, ‘c’ : ‘x’, ‘d’ : ‘w’, ‘e’ : ‘v’, ‘f’ : ‘u’, ‘g’ : ‘t’, ‘h’ : ‘s’, ‘i’ : ‘r’, ‘j’ : ‘q’, ‘k’ : ‘p’, ‘l’ : ‘o’, ‘m’ : ‘n’, ‘n’ : ‘m’, ‘o’ : ‘l’, ‘p’ : ‘k’, ‘q’ : ‘j’, ‘r’ : ‘i’, ‘s’ : ‘h’, ‘t’ : ‘g’, ‘u’ : ‘f’, ‘v’ : ‘e’, ‘w’ : ‘d’, ‘x’ : ‘c’, ‘y’ : ‘b’, ‘z’ : ‘a’} ‘’’