D^3CTF 2019 Web ezts Official WriteUp
没错,我就是 ezts 的傻逼出题人(求不被打
出题时间有点紧,题目描述在一开始也出了点问题,对不起各位师傅了
出题思路
作为一个整天摸鱼的 web 菜狗,实在是太有幸能在这种大型比赛出题了(感谢一下咕咕咕的老大 嘤
这道题的出题思路其实是源自日常无聊的日站行为。源头是 ThinkPHP
3.2.x 爆出的一枚 ORM
框架 bind 注入漏洞。在用了这个漏洞把某站的数据库给DROP了以后,引发了我的一些思考:ORM 框架真的安全吗?
ORM,对象关系映射,我一般将其理解为一种开发的思想,即代码和数据解耦。但是,代码是人写的,库也是人用的。ORM 库真的就没有漏洞吗?用 ORM 库就不会写出漏洞吗?显然不是
同样,针对现在的开发,大量使用的第三方库,会不会造成其他漏洞?
最后,拿到一个 shell 后,运维的配置会不会产生其他潜在的漏洞?
因此,这道题基本的出题思路就是:
- 通过开发者误用的 ORM 框架加上其本来的漏洞产生 SQL 注入,从而拿到关键数据
- 由于第三方库导致的一个语言级别漏洞拿到 shell
- 由于运维人员对 sudo 的错误配置产生权限提升
而映射到题目上,基本的解题思路就是:
- Koa 框架所使用的 Sequelize ORM 框架本身的漏洞加上开发者错误的代码导致搜索功能产生了一个 SQL 注入漏洞,通过该漏洞注出 admin 的密码
- 登陆 admin,发现有管理页面。在管理页面可以修改用户的资料。其实可以发现用户资料形式是以 JSON 在数据库存储的,在 admin 修改时存在合并操作。这里存在一个合并对象导致的原型链污染
- 原型链污染可以干嘛呢?本题没有编写相关的利用代码。而实际上产生漏洞的代码在使用的渲染引擎 ejs 上,通过对 ejs 的参数修改,我们可以直接拿到 shell
- 拿到 shell 后,用户实为低权限用户。但是 flag 却是 root 用户 0400 权限,也没有相关读取的二进制文件。有些师傅在这使用内核提权卡住了,实际上拿到 root 用户的最方便的方法不是 sudo 么?这里就是运维对 sudo 的一个错误配置,结合近期爆出的一枚 sudo 的 CVE 达到提权
下面会给出详细的 WP 和题目运维过程中产生的一些思考
针对题目描述问题,在仔细思考以后,确实有点不妥,但是我还是得说明一下
原题描述
ORM $eq No SQLi?
修改后描述
ORM $eq Not SQLi?
在比赛过程中有师傅反馈误以为是 NoSQL
注入,但是如果是 NoSQL
注入,题目意思不就变成 使用ORM框架等于NoSQL注入 ?这显然不是有点奇怪么?
而且在搜索界面的一个 '
就可以触发 500 响应,难道不是 SQL 注入的一个直接提醒么?
Write Up
根据出题思路,我将 WP 分为五个 stage
Stage 1 信息收集
Web 永远逃不掉信息收集。打开这个题目开始,这个网站有登陆,注册的功能。注册完成后登陆进用户界面,发现有增加档案,搜索档案的功能
同时,如果使用了 fuzz 工具,可以发现存在 /admin/
路由,也就意味着这题可能的思路就是访问到管理界面。注意到 Cookie
有 koa
的键名,基本可以判断后端是 Node.Js
的 Koa
框架
根据题目描述,基本可以判定这题使用了 ORM 框架,谷歌随便一搜 Koa
的或者 Node.Js
的 ORM 框架,就可以找到 Sequelize
这个库。也就是说,我们可以大致猜测出后端使用了哪些框架了
在搜索界面,注意到 '
可以触发 500 响应,基本可以判定这里存在一个 SQL
注入漏洞
Stage 2 SQL 注入
根据我们信息收集的结果,可以对搜索功能进行进一步的 fuzz,但是尝试了多种 SQL
注入 payload
都无果。这里想到猜测的后段 ORM 框架 Sequelize
。那么我们去 Snyk.io
去找一下这个框架有没有洞
显然,这个框架有一个较近的 CVE: CVE-2019-10752,而 Snyk.io 直接给出了相应的 PoC
。那么后面问题就很简单了,随便把 PoC
改成时间盲注,发现存在延时,就可以直接用布尔盲注注出数据了
这里也不放出脚本了,没有任何过滤的裸的注入,注出第一个用户,也就是 admin
,拿到密码就可以直接登陆后台管理了
Stage 3 原型链污染
登陆进入管理后台后,发现有管理用户数据和查询用户数据的功能。而管理用户数据可以直接对用户数据修改。这里注意到查询出的用户数据是 JSON 格式的,也就是说数据库中用户数据大概也是直接存放的。
有个小细节就是管理界面直接会车无效,需要点提交按钮,这个是我前端没写好的锅(
然后修改用户数据,可以发现提交数据格式必须也是 JSON 格式,而提交的 JSON 会被合并进原来的数据,或者说,会创建新的数据。这里的 JSON 合并也就是 js 的对象合并操作,很容易想到原型链污染这个漏洞
为了测试,我们可以先发一个小的 PoC
{"content": {"constructor": {"prototype": {"a": "b"}}}}
提交之后,再查看用户数据,可以发现键 content
中的数据消失了,这里可以初步认为存在原型链污染漏洞
(事实上,如果后端是 express
,原型链污染的数据会在 HTTP 响应头中显示出来)
还有更多的 PoC,比如
{"content": {"constructor": {"prototype": {"username": "asd"}}}}
可以发现刷新后立刻 500,等待服务重启之后 asd
用户也离奇消失了
这是原型链污染在其他依赖库中产生的一些副作用,而实际上,针对这题原型链污染,我对 Sequelize
库进行了修改,防止污染之后 ORM 框架出错(因为污染的数据会以键的形式插入到查询语句中)
到这,我们已经基本确定存在原型链污染漏洞了
Stage 4 ejs getshell
有了原型链污染了,我们可以做些啥呢。这里其实是参考了 X-NUCA 比赛过程中 hard_js 一题的非预期解。ejs 模版框架存在代码注入的问题,在遇到原型链污染时,我们可以将 js 代码注入到渲染模版中,最后引发命令执行,拿到 shell
具体的过程这里也不在赘述,可以参考我的这两篇博客
下面一篇有详细的代码调试跟进和分析
Stage 5 提权
拿到 shell 后,有师傅发现用户为 node
,而根目录下 flag 的文件权限为 0400。通常情况下,一般会预留一个 readflag 的文件来读取 flag,但是本题并没有。
很多师傅想到提权,但是事实上本题 Linux 内核非常新,目前没有提权洞(应该,除了潜在漏洞或 0day),而且作为一个 Web 题,在 docker
容器内 pwn 内核提权不是一个很奇怪的思路么?
而事实上,最简单的获取 root 权限的办法,不就是 sudo 么?
同样的,这里使用了非常新的 sudo
的 CVE: CVE-2019-14287。我在本地调试 PoC 通过以后,临时决定给这个题加点最后的难度,把 sudo
这个漏洞加进来了
那么后续就很简单了
sudo -l # 查看当前用户 sudo 配置
sudo -u#-1 /bin/cat /flag
运维过程中的思考
在运维过程中,遇到了很多蜜汁的问题,而很多在我看来完全不能理解,大概这就是上帝视角的感受吧。其实可能当我也拿到这个题的时候,我也会犯同样的错误
首先的一个就是,题目描述带来的问题。上面我也解释过了,确实挺奇怪的。但是就给我做,对于搜索功能里面一个单引号就能 500 的点我是永远不能放下不看的(这不是摆明了的 SQL 注入么???
其次,为啥会有队开扫描器暴力扫描?我觉得稍微信息收集一下就可以发现后端是 Koa 框架,扫描很明显就没有任何用处了啊。更有甚者,开了多线程高强度分布式扫描,以至于服务器 5分钟不到处理了 260k 个请求(这让我觉得 Node.Js
还是能做生产环境的
重新回到注入上面,为什么用 SQL 注入注出管理员的密码,然后还在管理界面尝试 NoSQL 注入?难道后会用两个数据库?个人表示不是很能理解
关于原型链污染,我在流量中看到了几种污染的 payload,但是是不能用的,这种其实本地调试一下就可以发现这个问题(可能用毒瘤 js/ts 的人实在是太少了
关于提权,这没啥好说的,其实在最后放出的 sudo 的提示,是想帮一些师傅一把,还是可以去尝试一下的,但是不知道为啥没有师傅愿意再去做了(可能是觉得题目太傻逼了,这个我得背锅
总结
这道题在出题时,我预期是一道非常简单的题,以至于预期分数是 400 分,但是没想到最后只有 1 解,可能是我在某些方面考虑欠妥,没有给予足够的提示和引导
此题的源码会在我的 GitHub 上调整为 Public,官方应该也会放出源码
最后,还是感谢师傅们愿意做我的题
发表评论
沙发空缺中,还不快抢~