Created
January 17, 2025 16:13
-
-
Save DarkCoderSc/769d1cb62bab7982ee2044f646779601 to your computer and use it in GitHub Desktop.
Simulate a portion of the old MSN protocol, focusing specifically on the authentication process, to make IceCold Reloaded believe it is functioning correctly. This script emulates both the essential components of the MSN protocol and the Passport authentication portal. (!) Ensure you update your host file to redirect the relevant domain names t…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Jean-Pierre LESUEUR (@DarkCoderSc) | |
# | |
# https://github.com/DarkCoderSc | |
# https://github.com/PhrozenIO | |
# https://www.phrozen.io | |
# | |
# Description: | |
# Simulate a portion of the old MSN protocol, focusing specifically on the authentication process, | |
# to make IceCold Reloaded believe it is functioning correctly. This script emulates both the | |
# essential components of the MSN protocol and the Passport authentication portal. (!) Ensure you | |
# update your host file to redirect the relevant domain names to localhost (!) | |
# | |
# For HTTPS Server (Passport): | |
# `openssl req -newkey rsa:2048 -nodes -keyout icecold.key -x509 -days 365 -out icecold.pem` | |
# `openssl x509 -outform der -in icecold.pem -out icecold.crt` | |
# (PowerShell) `Import-Certificate -FilePath "icecold.crt" -CertStoreLocation "Cert:\LocalMachine\Root"` | |
# | |
# (!) > You must ensure that the certificate is trusted; otherwise, requests made using the WinInet API will fail. | |
# | |
# (!) Important: | |
# -> Edit `hosts` file and add or edit: | |
# `127.0.0.1 localhost messenger.hotmail.com nexus.passport.com login.passport.com` | |
# | |
# Notice: This script is not designed for optimization; it is merely a proof of concept (PoC) intended to convince | |
# IceCold that it is functioning as expected (Back in 2003 - 2008). | |
import builtins | |
import http.server | |
import inspect | |
import socket | |
import ssl | |
import threading | |
import time | |
from colorama import Back, Fore, Style | |
original_print = builtins.print | |
def hooked_print(*args, **kwargs): | |
# Hacking hacking and hacking: Don't look to this code, this is for debugging, if you do so | |
# please forgive me :-) | |
stack = inspect.stack() | |
caller_frame = stack[1] | |
class_name = None | |
text_color = None | |
if "self" in caller_frame.frame.f_locals: | |
class_name = type(caller_frame.frame.f_locals["self"]).__name__ | |
if class_name == "FakeMSNProtocolServer": | |
text_color = Fore.GREEN | |
elif class_name == "FakePassportHTTPServer" or\ | |
caller_frame.function == "PassportProto_request_handler_thread": | |
text_color = Fore.CYAN | |
if text_color is not None: | |
args = list(args) | |
args[0] = text_color + args[0] | |
args[len(args) - 1] = args[len(args) - 1] + Style.RESET_ALL | |
original_print(*args, **kwargs) | |
builtins.print = hooked_print | |
# | |
# FakeMSNProtocolServer Class | |
# | |
class FakeMSNProtocolServer(): | |
# Below are the correct answers from the former MSN Messenger protocol. IceCold is specifically seeking | |
# certain attributes in some responses. For example, it looks for the `XFR 3 NS` response, which contains | |
# name server information, to process pre-flight authentication requests. Additionally, IceCold requires | |
# the USR 3 TWN response to include the `ct` parameter and the `tpf` parameter. These are parsed for subsequent | |
# authentication on the Passport service. | |
# Note that in our fake MSN Messenger protocol server, both the `ct` and `tpf` values are dummy placeholders. | |
# These two parameters were used historically to authenticate an MSN user. | |
MSN_PROTO_ANS_VER_1 = "VER 1 MSNP8 CVR0\r\n" | |
MSN_PROTO_ANS_CVR_2 = "CVR 2 7.5.0324 7.5.0324 7.0.0777\r\n" | |
MSN_PROTO_ANS_XFR_3 = "XFR 3 NS 127.0.0.2:1863 0 127.0.0.2:1863\r\n" | |
MSN_PROTO_ANS_USR_3 = "USR 3 TWN S lc=1033,id=507,tw=40,fs=1,ru=http%3A%2F%2Fmessenger%2Emsn%2Ecom,ct=1403201414,kpp=1,kv=5,ver=2.1.0173.1,tpf=0ff2953b76113fef0d792073e2aecb8c\r\n" | |
def __init__(self, exit_event): | |
self.exit_event = exit_event | |
print("Starting MSN Protocol Server...") | |
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: | |
s.settimeout(1) | |
# `0.0.0.0` is required to capture both `127.0.0.1` and `127.0.0.2` local ip's | |
s.bind(("0.0.0.0", 1863)) | |
s.listen() | |
print("Started! Waiting for clients...") | |
while not exit_event.is_set(): | |
try: | |
client, addr_info = s.accept() | |
local_ip, _ = client.getsockname() | |
client_info = f"{addr_info[0]}:{addr_info[1]}" | |
with client: | |
print(f"New Client[{client_info}]") | |
self.handle_client_initializaion(client) | |
# This part requires some explanation. When IceCold initiates a pre-flight request to the MSN Messenger | |
# protocol, it first communicates with the MSN server to determine which subsequent server to use. | |
# The MSN server responds with a special request, providing the necessary details about the server to | |
# connect to. The client then establishes a new connection using this information to handle the actual pre-flight request. | |
# The issue is that IceCold does not distinguish between the first and second requests. It sends the same packets for | |
# both, making it challenging to identify whether the first or second request is being processed. While it would be | |
# possible to assume a sequential order, I wanted to explore a more generic approach. | |
# To achieve this, in the `XFR 3 NS` request, we set the server IP to a different version of localhost. Both 127.0.0.1 | |
# and 127.0.0.2 resolve to localhost by design. However, a server can differentiate which IP the client is connecting | |
# to, allowing us to determine whether we are handling the first or second request. Initially, I considered using | |
# different ports, but the ports are hardcoded in IceCold, so I had to rely on this IP-based technique. | |
if local_ip == "127.0.0.1": | |
self.handle_primary_client(client) | |
else: | |
# Normally : "127.0.0.2" | |
self.handle_slave_client(client) | |
print(f"Client[{client_info}] gracefully closed!") | |
except (ConnectionResetError, socket.timeout): | |
pass | |
def recv_buff(self, client): | |
buff = client.recv(1024) | |
print("<" * 25 + "\n" + buff.decode("ascii") + "<" * 25) | |
def send_str(self, client, str): | |
buff = str.encode("ascii") | |
print(">" * 25 + "\n" + str + ">" * 25) | |
client.send(buff) | |
def handle_client_initializaion(self, client): | |
self.recv_buff(client) | |
self.send_str(client, self.MSN_PROTO_ANS_VER_1) | |
self.recv_buff(client) | |
self.send_str(client, self.MSN_PROTO_ANS_CVR_2) | |
self.recv_buff(client) | |
def handle_primary_client(self, client): | |
self.send_str(client, self.MSN_PROTO_ANS_XFR_3) | |
def handle_slave_client(self, client): | |
self.send_str(client, self.MSN_PROTO_ANS_USR_3) | |
# Not required in the context of IceCold | |
# self.recv_buff(client) | |
# self.send_str(client, "USR 4 OK [email protected] darkcodersc 1 0\r\n") | |
# | |
# FakePassportHTTPServer Class | |
# | |
class FakePassportHTTPServer(http.server.BaseHTTPRequestHandler): | |
def log_message(self, format, *args): | |
pass | |
def provide_response(self, response, content_type="text/html", extra_headers=None): | |
self.send_response(200) | |
self.send_header("Content-type", content_type) | |
if isinstance(extra_headers, dict): | |
for key, value in extra_headers.items(): | |
self.send_header(str(key), str(value)) | |
self.end_headers() | |
self.wfile.write(response.encode("utf-8")) | |
def do_GET(self): | |
self.log_request_content() | |
if self.path == "/rdr/pprdr.asp": | |
self.provide_response(response="OK", extra_headers={ | |
"PassportURLs": "DALogin=login.passport.com/login2.srf," + | |
# "DARealm=Passport.Net," + | |
# Bellow attributes are unused by IceCold | |
# "DAReg=http://register.passport.net/uixpwiz.srf," + | |
# "Properties=https://register.passport.net/editprof.srf," + | |
# "Privacy=http://www.passport.com/consumer/privacypolicy.asp," + | |
# "GeneralRedir=http://nexusrdr.passport.com/redir.asp," + | |
# "Help=http://memberservices.passport.net/memberservice.srf,ConfigVersion=11" + | |
"\r\n", | |
}) | |
elif self.path == "/login2.srf": | |
self.provide_response("IceCold Flood...") | |
else: | |
self.provide_response("404 : IceCold") | |
def log_request_content(self): | |
print("<" * 25) | |
print(f"@Path: {self.path}") | |
print("@Headers:") | |
for header, value in self.headers.items(): | |
print(f"\t{header}:{value}") | |
content_length = self.headers.get('Content-Length') | |
if content_length: | |
print("@Body:") | |
length = int(content_length) | |
body = self.rfile.read(length).decode('utf-8') | |
print(body) | |
print("<" * 25) | |
# | |
# Server Threads | |
# | |
def MSNProto_request_handler_thread(exit_event): | |
msn_server = FakeMSNProtocolServer(exit_event) | |
def PassportProto_request_handler_thread(exit_event): | |
print("Starting Passport Server...") | |
httpd = http.server.HTTPServer(('127.0.0.1', 443), FakePassportHTTPServer) | |
httpd.socket.settimeout(1) | |
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) | |
context.load_cert_chain(certfile="icecold.pem", keyfile="icecold.key") | |
httpd.socket = context.wrap_socket(httpd.socket, server_side=True) | |
print("Started! Waiting for requests...") | |
while not exit_event.is_set(): | |
try: | |
httpd.handle_request() | |
except socket.timeout: | |
pass | |
print("Passport Server Terminated!") | |
# | |
# Entrypoint | |
# | |
def main(): | |
req_exit_event = threading.Event() | |
threads = [] | |
threads.append(threading.Thread(target=MSNProto_request_handler_thread, args=(req_exit_event,))) | |
threads.append(threading.Thread(target=PassportProto_request_handler_thread, args=(req_exit_event,))) | |
for thread in threads: | |
thread.start() | |
print("Servers are running. Press Ctrl+C to stop.") | |
try: | |
while True: | |
if any(thread.is_alive() for thread in threads): | |
time.sleep(0.5) | |
except KeyboardInterrupt: | |
req_exit_event.set() | |
for thread in threads: | |
thread.join() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment