这是关于php代码审计的简单学习笔记,转移自有道笔记(MyDo->A学习->PHP->代码审计)
审计准备
- 获得源码:开源程序等等
- 本地搭建软件:本地搭建审计调试,跟踪动态变化
大致信息
- 网站结构
- 入口文件:
index.php
…可以知道程序架构,运行流程,包含得配置文件,包含的过滤文件,业务逻辑 - 配置文件:config.php等,保存数据库,程序相关得信息.数据库编码(如果是gbk可能存在款字节注入),变量用双引号则可能存在双引号解析代码执行.
- 过滤功能:
公共函数文件
安全过滤文件
掌握用户得输入哪些被过滤若何过滤,能否绕过过滤.
审计方法
通读全文:全文阅读
敏感函数参数回溯:逆向追踪参数传递过程.
定向功能分析
常见ini配置
- php.ini
- .user.ini 类似apache 得.htaccess
关闭全局变量
register_globals=off
设置为on得时候会将$_POST
$_GET
$_COOKIE
等中得$key=>$value直接注册为变量.引起大量安全问题.
短标签
short_open_tag=off
决定是否运行使用php代码开始标志得缩写形式(<??>).可以用于一句话变形
安全模式
safe_mode=off
禁用很多危险函数,默认关闭,自php5.3.0废弃.
安全模式下执行程序主目录
禁用类/函数
disable_classes
disable_function
接受都好分割得函数名列表作为参数.只能设置再php.ini中
设置上传以及文件大小
file_uploads
upload_max_filesize
上传文件临时目录
upload_tmp_dir
临时目录需要写入权限,如果不设置则使用系统得临时目录(/tmp,c:\windows\temp)
用户访问目录限制
open_basedir=.:/tmp/
(linux用冒号分割,windows用分号分隔)
一半设置为只能访问当前就目录(php脚本文件所在目录)和/tmp/目录,有效防止木马跨站运行.
错误信息控制
display_error
是否将错误信息作为输出得一部分,生产模式必须关闭,调试模式需要打开.
设置错误报告级别
error_reporting=E_ALL
这个参数,级别设置为最高,显示所有问题,方便查错.
错误日志
error_log
错误日志得位置,必须对web用户可写,默认写入到web服务器得错误日志中.
log_errors_max_length
日志关联信息最大长度,0代表武线长度
魔术引号
效率降低,5.3.0废弃magic_quotes_gpc=on
所有的单双引号,反斜杠,null被一个反斜杠自动转译magic_quotes_runtime=off
php的大部分函数自动的给从外部引入的(包括数据库或者文件)数据中的溢出字符加上反斜线
是否允许打开远程文件
allow_url_fopen
是否激活url形式得fopen封装协议使得可以访问url对象文件,默认的封装协议提供ftp和http协议访问.默认打开
是否允许包含远程文件
allow_url_include
是否允许include,include_once,require,require_once等函数使url形式得fopen封装协议.包含远程文件.默认关闭
常见危险函数和特殊函数
代码执行函数
eval/assert
mixed eval(string $code)
把字符串作为php代码执行
例如
1 | <?php @evla($_POST['haha']);?> |
bool assert(mixed $assertion [,string $description])
检查断言是否为false(把字符串$assertion作为php代码执行)
大多数杀软把eval列入黑名单,用assert替代.
preg_replace
执行正则表达式执行搜索和替换
mixed preg replace(mixed $pattern,mixed $replacement,mixed $subject[,int $limit=-1[int&$count]])
/e 修正符使preg_replace()将 replacement参数当作PHP代码
preg_replace(”/test/e”,$_GET[“h”],”jutst test”);如果我们提交?h=phpinfo),phpinfo0将会被执行
create_function
创建一个匿名函数,返回第一无二得函数名
string create_function(string $args,string $code)
创建一个匿名函数,并返回独一无二的函数名。
$newfunc=create_function($v’,’return system($v););
$newfunc(whoamif);就相当于system(whoami1);
回调函数
mixed call_user_func(callable $callback [,mixed $parameter L,mixed $..])
第一个参数callback是被调用的回调函数,其余参数是回调函数的参数。
mixed call_user_func array(callable $callback,array $param_arr)
把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入。
包含函数
require,include,require_once,include_once
命令执行函数
- exec() 执行一个外部命令
- passthru() 执行外部程序并且显示原始输出
- shell_exec() 通过shell环境执行命令并且将完整得输出以字符串方式返回.
- system() 执行外部程序并显示输出
- popen() 通过参数传递一条命令,并对popen()所打开得文件进行执行.
执行函数包括但不限于上述几个。
同样的道理、只要命令的参数可控就能够执行系统命令。
例如:
system(Scmd);或者system(ping-c3’.Starget);当Scmd可以空就能执行任意命令,而当Starget可控的话,可以用管道符等特殊字符截断从而执行任意命令。
Starget=’a l whoamit;
文件操作函数
- copy-拷贝文件
- file_get_contents-将整个文件读入一个字符串file_put_contents-将一个字符串写入文件file-把整个文件读入一个数组中
- fopen-打开文件或者URL - move_uploaded_file-将上传的文件移动到新位置readfile-输出文件
- rename—重命名一个文件或目录
- rmdir-删除目录
- unlink&delete—删除文件
…
任意文件读取、写入、删除往往是上面几个函数受到了控制(当然还有其他的函数)。
不同的函数在不同的场景有不同的作用和不同的利用手法。
读取:可以读取配置等文件,拿到key写入:可以写入shel代码相关的内容
删除:可以删除.lock文件而可以重新安装覆盖
特殊函数
- 信息泄漏
bool phpinfo([int $what=INFO_ALL])
输出PHP当前状态的大量信息,包含了PHP编译选项、启用的扩展、PHP版本、服务器信息和环境变量(如果编译为一个模块的话)、PHP环境变量、操作系统版本信息、path 变量、配置选项的本地值和主值、HTTP头和PHP授权信息(License)。 - 软连接-读取文件内容
bool symlink(string $target,string $link)
symlink)对于已有的target 建立一个名为link的符号连接。
string readlink(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_restore(string $varname)
恢复指定的配置选项到它的原始值。 - 数字判断
bool is_numeric(mixed $var)
如果 var是数字和数字字符串则返回 TRUE,否则返回 FALSE。
仅用is_numeric判断而不用intval转换就有可能插入16进制的字符串到数据库,进而可能导致sql二次注入。 - 数组相关
bool in array(mixed $needle,array $haystack L,bool $strict=FALSE])
在haystack 中搜索 needle,如果没有设置 strict 则使用宽松的比较。
该函数有一个特性,比较之前会进行自动类型转换。
$a=’1abc’;in_array($a,array(1,2,3))的返回值会是真 - 变量覆盖
void parse_str(string $str[array&$arr])
如果str是URL传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域。
int extract(array&$var array I,int $extract type=EXTR_OVERWRITE L,string $prefix=NULL]])
本函数用来将变量从数组中导入到当前的符号表中。检查每个键名看是否可以作为一个合法的变量名,同时也检查和符号表中已有的变量名的冲突。
bool mb parse str(string $encoded_string L,array&$result])
解析GET/POST/COOKIE数据并设置全局变量。由于PHP不提供原始 POST/COOKIE数据,目前它仅能够用于GET数据。它解析了URL编码过的数据,检测其编码,并转换编码为内部编码,然后设置其值为array的result或者全局变量。
bool import request variables(string $types L,string $prefix])
将GET/POST/Cookie变量导入到全局作用域中。如果你禁止了register_globals,但又想用到一些全局变量,那么此函数就很有用。 - 列目录
array glob(string $pattern L,int $flags=0])
glob0函数依照 libc glob0函数使用的规则寻找所有与pattern匹配的文件路径,类似于一般shells所用的规则一样。不进行缩写扩展或参数替代。 - 无参数获取信息
array get defined_vars(void)
返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。
array get defined_constants([bool $categorize=false])
返回当前所有已定义的常量名和值。这包含define0函数所创建的,也包含了所有扩展所创建的。
array get_defined_functions(void)
返回一个包含所有已定义函数列表的多维数组
array get_included_files(void)
返回所有被include、include_once、require和require_once的文件名。
XDebug
Xdebug是一个开放源代码的PHP程序调试器(即一个Debug工具)可以用来跟踪,调试和分析PHP程序的运行状况功能强大的神器,对审计有非常大的帮助。
配置-设置选项
日志
xdebug.trace_output_dir
-日志追踪输出目录xdebug.trace_output_name日志文件名,xdebug提供了一系列的标识符,生成相应格式的文件名,具体请参考官网xdebug.trace_options记录添加到文件中方式:1=追加(如果存在该文件).0(default)=覆盖(如果存在该文件)
显示数据
xdebug.collect_params
非零值=控制function的参数显示选项
- 0=不显示.
- 1=参数类型,值(例如:array(9)).
- 2=同上1,只是在CLI模式下略微有区别
- 3=所有变量内容
- 4=所有变量内容和变量名(例如:array(0=>9)).
xdebug.collect_return
1=显示function返回值.Default0不显示
xdebug.collect_vars
1=显示当前作用域使用了哪些变量,显示变量名,该选项不会记录变量的值xdebug.collect_assignments
1=添加一行显示变量赋值(若为1,形如$a=1;这类Assignment Expression会在race文件里显示)
格式
xdebug.trace_format
-日志追踪输出目录
- 0=人可读.从左至右每列分别表示:时间点,内存,内存差(需要设置xdebug.show_mem_delta=1),等级,函数名,函数参数(需要设置,xdebug.collect_params=1,只要是非零),当前代码行所在文件名,行号.
- 1=机器可读[1].需要借助第三方app,例如:xdebug trace file parser 或者xdebug trace viewer
- 2=html格式即table,用browser打开,显示table
行为
追踪方式有2种,一种是自动追踪,所有php脚本运行时,都会产生trace文件;另一种是触发方式追踪。xdebug.auto_trace
1=打开自动追踪。
xdebug.trace_enable_trigger
注:该特性只在2.2+版本才能设置1=使用XDEBUG_TRACE GET/POST触发追踪,或者通过设置cookie XDEBUG_TRACE.为了避免每次请求时,都会生成相应trace追踪文件,你需要把auto_trace设置为0
其他配置选项请自行研究