代码审计(code audit)是一种通过分析源代码来发现程序错误、安全漏洞和违反程序规范的技术。它属于防御性编程的一部分,旨在减少程序发布前的错误。
C和C++源代码是最常见的审计对象,因为其他高级语言如python,其底层实现依赖于c语言,经过处理后被Python封装,易受攻击的功能较少(例如,Python几乎不需要考虑边界检查函数的漏洞)。
代码审计的对象包括但不限于对windows和linux系统下的以下语言进行审核:Java,C,C#,ASP,php,jsp,.NET。
本系列代码审计文章主要针对PHP语言展开,第一课首先为大家介绍一些PHP基础知识。
PHP网页工作原理
立即学习“PHP免费学习笔记(深入)”;
要学习PHP代码审计,首先需要了解PHP网页的组成部分。
- 语言网页类型:网页中有动态语言和静态语言,具体区别如下表所示。
动态语言 | 静态语言 |
---|---|
PHP语言 | html,文本文件,图片文件 |
需要专门的解释器才能被服务器识别 | 可以被服务器(如nginx、apache等Web服务器)直接识别 |
对每个客户端的返回可能不一样 | 返回固定 |
因此,引入了FastCGI,实现了master进程和worker进程常驻,优化了效率。
Web服务器和CGI接口通过一些环境变量向CGI程序传递重要参数。以下是一些常用的CGI环境变量:
变量名 | 描述 |
---|---|
CONTENT_TYPE | 指示所传递信息的MIME类型。通常为application/x-www-form-urlencoded,表示数据来自HTML表单。 |
CONTENT_LENGTH | 如果服务器与CGI程序信息的传递方式是POST,这个环境变量的值表示从标准输入STDIN中可读的有效数据的字节数。在读取输入数据时必须使用。 |
http_Cookie | 客户机内的COOKIE内容。 |
HTTP_USER_AGENT | 提供包含版本号或其他专有数据的客户浏览器信息。 |
PATH_INFO | 表示紧接在CGI程序名之后的其他路径信息,常作为CGI程序的参数出现。 |
QUERY_STRING | 如果服务器与CGI程序信息的传递方式是GET,这个环境变量的值表示所传递的信息,信息跟在CGI程序名之后,两者中间用一个问号’?’分隔。 |
REMOTE_ADDR | 发送请求的客户机的IP地址。例如上面的192.168.1.67。这个值总是存在的,是Web客户机需要提供给Web服务器的唯一标识,可用于区分不同的Web客户机。 |
REMOTE_HOST | 包含发送CGI请求的客户机的主机名。如果不支持查询,则无需定义此环境变量。 |
REQUEST_METHOD | 提供脚本被调用的方法。对于使用HTTP/1.0协议的脚本,仅GET和POST有意义。 |
SCRIPT_FILENAME | CGI脚本的完整路径 |
SCRIPT_NAME | CGI脚本的名称 |
SERVER_NAME | 你的WEB服务器的主机名、别名或IP地址。 |
SERVER_SOFTWARE | 包含了调用CGI程序的HTTP服务器的名称和版本号。例如,上面的值为Apache/2.2.14(unix) |
- 服务器:不同服务器的特点、优点和缺点如下表所示。
Web Server | 特点 | 优点 | 缺点 |
---|---|---|---|
Nginx | 基于事件驱动 | 性能、负载均衡,稳定性高,支持热部署 | / |
Apache | 基于进程驱动 | 支持几乎所有平台,组件多 | 系统压力大,不支持热部署 |
iis | 最适合ASP.NET、ASP | 产品相较成熟 | 只能运行在Windows平台 |
PHP核心配置
代码在不同环境下执行结果会有不同,不同版本会导致指令的变更,因此代码审计需要熟悉各个版本配置文件的核心指令,以达到以下两个基本目的:
- 扩展审计过程中的攻击面(访问目录,访问输出内容,数据过滤,文件处理范围,函数调用等)。
- 方便审计过程中调试和信息的输出(display_errors, error_reporting控制错误信息显示)。
两个主要的PHP配置文件:
- php.ini:PHP配置中的几种模式及其含义如下:
模式 | 含义 |
---|---|
PHP_INI_USER | 可在用户脚本(例如ini_set())或Windows注册表以及.user.ini中设定 |
PHP_INI_PERDIR | 可在php.ini, .htAccess或httpd.conf设定 |
PHP_INI_SYSTEM | 可在php.ini或https.conf中设定 |
PHP_INI_ALL | 可在任何地方设定 |
- .user.ini:用户自定义的小型php.ini,会影响到PHP_INI_USER, PHP_INI_PERDIR模式下的配置。除了主php.ini之外,PHP还会在每个目录下扫描.user.ini文件,且.user.ini能被动态加载,修改后不需要重启服务器。
注意,PHP的核心配置项不一定是在php.ini中设置的。
审计中常涉及的配置:
-
register_globals(在PHP5.4.0被移除):隐患是直接将用户通过GET、POST提交的参数注册为全局变量,并初始化值为参数对应的值,使参数可以在脚本中直接使用。
-
magic_quote_gpc(PHP 5.4后被取消):开启后会对GET、POST、Cookie变量中的单引号、双引号、反斜杠以及空字符(NULL)前面加上反斜杠。隐患是若在代码层再次对单引号等进行特殊转换过滤,可能导致过滤失效。
-
magic_quotes_runtime:与magic_quotes_gpc相同,是在单引号、双引号、反斜杠以及空字符前面加反斜杠,区别是magic_quotes_runtime是对从数据库或文件中获取的数据进行过滤。隐患是当该项开启时,若在代码层再次对单引号等进行特殊转换过滤,可能导致过滤失败。
-
magic_quotes_sybase(PHP5.4后被取消):当magic_quotes_gpc也开启时,会覆盖掉magic_quotes_gpc的配置,同样对GET、POST、Cookie进行处理,但仅仅是把转移空字符和单引号变成双单引号(用单引号来转义单引号)。
- Magic_quotes_sybase=0时,addslashes将对” ‘ 进行转义操作;
- Magic_quotes_sybase=1时,addslashes将对’进行”转义操作。
隐患是当该项与magic_quotes_gpc同时开启时,可能导致参数意外闭合。
实例如下:
注意,%27经URL解码后为’
-
safe_mode(PHP5.3以上的版本弃用):PHP的安全模式是一个非常重要的内嵌安全机制,能够控制一些PHP中的函数是否能够正常使用,比如system(),同时对很多文件操作函数进行权限控制,也不允许对某些关键文件进行操作,比如/etc/passwd,除非使用safe_mode_include_dir和safe_mode_exec_dir指定一个可被包含和存放了外部脚本的安全目录。
隐患是当该项开启时,如果本身敏感操作的拼接为双引号,可能导致参数意外闭合。
-
allow_url_include、allow_url_fopen:使include()、include_once()、require()、require_once()都可以引入URL类型协议,允许其包含远程文件。
- allow_url_fopen:是否允许将URL(如http://或ftp://)作为文件处理。
- allow_url_include:是否允许include/require打开URL(如http://或ftp://)作为文件处理。
隐患是从PHP5.2开始allow_url_include就默认为Off,而allow_url_fopen一直是On的。在文件包含漏洞中,会产生远程文件包含的动作,增加攻击面。
-
open_basedir:open_basedir是PHP设置中为了防御PHP跨目录进行文件(目录)读写的方法,所有PHP中有关文件读、写的函数都会经过open_basedir的检查。将PHP所有能打开的文件限制在指定的目录树,包括文件本身。
本指令不受安全模式打开或关闭的影响。
隐患是虽然在PHP5.3以后很少有能够绕过open_basedir读写文件的方法,但是有很多可以绕过open_basedir的限制对其进行输出目录的绕过方法。
-
disable_functions:可以禁止一些敏感函数的使用。
一个目前推荐的禁用函数列表有:
函数 作用 dl() 载入指定参数的PHP扩展 exec() 执行一个外部程序 system() 执行外部程序,并且显示输出 passthru() 同exec()函数类似,passthru()函数也是用来执行外部命令的 proc_open() 执行一个命令,并且打开用来输入/输出的文件指针 pcntl_exec() 在当前进程空间执行指定程序,可指定参数 shell_exec() 通过shell环境执行命令,并且将完整的输出以字符串的方式返回 mail() 发送邮件 imap_open() 打开IMAP流,参数username为用户帐号。参数password为用户的密码 imap_mail() 通过IMAP发送邮件 putenv() 添加setting到服务器环境变量。环境变量仅存活于当前请求期间。在请求结束时环境会恢复到初始状态 ini_set() 设置指定配置选项的值。这个选项会在脚本运行时保持新的值,并在脚本结束时恢复 apache_setenv() 设置Apache子进程环境变量 symlink() 建立符号连接 link() 建立一个硬连接 -
display_errors、error_reporting:在调试PHP时,打开错误信息,设置错误显示级别。
隐患是敏感信息泄露。在审计中,开启错误信息显示可以更加直观地了解数据产生的错误提示,会暴露目录、绝对路径,造成sql注入的错误信息。
-
auto_append_file、auto_prepend_file:自动在文件的尾或头处通过require引入特定的文件。
隐患是通过上传.user.ini可能造成上传白名单扩展名的文件也能getshell。