▶️ 代码是javascript写的,基于Sails进行部署开发,所以也是我第一次意义上的审计javascript后端项目

项目地址:GitHub - pantsel/konga: More than just another GUI to Kong Admin API

先上一下历史ISSUE以及PR中提到的问题

1、privilege escalation: Fix priviledged escalation · pantsel/konga@af43bee · GitHub

2、xss vuln: Merge branch ‘fix/xss-vuln’ into next · pantsel/konga@16e36e8 · GitHub

3、multiple registed: Prevent admin users to be registered multiple times via the /register… · pantsel/konga@37fce1c · GitHub

上述三个为公开的漏洞,但是没有CVE编码,外网公开的还有一个Secret的问题,但是其实出现的概率很多,只有没有设置TOKEN_SECRET的时候才会出现密钥泄漏,最后导致可自定义构造JWT进行任意用户登陆,但是感觉用处也比较少

后台nodemailer组件+配置修改->RCE

这一步有点运气,主要是看到dependency-check那边报了组件问题nodemailer在send()函数存在可命令执行的地方,前提是需要覆盖两个参数或者进行原型污染

可以参考来自这篇文章:D^3CTF 8-bit-pub - byc_404’s blog

问题主要是以下代码处:lib/sendmail-transport/index.js,在SendmailTransport类中的send()方法,在发送之前会调用this._spoawn(this.path, args),其中const spawn = require('child_process').spawn;,所以会进行命令执行

this.path以及args是在初始化(new SendmailTransport)的时候进行赋值,也就是说,只要控制了this.path以及args,就可以达成命令执行

可以来看下konga中怎么实现的,有三个关键的文件如下,这三个文件都会调用到邮件来进行发送,三个文件的导出函数都一样,所以这里用其中的一个来讲即可

1
2
3
konga-0.13.0/api/events/node-health-checks.js
konga-0.13.0/api/events/api-health-checks.js
konga-0.13.0/api/events/user-events.js

其中关注点为导出函数createTransporter以及notify


createTransporter为创建一个transport,也就是邮件发送的方式,其中transport为可控,也就是我们传进去的参数

可以看到下面的图,当options.sendmail为true的时候,会new一个SendmailTransport,我们这里可以构造transport.settings其中一个参数的值为:sendmail:true,即可以进入到SendmailTransport的构造方法

其实我们也不难发现,options参数,也就是transport.settings参数也会被带入到SendmailTransport中,也就以为着参数中存在path以及args即可以造成RCE,此处完整payload如下

"settings":{"sendmail":"true","path":"/bin/bash","args":["-c","echo 1 > /tmp/44.txt"]}

当创建好了SendmailTransport的时候,就需要用到发送的函数,在konga中是notify,在notify中会直接调用sendmail函数,也就是在发送邮件的时候会造成RCE

mailer.sendMail()会继续调用到this.transporter.send(),而this.transporter.send()为SendmailTransport.send()

数据包为如下:

当default_transport为smtp或者mailgun的时候,即可以造成RCE,这里有一个比较可惜的一个点,这个default_transport的设置需要管理员,不然普通用户通过api的接口也可以实现RCE

1
这里发送邮件有三种方式,当一个node或者api下线或者掉线的时候会发送邮件(node及时发生,api需要经过15分钟),或者当一个用户singup的时候,也会发送邮件,其中api下线或者掉线都可以通过普通用户权限来实现

最后给以下完整的流程

先开启其中一个

接着配置邮件

可以抓到包,替换一下payload,替换的是data字段

数据库也被成功写入

设置api或者node下线或者掉线又或者用户注册的时候,即可以实现RCE

下图为成功进行用户注册

成功写入文件

这里要提一下,之前有人提过nodemailer的这个问题,不过是注入邮件,来自如下链接:Command Injection in nodemailer | CVE-2020-7769 | Snyk

官方的修复手法是添加校验发送地址以及接收地址是否存在,所以我更愿意觉得这个spawn()是nodemailer的一个特性,最新一版依旧可以利用

▶️ 思考:是否可以通过原型污染的方式来覆盖settings[0].data.default_transport的方式来实现低权限用户RCE?

signup接口的crash服务

漏洞原理很简单,也是无意间测试出来的,路径:konga-0.13.0/api/controllers/AuthController.js

在注册的时候会对protocol进行赋值

而protocol的参数又是必须的,路径:konga-0.13.0/api/models/Passport.js

所以会导致当注册的时候不存在参数passports.protocol的时候,会直接带出异常,又因为是在赋值的是时候抛出的异常,还没进入到create()函数中,所以没有进行异常处理,最后导致服务crash掉,直接引用GPT的解释

正常的注册包

删除参数后的注册包

贴一下konga和kong的部署
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
docker run -d --name kong-database \
-p 5432:5432 \
-e "POSTGRES_USER=kong" \
-e "POSTGRES_DB=kong" \
-e "POSTGRES_PASSWORD=kongpass" \
postgres:9.6


docker run --rm \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=10.211.55.115" \
-e "KONG_PG_USER=kong" \
-e "KONG_PG_PASSWORD=kongpass" \
rjshrjndrn/kong:0.14.1 kong migrations up


docker run -d --name kong \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=10.211.55.115" \
-e "KONG_PG_USER=kong" \
-e "KONG_PG_PASSWORD=kongpass" \
-e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
-e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
-e "KONG_ADMIN_GUI_URL=http://localhost:8002" \
-e KONG_LICENSE_DATA \
-p 8000:8000 \
-p 8443:8443 \
-p 8001:8001 \
-p 8444:8444 \
-p 8002:8002 \
-p 8445:8445 \
-p 8003:8003 \
-p 8004:8004 \
rjshrjndrn/kong:0.14.1

curl -i -X GET --url http://10.211.55.115:8001/services


sudo docker pull pantsel/konga:0.13.0


docker run --rm pantsel/konga:0.13.0 -c prepare -a postgres -u postgres://kong:kongpass@10.211.55.115:5432/konga


docker run -p 1337:1337 --name konga \
-e "TOKEN_SECRET=@Aa@123456" \
-e "DB_ADAPTER=postgres" \
-e "DB_HOST=10.211.55.115" \
-e "DB_PORT=5432" \
-e "DB_USER=kong" \
-e "DB_PASSWORD=kongpass" \
-e "DB_DATABASE=konga" \
-e "NODE_ENV=production" \
pantsel/konga:0.13.0