CVE-2014-0121: Improper Authentication (CWE-287)
value: The admin terminal in Hawt.io does not require authentication, which allows remote attackers to execute arbitrary commands via the k parameter.
CWE description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
CVSS v3 base score: 9.8
Published: 2017-12-29T22:29Z
Repository: https://github.com/hawtio/hawtio
Language: Java
Files changed: hawtio-karaf-terminal/pom.xml, hawtio-karaf-terminal/src/main/java/io/hawt/web/plugin/karaf/terminal/KarafTerminalContextListener.java, hawtio-karaf-terminal/src/main/java/io/hawt/web/plugin/karaf/terminal/TerminalServlet.java, hawtio-karaf-terminal/src/main/webapp/WEB-INF/web.xml, hawtio-karaf-terminal/src/main/webapp/app/js/gogo.js, hawtio-karaf-terminal/src/main/webapp/app/js/gogoPlugin.js, hawtio-system/src/main/java/io/hawt/web/AuthenticationFilter.java
Commit date: 2014-03-05 10:48:22 -0500
Commit message: Ensure we secure hawtio-karaf-terminal's /term context

Diff: diff --git a/hawtio-karaf-terminal/pom.xml b/hawtio-karaf-terminal/pom.xml
index bdc9e534..b1772dc6 100644
--- a/hawtio-karaf-terminal/pom.xml
+++ b/hawtio-karaf-terminal/pom.xml
@@ -28,10 +28,16 @@
       <groupId>io.hawt</groupId>
       <artifactId>hawtio-plugin-mbean</artifactId>
       <version>${project.version}</version>
     </dependency>
 
+    <dependency>
+      <groupId>io.hawt</groupId>
+      <artifactId>hawtio-system</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
     <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>servlet-api</artifactId>
       <version>${servlet-api-version}</version>
       <scope>provided</scope>
diff --git a/hawtio-karaf-terminal/src/main/java/io/hawt/web/plugin/karaf/terminal/KarafTerminalContextListener.java b/hawtio-karaf-terminal/src/main/java/io/hawt/web/plugin/karaf/terminal/KarafTerminalContextListener.java
new file mode 100644
index 00000000..5aa802cd
--- /dev/null
+++ b/hawtio-karaf-terminal/src/main/java/io/hawt/web/plugin/karaf/terminal/KarafTerminalContextListener.java
@@ -0,0 +1,38 @@
+package io.hawt.web.plugin.karaf.terminal;
+
+import io.hawt.system.ConfigManager;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+/**
+ * @author Stan Lewis
+ */
+public class KarafTerminalContextListener implements ServletContextListener {
+
+  private ConfigManager configManager = new ConfigManager();
+
+  @Override
+  public void contextInitialized(ServletContextEvent sce) {
+    try {
+      configManager.init();
+    } catch (Exception e) {
+      throw createServletException(e);
+    }
+    sce.getServletContext().setAttribute("ConfigManager", configManager);
+  }
+
+  @Override
+  public void contextDestroyed(ServletContextEvent sce) {
+    try {
+      configManager.destroy();
+    } catch (Exception e) {
+      throw createServletException(e);
+    }
+
+  }
+  protected RuntimeException createServletException(Exception e) {
+    return new RuntimeException(e);
+  }
+
+}
diff --git a/hawtio-karaf-terminal/src/main/java/io/hawt/web/plugin/karaf/terminal/TerminalServlet.java b/hawtio-karaf-terminal/src/main/java/io/hawt/web/plugin/karaf/terminal/TerminalServlet.java
index fb2dcb49..1e8b872e 100644
--- a/hawtio-karaf-terminal/src/main/java/io/hawt/web/plugin/karaf/terminal/TerminalServlet.java
+++ b/hawtio-karaf-terminal/src/main/java/io/hawt/web/plugin/karaf/terminal/TerminalServlet.java
@@ -1,20 +1,25 @@
 package io.hawt.web.plugin.karaf.terminal;
 
+import io.hawt.system.Helpers;
 import org.apache.felix.service.command.CommandProcessor;
 import org.apache.felix.service.command.CommandSession;
 import org.apache.felix.service.threadio.ThreadIO;
 import org.apache.karaf.shell.console.jline.Console;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.security.auth.Subject;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
 import java.io.*;
 import java.lang.reflect.Constructor;
