Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
165 lines (135 sloc) 7.85 KB
#!/bin/python3
#----------------------------------------------------------------------------------#
# CVE-2005-1794 Check #
# http://www.oxid.it/ca_um/topics/apr-rdp.htm #
# Simple script to demonstrate a host is vulnerable to CVE-2005-1794. #
# Tests for Windows remote desktop (RDP) hard coded RSA private key. #
# This uses code from https://github.com/SySS-Research/Seth to parse a certifcate #
# from a host and extract the signature of the certificate, it then uses the known #
# hardcoded RSA key to sign the certificate and compares the two Signatures #
# if they match, the host is vulnerable. #
# Author: David Yesland @daveysec, Rhino Security Labs #
#----------------------------------------------------------------------------------#
import socket
import hashlib
import struct
import binascii
import re
import sys
import textwrap
# Taken from https://github.com/SySS-Research/Seth/blob/master/seth/consts.py
TERM_PRIV_KEY = { # little endian, from [MS-RDPBCGR].pdf
"n": [ 0x3d, 0x3a, 0x5e, 0xbd, 0x72, 0x43, 0x3e, 0xc9, 0x4d, 0xbb, 0xc1,
0x1e, 0x4a, 0xba, 0x5f, 0xcb, 0x3e, 0x88, 0x20, 0x87, 0xef, 0xf5,
0xc1, 0xe2, 0xd7, 0xb7, 0x6b, 0x9a, 0xf2, 0x52, 0x45, 0x95, 0xce,
0x63, 0x65, 0x6b, 0x58, 0x3a, 0xfe, 0xef, 0x7c, 0xe7, 0xbf, 0xfe,
0x3d, 0xf6, 0x5c, 0x7d, 0x6c, 0x5e, 0x06, 0x09, 0x1a, 0xf5, 0x61,
0xbb, 0x20, 0x93, 0x09, 0x5f, 0x05, 0x6d, 0xea, 0x87 ],
# modulus
"d": [ 0x87, 0xa7, 0x19, 0x32, 0xda, 0x11, 0x87, 0x55, 0x58, 0x00, 0x16,
0x16, 0x25, 0x65, 0x68, 0xf8, 0x24, 0x3e, 0xe6, 0xfa, 0xe9, 0x67,
0x49, 0x94, 0xcf, 0x92, 0xcc, 0x33, 0x99, 0xe8, 0x08, 0x60, 0x17,
0x9a, 0x12, 0x9f, 0x24, 0xdd, 0xb1, 0x24, 0x99, 0xc7, 0x3a, 0xb8,
0x0a, 0x7b, 0x0d, 0xdd, 0x35, 0x07, 0x79, 0x17, 0x0b, 0x51, 0x9b,
0xb3, 0xc7, 0x10, 0x01, 0x13, 0xe7, 0x3f, 0xf3, 0x5f ],
# private exponent
"e": [ 0x5b, 0x7b, 0x88, 0xc0 ] # public exponent
}
req1 = b'\x03\x00\x00\x24\x1F\xE0\x00\x00\x00\x00\x00\x43\x6F\x6F\x6B\x69\x65\x3A\x20\x6D\x73\x74\x73\x68\x61\x73\x68\x3D\x6E\x65\x73\x73\x75\x73\x0D\x0A'
req2 = b'\x03\x00\x01\x96\x02\xF0\x80\x7F\x65\x82\x01\x8A\x04\x01\x01\x04\x01\x01\x01\x01\xFF\x30\x20\x02\x02\x00\x22\x02\x02\x00\x02\x02\x02\x00\x00\x02\x02\x00\x01\x02\x02\x00\x00\x02\x02\x00\x01\x02\x02\xFF\xFF\x02\x02\x00\x02\x30\x20\x02\x02\x00\x01\x02\x02\x00\x01\x02\x02\x00\x01\x02\x02\x00\x01\x02\x02\x00\x00\x02\x02\x00\x01\x02\x02\x04\x20\x02\x02\x00\x02\x30\x20\x02\x02\xFF\xFF\x02\x02\xFC\x17\x02\x02\xFF\xFF\x02\x02\x00\x01\x02\x02\x00\x00\x02\x02\x00\x01\x02\x02\xFF\xFF\x02\x02\x00\x02\x04\x82\x01\x17\x00\x05\x00\x14\x7C\x00\x01\x81\x0E\x00\x08\x00\x10\x00\x01\xC0\x00\x44\x75\x63\x61\x81\x00\x01\xC0\xD4\x00\x04\x00\x08\x00\x00\x04\x00\x03\x01\xCA\x03\xAA\x09\x04\x00\x00\x28\x0A\x00\x00\x74\x00\x65\x00\x6e\x00\x61\x00\x62\x00\x6C\x00\x65\x00\x73\x00\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x0C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xCA\x01\x00\x00\x00\x00\x00\x08\x00\x07\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xC0\x0C\x00\x09\x00\x00\x00\x00\x00\x00\x00\x02\xC0\x0C\x00\x1B\x00\x00\x00\x00\x00\x00\x00\x03\xC0\x14\x00\x01\x00\x00\x00\x63\x6C\x69\x70\x72\x64\x72\x00\xC0\xA0\x00\x00'
#Taken from https://github.com/SySS-Research/Seth/blob/master/seth/crypto.py
def sign_certificate(cert,slength):
"""Signs the certificate with the private key"""
m = hashlib.md5()
m.update(cert)
m = m.digest() + b"\x00" + b"\xff"*45 + b"\x01"
m = int.from_bytes(m, "little")
d = int.from_bytes(TERM_PRIV_KEY["d"], "little")
n = int.from_bytes(TERM_PRIV_KEY["n"], "little")
s = pow(m, d, n)
return s.to_bytes(slength, "little")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host_port = sys.argv[1].split(':')
host = host_port[0]
port = int(host_port[1])
s.connect((host, port))
s.sendall(req1)
s.recv(11)
s.sendall(req2)
blob = s.recv(4096)
#Taken from https://github.com/SySS-Research/Seth/blob/master/seth/parsing.py
def substr(s, offset, count):
return s[offset:offset+count]
def extract_server_cert(bytes):
# Reference: [MS-RDPBCGR].pdf from 2010, v20100305
m2 = re.match(b".*010c.*030c.*020c", binascii.hexlify(bytes))
offset = len(m2.group())//2
size = struct.unpack('<H', substr(bytes, offset, 2))[0]
encryption_method, encryption_level, server_random_len, server_cert_len = (
struct.unpack('<IIII', substr(bytes, offset+2, 16))
)
server_random = substr(bytes, offset+18, server_random_len)
server_cert = substr(bytes, offset+18+server_random_len,
server_cert_len)
# cert_version = struct.unpack('<I', server_cert[:4])[0]
# 1 = Proprietary
# 2 = x509
# TODO ignore right most bit
dwVersion, dwSigAlg, dwKeyAlg = struct.unpack('<III',
substr(server_cert, 0, 12))
pubkey_type, pubkey_len = struct.unpack('<HH', substr(server_cert, 12, 4))
pubkey = substr(server_cert, 16, pubkey_len)
assert pubkey[:4] == b"RSA1"
sign_type = struct.unpack('<H', substr(server_cert, 16+pubkey_len, 2))[0]
sign_len = struct.unpack('<H', substr(server_cert, 18+pubkey_len, 2))[0]
sign = substr(server_cert, 20+pubkey_len, sign_len)
key_len, bit_len = struct.unpack('<II', substr(pubkey, 4, 8))
assert bit_len == key_len * 8 - 64
data_len, pub_exp = struct.unpack('<II', substr(pubkey, 12, 8))
modulus = substr(pubkey, 20, key_len)
first5fields = struct.pack("<IIIHH",
dwVersion,
dwSigAlg,
dwKeyAlg,
pubkey_type,
pubkey_len )
crypto = {"modulus": modulus,
"pub_exponent": pub_exp,
"data_len": data_len,
"server_rand": server_random, # little endian
"sign": sign,
"first5fields": first5fields,
"pubkey_blob": pubkey,
"client_rand": b"",
}
crypto["pubkey"] = {
"modulus": int.from_bytes(modulus, "little"),
"publicExponent": pub_exp,
}
return {"crypto": crypto}
encoded_blob = binascii.hexlify(blob)
sig_hex = encoded_blob[-16+-128:-16]
def md5hash(inp):
m = hashlib.md5()
m.update(inp)
return m.hexdigest()
print("\nCertificate signature sent by server:\n")
print(textwrap.indent(textwrap.fill(sig_hex.decode(),32),'\t'))
print()
crypto = extract_server_cert(blob)['crypto']
resigned = sign_certificate(crypto["first5fields"] + crypto["pubkey_blob"],len(crypto["sign"]))
hex_resigned = binascii.hexlify(resigned)
print("[+]Attempting to sign certificate with hardcoded RSA key and produce matching signature...")
print()
print("Certificate signature generated using hardcoded RSA private key:\n")
resigned = hex_resigned[:len(sig_hex)]
print(textwrap.indent(textwrap.fill(resigned.decode(),32),'\t'))
print()
print("MD5 hashes of signatures:\n")
print("Original signature md5 hash: "+md5hash(sig_hex))
print("Hardcoded signed hash: "+md5hash(resigned))
if hex_resigned[:len(sig_hex)] == sig_hex:
print()
print("VULNERABLE: Signatures match, certificate was able to be signed with the hardcoded RSA key.")
else:
print("Not vulnerable.")
You can’t perform that action at this time.