PHP コード監査 18 — PHP コード監査の概要


序文: コード監査の学習期間を経て、ここにコード監査の知識の簡単な概要を示します。その一部は個人的な知識の要約です。コード監査方法の結論をより包括的に要約するために、私はまた、多くの文献を参照します。記事の最後に情報が書かれています。

1. 事前準備

1. ツールの準備

ここでは、私が使用したことがある、またはよく知っているいくつかのツールの概要を示します。

  • PHPコードデバッグツール

    • PHPstorm+xdebug
  • コード読み取りツール

    • メモ帳++
    • キュブリムTXT 3
  • 静的コード監査ツール

    • 強化する

      HP出的一款静态代码审计工具,支持21种开发语言,是一款功能强大的商业代码审计工具。
      
    • シーイ

      Seay大佬协议的代码审计工具,国产、开源。
      
    • コードQl

      Githubt推出的代码审计项目,在国外受到众多安全研究着追捧。
      
    • Xcheck

      Xcheck的php引擎支持原生php的安全检查,也支持对国内主流框架编写的web应用进行安全检查,覆盖包括Thinkphp,Laravel,CodeIgniter,Yii,Yaf等web框架。
      
    • リプス

      轻量级的代码审计工具,来源于国外专业的代码审计公司RIPS。但是RIPS的开源版本已经多年未更新,并且不支持面向对象的代码审计。
      
  • ブラウザとプラグイン

    • ブラウザ: ファイアフォックス
    • ブラウザプラグイン: HackBar、FoxyProxy、EditThisCookie
  • データベース管理ソフトウェア

    • プミャドミン
    • ナビキャット

2. 監査環境の準備

  • オペレーティング·システム

    一般采用Windows系统进行审计,对于大多数审计工具来说,Windows系统下图形化界面在使用的时候会更加方便。
    
  • PHP統合環境

    • php勉強
    • パゴダパネル
  • PHPのバージョン

    对于PHP版本的选择,为了使用实际使用环境,应该尽量使用php5.4以后的版本
    
  • データベース

    对于数据库,对于PHP程序来说,一般都是采用的Mysql,在审计过程中,最好将mysql设置为5.7版本以后。
    

2. システムアーキテクチャを理解する

システムアーキテクチャについては、監査対象を確認した後、監査前に、ディレクトリの状況、フレームワークの利用の有無、どのようなルートが存在するか、セキュリティフィルタ機能の有無など、対象システムの基本的なアーキテクチャを理解する必要があります。 、グローバルパラメータフィルタリングがあるかどうかなど。ここでは、フレーム付きとフレームなしに分けて分析する必要があります。

1. 開発フレームワークの使用

1) ThinkPHP フレームワーク

ここでは例として ThinkPHP5 を示します。フレームワークの一般的なディレクトリ構造は次のとおりです。

www  WEB部署目录(或者子目录)
├─application           应用目录
│  ├─common             公共模块目录(可以更改)
│  ├─module_name        模块目录
│  │  ├─config.php      模块配置文件
│  │  ├─common.php      模块函数文件
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录
│  │  ├─view            视图目录
│  │  └─ ...            更多类库目录
│  │
│  ├─command.php        命令行工具配置文件
│  ├─common.php         公共函数文件
│  ├─config.php         公共配置文件
│  ├─route.php          路由配置文件
│  ├─tags.php           应用行为扩展定义文件
│  └─database.php       数据库配置文件
│
├─public                WEB目录(对外访问目录)
│  ├─index.php          入口文件
│  ├─router.php         快速测试文件
│  └─.htaccess          用于apache的重写
│
├─thinkphp              框架系统目录
│  ├─lang               语言文件目录
│  ├─library            框架类库目录
│  │  ├─think           Think类库包目录
│  │  └─traits          系统Trait目录
│  │
│  ├─tpl                系统模板目录
│  ├─base.php           基础定义文件
│  ├─console.php        控制台入口文件
│  ├─convention.php     框架惯例配置文件
│  ├─helper.php         助手函数文件
│  ├─phpunit.xml        phpunit配置文件
│  └─start.php          框架入口文件
│
├─extend                扩展类库目录
├─runtime               应用的运行时目录(可写,可定制)
├─vendor                第三方类库目录(Composer依赖库)
├─build.php             自动生成定义文件(参考)
├─composer.json         composer 定义文件
├─LICENSE.txt           授权说明文件
├─README.md             README 文件
├─think                 命令行入口文件