+import java.security.AccessControlContext;
+import java.security.AccessController;
 import java.util.zip.GZIPOutputStream;
 
 /**
  *
  */
@@ -36,16 +41,36 @@ public class TerminalServlet extends HttpServlet {
         return ThreadIOHolder.getThreadIO();
     }
 
     @Override
     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+
+        HttpSession session = request.getSession(false);
+        if (session == null) {
+          AccessControlContext acc = AccessController.getContext();
+          Subject subject = Subject.getSubject(acc);
+          if (subject == null) {
+            Helpers.doForbidden(response);
+            return;
+          }
+          session = request.getSession(true);
+          session.setAttribute("subject", subject);
+        } else {
+          Subject subject = (Subject) session.getAttribute("subject");
+          if (subject == null) {
+            session.invalidate();
+            Helpers.doForbidden(response);
+            return;
+          }
+        }
+
         String encoding = request.getHeader("Accept-Encoding");
         boolean supportsGzip = (encoding != null && encoding.toLowerCase().indexOf("gzip") > -1);
-        SessionTerminal st = (SessionTerminal) request.getSession(true).getAttribute("terminal");
+        SessionTerminal st = (SessionTerminal) session.getAttribute("terminal");
         if (st == null || st.isClosed()) {
             st = new SessionTerminal(getCommandProcessor(), getThreadIO());
-            request.getSession().setAttribute("terminal", st);
+            session.setAttribute("terminal", st);
         }
         String str = request.getParameter("k");
         String f = request.getParameter("f");
         String dump = st.handle(str, f != null && f.length() > 0);
         if (dump != null) {
diff --git a/hawtio-karaf-terminal/src/main/webapp/WEB-INF/web.xml b/hawtio-karaf-terminal/src/main/webapp/WEB-INF/web.xml
index 0802579a..1c14510a 100644
--- a/hawtio-karaf-terminal/src/main/webapp/WEB-INF/web.xml
+++ b/hawtio-karaf-terminal/src/main/webapp/WEB-INF/web.xml
@@ -6,17 +6,59 @@
 	      version="2.4">
 
   <description>hawtio</description>
   <display-name>hawtio Karaf terminal plugin</display-name>
 
+  <env-entry>
+    <description>Enable/disable hawtio's authentication filter, value is really a boolean</description>
+    <env-entry-name>hawtio/authenticationEnabled</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <env-entry-value>false</env-entry-value>
+  </env-entry>
+
+  <env-entry>
+    <description>Authorized user role, empty string disables authorization</description>
+    <env-entry-name>hawtio/role</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <env-entry-value></env-entry-value>
+  </env-entry>
+
+  <env-entry>
+    <description>JAAS classname that would contain the role principal, empty string disables authorization</description>
+    <env-entry-name>hawtio/rolePrincipalClasses</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <env-entry-value></env-entry-value>
+  </env-entry>
+
+  <env-entry>
+    <description>JAAS realm used to authenticate users</description>
+    <env-entry-name>hawtio/realm</env-entry-name>
+    <env-entry-type>java.lang.String</env-entry-type>
+    <env-entry-value>*</env-entry-value>
+  </env-entry>
+
+  <filter>
+    <filter-name>AuthenticationFilter</filter-name>
+    <filter-class>io.hawt.web.AuthenticationFilter</filter-class>
+  </filter>
+  <filter-mapping>
+    <filter-name>AuthenticationFilter</filter-name>
+    <url-pattern>/term/*</url-pattern>
+  </filter-mapping>
+
+
   <servlet>
     <servlet-name>TerminalServlet</servlet-name>
     <servlet-class>io.hawt.web.plugin.karaf.terminal.TerminalServlet</servlet-class>
     <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
     <servlet-name>TerminalServlet</servlet-name>
     <url-pattern>/term/*</url-pattern>
   </servlet-mapping>
 
+  <listener>
+    <listener-class>io.hawt.web.plugin.karaf.terminal.KarafTerminalContextListener</listener-class>
+  </listener>
+
 </web-app>
 
diff --git a/hawtio-karaf-terminal/src/main/webapp/app/js/gogo.js b/hawtio-karaf-terminal/src/main/webapp/app/js/gogo.js
index 14bd1c01..210110c0 100644
--- a/hawtio-karaf-terminal/src/main/webapp/app/js/gogo.js
+++ b/hawtio-karaf-terminal/src/main/webapp/app/js/gogo.js
@@ -4,11 +4,11 @@
 //  Public Domain License
 //
 
 gogo = { };
 
-gogo.Terminal_ctor = function(div, width, height) {
+gogo.Terminal_ctor = function(div, width, height, authHeader) {
 
    var query0 = "w=" + width + "&h=" + height;
    var query1 = query0 + "&k=";
    var buf = "";
    var timeout;
@@ -45,10 +45,11 @@ gogo.Terminal_ctor = function(div, width, height) {
            if (force) {
                query = query + "&f=1";
                force = 0;
            }
            r.open("POST", "hawtio-karaf-terminal/term", true);
+           r.setRequestHeader('Authorization', authHeader);
            r.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            r.onreadystatechange = function () {
                if (r.readyState == 4) {
                    if (r.status == 200) {
                        window.clearTimeout(error_timeout);
@@ -221,9 +222,9 @@ gogo.Terminal_ctor = function(div, width, height) {
 
    init();
 
 }
 
-gogo.Terminal = function(div, width, height) {
-   return new this.Terminal_ctor(div, width, height);
+gogo.Terminal = function(div, width, height, authHeader) {
+   return new this.Terminal_ctor(div, width, height, authHeader);
 }
 
diff --git a/hawtio-karaf-terminal/src/main/webapp/app/js/gogoPlugin.js b/hawtio-karaf-terminal/src/main/webapp/app/js/gogoPlugin.js
index f5b350ee..f323dbeb 100644
--- a/hawtio-karaf-terminal/src/main/webapp/app/js/gogoPlugin.js
+++ b/hawtio-karaf-terminal/src/main/webapp/app/js/gogoPlugin.js
@@ -9,11 +9,11 @@ var Gogo = (function() {
     }).config(function($routeProvider) {
       $routeProvider.
         when('/gogo', {
             templateUrl: 'hawtio-karaf-terminal/app/html/gogo.html'
           });
-    }).directive('gogoTerminal', function(log) {
+    }).directive('gogoTerminal', function(log, userDetails) {
       return {
         restrict: 'A',
         link: function(scope, element, attrs) {
 
           var width = 120;
@@ -48,11 +48,13 @@ var Gogo = (function() {
             height: cssHeight,
             'min-width': cssWidth,
             'min-height': cssHeight
           });
 
-          gogo.Terminal(element.get(0), width, height);
+          var authHeader = Core.getBasicAuthHeader(userDetails.username, userDetails.password);
+
+          gogo.Terminal(element.get(0), width, height, authHeader);
 
           scope.$on("$destroy", function(e) {
             document.onkeypress = null;
             document.onkeydown = null;
           });
diff --git a/hawtio-system/src/main/java/io/hawt/web/AuthenticationFilter.java b/hawtio-system/src/main/java/io/hawt/web/AuthenticationFilter.java
index b9c9f5d9..4b5cecb4 100644
--- a/hawtio-system/src/main/java/io/hawt/web/AuthenticationFilter.java
+++ b/hawtio-system/src/main/java/io/hawt/web/AuthenticationFilter.java
@@ -103,13 +103,11 @@ public class AuthenticationFilter implements Filter {
                 executeAs(request, response, chain, subject);
                 return;
             }
         }
 
-        boolean doAuthenticate = path.startsWith("/auth") ||
-                path.startsWith("/jolokia") ||
-                path.startsWith("/upload");
+        boolean doAuthenticate = true;
 
         if (doAuthenticate) {
             LOG.debug("Doing authentication and authorization for path {}", path);
             switch (Authenticator.authenticate(configuration.getRealm(), configuration.getRole(), configuration.getRolePrincipalClasses(),
                     configuration.getConfiguration(), httpRequest, new PrivilegedCallback() {
@@ -127,11 +125,11 @@ public class AuthenticationFilter implements Filter {
                     //doAuthPrompt((HttpServletResponse)response);
                     Helpers.doForbidden((HttpServletResponse) response);
                     break;
             }
         } else {
-            LOG.debug("No authentication needed for path {}", path);
+            LOG.warn("No authentication needed for path {}", path);
             chain.doFilter(request, response);
         }
     }
 
     private static void executeAs(final ServletRequest request, final ServletResponse response, final FilterChain chain, Subject subject) {

Vulnerable code:         SessionTerminal st = (SessionTerminal) request.getSession(true).getAttribute("terminal");
            request.getSession().setAttribute("terminal", st);
gogo.Terminal_ctor = function(div, width, height) {
gogo.Terminal = function(div, width, height) {
   return new this.Terminal_ctor(div, width, height);
    }).directive('gogoTerminal', function(log) {
          gogo.Terminal(element.get(0), width, height);
        boolean doAuthenticate = path.startsWith("/auth") ||
                path.startsWith("/jolokia") ||
                path.startsWith("/upload");
            LOG.debug("No authentication needed for path {}", path);
Fixed code:     <dependency>
      <groupId>io.hawt</groupId>
      <artifactId>hawtio-system</artifactId>
      <version>${project.version}</version>
    </dependency>

package io.hawt.web.plugin.karaf.terminal;

import io.hawt.system.ConfigManager;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * @author Stan Lewis
 */
public class KarafTerminalContextListener implements ServletContextListener {

  private ConfigManager configManager = new ConfigManager();

  @Override
  public void contextInitialized(ServletContextEvent sce) {
    try {
      configManager.init();
    } catch (Exception e) {
      throw createServletException(e);
    }
    sce.getServletContext().setAttribute("ConfigManager", configManager);
  }

  @Override
  public void contextDestroyed(ServletContextEvent sce) {
    try {
      configManager.destroy();
    } catch (Exception e) {
      throw createServletException(e);
    }

  }
  protected RuntimeException createServletException(Exception e) {
    return new RuntimeException(e);
  }

}
import io.hawt.system.Helpers;
import javax.security.auth.Subject;
import javax.servlet.http.HttpSession;
import java.security.AccessControlContext;
import java.security.AccessController;

        HttpSession session = request.getSession(false);
        if (session == null) {
          AccessControlContext acc = AccessController.getContext();
          Subject subject = Subject.getSubject(acc);
          if (subject == null) {
            Helpers.doForbidden(response);
            return;
          }
          session = request.getSession(true);
          session.setAttribute("subject", subject);
        } else {
          Subject subject = (Subject) session.getAttribute("subject");
          if (subject == null) {
            session.invalidate();
            Helpers.doForbidden(response);
            return;
          }
        }

        SessionTerminal st = (SessionTerminal) session.getAttribute("terminal");
            session.setAttribute("terminal", st);
  <env-entry>
    <description>Enable/disable hawtio's authentication filter, value is really a boolean</description>
    <env-entry-name>hawtio/authenticationEnabled</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value>false</env-entry-value>
  </env-entry>

  <env-entry>
    <description>Authorized user role, empty string disables authorization</description>
    <env-entry-name>hawtio/role</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value></env-entry-value>
  </env-entry>

  <env-entry>
    <description>JAAS classname that would contain the role principal, empty string disables authorization</description>
    <env-entry-name>hawtio/rolePrincipalClasses</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value></env-entry-value>
  </env-entry>

  <env-entry>
    <description>JAAS realm used to authenticate users</description>
    <env-entry-name>hawtio/realm</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value>*</env-entry-value>
  </env-entry>

  <filter>
    <filter-name>AuthenticationFilter</filter-name>
    <filter-class>io.hawt.web.AuthenticationFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>AuthenticationFilter</filter-name>
    <url-pattern>/term/*</url-pattern>
  </filter-mapping>


  <listener>
    <listener-class>io.hawt.web.plugin.karaf.terminal.KarafTerminalContextListener</listener-class>
  </listener>

gogo.Terminal_ctor = function(div, width, height, authHeader) {
           r.setRequestHeader('Authorization', authHeader);
gogo.Terminal = function(div, width, height, authHeader) {
   return new this.Terminal_ctor(div, width, height, authHeader);
    }).directive('gogoTerminal', function(log, userDetails) {
          var authHeader = Core.getBasicAuthHeader(userDetails.username, userDetails.password);

          gogo.Terminal(element.get(0), width, height, authHeader);
        boolean doAuthenticate = true;
            LOG.warn("No authentication needed for path {}", path);