Vulnerabilities > CVE-2018-8715 - Improper Authentication vulnerability in Embedthis Appweb

047910
CVSS 8.1 - HIGH
Attack vector
NETWORK
Attack complexity
HIGH
Privileges required
NONE
Confidentiality impact
HIGH
Integrity impact
HIGH
Availability impact
HIGH
network
high complexity
embedthis
CWE-287
nessus

Summary

The Embedthis HTTP library, and Appweb versions before 7.0.3, have a logic flaw related to the authCondition function in http/httpLib.c. With a forged HTTP request, it is possible to bypass authentication for the form and digest login types.

Common Weakness Enumeration (CWE)

Common Attack Pattern Enumeration and Classification (CAPEC)

  • Authentication Abuse
    An attacker obtains unauthorized access to an application, service or device either through knowledge of the inherent weaknesses of an authentication mechanism, or by exploiting a flaw in the authentication scheme's implementation. In such an attack an authentication mechanism is functioning but a carefully controlled sequence of events causes the mechanism to grant access to the attacker. This attack may exploit assumptions made by the target's authentication procedures, such as assumptions regarding trust relationships or assumptions regarding the generation of secret values. This attack differs from Authentication Bypass attacks in that Authentication Abuse allows the attacker to be certified as a valid user through illegitimate means, while Authentication Bypass allows the user to access protected material without ever being certified as an authenticated user. This attack does not rely on prior sessions established by successfully authenticating users, as relied upon for the "Exploitation of Session Variables, Resource IDs and other Trusted Credentials" attack patterns.
  • Exploiting Trust in Client (aka Make the Client Invisible)
    An attack of this type exploits a programs' vulnerabilities in client/server communication channel authentication and data integrity. It leverages the implicit trust a server places in the client, or more importantly, that which the server believes is the client. An attacker executes this type of attack by placing themselves in the communication channel between client and server such that communication directly to the server is possible where the server believes it is communicating only with a valid client. There are numerous variations of this type of attack.
  • Utilizing REST's Trust in the System Resource to Register Man in the Middle
    This attack utilizes a REST(REpresentational State Transfer)-style applications' trust in the system resources and environment to place man in the middle once SSL is terminated. Rest applications premise is that they leverage existing infrastructure to deliver web services functionality. An example of this is a Rest application that uses HTTP Get methods and receives a HTTP response with an XML document. These Rest style web services are deployed on existing infrastructure such as Apache and IIS web servers with no SOAP stack required. Unfortunately from a security standpoint, there frequently is no interoperable identity security mechanism deployed, so Rest developers often fall back to SSL to deliver security. In large data centers, SSL is typically terminated at the edge of the network - at the firewall, load balancer, or router. Once the SSL is terminated the HTTP request is in the clear (unless developers have hashed or encrypted the values, but this is rare). The attacker can utilize a sniffer such as Wireshark to snapshot the credentials, such as username and password that are passed in the clear once SSL is terminated. Once the attacker gathers these credentials, they can submit requests to the web service provider just as authorized user do. There is not typically an authentication on the client side, beyond what is passed in the request itself so once this is compromised, then this is generally sufficient to compromise the service's authentication scheme.
  • Man in the Middle Attack
    This type of attack targets the communication between two components (typically client and server). The attacker places himself in the communication channel between the two components. Whenever one component attempts to communicate with the other (data flow, authentication challenges, etc.), the data first goes to the attacker, who has the opportunity to observe or alter it, and it is then passed on to the other component as if it was never intercepted. This interposition is transparent leaving the two compromised components unaware of the potential corruption or leakage of their communications. The potential for Man-in-the-Middle attacks yields an implicit lack of trust in communication or identify between two components.

Nessus

NASL familyWeb Servers
NASL idAPPWEB_SERVER_7_0_3.NASL
descriptionAccording to its banner, the version of Appweb installed on the remote host is prior to 7.0.3. It is, therefore, have a logic flaw related to the authCondition function in http/httpLib.c. With a forged HTTP request, it is possible to bypass authentication for the form and digest login types. Note that Nessus did not actually test for this issue, but instead has relied on the version in the server
last seen2020-06-01
modified2020-06-02
plugin id118710
published2018-11-02
reporterThis script is Copyright (C) 2018-2019 and is owned by Tenable, Inc. or an Affiliate thereof.
sourcehttps://www.tenable.com/plugins/nessus/118710
titleAppweb < 7.0.3 authCondition Authentication Bypass Vulnerability
code
#
# (C) Tenable Network Security, Inc.
#

