php代码审计总结(上)

一、审计前的准备

1、核心配置详解
register_globals 全局变量注册开关 PHP5.4.0以下可用
allow_url_include 是否允许远程文件包含 php5.2.0及以后默认off
magic_quotes_gpc 魔术引号自动过滤,GET/POST/COOKIE的变量符号前加\进行转义 php5.4以下版本可用
magic_quotes_runtime 可由程序员从代码处设置开启和关闭,当 magic_quotes_runtime开启时,从数据库或者fread函数读取的字符会进行反斜杠转义,从而避免溢出 php5.4以下版本可用
safe_mode 使用后所有的文件函数会受到限制,通过命令函数执行命令或程序时会报错 php5.4以后版本可用
open_basedir php可访问目录,限制php只访问目录
disable function 禁止一些敏感函数的使用
display_errors & error_reporting 前者是显示脚本内部错误选项,后者是设置错误回显
2、代码审计工具

php的代码审计工具我一般常用的是两个,分别是seay 源码审计工具RIPS 。前者是基于C#开发的工具,后者是基于php开发的审计工具。此处可以下载这两个工具:https://www.freebuf.com/sectool/11126.html
http://rips-scanner.sourceforge.net/

二、代码审计思路

此处的思路借鉴了seay源码审计书中的方法,比较全面。但是其实代码审计还是看个人读代码和理解代码的习惯,根据个人的编程习惯来进行代码审计效率会更高。

1、通读全文

这里的全文通读并不是盲目的从头读所有代码,而且有技巧的全文通读,首先要看代码的大致结构,主目录(查找文件入口点和一些公共函数文件)、配置目录以及文件的创建日期和文件大小等信息,看了这些信息就大概知道核心文件在什么位置了,重点关注一下几个文件:

  • 函数集文件:通常命名中包含function和common等关键字,这些文件里面是一些公共的函数,提供给其他文件统一调用

  • 配置文件:命名包含config关键字,包含web运行的配置选项和数据库相关的配置信息

  • 安全过滤文件:通常命名中有filter、safe、check等关键字,这类文件主要是对参数进行过滤。

  • index文件:index是一个程序的入口文件,所以通常只要读一遍index文件就可以大致的了解整个程序的架构,运行的流程,包含到的文件等。

2、查找可控变量

查找可控变量,正向追踪变量传递的过程,查找可能存在安全漏洞的变量,从变量处发现安全问题。

3、寻找敏感功能点

通读功能点的代码,尤其关注于易出现漏洞的功能点,如:文件上传、留言板、登录认证功能、找回密码等。通过从敏感功能点入手来查找安全问题。

三、漏洞挖掘与利用

1、安装问题

[1] 一般网站会通过生成一个.lock文件确认网站是否安装成功,当lock文件被删除时,网站检测不到该文件则会自动重新安装,这样就会导致数据库内容被覆盖重写。

[2] 当网站不通过lock文件判断结束也没有自动删除文件时,误启动安装向导时,便会造成网站自动安装,数据库被覆盖重写

[3] 当通过lock文件判断结束,若存在,则header到index.php,但是在index文件中并没有进行exit,所以并不会退出。

[4] 通过变量覆盖函数覆盖掉变量$insLockfile,从而使检测file_exist为false便不会退出。

[5] 安装文件的代码逻辑问题。通过直接修改url中的step参数,直接可以跳过部分安装步骤

[6] 在安装完成后将install.php重命名为install.php.bak。由于apache的变量名解析漏洞,bak无法解析时后向上寻找,所以还是将其当做php文件解析,则又会重装。

2、文件上传

首先搜索上传函数move_uploaded_file()寻找上传点

[1] 没有进行后缀名验证或者是仅由客户端代码进行验证,可直接上传

[2] 扩展名黑名单/白名单验证。

