Skip to content

Instantly share code, notes, and snippets.

@DarkCoderSc
Created January 17, 2025 16:13
Show Gist options
  • Save DarkCoderSc/769d1cb62bab7982ee2044f646779601 to your computer and use it in GitHub Desktop.
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…
# 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