I’m back at it again with another little challenge from HackTheBox. This time it’s a crypto one, called Android-in-the-Middle.
To know what we have to do and how to get the flag, let’s look at the code they give us in source.py
:
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
import hashlib
import random
import socketserver
import signal
FLAG = "HTB{--REDACTED--}"
DEBUG_MSG = "DEBUG MSG - "
p = 0x509efab16c5e2772fa00fc180766b6e62c09bdbd65637793c70b6094f6a7bb8189172685d2bddf87564fe2a6bc596ce28867fd7bbc300fd241b8e3348df6a0b076a0b438824517e0a87c38946fa69511f4201505fca11bc08f257e7a4bb009b4f16b34b3c15ec63c55a9dac306f4daa6f4e8b31ae700eba47766d0d907e2b9633a957f19398151111a879563cbe719ddb4a4078dd4ba42ebbf15203d75a4ed3dcd126cb86937222d2ee8bddc973df44435f3f9335f062b7b68c3da300e88bf1013847af1203402a3147b6f7ddab422d29d56fc7dcb8ad7297b04ccc52f7bc5fdd90bf9e36d01902e0e16aa4c387294c1605c6859b40dad12ae28fdfd3250a2e9
g = 2
class Handler(socketserver.BaseRequestHandler):
def handle(self):
signal.alarm(0)
main(self.request)
class ReusableTCPServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
def sendMessage(s, msg):
s.send(msg.encode())
def recieveMessage(s, msg):
sendMessage(s, msg)
return s.recv(4096).decode().strip()
def decrypt(encrypted, shared_secret):
key = hashlib.md5(long_to_bytes(shared_secret)).digest()
cipher = AES.new(key, AES.MODE_ECB)
message = cipher.decrypt(encrypted)
return message
def main(s):
sendMessage(s, DEBUG_MSG + "Generating The Global DH Parameters\n")
sendMessage(s, DEBUG_MSG + f"g = {g}, p = {p}\n")
sendMessage(s, DEBUG_MSG + "Calculation Complete\n\n")
sendMessage(s, DEBUG_MSG + "Generating The Public Key of CPU...\n")
c = random.randrange(2, p - 1)
C = pow(g, c, p)
sendMessage(s, DEBUG_MSG + "Calculation Complete\n")
sendMessage(s, DEBUG_MSG + "Public Key is: ???\n\n")
M = recieveMessage(s, "Enter The Public Key of The Memory: ")
try:
M = int(M)
except:
sendMessage(s, DEBUG_MSG + "Unexpected Error Occured\n")
exit()
sendMessage(s, "\n" + DEBUG_MSG + "The CPU Calculates The Shared Secret\n")
shared_secret = pow(M, c, p)
sendMessage(s, DEBUG_MSG + "Calculation Complete\n\n")
encrypted_sequence = recieveMessage(
s, "Enter The Encrypted Initialization Sequence: ")
try:
encrypted_sequence = bytes.fromhex(encrypted_sequence)
assert len(encrypted_sequence) % 16 == 0
except:
sendMessage(s, DEBUG_MSG + "Unexpected Error Occured\n")
exit()
sequence = decrypt(encrypted_sequence, shared_secret)
if sequence == b"Initialization Sequence - Code 0":
sendMessage(s, "\n" + DEBUG_MSG +
"Reseting The Protocol With The New Shared Key\n")
sendMessage(s, DEBUG_MSG + f"{FLAG}")
else:
exit()
if __name__ == '__main__':
socketserver.TCPServer.allow_reuse_address = True
server = ReusableTCPServer(("0.0.0.0", 1337), Handler)
server.serve_forever()
Taking a quick look at the code, we can understand what it’s doing:
- We get this really really really big parameter
p
. Usingp
as upper range, it randomly generatesc
- We are prompted to enter an integer
M
(if it’s not an int, an error will be thrown) shared_secret
is calculated usingM
,c
andp
- We are prompted to enter a sequence in hexadecimal (we know it because it uses the function
bytes.fromhex()
to convert it to bytes) - This sequence gets decrypted with the
decrypt
function we see in the code. It does the following:- hash
shared_secret
- generates the cipher with hash as key
- decrypts using the library’s method
- hash
- If the decrypted message equals
b"Initialization Sequence - Code 0"
, return the flag
Our objective is then to manage to encrypt this sequence of bytes taking advantage of the fact that we control M
, which will be used to calculate shared_secret
, which will end up being the key (shared_secret
is calculated like shared_secret = pow(M, c, p)
).
Even though we cannot know the value of c
, and it might make this seem impossible to solve, what would happen if we indicate that M=0
? Suddenly it doesn’t matter what number c
is.
- 0 multiplied by 0
c
times is still 0 - 0
mod
p
is still 0
Which basically means… that we know shared_secret
! It’s as easy as 0!
Now we just need to encrypt the sequence of bytes following everything that is done in the decrypt
function.
- We will generate the key the same way, since we have access to the
hashlib
library:1
key = hashlib.md5(long_to_bytes(shared_secret)).digest()
- We will also generate the cipher the same way, since we know it’s using the
Crypto.Cipher
library:1
cipher = AES.new(key, AES.MODE_ECB)
- Since this library (
Crypto.Cipher
) has adecrypt
method, we will assume it also contains anencrypt
method which works opposite todecrypt
:1
message = cipher.encrypt(sequence)
- Now we managed to encrypt the sequence of bytes, so we are left with
b'\x1a\xf7a1J\x07\xbfy\xf3\x1a\xebS\xbc\x9e\x135\xe1t\x9e\x11B\xb3&\xd8*<)\xac7\xa0B\xbf'
. However, one of the first things the script does when receiving the encrypted message, is to performbytes.fromhex(encrypted_sequence)
, so we need to deliver an hexadecimal version of the encrypted message:1
message_hex = message.hex()
If we put everything together, we are left with the following script.py
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
import hashlib
sequence = b"Initialization Sequence - Code 0"
shared_secret = 0
# encrypt
key = hashlib.md5(long_to_bytes(shared_secret)).digest()
cipher = AES.new(key, AES.MODE_ECB)
message = cipher.encrypt(sequence)
message_hex = message.hex()
print(message_hex)
We run it, and get the encrypted sequence: 1af761314a07bf79f31aeb53bc9e1335e1749e1142b326d82a3c29ac37a042bf
Whoop whoop!
Let’s try to use it now on the hackthebox instance:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
└─$ netcat 134.122.103.40 31011
DEBUG MSG - Generating The Global DH Parameters
DEBUG MSG - g = 2, p = 10177459997049772558637057109490700048394574760284564283959324525695097805837401714582821820424475480057537817583807249627119267268524840254542683041588432363128111683358536204391767254517057859973149680238170237977230020947732558089671785239121778309357814575486749623687357688511361367822815452806637006568922401890961240475060822815400430220536180181951862931844638638933951683988349468373510128406899660648258602475728913837826845743111489145006566908004165703542907243208106044538037004824530893555918497937074663828069774495573109072469750423175863678445547058247156187317168731446722852098571735569138516533993
DEBUG MSG - Calculation Complete
DEBUG MSG - Generating The Public Key of CPU...
DEBUG MSG - Calculation Complete
DEBUG MSG - Public Key is: ???
Enter The Public Key of The Memory: 0
DEBUG MSG - The CPU Calculates The Shared Secret
DEBUG MSG - Calculation Complete
Enter The Encrypted Initialization Sequence: 1af761314a07bf79f31aeb53bc9e1335e1749e1142b326d82a3c29ac37a042bf
DEBUG MSG - Reseting The Protocol With The New Shared Key
DEBUG MSG - HTB{7h15_15_cr3@t3d_by_Danb3er_@nd_h@s_c0pyr1gh7_1aws!_!}
BINGO! So the flag is HTB{7h15_15_cr3@t3d_by_Danb3er_@nd_h@s_c0pyr1gh7_1aws!_!}