is_array()查看字符串是否在字符数组中 strrchr() 查看字符最后一次出现在字符串中的位置,并且截取到直到字符串末尾 trim()去掉字符两边的空格 $_FILES['upfile']['name'] 客户端文件的原名称;$_FILES['upfile']['type']文件类型;$_FILES['upfile']['tmp_name']文件存储服务器后存储的临时文件名

绕过方式:

  • 大小写绕过

  • 扩展名绕过(黑名单内没有的)如:Php: php3 phtml

  • 利用windows特性。(如果没用trim可以给后缀名加空格)(upload.php::$DATA的方式)(小数点绕过upload.php.)

  • 截断绕过 %00(验证了上传后缀但是文件名不重命名,upload.php%00.jpg)

  • 解析漏洞(apache:upload.php.xxx)(nginx:upload.jpg/1.php)

  • .htaccess文件。通过.htaccess文件调用php解析器去解析一个文件名中只要包含'haha'这个字符串的任意文件,无论扩展名是什么(没有也行),都以php的方式来解析

  • .user.ini文件。只要在.user.ini中添加auto_prepend_file=aa.jpg
    这句话,就可以让其他php文件执行前自动包含aa.jpg,和require()类似。

[3] MIME验证,验证文件头的content-type,可通过抓包修改

[4] 文件幻数和文件相关内容验证
绕过方式

  • 在文件首部加上如下幻数即可绕过

    JFIF    FF D8 FF E0 00 10 4A 46 49 46
     GIF89a  47 49 46 38 39 61
     PNG     89 50 4E 47
  • 文件相关内容检测通常用的是getimagesize(),只需要在幻数上添加一些文件信息即可绕过

[5]上传路径可控

3、文件包含

文件包含常用函数:

include() include_once() require() require_once() ......

文件包含分为本地文件包含和远程文件包含。

[1] 本地文件包含一般都限制了包含的后缀名,比如:$a.’.php’,可以通过各种截断进行绕过
绕过方式

  • %00截断(gpc off && php <5.3.4)

  • 长文件名截断 (windows和linux长度不同)

  • 转换字符集截断(iconv(),使用iconv处理文件名时,若编码错误则会导致截断)

  • 利用PHP协议绕过(使用zip或者phar绕过,phar协议字php5.3.0开始有效)(data:php5.2以后版本)

新建shell.php代码内容:

<?phpinclude 'phar://test.rar/test.txt';?>

新建test.txt里的内容:

<?phpphpinfo();?>

则运行shell.php文件时,运行结果为phpinfo的内容。

[2]远程文件包含,可包含远程服务器的文件

  • allows_url_include 为on的情况下可以实现远程文件包含,默认为off

  • 可用?进行截断,不受gpc和版本控制

  • 可使用协议php://input 和php://filter

4、找回密码

[1] 验证token。在找回密码的时候生成一个token,然后存储在数据库中,然后把找回密码的地址发到邮箱中,url中就含有token,由用户点开就能修改密码

[2] rand函数生成token

  • $resetpwd = md5(rand()) 对rand函数的结果进行MD5加密

  • $encryptstring = md5($this->time.$verification.$auth);

$timetemp=date('Y-m-d H:i:s',$this->time);
$auth=util::strcode($timetemp,'ENCODE');

算法的 KEY 并没有初始化,如果知道了这个时间,就可以生成加密的字符串
[3] 延伸
一些cms的密码的加密方式很难破解,有时拿到了管理的密码破解不掉。一般解决方法:一般找回密码都用的是邮箱,首先把管理的邮箱注入出来,然后再去找回密码,再把数据库的token注入出来,构造一下地址就可以重置密码了。

5、文件读取

文件读取常用的函数

file_get_contents() highlight_file() fopen() readfile() fread() fgetss() fgets() parse_ini_file() show_source() ......

[1] file_get_contents()与php://filter一起利用可以读取页面源代码。前提是存在include()

file_put_contents('php://filter/write=string.rot13/resource=example.txt','hello world');
file_get_contents('php://filter/read=convert.base64-encode/resource=1.php')
6、其他的文件操作

