Last active
February 23, 2026 23:00
-
-
Save barneygale/f5dfcb1dd87a0533aeb13bda798223f2 to your computer and use it in GitHub Desktop.
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
| from os import urandom | |
| from socket import create_connection | |
| from cryptography.hazmat.primitives.asymmetric import padding | |
| from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | |
| from cryptography.hazmat.primitives.serialization import load_der_public_key | |
| def read(sock, length): | |
| result = b'' | |
| while len(result) < length: | |
| result += sock.recv(length - len(result)) | |
| return result | |
| def read_int(sock, length): | |
| return int.from_bytes(read(sock, length), 'big') | |
| def write(sock, data): | |
| sock.sendall(data) | |
| def pack_credential(data): | |
| data = data.encode('utf-8') + b'\x00' | |
| if len(data) < 64: | |
| data += urandom(64 - len(data)) | |
| else: | |
| data = data[:64] | |
| return data | |
| def connect(host, port, username, password, public_key=None): | |
| sock = create_connection((host, port)) | |
| # Standard VNC version number exchange | |
| write(sock, b'RFB 003.889\n') | |
| read(sock, 12) | |
| # Standard VNC auth types exchange | |
| read(sock, read_int(sock, 1)) | |
| write(sock, b'\x21') | |
| # ---- begin Apple VNC auth ---- | |
| if public_key is None: | |
| write(sock, b'\x00\x00\x00\x0a' # packet length (10 bytes) | |
| b'\x01\x00' # packet version | |
| b'RSA1' # host key algorithm | |
| b'\x00\x00' # has credentials? (no) | |
| b'\x00\x00') # has AES key? (no) | |
| read(sock, 4) # packet length | |
| read(sock, 2) # packet version | |
| public_key_length = read_int(sock, 4) | |
| public_key = load_der_public_key(read(sock, public_key_length)) | |
| read(sock, 1) # unknown (zero) | |
| aes_key = urandom(16) | |
| encryptor = Cipher(algorithms.AES(aes_key), modes.ECB()).encryptor() | |
| write(sock, b'\x00\x00\x01\x8a' # packet length (394 bytes) | |
| b'\x01\x00' # packet version | |
| b'RSA1' # host key algorithm | |
| b'\x00\x01' + # has credentials? (yes) | |
| encryptor.update( | |
| pack_credential(username) + | |
| pack_credential(password)) + | |
| b'\x00\x01' + # has AES key? (yes) | |
| public_key.encrypt(aes_key, padding=padding.PKCS1v15())) | |
| read(sock, 4) # unknown (all zeroes) | |
| # ---- end Apple VNC auth ---- | |
| # Standard VNC auth response | |
| if read_int(sock, 4) != 0: | |
| raise PermissionError("Authentication failed.") | |
| # Standard VNC client init etc | |
| write(sock, b'\x01') | |
| connect('localhost', 5900, 'username', 'password') |
Author
Hi!
I have a weird issue with this script.
The 0x21 must be sent with its following packet like the code below, or the server will close my connection.
write(
sock,
b"\x21" # security type 33 (Apple RSA Auth)
b"\x00\x00\x00\x0a" # packet length (10 bytes)
b"\x01\x00" # packet version
b"RSA1" # host key algorithm
b"\x00\x00" # has credentials? (no)
b"\x00\x00",
) # has AES key? (no)Whether "has credentials?" and "has aes key?" are really 2-byte booleans. An alternative reading using 1-byte booleans:
For so-called has credentials: yes they are
here are some decompiled code
v11 = bswap32((unsigned __int16)v6[3]) >> 16;
switch ( v11 )
{
case 2u:
*(_WORD *)(a1 + 4248) = 2;
v12 = SendRSAResponseSRPAuthentication((unsigned int *)a1, (__int64)v6);
goto LABEL_29;
case 1u:
*(_WORD *)(a1 + 4248) = 1;
v12 = SendRSAResponsePlainAuthentication((unsigned int *)a1, (__int64)v6);
goto LABEL_29;
case 0u:
v12 = SendRSAResponseKeyRequest(a1);
LABEL_29:
v3 = v12;
LABEL_30:
free(v6);
return v3;
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Things I'm fuzzy on:
RSA1is null-terminated