TLDR: It is frightening, a patch was made available the same day it was disclosed and everybody should update their servers.
Impact
Butor Portal is affected by a Path Traversal vulnerability leading to pre-authentication arbitrary file downloads. Every file that can be read by the local user running the Butor Portal Web service could be exfiltrated by an anonymous attacker.
With the ability of reading most files on a server, an unauthenticated attacker could not only fully compromise the Butor application, but also the underlying infrastructure such as the database or the LDAP server using credentials stored in plain text in configuration files.
Exploitation of this vulnerability does not require advanced skill and can be automated.
What is Butor Portal and why does it matter?
Butor’s Web Portal, Single Sign On (SSO) and Access management apps are part of an open-source project called the Butor Platform. It is used by various financial institutions in systems hosting security critical processes and information.
Who is affected?
Systems using Butor Portal version 1.0.27 and earlier are affected.
Technical Analysis
In order to serve static files, the Butor Portal uses a Java Servlet. It takes two arguments from query parameters: a file name (h) and a theme name (t). The file name is sanitized while the theme name is not. Both parameters are then concatenated to a path used to determine which static file should be served. The file content is then echoed in the response.
As the theme name parameter is not sanitized, by tampering the request, it is possible to retrieve files outside the intended theme folder.
While a legitimate request should be as follows: https://example.com/wl?t=mytheme&h=portal.css
It can be tampered like this to fetch other files: https://example.com/wl?t=../../../../../../etc&h=passwd
The following excerpt contains the code that is responsible for this vulnerability. Code sections outside the code path used for exploitation were stripped and important lines of code were highlighted for the sake of clarity.
1 /**
2 * Copyright 2013-2018 butor.com
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.butor.portal.web.servlet;
17
[...]
43
44 public class WhiteLabelingServlet extends HttpServlet {
45 protected Logger logger = LoggerFactory.getLogger(getClass());
46 private static final long serialVersionUID = -4573847543526921509L;
47 private Map<Pattern, String> wlMap;
48 private String resDir;
49 private String themesByDomain;
50 private String defaultTheme = "default";
51 private String applyCommonByDomain;
52
53 @Override
54 public void init(ServletConfig config) {
[...]
72 }
73
74 @Override
75 protected void service(HttpServletRequest request,
76 HttpServletResponse response) throws IOException, ServletException {
77 String c = (String) request.getParameter("c"); // css file
78 String j = (String) request.getParameter("j"); // js file
79 String a = (String) request.getParameter("a"); // avatar image
80 String i = (String) request.getParameter("i"); // image
81 String h = (String) request.getParameter("h"); // html file
82 String t = (String) request.getParameter("t"); // theme
83 String s = request.getServerName();
84
85 logger.info("ServerName={}", s);
86
87 if (isNullOrEmpty(t)) {
88 for (Pattern p : wlMap.keySet()) {
89 Matcher matcher = p.matcher(s);
90 if (matcher.find()) {
91 t = wlMap.get(p);
92 break;
93 }
94 }
95 }
96 if (t == null) {
97 t = defaultTheme;
98 }
99
100 File comFile = null;
101 File file = null;
102 if (j != null) {
[...]
107 } else if (c != null) {
[...]
112 } else if (h != null) {
113 t += "/" +extractFilename(h);
114 file = new File(resDir, "theme/" +t);
115 if (h.endsWith(".css")) {
116 response.setContentType("text/css");
117 } else if (h.endsWith(".js")) {
118 response.setContentType("text/javascript");
119 } else if (h.endsWith(".html")) {
120 response.setContentType("text/html");
121 }
122 } else if (a != null) {
[...]
130 } else if (i != null) {
[...]
139 } else {
140 //TODO
141 }
142
143 if (comFile != null && comFile.exists()) {
[...]
167 }
168
169 if (file != null && file.exists()) {
170 FileInputStream fis = new FileInputStream(file);
171 IOUtils.copy(fis, response.getOutputStream());
172 Closeables.close(fis, false);
173 }
174 }
175 private String extractFilename(String name) {
176 if (Strings.isNullOrEmpty(name)) {
177 return null;
178 }
179 // remove any path
180 name = new File(name).getName();
181 // remove any args
182 int pos = name.indexOf("?");
183 if (pos > -1) {
184 name = name.substring(0, pos);
185 }
186 return name;
187 }
188 }
Butor Portal’s WhiteLabelingServlet – Original Code : Source
Exploitation
The biggest difficulty in exploiting this vulnerability is the lack of directory listing. However, it is possible to determine whether a folder exists or not by trying to access it. If it exists, a 500 error will be returned, otherwise a 200 empty response will be returned.
While brute force could be viable, it is a lot more efficient to gather information to determine where the desired files are.
To that extent, various configuration files can be retrieved using relative path traversal, their path can be found here :
https://bitbucket.org/butor-team/butor-stack-setup/src/master/conf/
Here are a few examples:
- Accessing https://example.com/wl?t=../../../mule/common&h=conf.properties will return the server’s configuration file containing LDAP and SQL server configuration as well as the name of the associated administrative accounts;
- Accessing https://example.com/wl?t=../../../pwd&h=ldap.portal.pwd will return the password for the LDAP administrative account;
- Accessing https://example.com/wl?t=../../../pwd&h=db.portal.pwd will return the password for the database account
Moreover, it is possible to gather a lot more information using the proc Linux virtual file system.
6 Exploitation Tricks for Path Traversals
Here are the essential tricks and steps:
- Access /proc/self/environ and /proc/self/cmdline : this will retrieve both the command line arguments and the environment variables that were passed to the java process. You will be able to find the user running the process and its home folder. Look for bash history and keys belonging to that user. Don’t underestimate the known_hosts file, that user might reuse the same password on the servers listed there.
- In those variables, it will also be possible to find the absolute path to the application and Catalina’s logs as well as various configuration folders, thus enabling the retrieval of more files. While log files might seem boring at first glance, if the verbosity is high enough, it might be possible to retrieve valuable information. That server will likely be used for SSO, a simple configuration file may contain the keys to the castle.
- Access /proc/sys/kernel/pid_max to determine the PID range of the target machine, brute force them through the /proc/{pid}/cmdline endpoint. Thankfully, the vast majority of systems use the 32768 default limit so it is absolutely doable. It will be possible to enumerate every process running on the machine. Most importantly, it will also reveal their command line parameters. It is surprising how many secrets are passed this way. Do not bother with the environ endpoint as it requires root privileges to be accessed.
- Multiple endpoints disclose networking information. My favourite one is /proc/self/net/arp, it will allow you to enumerate adjacent machines. While this vulnerability does not allow SSRF, a lot of Path Traversals do, and combined with the disclosed network information, it is possible to skip a good portion of the network discovery and scan hurdles to pivot to other systems.
- Access /proc/self/smaps to retrieve very detailed process memory information. While it will seem overwhelming at first, the important part is that libraries loaded in memory alongside their path and version will be exposed.
- This should be enough information to download the application itself. While the keys of a castle can be changed, its plans cannot. On complex systems, the software itself may be as valuable as the data it is using, and its exfiltration could enable thorough reverse engineering which could yield more vulnerabilities that could be used later.
Time Line
- GoSecure Inc. disclosed the vulnerability to Butor Inc. on July 5, 2019
- Butor Inc. acknowledged and issued a fix on July 5, 2019
Syntax coloration provided by IntelliJ and the Copy as HTML plugin.
Art fromブラックジャックによろしく12. ©佐藤 秀峰