Machine name | OS | IP | Difficulty |
---|---|---|---|
W1seGuy | ? | 10.10.191.163:1337 | Easy |
First flag
- Review the Python script
Since we have access to the script running on the other side of the connection, we can inspect how the encryption works:
- It generates a random key using letters (uppercase or lowercase) and numbers (0-9).
- The key length is 5 characters.
- The flag is then XORed with the generated key (the modulo `%`` is used because the flag is longer than the key, so the key is repeated):
for i in range(0,len(flag)):
xored += chr(ord(flag[i]) ^ ord(key[i%len(key)]))
- The encrypted flag is encoded in HEX:
hex_encoded = xored.encode().hex()
| NOTE This image illustrates how the XOR cipher works (source - https://i.pcmag.com/imagery/encyclopedia-terms/xor-xor.fit_lim.size_1050x.gif):
- Retrieve the encryption key
XOR encrypts character by character, making it a stream cipher. We also know that the encrypted text starts with THM{
(in TryHackMe flag format).
As the server returns the ciphered text, we can extract the first 4 bytes (each byte is represented by 2 HEX characters) and deduce the first 4 characters of the key. This can be accomplished using the following script:
import socket
import string
HOST = "10.10.191.163"
PORT = 1337
def setup(key, flag, encode):
xored = ""
for i in range(0,len(flag)):
xored += chr(ord(flag[i]) ^ ord(key[i%len(key)]))
if encode:
return xored.encode().hex()
return xored
def main():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
data = s.recv(1024).decode('utf-8')
xor_text = ""
while not "What is the encryption key?" in data:
if "This XOR encoded text has flag 1" in data:
xor_text = data.split(":")[1].strip()
data = s.recv(1024).decode('utf-8')
final_key = ""
for character in (string.ascii_letters + string.digits):
if setup(character, "T", True) == str(xor_text[0:2]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "H", True) == str(xor_text[2:4]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "M", True) == str(xor_text[4:6]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "{", True) == str(xor_text[6:8]):
final_key = final_key + character
print(final_key)
if __name__ == "__main__":
main()
Next, we need to guess the last character. We can simply brute-force it and check if the last character is equal to }
(since the flag follows the format THM{<whatever>}
), and automate the process with the following script:
import socket
import string
HOST = "10.10.191.163"
PORT = 1337
def setup(key, flag, encode):
xored = ""
for i in range(0,len(flag)):
xored += chr(ord(flag[i]) ^ ord(key[i%len(key)]))
if encode:
return xored.encode().hex()
return xored
def main():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
data = s.recv(1024).decode('utf-8')
xor_text = ""
while not "What is the encryption key?" in data:
if "This XOR encoded text has flag 1" in data:
xor_text = data.split(":")[1].strip()
data = s.recv(1024).decode('utf-8')
final_key = ""
for character in (string.ascii_letters + string.digits):
if setup(character, "T", True) == str(xor_text[0:2]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "H", True) == str(xor_text[2:4]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "M", True) == str(xor_text[4:6]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "{", True) == str(xor_text[6:8]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
tmp_key = final_key + character
flag = setup(tmp_key, bytes.fromhex(xor_text).decode('utf-8'), False)
if flag[-1] == "}":
print("The flag is {} with key {}".format(flag, tmp_key))
if __name__ == "__main__":
main()
| NOTE To decrypt the XOR cipher, we simply need to encrypt it again with the same key. That’s the fundamental principle of XOR. If we XOR 1
and 1
, we get 0
. However, if we XOR 0
with a key where the bit is set to 1
, the XOR operation between 1
and 0
results in 1
.
| NOTE 2 When decrypting the final message, there's no need to encode it into HEX format.
After running this script, we retrieve the encryption key, which is used to decrypt the message, and we obtain the first flag.
Second flag
- Locate the second flag
The script sends the encryption key to the connection, and the second flag is returned in the response text. This process can be automated using the following script:
import socket
import string
HOST = "10.10.191.163"
PORT = 1337
def setup(key, flag, encode):
xored = ""
for i in range(0,len(flag)):
xored += chr(ord(flag[i]) ^ ord(key[i%len(key)]))
if encode:
return xored.encode().hex()
return xored
def main():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
data = s.recv(1024).decode('utf-8')
xor_text = ""
while not "What is the encryption key?" in data:
if "This XOR encoded text has flag 1" in data:
xor_text = data.split(":")[1].strip()
data = s.recv(1024).decode('utf-8')
final_key = ""
for character in (string.ascii_letters + string.digits):
if setup(character, "T", True) == str(xor_text[0:2]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "H", True) == str(xor_text[2:4]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "M", True) == str(xor_text[4:6]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "{", True) == str(xor_text[6:8]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
tmp_key = final_key + character
flag = setup(tmp_key, bytes.fromhex(xor_text).decode('utf-8'), False)
if flag[-1] == "}":
s.sendall((tmp_key + "\n").encode('utf-8'))
data = s.recv(1024).decode('utf-8')
print("Returned text: {}".format(data))
if __name__ == "__main__":
main()
Retrieve both flags using the script
- Full script
With the following script, we can retrieve both flags:
import socket
import string
HOST = "10.10.191.163"
PORT = 1337
def setup(key, flag, encode):
xored = ""
for i in range(0,len(flag)):
xored += chr(ord(flag[i]) ^ ord(key[i%len(key)]))
if encode:
return xored.encode().hex()
return xored
def main():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
data = s.recv(1024).decode('utf-8')
xor_text = ""
while not "What is the encryption key?" in data:
if "This XOR encoded text has flag 1" in data:
xor_text = data.split(":")[1].strip()
data = s.recv(1024).decode('utf-8')
final_key = ""
for character in (string.ascii_letters + string.digits):
if setup(character, "T", True) == str(xor_text[0:2]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "H", True) == str(xor_text[2:4]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "M", True) == str(xor_text[4:6]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
if setup(character, "{", True) == str(xor_text[6:8]):
final_key = final_key + character
for character in (string.ascii_letters + string.digits):
tmp_key = final_key + character
flag = setup(tmp_key, bytes.fromhex(xor_text).decode('utf-8'), False)
if flag[-1] == "}":
s.sendall((tmp_key + "\n").encode('utf-8'))
data = s.recv(1024).decode('utf-8')
print("Returned text: {}".format(data))
print("The flag is {} with key {}".format(flag, tmp_key))
if __name__ == "__main__":
main()