1 审计方法与步骤
A 审计前的准备
1 获得源码-安装网站(在本地搭建网站,一边审计一边测试,实时跟踪各种动态变化)
2 把我大局
- 网站结构(浏览源码文件夹,了解程序的大致目录)
- 入口文件(index.php,admin.php文件一般是整个文件的入口,详细读一下index.php文件可知道程序的架构,运行流程等)
- 配置文件(一般类似config.php等文件,保存一些数据库相关信息程序的一些信息。先看看数据库编码,如果是gbk则可能存在宽字节注入。如果变量的值是双引号,则可能存在双引号解析代码执行问题)
- (重点)过滤功能-通过详读公共函数文件和安全过滤文件,清晰掌握用户输入的数据,哪些被过滤,哪些无过滤;过滤方式是替换还是正则?有没有GPC?有没有使用addslasher()处理
B 审计方法
- 通读源码(一般是企业对自身产品的审计,对于小型应用也可读一读)—-方法:把握大局,然后根据入口文件进行各个模块的审计
- 敏感函数参数回朔法(shell_exec)—利用Seay法师审计系统,然后可以分析判断敏感函数的上下文,追踪参数源头
(重要)定向功能分析法–主要根据程序的业务逻辑来审计,首先用浏览器逐个访问浏览,看看这套程序有哪些功能。根据相关功能,大概存在哪些漏洞。
常见功能漏洞:(包括但不限于)
初始化安装
站点信息泄露
文件上传,管理
登录认证,权限管理
数据库备份恢复
找回密码
验证码
总结:首先,把握大局,不管什么程序,都要把握大局
- 其次,根据定向功能针对每一项功能进行审计;
- 最后,就是明娜函数参数回溯
2 常见的INI配置
A 配置文件
- php.ini: 在PHP启动时被读取。对于服务器模块版本的PHP,仅在web服务器启动时读取一次
- .user.ini文件:自PHP5.3.0起,php支持基于每个目录的.htaccess风格的INI文件
还可以在httpd.conf中覆盖php.ini的值,以进行更灵活的配置
B 配置文件语法
C 变量相关的配置
php.ini
-
变量相关:
启用全局变量 register_globals = off 作用是关闭自动注册的全局变量,在设置为on的时候,php会将$_POST,$_GET,$_COOKIE,$_ENV,$_SESSION数组总的$key->$value直接注册为变量,比如$_POST[“username”]就会被注册为$username虽然方便了调用,但是有三个问题:
- 不知道变量是哪里来的$_POST的还是$_SESSION来的,非常不方便阅读代码
- 变量之间互相覆盖,引起不必要的麻烦(重点)
安全问题,所以要设置为off
短标签:
short_open_tag = on
这个设置决定是否使用PHP代码开始标志的缩写形式(<??>),若禁用,开始标签必须是完整形式(<?php ?>)
同时会影响到缩写形式<?=,它和<?echo等价,从php5.4.0起,<?=总是可用的
主要在文件上传使用到,若开启我们可以在上传一句话等使用变形D 安全模式的配置
safe_mode = off(默认)
能够控制一些php中的函数,比如system(),同时把很多文件操作函数进行了权限控制,也不允许对某些关键文件的文件,比如/etc/passwd,但是默认的php.ini是没有打开安全模式的
本特性已经在PHP5.3.0起飞起并将PHP5.40起移除禁用类/函数
disable_classes=,disable_functions=,disable_function=opendir,readir,scandir,fopen,unlink
禁用某些类,禁用某些函数。接受函数分隔的函数名列表作为参数。只能设置在php.ini中E 上传文件及目录权限的配置
设置上传及最大上传文件大小
file_uploads = on
upload_max_filesze = 8M
文件上传的临时目录
upload_tmp_dir=
上传临时文件保存的目录,需要可写,如果不设置,则采用系统临时目录(/tmp,C:\Windows\temp)
用户访问目录限制
open_basedir = .:/tmp/ linux下:代表不同目录的分隔;windows下;代表不同目录分割
能够避免PHP脚本访问不应该访问的文件,一定程度上限制了phpshell的危害。我们一般可以设置为只能访问网站目录,表示允许访问当前目录(即PHP脚本文件所在目录)和/tmp/目录,有效防止php木马跨站运行F 错误信息的配置
错误信息控制:
display_error = On
站点发布后应关闭此功能,以免暴漏信息,调试的时候为了输出错误信息,故打开
设置错误报告级别:
error_reporting = E_ALL
这个设置的作用是将错误级别设置为最高,可以显示所有的问题,方便查错,也有利于写出高质量的代码。推荐使用E_ALL|E_STRICT,即所有级别。
错误日志:
error_log=
错误日志的位置,必须对web用户可写入,如果不定义则默认写入到web服务器的错误日志中
log_errors = on
如下所言,建议将错误日志输出到文件,而不知直接输出到前端G 魔术引号及远程文件的配置
魔术引号(本特性已自PHP5.3.0起废弃并将自PHP5.4.0起移除
magic_quotes_gpc = On
gagic_quotes_runtime = Off
为GPC(Get/Post/Cookie)操作设置magic_quotes状态,当magic_quotes为on,所有单引号,双引号,反斜杠和NULL(%00)被一个反斜杠自动转义
是否允许打开远程文件
allow_url_fopen = on
是否允许包含远程文件(include/require)
allow_url_include = false3 常见危险函数及特殊函数
代码执行函数
eval$assert(调试函数,和eval同样有把字符长当做php执行的功能)&preg_replace
- mixed eval(string $code)
把字符串$code作为php代码执行
很多的webshell都是用的eval执行具体的操作<?php @eval($_POST[“0”]);?> - bool assert(mixed $assertion[string $description])
检查一个断言是否为FALSE
因为大多数杀软把eval列入黑名单,故用assert来替代eval来执行具体的操作 - preg_replace($pattern, $replacement, $string)
搜索$string中符合正则规则$pattern的部分,以$replacement替换,返回替换后的内容。
/e修正符使preg_replace()将replacement参数当做php代码包含函数
require,include,require_once,include_once
包含函数也能读取任意文件内容,这需要用到[支持的协议和封装协议]和[过滤器]
例如:利用php流filter读取任意文件
include($_GET[“file”]);
?file=php://filter/convert.base64.encode/resource=index.php命令执行函数
- exec() -执行一个外部程 *
- passthru() - 执行外部程序并显示原始输出 *
- proc_open() - 执行一个命令并打开文件指针用于读取以及写入
- shell_exec() - 通过 Shell 执行命令,并将执行结果作为字符串返回 *
- system() - 允许执行一个外部程序并回显输出,类似于 passthru()。 *
- popen() - 通过popen()参数传递一条命令,并对popen()所打开的文件进行执行
只要命令的参数可控就能执行系统命令
例如:
system($cmd);或者system('ping -c 3'.$target);
当$cmd可以控就能执行任意命令
当$target可控的话,可以使用管道符等特殊函数截断从而执行任意命令
$target = 'a | whoami':
大体的思路就是,先把握大局->针对漏洞有目的的搜索危险函数->定位危险函数所在文件->回溯危险源->找到执行的函数->过滤防护
文件操作函数
- copy -拷贝函数
- file_get_contents - 把整个文件读入为一个字符串
- file_put_contents -将一个字符串写入文件
- file -把整个文件读入一个数组中
- fopen - 打开文件或者URL
- move_uploaded_file -将上传的文件移动到新的位置
- readfile - 输出文件
- rename -重命名一个文件或目录
- rmdir -删除目录
- unlink&delete - 删除文件
任意文件读取写入删除往往是上面几个函数收到了控制
特殊函数
- string getenv(string $varname)
获取一个环境变量 - bool putenv(string $setting)
添加setting到服务器环境变量,环境变量仅存活与当前请求期间,在请求结束时环境就会自动恢复到初始状态
配置相关
- string ini_get(string $varname) 成功时返回配置选项的值
- string ini_set(string $varname,string $newvalue)
- string ini_alter(string $varname,string $newvalue)
设置指定配置选项的值,这个选项会在脚本运行时保持新的值,并在脚本结束时恢复。
<?php
splay_errors = ".(ini_get('display_errors')?'On':'Off');
ini_set("display_errors",0);
echo "\r\n<br/>display_errors = ".(ini_get('display_errors')?'On':'Off');
?>
数字判断
- bool is_numeric(mixed $var)
如果var是数字和数字字符串则返回TURE,否则返回FALSE,如果仅用is_numeric判断而不用inval转换就有可能插入16进制的字符创到数据库中,进而导致sql的二次注入
数组相关
- bool in_array(mixed $needle,array $haystack[,bool $strict = FALSE])
在haystack中搜索needle,若没有设置strict则使用宽松的比较。
该函数有一个特性,比较之前会进行自动类型转换
$a = '1abc'
in_array($a,array(1,2,3)的返回值会是真
变量覆盖
- void parse_str(string $str[,arrary &$arr])
若str是URL传递的查询字符串(query string),则将它解析为变量并设置到当前域。
<?php
//parse_str
$str = "first=value&arr[]=foobar&arr[]=baz";
echo "<pre>";
parse_str($str,$array);
print_r($array);
var_dump(isset($first));//已经复制到数组中,不在当前域
parse_str($str);
var_dump(isset($first));
echo "\$first = $first";
echo "r\n<br />";
echo "\$arr[0]=$arr[0]";
echo "r\n<br />";
echo "\$arr[1]=$arr[1]";
?>
<pre>Array
(
[first] => value
[arr] => Array
(
[0] => foobar
[1] => baz
)
)
bool(false)
bool(true)
$first = valuer
<br />$arr[0]=foobarr
<br />$arr[1]=baz[Finished in 0.1s]
列目录
glob()函数依照libc glob()函数使用的规则寻找所有与pattern匹配的文件路径;
<?php
//glob
echo "<pre>";
print_r(glob("t*.php"));//匹配t开头的
?>