如:任意文件删除、任意文件复制、任意文件下载等。

一般步骤就是:首先尝试拿到配置文件的数据库用户名和密码,通过外向连接来拿到想要的信息,还有就是通过配置文件,得到加密的key,生成加密字符串

7、加密函数

拿到加密函数的key,通过key加密一些字符串生成加密字符串。

[1] 加密可逆

  • 弱算法,知道明文或者知道密文,可以进行逆运算。拿到加密函数的key生成自己想要的加密后的字符串

[2] 加密可控

  • 要加密的内容是可控的,密文会输出,这个可控点可以引入特殊字符串,然后在生成加密字符串后又在某个地方解密后输出,在这个可控点也可以获取信息进行利用

[3] key泄露

  • PHPcms v9 存在key泄漏的漏洞

8、命令执行

指的是可以执行系统或者应用指令(如CMD命令或者bash命令)的漏洞。主要是基于一些函数的参数过滤不严

可以执行命令的函数:

system():显示shell命令输出的结果,命令执行状态为0时表示执行成功 exec(): 返回shell结果的最后一行,命令执行状态为0时表示执行成功 shell_exec() passthru() pcntl_exec() popen() ......

[1] 一般的防御方式有两种,一种是黑白名单,一种是命令防注入函数。

  • escapeshellcmd() :过滤整条命令。过滤的字符包含:& ; * ? ~ < > ^ () [ ] { } $ \ \x0A \xff % ’ '’ 。windows下的过滤就是给字符前加^,linux下就是给字符前加\

  • escapesshellarg():过滤参数。将参数限制在双引号里,将双引号替换为空格

[2] 反引号(’)也可以执行命令,实际上这种方式也是调用shell_exec函数

绕过方式

  • 若在linux下,先使用escapesshellarg()再使用escapeshellcmd()可以用来逃逸单引号

  • 可以将恶意的系统命令拼接在正常命令后

&  依次执行
&& 前一个执行成功才执行
| 管道符号
|| 前一个执行失败才执行
%0a 换行
  • 当具体命令被限制时,可以使用正则来绕过,如

/???/??? => /bin/cat

但是这样的缺点是会产生大量的干扰输出

  • 可以通过Bash环境下相邻字符自动连接特性来进行绕过

$ /bin/cat /etc/passwd
$ /bin/cat /e'tc'/pa'ss'wd
$ /bin/c'at' /e'tc'/pa'ss'wd
$ /b'i'n/c'a't /e't'c/p'a's's'w'd'
9、代码执行

应用程序本身过滤不严,用户可以通过请求将代码注入到应用中去执行

代码执行函数有以下几种:

eval()需要分号结尾 assert()不需要分号结尾 preg_replace() 将目标字符中符合正则规则的字符替换为替换字符,如果正则规则中使用/e修饰符,则存在代码执行漏洞 call_user_func() call_user_func_array() array_map() ......

[1] 除了上面的代码执行函数为,还可以通过” “双引号,在双引号下变量自动解析

[2] ${}可以执行代码并将代码返回值作为变量,无下划线shell构造

[3] 动态函数

$_GET['a']($_GET['b']);

绕过方式:

  • 双引号、单引号被过滤时,可以使用heredoc和nowdoc

  • 可以结合命令执行函数

10、变量覆盖

是指用自定义的参数替换程序原有的变量值

[1] extract():将数组中的键值对注册成变量

[2] parse_str():解析字符串并且注册变量,在注册之前不会验证变量是否存在

[3] important_request_variables():把GET/POST/COOKIE的参数注册成变量(前提条件是:register_globals为off且php版本在4.1-5.4之间)

[4] 全局变量覆盖:传递过来的值会被直接的注册为全局变量直接使用。(前提是register_globals为on)

[5] $$导致的变量覆盖问题

防御

  • 使用原始变量

  • 验证变量是否存在

(0)

相关推荐