include("compat.inc");

if (description)
{
  script_id(118710);
  script_version("1.3");
  script_cvs_date("Date: 2019/11/01");

  script_cve_id("CVE-2018-8715");

  script_name(english:"Appweb < 7.0.3 authCondition Authentication Bypass Vulnerability");
  script_summary(english:"Checks version in Server response header.");

  script_set_attribute(attribute:"synopsis", value:
"The remote web server may be affected by a authentication bypass 
vulnerability.");
  script_set_attribute(attribute:"description", value:
"According to its banner, the version of Appweb installed on the
remote host is prior to 7.0.3. It is, therefore, have a logic flaw
related to the authCondition function in http/httpLib.c. With a 
forged HTTP request, it is possible to bypass authentication for the
form and digest login types.

Note that Nessus did not actually test for this issue, but instead 
has relied on the version in the server's banner.");
  # https://github.com/embedthis/appweb/issues/610
  script_set_attribute(attribute:"see_also", value:"http://www.nessus.org/u?b2bbda6c");
  script_set_attribute(attribute:"solution", value:
"Upgrade to Appweb version 7.0.3 or later.");
  script_set_cvss_base_vector("CVSS2#AV:N/AC:M/Au:N/C:P/I:P/A:P");
  script_set_cvss_temporal_vector("CVSS2#E:U/RL:OF/RC:C");
  script_set_cvss3_base_vector("CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H");
  script_set_cvss3_temporal_vector("CVSS:3.0/E:U/RL:O/RC:C");
  script_set_attribute(attribute:"cvss_score_source", value:"CVE-2018-8715");

  script_set_attribute(attribute:"exploitability_ease", value:"No known exploits are available");

  script_set_attribute(attribute:"vuln_publication_date", value:"2018/03/14");
  script_set_attribute(attribute:"patch_publication_date", value:"2018/03/14");
  script_set_attribute(attribute:"plugin_publication_date", value:"2018/11/02");

  script_set_attribute(attribute:"plugin_type", value:"remote");
  script_set_attribute(attribute:"cpe", value:"cpe:/a:mbedthis_software:mbedthis_appweb_http_server");
  script_end_attributes();

  script_category(ACT_GATHER_INFO);
  script_family(english:"Web Servers");

  script_copyright(english:"This script is Copyright (C) 2018-2019 and is owned by Tenable, Inc. or an Affiliate thereof.");

  script_dependencies("appweb_server_detect.nasl", "os_fingerprint.nasl");
  script_require_keys("www/appweb");
  script_require_ports("Services/www", 80, 7777);

  exit(0);
}

include("vcf.inc");
include("audit.inc");
include("http.inc");

port = get_http_port(default:80);

# Make sure this is Appweb.
get_kb_item_or_exit('www/'+port+'/appweb');

app_info = vcf::get_app_info(app:"Appweb", kb_ver:'www/appweb/'+port+'/version', service:FALSE);

constraints = [{ "min_version" : "4.0", "fixed_version" : "7.0.3"  }];

vcf::check_version_and_report(app_info:app_info, constraints:constraints, severity:SECURITY_WARNING, strict:FALSE);

Seebug

bulletinFamilyexploit
description### Vulnerability Summary A critical vulnerability in the EmbedThis HTTP library, and Appweb versions 5.5.x, 6.x, and 7.x including the latest version present in the git repository. In detail, due to a logic flaw, with a forged HTTP request it is possible to bypass the authentication for form and digest login types. ### Confirmed Vulnerable Appweb version 7.0.2 and prior ### Credit An independent security researcher, Davide Quarta (@_ocean) and Truel IT, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program. Vendor Response Vendor response was exceptionally quick, within 2 days from reporting the vulnerability to them they had a patch available and new version Appweb version 7.0.3 and information available to the public: https://github.com/embedthis/appweb/issues/610 ### CVE CVE-2018-8715 ### Vulnerability Details Due to a logical flaw in the authentication procedure, knowing the target username, it is possible to completely bypass authentication of both form and digest type authentications, by means of a crafted HTTP POST request. File http/httpLib.c – function authCondition() This function is responsible for calling the two functions that are responsible of authentication: getCredentials, and httpLogin. Notice the lack of checks around httpGetCredentials, it will be useful later. ``` 14559 static int authCondition(HttpConn *conn, HttpRoute *route, HttpRouteOp *op) 14560 { 14561 HttpAuth *auth; 14562 cchar *username, *password; 14563 14564 assert(conn); 14565 assert(route); 14566 14567 auth = route->auth; 14568 if (!auth || !auth->type) { 14569 /* Authentication not required */ 14570 return HTTP_ROUTE_OK; 14571 } 14572 if (!httpIsAuthenticated(conn)) { 14573 httpGetCredentials(conn, &username, &password); 14574 if (!httpLogin(conn, username, password)) { 14575 if (!conn->tx->finalized) { 14576 if (auth && auth->type) { 14577 (auth->type->askLogin)(conn); 14578 } else { 14579 httpError(conn, HTTP_CODE_UNAUTHORIZED, "Access Denied, login required"); 14580 } 14581 /* Request has been denied and a response generated. So OK to accept this route. */ 14582 } 14583 return HTTP_ROUTE_OK; 14584 } 14585 } 14586 if (!httpCanUser(conn, NULL)) { 14587 httpTrace(conn, "auth.check", "error", "msg:'Access denied, user is not authorized for access'"); 14588 if (!conn->tx->finalized) { 14589 httpError(conn, HTTP_CODE_FORBIDDEN, "Access denied. User is not authorized for access."); 14590 /* Request has been denied and a response generated. So OK to accept this route. */ 14591 } 14592 } 14593 /* OK to accept route. This does not mean the request was authenticated - an error may have been already generated */ 14594 return HTTP_ROUTE_OK; 14595 } ``` File http/httpLib.c – function httpGetCredentials() This function receives two pointers to char arrays that will contain the username and password parsed from the request. Since there are no checks in authCondition, it doesn’t matter if the “parseAuth” function fail, this means we can insert in the WWW-Authenticate header or in the post data for authentication any field we want: ``` 1640 /* 1641 Get the username and password credentials. If using an in-protocol auth scheme like basic|digest, the 1642 rx->authDetails will contain the credentials and the parseAuth callback will be invoked to parse. 1643 Otherwise, it is expected that "username" and "password" fields are present in the request parameters. 1644 1645 This is called by authCondition which thereafter calls httpLogin 1646 */ 1647 PUBLIC bool httpGetCredentials(HttpConn *conn, cchar **username, cchar **password) 1648 { 1649 HttpAuth *auth; 1650 1651 assert(username); 1652 assert(password); 1653 *username = *password = NULL; 1654 1655 auth = conn->rx->route->auth; 1656 if (!auth || !auth->type) { 1657 return 0; 1658 } 1659 if (auth->type) { 1660 if (conn->authType && !smatch(conn->authType, auth->type->name)) { 1661 if (!(smatch(auth->type->name, "form") && conn->rx->flags & HTTP_POST)) { 1662 /* If a posted form authentication, ignore any basic|digest details in request */ 1663 return 0; 1664 } 1665 } 1666 if (auth->type->parseAuth && (auth->type->parseAuth)(conn, username, password) < 0) { 1667 return 0; 1668 } 1669 } else { 1670 *username = httpGetParam(conn, "username", 0); 1671 *password = httpGetParam(conn, "password", 0); 1672 } 1673 return 1; 1674 } ``` File http/httpLib.c – function httpLogin() This function will check for the username to be not null, when there is already a session associated, the password pointer can instead be null. ``` 1686 PUBLIC bool httpLogin(HttpConn *conn, cchar *username, cchar *password) 1687 { 1688 HttpRx *rx; 1689 HttpAuth *auth; 1690 HttpSession *session; 1691 HttpVerifyUser verifyUser; 1692 1693 rx = conn->rx; 1694 auth = rx->route->auth; 1695 if (!username || !*username) { 1696 httpTrace(conn, "auth.login.error", "error", "msg:'missing username'"); 1697 return 0; 1698 } 1699 if (!auth->store) { 1700 mprLog("error http auth", 0, "No AuthStore defined"); 1701 return 0; 1702 } 1703 if ((verifyUser = auth->verifyUser) == 0) { 1704 if (!auth->parent || (verifyUser = auth->parent->verifyUser) == 0) { 1705 verifyUser = auth->store->verifyUser; 1706 } 1707 } 1708 if (!verifyUser) { 1709 mprLog("error http auth", 0, "No user verification routine defined on route %s", rx->route->pattern); 1710 return 0; 1711 } 1712 if (auth->username && *auth->username) { 1713 /* If using auto-login, replace the username */ 1714 username = auth->username; 1715 password = 0; 1716 } 1717 if (!(verifyUser)(conn, username, password)) { 1718 return 0; 1719 } 1720 if (!(auth->flags & HTTP_AUTH_NO_SESSION) && !auth->store->noSession) { 1721 if ((session = httpCreateSession(conn)) == 0) { 1722 /* Too many sessions */ 1723 return 0; 1724 } 1725 httpSetSessionVar(conn, HTTP_SESSION_USERNAME, username); 1726 httpSetSessionVar(conn, HTTP_SESSION_IP, conn->ip); 1727 } 1728 rx->authenticated = 1; 1729 rx->authenticateProbed = 1; 1730 conn->username = sclone(username); 1731 conn->encoded = 0; 1732 return 1; 1733 } <em>File http/httpLib.c – function configVerfiyUser()</em> The following function will first check for the presence of a valid user, either because it was already set in the session, or because it was passed, since we are able to pass a null password (line 2031), we can bypass the actual checks and successfully authenticate reaching line 2055. 2014 /* 2015 Verify the user password for the "config" store based on the users defined via configuration directives. 2016 Password may be NULL only if using auto-login. 2017 */ 2018 static bool configVerifyUser(HttpConn *conn, cchar *username, cchar *password) 2019 { 2020 HttpRx *rx; 2021 HttpAuth *auth; 2022 bool success; 2023 char *requiredPassword; 2024 2025 rx = conn->rx; 2026 auth = rx->route->auth; 2027 if (!conn->user && (conn->user = mprLookupKey(auth->userCache, username)) == 0) { 2028 httpTrace(conn, "auth.login.error", "error", "msg: 'Unknown user', username:'%s'", username); 2029 return 0; 2030 } 2031 if (password) { 2032 if (auth->realm == 0 || *auth->realm == '\0') { 2033 mprLog("error http auth", 0, "No AuthRealm defined"); 2034 } 2035 requiredPassword = (rx->passwordDigest) ? rx->passwordDigest : conn->user->password; 2036 if (sncmp(requiredPassword, "BF", 2) == 0 && slen(requiredPassword) > 4 && isdigit(requiredPassword[2]) && 2037 requiredPassword[3] == ':') { 2038 /* Blowifsh */ 2039 success = mprCheckPassword(sfmt("%s:%s:%s", username, auth->realm, password), conn->user->password); 2040 2041 } else { 2042 if (!conn->encoded) { 2043 password = mprGetMD5(sfmt("%s:%s:%s", username, auth->realm, password)); 2044 conn->encoded = 1; 2045 } 2046 success = smatch(password, requiredPassword); 2047 } 2048 if (success) { 2049 httpTrace(conn, "auth.login.authenticated", "context", "msg:'User authenticated', username:'%s'", username); 2050 } else { 2051 httpTrace(conn, "auth.login.error", "error", "msg:'Password failed to authenticate', username:'%s'", username); 2052 } 2053 return success; 2054 } 2055 return 1; 2056 } ``` To be able to bypass the authentication we need to be able to pass a null password pointer, fortunately, both for form and digest authentication, the functions used to parse authentication details (line 1666) will allow us to set a null password pointer, and even with an error returned, in the end, it won’t be checked by authCondition, allowing us to completely bypass authentication, the only condition to exploit this is to know a username in the hashmap. To overcome this limitation, it must be considered that the size of the hashmap is usually small, and the hash algorithm (FNV) used in the hashmap is weak: with a limited number of tries it could be possible to find a collision, and login without knowing a valid username (untested).
idSSV:97181
last seen2018-06-26
modified2018-03-15
published2018-03-15
reporterMy Seebug
sourcehttps://www.seebug.org/vuldb/ssvid-97181
titleAppWeb Authentication Bypass (Digest, Basic and Forms)(CVE-2018-8715)