This was a super fun ctf organized by DiaryofHackers and hosted by HackTheBox. The challs were kinda easy really so, my team was able to complete all the challs and landed second place.
I solved the cryptos and forensics.
This was a crypto chall. You're given an encrypted file called top_secret and the python script used to encrypt the file.
def encrypt(key, msg):
crypto = AES.new(key, AES.MODE_CBC,key)
return crypto.encrypt(pad(msg, 16))
key = os.urandom(16)
From the script, the file seems to be encrypted using AES algorithm using a random key.
The AES mode used is CBC which normally uses an Initialization Vector(iv) with the encryption key. In the code it uses the key as the iv, so the only task is to find the encyption key and we can easily decrypt the file.
e = 65537
p = getPrime(2048)
q = getPrime(2048)
n = p * q
phi = (p-1) * (q-1)
d = inverse(e,phi)
leak = d%(p-1)
m = bytes_to_long(key)
c = pow(m, e, n)
print("n =", n)
print("e =", e)
print("c =", c)
print("leak =", leak)
The first few lines are nothing too interesting, there's the normal public exponent with the usual integer value, the two random secret prime numbers, p and q, and n which is the multiple of p and q. We also get d which is the secret exponent and a leak which is d%(p-1)
.
As i'm not too good with crypto, i used this blog to finish up the chall with a quick script.
#!/usr/bin/python3
import sys
from Crypto.Util.number import inverse, long_to_bytes
from Crypto.Cipher import AES
#### Constant - Known Values of RSA
e = 65537
n = ...[snip]...
dp = ...[snip]...
c = ...[snip]...
def RSA_find_p():
for k in range(1, e):
p = (e*dp - 1 + k) // k
# Check if p can divide n
if n % p == 0:
return p
return -1
def AES_decrypt(key, msg):
crypto = AES.new(key, AES.MODE_CBC,key)
return crypto.decrypt(msg)
if __name__ == '__main__':
top_secret_file = sys.argv[1]
#### Find p
p = RSA_find_p()
print('p =', p)
if p < 0:
print('Can not find p')
exit()
#### Find q
q = n // p
print('q =', q)
#### Find d
phi = (p-1) * (q-1)
d = inverse(e,phi)
print('d =', d)
#### Find m (Decrypt)
m = pow(c, d, n)
print('m =', m)
#### Find AES key
key = long_to_bytes(m)
print('m (bytes) = ', key)
#### Decrypt file
dt = open(top_secret_file, 'rb').read()
print('Encrypted file:', dt)
decr_dt = AES_decrypt(key, dt)
print('Decrypted file:', decr_dt)
Running this, got the flag.
➜ Simple_RSA_COMPLETE python3 a.py top_secret.enc | grep -oE "HTB{.*}"
HTB{RS4_15_n07_41w4y5_53cu23_}
We're given a python script and the flag which is encrypted.
#!/usr/bin/python3
from Crypto.Util.number import getPrime, long_to_bytes, inverse
flag = open('flag.txt', 'r').read().strip().encode()
class RSA:
def __init__(self):
self.p = getPrime(512)
self.q = getPrime(512)
self.e = 3
self.n = self.p * self.q
self.d = inverse(self.e, (self.p-1)*(self.q-1))
def encrypt(self, data: bytes) -> bytes:
pt = int(data.hex(), 16)
ct = pow(pt, self.e, self.n)
return long_to_bytes(ct)
def decrypt(self, data: bytes) -> bytes:
ct = int(data.hex(), 16)
pt = pow(ct, self.d, self.n)
return long_to_bytes(pt)
def main():
crypto = RSA()
print ('Flag:', crypto.encrypt(flag).hex())
if __name__ == '__main__':
main()
This challenge is pretty simple since the decrypt function is included here. So, the idea was to basically use the decrypt function from the script to recursively decrypt the hex encoded flag until we get the flag. Made a quick script for that.
#!/usr/bin/python3
# @author: mug3njutsu
from challenge import RSA
def main():
encoded_flag = open('output.txt').read().split(' ')[1].strip()
flag = b''
while b'HTB' not in flag:
crypto = RSA()
flag = crypto.decrypt(bytearray.fromhex(encoded_flag))
print(flag.decode())
if __name__ == '__main__':
main()
Running this gives us the flag.
➜ lost_COMPLETE python3 a.py
HTB{y0u_f0und_7h3_m0du1u5!$}
But, the logic behind my script is that, we already know the flag format right, but, what if that wasn't in our disposal. So, my friend f11snipe came up with his own version to solve this.
#!/usr/bin/python3
# @author: f11snipe
from Crypto.Util.number import bytes_to_long
from challenge import RSA
def main():
output = open('output.txt').read().split(' ')[1].strip()
crypto = RSA()
flag = b''
first = bytes_to_long(bytearray.fromhex(output))
num = first
print(f"first = {first}\n------------------------------")
while num >= first:
crypto = RSA()
flag = crypto.decrypt(bytearray.fromhex(output))
num = bytes_to_long(flag)
#print(f"p = {crypto.p}")
#print(f"q = {crypto.q}")
#print(f"e = {crypto.e}")
#print(f"n = {crypto.n}")
#print(f"d = {crypto.d}")
#print(f"num = {num}")
#print(f"mod = {num % crypto.n}")
#print(f"test = {first % num}")
print(flag.decode())
if __name__ == '__main__':
main()
Running this as well, gets the flag.
➜ lost_COMPLETE python3 solve.py
first = 441954015487784238065800746069167413766489818339596249368474949558073707237645131123453492038238430172269635895041037102891174674853340892116191103407227390293503583878718677303759319458055278001953125
------------------------------
HTB{y0u_f0und_7h3_m0du1u5!$}
This might just be one of the easiest forensics challs out there. We are given a macro enabled word document and when i think of macro in a ctf i think olevba
. This is a tool used for extracting macro from word documents. To install it, just run sudo -H pip install -U oletools
.
➜ instructions_COMPLETE olevba vpn_instructions.docm
...[snip]...
str = "https://windowsliveupdater.com/HTB{" + Chr(110) + "3" + Chr(Asc("w")) + "_VP" + Chr(78) + "_" + Chr(Asc("n")) + "3" + Chr(119) + "_b4ck"
sec = Replace("door}/bin", "o", "0")
This looks like a flag. Using python to convert the unicode integers to ascii, got the flag, kinda.
HTB{n3w_VPN_n3w_b4ckdoor}
If you look keenly, Replace is being used here to flip the o's to 0's so, the flag should actually be HTB{n3w_VPN_n3w_b4ckd00r}