このうち、アプリケーションディレクトリと弊社パブリックディレクトリは、弊社アプリケーションファイルディレクトリと運用データ格納ディレクトリが重要な注目対象となります。

同時に、アプリケーション ディレクトリ内の config.php、database.php、およびroute.phpは、システム構成ファイル、データベース操作ファイル、およびシステム ルーティング ファイルであるため、注意深く分析する必要があります。

ThinkPHP フレームワークによって開発されたアプリケーションの場合、システムにアクセスするための URL は通常次のようになります。/index.php/模块名/控制器名/函数名/[参数名/参数值]

モジュール名はアプリケーションディレクトリ下のフォルダー名に対応します。コントローラ名は、モジュール名ディレクトリ下のコントローラフォルダ下のphpファイル名に対応します。パラメータはオプションであり、/参数名/参数值メソッドに応じて複数のパラメータを同時に渡すことも、?参数1=参数值1&参数2=参数值2従来の方法で変数を渡すこともできます。

2)laravelフレームワーク

基本的なディレクトリ構造:

 |——app  包含了站点的controllers(控制器),models(模型),views(视图)和assets(资源
 |——bootstrap 存放系统启动时的必要文件,这些文件会被index.php这样的文件调用。
 |——public 系统运行的公开数据,包括静态资CSS文件、js文件等
 |——vender 第三方类库

thinkPHP と同様に、メインのアプリケーション ファイルは app ディレクトリに保存されます。

2. 開発フレームワークは使用しません

開発フレームワークを使用しない場合、アプリケーションが MVC モードを採用しているかどうかを判断する必要があり、MVC モードを採用している場合は、システムのルーティング ファイルを確認して、制御プログラムがルーティングによってどのように配置されているかを確認する必要があります。たとえば、PHPCMS はルーティングに独自に開発された MVC コントローラーを使用します。

Index.php にアクセスすると、phpcms/base.php が含まれていることがわかります。

define('PHPCMS_PATH', dirname(__FILE__).DIRECTORY_SEPARATOR);

include PHPCMS_PATH.'/phpcms/base.php';

Base.php ファイルを入力すると、ここで多数のルートといくつかの初期化されたクラスとメソッドが定義されていることがわかります。

//PHPCMS框架路径
define('PC_PATH', dirname(__FILE__).DIRECTORY_SEPARATOR);

if(!defined('PHPCMS_PATH')) define('PHPCMS_PATH', PC_PATH.'..'.DIRECTORY_SEPARATOR);

//缓存文件夹地址
define('CACHE_PATH', PHPCMS_PATH.'caches'.DIRECTORY_SEPARATOR);
//主机协议
define('SITE_PROTOCOL', isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == '443' ? 'https://' : 'http://');
//当前访问的主机名
define('SITE_URL', (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''));
//来源
define('HTTP_REFERER', isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
//定义网站根路径
define('WEB_PATH',pc_base::load_config('system','web_path'));
//js 路径
define('JS_PATH',pc_base::load_config('system','js_path'));
//css 路径
define('CSS_PATH',pc_base::load_config('system','css_path'));
//img 路径
define('IMG_PATH',pc_base::load_config('system','img_path'));
//动态程序路径
define('APP_PATH',pc_base::load_config('system','app_path'));
//应用静态文件路径
define('PLUGIN_STATICS_PATH',WEB_PATH.'statics/plugin/');
class pc_base {
    
    
  ......
}

フレームワークやカスタム MVC モデルを使用しないプログラムの場合は、従来のスクリプト形式のアクセス方法に従い、index.php をエントリ ポイントとして使用し、関連するコードを順番に分析するだけで済みます。

3. パラメータフィルタリング分析

従来のパラメータ フィルタリング分析は通常、関数の形式でフィルタリングしますが、一部のシステムではパラメータが渡された後にグローバル パラメータ フィルタリングを実行する場合もあるため、グローバル パラメータ フィルタリングを使用するかどうかを監査する前に、アプリケーションでどのフィルタリング方法が使用されるかを知る必要があります。

1. MVCモードでのフィルタリング状況の分析

まず、ThinkPHPの場合、パラメータを取得するにはネイティブ$_GETメソッドで取得する方法と$Request()オブジェクトで取得する方法の2通りがあります。thinkPHP プログラムを監査する場合、パラメータを取得するこれら 2 つの方法を監査する必要があります。

ThinkPHP では、デフォルトのグローバル フィルターが構成ファイルで定義されています。

// 默认全局过滤方法 用逗号分隔多个
'default_filter'         => '',

たとえば、上記では、デフォルトのフィルターは空です。つまり、$_GET元のメソッドを使用してパラメータを取得する場合、フィルタリングされません。

次に、$request オブジェクトがパラメーターを取得する方法を見てみましょう。たとえば、erquest オブジェクトの get メソッドは、GET によって渡されたパラメーターを取得するために使用されます。

public function get($name = '', $default = null, $filter = '')
    {
    
    
        if (empty($this->get)) {
    
    
            $this->get = $_GET;
        }
        if (is_array($name)) {
    
    
            $this->param      = [];
            return $this->get = array_merge($this->get, $name);
        }
        return $this->input($this->get, $name, $default, $filter);
    }

ご覧のとおり、ここでは 3 つの仮パラメータ、つまり name、default、filter が定義されています。このうち、name は取得したい変数の名前、filter は使用するフィルターです。デフォルトでは空です。つまり、フィルター処理は実行されません。

したがって、監査する場合は、デフォルトのグローバル フィルタリング オプションが設定されているかどうか、またはパラメータを取得するときに新しいフィルタが設定されているかどうかを判断する必要があります。

2. ネイティブ PHP モードでのフィルター分析

ネイティブPHPで開発されたアプリケーションの場合、フィルタリングルールが関数で設定されている場合が多いため、パラメータを取得する箇所でパラメータフィルタリング関数が呼び出されてフィルタリングされているかを確認し、有効なフィルタリングであるかどうかを判断する必要があります。

ここでは、Imperial CMS を例として簡単な分析を実行します。Empire CMS のフィルタ機能は、e/class/connect.php に 3 つのパラメータ処理関数が定義されており、ここではそのうちの 1 つを例として分析します。

//参数处理函数
function RepPostVar($val){
    
    
	if($val!=addslashes($val))
	{
    
    
		exit();
	}
	CkPostStrChar($val);
	$val=str_replace("%","",$val);
	$val=str_replace(" ","",$val);
	$val=str_replace("`","",$val);
	$val=str_replace("\t","",$val);
	$val=str_replace("%20","",$val);
	$val=str_replace("%27","",$val);
	$val=str_replace("*","",$val);
	$val=str_replace("'","",$val);
	$val=str_replace("\"","",$val);
	$val=str_replace("/","",$val);
	$val=str_replace(";","",$val);
	$val=str_replace("#","",$val);
	$val=str_replace("--","",$val);
	$val=RepPostStr($val,1);
	$val=addslashes($val);
	//FireWall
	FWClearGetText($val);
	return $val;
}

ここでは、%、スペース、`、%20、%27、'、" などがフィルタリングされていることがわかります。

次に、特定のファイルを入力して、渡されたすべてのパラメータがフィルタリングのためにこの関数を呼び出したかどうかを確認できます。

//审核评论
{
    
    
	$plid=$_POST['plid'];
	$id=$_POST['id'];
	$bclassid=$_POST['bclassid'];
	$classid=$_POST['classid'];
	CheckPl_all($plid,$id,$bclassid,$classid,$logininid,$loginin);
}

function CheckPl_all($plid,$id,$bclassid,$classid,$userid,$username){
    
    
	global $empire,$class_r,$dbtbpre,$public_r;
	//验证权限
	$restb=(int)$_POST['restb'];
	$count=count($plid);
	if(empty($count)||!$restb){
    
    
		printerror("NotCheckPlid","history.go(-1)");
	}
	if(!strstr($public_r['pldatatbs'],','.$restb.',')){
    
    
		printerror("NotCheckPlid","history.go(-1)");
	}
	$add='';
	$docheck=(int)$_POST['docheck'];
	$docheck=$docheck?1:0;
	for($i=0;$i<$count;$i++){
    
    
		$add.="plid='".intval($plid[$i])."' or ";
	}
	$add=substr($add,0,strlen($add)-4);
	$sql=$empire->query("update {
      
      $dbtbpre}enewspl_{
      
      $restb} set checked='$docheck' where ".$add);
	if($sql)
	{
    
    
		....
  }
}

例えばここでは、バックグラウンドでコメントを管理しているところで、コメントIDなどのパラメータを取得し、フィルタリングせずにCheckPl_all()関数を渡しているのですが、ここのパラメータはいずれも呼び出しによって処理されていないことがわかります。フィルタ関数ですが、幸いなことに、これらのパラメータにはすべて int キャストが使用されました。比較的安全です。

4. 一般的な脆弱性監査方法の概要

1.SQLインジェクション

まず、一般的なビジネス シナリオと SQL インジェクションの脆弱性の種類を理解する必要があります。

  • ユーザーログイン - ユニバーサルパスワード
  • データ検索 - 検索インジェクション
  • HTTPヘッダーを取得する
  • 商品の購入 - 挿入、更新挿入など。
  • 情報クエリ - ユニオンジョイントインジェクション、エラーインジェクションなど

SQL インジェクションはデータベースと対話するあらゆる場所に存在する可能性があり、その脆弱性にはさまざまなシナリオがあるため、ここでは良い例ではありません。

コード監査におけるSQLインジェクション監査手法では、まずデータベース操作のキーワードに注目します。

たとえば、PHP ネイティブ コードでは、次のキーワードにさらに注意を払うことができます。

select
mysqli_connect
mysqli_query
mysqli_fetch_row
mysqli_fetch_array
update
indert into
delete
.......

CMS またはフレームワークでは、次のキーワードまたはメソッドにさらに注意を払う必要があります。

name()
where()
find()
select()
.....

これらのキーワードを見つけることで、SQL 文が実行される場所を特定し、SQL 文内のパラメータが SQL を継ぎ合わせて実行されるかどうか、実行される場合にはパラメータのフィルタリングがあるかどうか、およびパラメータが制御可能かどうかを判断できます。

パラメータが制御不能で、特定の SQL ステートメントのクエリ結果に由来する場合、パラメータが他のユーザー入力に由来するかどうかに焦点を当てる必要があり、そうである場合は、マスターの二次インジェクションの状況を考慮する必要があります。

2. XSSの脆弱性

XSS の脆弱性の焦点は、次のようないくつかの出力関数です。

print()
echo 
print_f()
die()
var_dump()
print_r()
......

次に、出力コンテンツに制御可能な変数があるかどうかを判断し、それらの変数が入出力時に Html エンティティ エンコーディングを使用しているかどうか、または入力がフィルターされているかどうかを検出します。何もない場合は、出力コンテンツが制御可能な場合に XSS 脆弱性が存在する可能性があります。

3. コードの実行

ここでのコード実行には、コード実行とコマンド実行の 2 つの部分が含まれます。同様に、次のような注目すべき機能がいくつかあります。

代码执行:
eval()
assert()
preg_replace()
array_map()
call_user_funcn()
.....
命令执行:
system()
exec()
shell_exec()
passthru()
popen()
proc_open()
.....

コード実行関数の場合、eval() 関数は最も一般的なコード実行関数であるため、注意が必要です。

assert()関数はPHPのevalに似ていますが、1行のコードしか実行できませんが、PHP7ではこの関数の動的コードを実行する機能がキャンセルされ、固定コードが実行されます。

他のコード実行関数 (そのほとんどはコールバック関数) には、php コードを呼び出す機能があります。

コマンド実行の脆弱性については、主にコマンドの実行によりシステム情報を取得するために一部の箇所に存在しており、実行されたコマンドが制御可能な場合、コマンドの実行につながる可能性があります。

コマンドの実行方法にはいくつかありますが、いくつかの違いがあります。

  • system() はシステムコマンドを実行し、実行結果を返します。
  • exec() はコマンドの実行後に結果ハンドルを返しますが、結果を直接返しません。
  • shell_exec() は、コマンドの実行後に情報を返しません。

{}ただし、コードの実行とコマンドの実行については、PHPではメソッドを通じてコードを実行することもできると同時に、コードを動的に結合することによって PHP コードを実行することもできることに注意してください二重バッククォートを使用することで、システムのコマンド実行効果を待つこともできます。

4. ファイルのアップロード、削除、ダウンロード

1) ファイルアップロードの脆弱性

一般的なビジネス シナリオ:

  • アバターのアップロード
  • バックアップファイルのアップロード
  • 設定ファイルのアップロード

ファイルアップロードの脆弱性に関して、注意が必要な関数は move_uplaod_file() 関数です。

監査する場合は、Osso 関数を検索するだけで、ファイルのアップロード形式を制限するかどうか、またはファイルのサフィックス制限を回避できるかどうかを判断できます。

バイパスできない場合は、ファイル解析の脆弱性またはファイルのインクルードの脆弱性の有無を検出してバイパスする必要があります。

2) ファイル削除の脆弱性

ファイル削除の脆弱性については、脆弱性を引き起こす関数は unlink() 関数のみであり、監査の際にはファイル名が制御可能かどうかを判断すると同時に、パスをまたぐ文字が許可されているかどうかを確認します。ヒットしたファイルに、それが許可されている場合、任意のファイル削除の脆弱性の方が有害であることを意味します。

3) ファイルダウンロードの脆弱性

ファイル読み取りまたはファイルダウンロードの脆弱性、脆弱性を引き起こす一般的な機能は次のとおりです。

file_get_content()
fopen()
readfile()
fread()
file()
......

この種の脆弱性の場合、ブラック ボックスの場合、まずファイルの読み取りとダウンロードの機能ポイントを見つけ、次に要求された URL を通じてその機能に対応する特定のコードを解析し、ファイル名が適切かどうかを判断します。読み取りは有効です。ファイル名などにパスをまたぐ文字を許可するかどうかを制御します。

抜け穴がある場合は、しばらくシステムファイルを読む方法を使用しますが、Webサイトのソースコードを読むときは、php疑似プロトコルを使用してソースコードを読み取ることができます。

5. XXE の脆弱性

XXE 脆弱性の場合、注意すべき関数は simplexml_load_string() で、解析された XML データが外部から入力できるかどうか、外部エンティティを置き換えることができるかどうかを判断し、詳細な分析と活用を行う必要があります。

6. SSRFの脆弱性

SSRF 脆弱性のトリガー関数は主に、リモートでリソースを要求するために使用できる次のような関数です。

file-get_content()
curl()
fopen()
readline()
......

一般的な脆弱性シナリオは次のとおりです。

  • ソーシャルシェアリング
  • トランスコーディングサービス
  • オンライン翻訳
  • imタグを使用しないリモート画像の読み込み

検出方法は主に、これらの機能で取得したURLやファイル名が制御可能かどうかを判断するものですが、制御可能であれば抜け穴が発生する可能性があり、また、前述した任意のファイル読み取りの抜け穴も発生しやすくなります。

V. 関連する監査スキルの概要

1. GPC の抜け穴を利用する

まず、GPC について簡単に紹介します。magic_quotes_gpc は php.ini の設定オプションです。php5.2 より前のバージョンではデフォルトで有効になっており、php 5.2 から 5.4 までのバージョンではデフォルトで無効になっています。PHP 5.4 以降のバージョンでは、この設定項目は直接キャンセルされます。

'この構成項目の機能は、 で渡す POST、GET、および COOKIE 変数の前にある 、"などの文字をエスケープすることです。\NULL\

PHP では、カーキ GPC を使用する方法が 2 つあります。1 つは、PHP の magic_quotes_gpc オプションの値を設定する方法で、エスケープするためにlashes() 関数を追加します。例えば:

if (!get_magic_quotes_gpc()) {
    
      //magic_quotes_gpc 配置为 ON 则返回1,如果配置为OFF 则返回0
	$_POST['message'] = addslashes($_POST['message']);
} else {
    
    
	.......
}

PHP では、POST などの変数が GPC のグローバル設定の影響を受けると説明しましたが、影響を受けない変数、つまり取得した http ヘッダーが GPC によって保護されていない $_SERVER があります。取得したHTTP データベース連携の動作にヘッダーが存在すると、SQLインジェクションが発生する可能性があります。

同時に、GPC は特殊文字をエスケープするために使用されるため、ワイドバイト インジェクションの脆弱性の原理は誰もが知っています。GPC が有効でデータベース エンコーディングが GBK モードに設定されている場合、ワイドバイト インジェクションが発生する可能性があります。 。PHP でデータベース エンコーディングを設定するには 2 つの方法があります。

方法1mysqli_set_charset($connnect,'GBK')
方法2mysqli_query("set names 'gbk'")

これら 2 つの方法はデータベースのエンコード モードを設定できますが、GPC が有効になっている場合、ワイド バイト インジェクションの問題が発生しやすくなります。

2. キャラクターを使って問題に対処する

1) 文字処理機能を使用してエラーを報告する

この問題を理解する前に、PHP ではエラー メッセージが複数のレベルに分割されていることを知っておく必要があります。PHP.ini で display_error =on を設定するか、コード内で error_reporting() を使用してエラー レベルを設定できます。一般的なエラー レベルは次のとおりです。以下に続きます:

1E_ERROR             //致命的运行时错误。这类错误一般是不可恢复的情况,例如内存分配导致的问题。后果是导致脚本终止不再继续运行。
2E_WARNING          //运行时警告 (非致命错误)。仅给出提示信息,但是脚本不会终止运行。
3E_PARSE            //编译时语法解析错误。解析错误仅仅由分析器产生
4E_NOTICE           //运行时通知。
5E_USER_ERROR       //用户产生的错误信息。
6E_USER_WARNING     //用户产生的警告信息。
7E_USER_NOTICE
8E_STRICT           //启用 PHP 对代码的修改建议,以确保代码具有最佳的互操作性和向前兼容性。
9E_ALL              //E_STRICT除外的所有错误和警告信息。

PHP では、ほとんどのエラーで間違ったファイル パスと集合的な場所が表示されます。ペネトレーション テストでは、Web シェルへのアップロードや書き込みの状況によく遭遇し、Web サイトの絶対パスを知る必要があります。現時点では、PHP の使用を検討できます。エラーを報告して Web サイトのパスを取得します。

PHP プログラムの場合、ほとんどの開発者は、trim() 関数を使用してパラメータの先頭と末尾の空調を削除しますが、渡すパラメータが配列の場合、たとえばスペースを削除するために使用される場合は、この時点で a パラメータを指定する/index.php?a[]=testと、trim($_GET('a'))プログラムがエラーを報告します。

次のような同様の関数が多数あります。

addcslashes()
bin2hex()
chr()
exho()
explode()
crypto()
md5()
..........

2) 文字列の切り詰めを利用する

文字列の切り捨てはファイルをアップロードするときに最もよく使用されますが、%00 の空文字は NULL であるため、GPC がオンになっている場合は影響を受け、通常は使用できません。同時に、PHP5.3 以降ではこの問題が修正されているため、使用シナリオは現在に比べて比較的まれです。ここで簡単に言及します。

ただし、文字列が切り捨てられる別のケースもあります。つまり、iconv() 関数がエンコード変換に使用される場合、たとえば、UTF-8 リングが GBK としてエンコードされる場合、常に何らかの差異が生じ、結果として文字化けが発生します。会話。そのため、iconv()関数を使用して変換した場合、エラーが発生すると変換が行われず、文字列が切り捨てられてしまう問題が発生します。

ある偉い人が関連する問題をテストし、iconv() 関数を使用して UTF-8 エンコーディングの准尉 GBK をエンコードするときに、cahr(128) から chr(255) までの文字が切り捨てられるかどうかを確認しました。

3. 不規則な正規表現

たとえば、次のような状況です。

$ip=$_SERVER('HTTP_CLIENT_IP');
if(preg_match('/\d+\.\d+\.\d+\.\d+/',$ip)){
    
    
	echo $ip;
}

ここで client_ip を次のように渡すと、 127.0.0.1</scritp>alert(xss)</scritp>

定期的なチェックに合格し、IP 情報が出力されるため、XSS 脆弱性が発生します。

5. 参考文献

おすすめ

転載: blog.csdn.net/qq_45590334/article/details/126517767