开始涉足代码审计,将自己的一些笔记记录下来!
PHP代码执行函数
eval
Grammar: eval(phpcode)
eval() 函数把字符串按照 PHP 代码来计算。
该字符串必须是‘合法的 PHP 代码’,且必须以‘分号’结尾。
如果没有在代码字符串中调用 return 语句,则返回 NULL。如果代码中存在解析错误,则 eval() 函数返回 false。
<?php
$string = "beautiful";
$time = "winter";
$str = 'This is a $string $time morning!';
echo $str. "<br />";
eval("\$str = \"$str\";"); /*一定要用\转义,不然会出现语法错误 */
echo $str;
?>
/* eval 还可以实现
1.运行普通字符串 eg: eval("echo 'aaa';")
2.运行带<? php开头的字符串
3.运行<? Php开头,?>结尾的字符串
4,运行HTML与PHP混合的字符串
5.从MySQL读取代码(字符串形式),并动态加载
6.运行包含include和request的代码
7.知道运行的过程是否出现异常(但无法捕捉到详细异常信息)
*/
assert
assert函数在php中用于判断一个表达式是否成立。返回bool;
Grammar:
PHP 5
``` bool assert ( [mixed $assertion [, string $description ] )
bool assert ( mixed $assertion [, string $description ] )
PHP 7
``` bool assert ( mixed $assertion [, Throwable $exception ] )
bool assert ( mixed $assertion [, Throwable $exception ] )
mixed
说明一个参数可以接受多种不同的(但不一定是所有的)类型。
例如 gettype() 可以接受所有的 PHP 类型,str_replace() 可以接受字符串和数组。
<?php
$s = 123;
echo assert("is_int($s)");
?>
// 1 [Finished in 0.0s]
从这个例子可以看到字符串参数会被执行,这跟eval()类似。不过eval($code_str)只是执行符合php编码规范的$code_str。
assert的用法却更详细一点。
assert_option()可以用来对assert()进行一些约束和控制;
默认值
ASSERT_ACTIVE=1 //Assert函数的开关
ASSERT_WARNING =1 //当表达式为false时,是否要输出警告性的错误提示,issue a PHP warning for each failed assertion
ASSERT_BAIL= 0 //是否要中止运行;terminate execution on failed assertions
ASSERT_QUIET_EVAL= 0 //是否关闭错误提示,在执行表达式时;disable error_reporting during assertion expression evaluation
ASSERT_CALLBACK= (NULL) // 是否启动回调函数 user function to call on failed assertions
preg_replace +'/e'
preg_replace — 执行一个正则表达式的搜索和替换
Grammar:
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
搜索subject
中匹配pattern
的部分(通常用正则匹配), 以replacement
进行替换。
/e 修正符使preg_replace()将replacement 参数当作php代码执行,但是php5.5.0以及后面的php7都已经不再支持了,用 preg_replace_callback() 代替。
创建函数
Grammar:
string create_function (string $args,string $code)
创建一个匿名函数,但不是真正的匿名函数,因为它返回值为函数名
$args 代表参数,多个参数用逗号分隔,
$code 代表函数体
eg:
func2=create_function("$args1,$args2","return $args1+$args2");
返回值为创建的函数名,是唯一的一个字符串
函数调用
func2(3,5); //返回值为8
创建函数有两种方式:
1.使用function关键字
2.用string create_function (string $args,string $code)
php $cfunc = create_function('$v', 'return system($v);'); $cfunc = ('whoami'); //上下两者创建匿名函数的方式不同,但是结果一样 $cfunc = 'sys'.'tem';//用'.'来拼接 $cfunc = ('whoami');
两种定义函数方式的区别
function定义的函数是在编译阶段生成的
create_function创建的函数是在执行阶段生成的
所以,可以在function定义的函数定义之前或则之后调用它。
callable
call_user_func_array() 和。call_user_func()
<?php
function test($v1){
echo "testing $v1";
}
call_user_func('test','karma');
?>
//输出 testing karma
//若为function test(callable $v1) 强制回调类型,则会出现错误 (why?)
//
array_map
包含函数
include
include如果引入的文件不存在,试图继续往下执行,报一个warning
(如果你不介意之前的内容是否被包含,之后的内容都要执行,就使用include)
Grammar:
include $file;
在变量$file 可控的情况下,我们就可以包含任意文件。
由于配置环境不同,可以分为远程文件和本地文件包含。
包含函数也能读取任意文件内容,需要用到【支持的协议和封装协议】和【过滤器】
eg:用php留filter读取任意文件
include($_GET['file']);
//?file=php://filter/convert.base64-encode/resource=index.php
require
比include更严谨:
require如果引入的文件不存在,报fatal error,不再继续执行.
(如果之前的内容一定要被包含,才允许继续执行之后的代码,就使用require)
Include/require 与 include_once /require_once的区别
_once 会自动判断文件是否已经引入,如果引入,不再重复执行.
即:保证被包含文件只可能被引入一次.
(如果包含的文件里有定义函数,那么被包含的文件只能被包含一次,如果多次包含,就会出现函数重定义的错误,php是不运行函数重定义的,会出现致命错误,之后代码不在运行)
allow_url_include = on
远程文件包含
命令执行函数
exec()
Grammar:
function exec(string $command,array[optional] $output,int[optional] $return_value)
执行一个外部程序
exec 执行系统外部命令时不会输出结果,而是返回结果的最后一行,如果你想得到结果你可以使用第二个参数,让其输出到指定的数组,此数组一个记录代表输出的一行,即如果输出结果有20行,则这个数组就有20条记录,所以如果你需要反复输出调用不同系统外部命令的结果,你最好在输出每一条系统外部命令结果时清空这个数组,以防混乱。第三个参数用来取得命令执行的状态码,通常执行成功都是返回0。
passthru()
Grammar:
function passthru(string $command,int[optional] $return_value)
执行外部程序并且显示原始输出
passthru与system的区别,passthru直接将结果输出到浏览器,不需要使用 echo 或 return 来查看结果,不返回任何值,且其可以输出二进制,比如图像数据。
proc_open()
Grammar:
resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] )
执行一个命令,并且打开用来输入/输出的文件指针
shell_exec()&``
shell_exec() 函数实际上仅是反撇号 (`) 操作符的变体
通过shell环境执行命令,并且将完整的输出以字符串的方式返回。
system()
Grammar:
function system(string $command,int[optional] $return_value)
执行外部程序,并且显示输出
system和exec的区别在于system在执行系统外部命令时,直接将结果输出到浏览器,不需要使用 echo 或 return 来查看结果,如果执行命令成功则返回true,否则返回false。第二个参数与exec第三个参数含义一样。
popen()
Grammar:
popen(command,mode)
通过popen()的参数传递一条命令,并对popen()所打开的文件进行执行。
fcntl_exec()
ob_start()
escapeshellcmd()
// 该函数用于过滤
……
文件操作函数
copy
Grammar:
copy(source,destination)
拷贝文件
file_get contents
Grammar:
file_get_contents(path,include_path,context,start,max_length)
将整个文件读入一个字符串
file_put contents
Grammar:
file_put_contents(file,data,mode,context)
将一个字符串写入文件
file
Grammar:
file(path,include_path,context)
把整个文件读入一个数组中
fopen
Grammar:
fopen(filename,mode,include_path,context)
打开文件或者url
move_uploaded_file
Grammar:
move_uploaded_file(file,newloc)
将上传的文件移动到新的位置
readfile
Grammar:
readfile(filename,include_path,context)
输出文件
rename
Grammar:
rename(oldname,newname,context)
重命名一个文件或目录
rmdir
Grammar:
rmdir(dir,context)
删除目录
unlink & delete
Grammar:
unlink(filename,context)
删除文件
读文件
hightlight_file($filename);
show_source($filename);
print_r(php_strip_whitespace($filename));
print_r(file_get_contents($filename));
readfile($filename);
print_r(file($filename)); // var_dump
fread(fopen(filename,”r”),filename,”r”),size);
include($filename); // 非php代码
include_once($filename); // 非php代码
require($filename); // 非php代码
require_once($filename); // 非php代码
print_r(fread(popen(“cat flag”, “r”), $size));
print_r(fgets(fopen($filename, “r”))); // 读取一行
fpassthru(fopen($filename, “r”)); // 从当前位置一直读取到 EOF
print_r(fgetcsv(fopen(filename,”r”),filename,”r”),size));
print_r(fgetss(fopen($filename, “r”))); // 从文件指针中读取一行并过滤掉 HTML 标记
print_r(fscanf(fopen(“flag”, “r”),”%s”));
print_r(parse_ini_file($filename)); // 失败时返回 false , 成功返回配置数组
……
特殊函数
### 信息泄漏
bool phpinfo ([int $what = INFO_ALL])
输出php当前状态的大量信息
软连接-读取文件内容:
bool symlink (string $target ,string $link)
symlink()对于已有的target建立一个名为link的符号连接
string redline (string $path)
readlink()和同名的c函数做同样的事,返回符号连接的内容
环境变量
string getenv (string $varname)
获取一个环境变量的值
bool putenv (string $setting)
增加setting到服务器环境变量。环境变量仅存活于当前请求期间。在请求结束时环境会恢复到初始状态
加载扩展
bool dl (string $library)
载入指定参数library的php扩展
配置相关
string ini_get (string $varname)
成功时返回配置选项的值
string ini_set (string $varname , string $newvalue)
string ini_alter (string $varname , string $newvalue)
设置指定配置选项。这个选项会在脚本执行时 保持新的值,并在脚本结束后恢复
void ini_numeric (mixed $var)
恢复指定的配置选项到它的原始值
数字判断
Bool is_numeric (mixed $var)
如果var时数字和数字字符串则返回true,否则返回false
仅用is_numeric判断而不用intval转换就有可能插入16进制的字符串到数据库,进而可能导致sql二次注入
数组相关
Bool in_array (mixed. $needle , array $haystack [,bool $strict =false])
在haystack中搜索needle,如果没有设置strict则使用宽松的比较
该函数有一个特性,比较之前会进行自动类型转换:$a = '1abc';
超全局变量
变量覆盖
void parse_str (string $str [,array & &str])
如果str是URL传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域
int extract (array &var_array [,int $extract_type = EXTR_OVERWRITE [, srting $orefix = NULL]])
本函数用来将变量从数组中导入到当前的符号表中。检查每个键名看是否可以作为一个合法的变量名,同时也检查和符号表中已有的变量名的冲突
Bool mb_parse_str ( string $encoded_string [, array. &result ])
解析GET/POST/Cookie 变量导入到全局作用域中。如果你禁止了 register_glovals,但又想用到一些全局变量,那么此函数就很有用
extract()
import_request_variables()
parse_str()
mb_parse_str()
全局变量覆盖:register_globals为ON,$GLOBALS
列目录
array glob ( string $pattern [, int $flags =0])
glob()函数依照libc glob()函数使用的规则寻找所有与pattern匹配的文件路径,类似于一般shells所用的规则一样。不进行缩写扩展或参数替代。
print_r(glob(“”)); // 列当前目录
print_r(glob(“/”)); // 列根目录 print_r(scandir(“.”));
print_r(scandir(“/“));
$d=opendir(".");while(false!==($f=readdir($d))){echo"$f\n";}
$d=dir(".");while(false!==($f=$d->read())){echo$f."\n";}
无参数获取信息
array get_defined_vars (void)
返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户自定义变量
array get_defined_constants ([bool $categorize = false])
返回当前所有已定义的常量名和值。这包含define()函数所创建的,也包含了所有拓展所创建的
array get_defined_functions (void)
返回一个包含所有已定义函数列表的多位数组
array get_included_files (void)
返回所有被include、include_once、require和require_once的文件名
序列化函数
serialize()
unserialize()
ini_set(‘session.serialize_handler’, ‘php_serialize’);
文件上传
move_uploaded_file()
getimagesize() //验证文件头只要为GIF89a,就会返回真
Reference
代码执行函数:
PHP preg_replace() 正则替换所有符合条件的字符串
包含函数:
命令执行函数:
PHP——执行系统外部命令函数:exec()、passthru()、system()、shell_exec()
文件操作函数:
特殊函数: