系统安全:php安全代码审计小结

2013 年 7 月 29 日4620

  文章仅为一段时间整理的php安全代码审计笔记总结,一个分析框架,没有实例化分析。

  一、工具篇

  编辑器(notepad++,editplus,UE等等,看个人习惯)

  TommSearch(字符串检索) || grep

  HttpProtocolDebugger(http协议调试器)

  Fiddler(分析包,改包)

  Seay PHP代码审计工具(php-code-audit分析辅助)

  几个有趣的项目

  dvwa(代码审计测试平台)

  phpmvs

  php security audit check

  PHP Vulnerability Hunter

  二、函数篇

  addslashed()添加反斜杠

  stripslashed()去掉反斜杠

  get_magic_quotes_gpc() 判断是否开启gpc

  expode(".",$array)分割成数组

  is_numeric()判断是否为数字

  sizeof()判断长度

  trim() 去除字符串开头和末尾的空格或其他字符

  system() 输出并返回最后一行shell结果。

  exec() 不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面。

  passthru() 只调用命令,把命令的运行结果原样地直接输出到标准输出设备上。

  EscapeShellCmd(),把一个字符串中所有可能瞒过Shell而去执行另外一个命令的字符转义。这些字符在Shell中是有特殊含义的,象分号(;),重定向(>)和从文件读入 (<)等。

  EscapeShellArg() 。在给定的字符串两边加上单引号,并把字符串中的单引号转义,这样这个字符串就可以安全地作为命令的参数。

  用popen()函数打开进程

  上面的方法只能简单地执行命令,却不能与命令交互。但有些时候必须向命令输入一些东西,如在增加Linux的系统用户时,要调用su来把当前用户换到root才行,而su命令必须要在命令行上输入root的密码。这种情况下,用上面提到的方法显然是不行的。

  popen ()函数打开一个进程管道来执行给定的命令,返回一个文件句柄。既然返回的是一个文件句柄,那么就可以对它读和写了。在PHP3中,对这种句柄只能做单一 的操作模式,要么写,要么读;从PHP4开始,可以同时读和写了。除非这个句柄是以一种模式(读或写)打开的,否则必须调用pclose()函数来关闭 它。

  例子1:

  /* PHP中如何增加一个系统用户

  下面是一段例程,增加一个名字为james的用户,

  root密码是 verygood。仅供参考

  */

  $sucommand = "su --login root --command";

  $useradd = "useradd ";

  $rootpasswd = "verygood";

  $user = "james";

  $user_add = sprintf("%s "%s %s"",$sucommand,$useradd,$user);

  $fp = @popen($user_add,"w");

  @fputs($fp,$rootpasswd);

  @pclose($fp);

  require在被包含文件有错误代码时将不再往下执行

  include在被包含文件有错误代码时仍然往下执行

  htmlspecialchars() 函数把一些预定义的字符转换为 HTML 实体。

  预定义的字符是:

  &(和号) 成为 &

  "(双引号) 成为 "

  ' (单引号) 成为 '

  <(小于) 成为 <

  >(大于) 成为 >

  move_uploaded_file() 函数将上传的文件移动到新位置。

  extract() 函数从数组中把变量导入到当前的符号表中。

  对于数组中的每个元素,键名用于变量名,键值用于变量值。

  第二个参数 type 用于指定当某个变量已经存在,而数组中又有同名元素时,extract() 函数如何对待这样的冲突。

  本函数返回成功设置的变量数目。

  语法

  extract(array,extract_rules,prefix)

  parse_str() 函数把查询字符串解析到变量中. (常见于变量覆盖漏洞)

  语法

  parse_str(string,array)

  参数 描述

  string 必需。规定要解析的字符串。

  array 可选。规定存储变量的数组名称。该参数指示变量存储到数组中。

  针对变量指定攻击

  不使用foreach遍历$_GET变量,改用$_GET[(index)]

  eval() 函数把字符串按照 PHP 代码来计算。该字符串必须是合法的 PHP 代码,且必须以分号结尾。

  如果没有在代码字符串中调用 return 语句,则返回 NULL。如果代码中存在解析错误,则 eval() 函数返回 false。

  preg_replace 执行一个正则表达式的搜索和替换

  /e参数执行代码

  三、漏洞篇

  -----------------------------------------------

  [1].Sql-Injection

  留意:cookie及x-forward-for,宽字节,报错注射等

  挖掘漏洞参考

  变量

  $_GET[""],$_POST[""],$_COOKIE[""], $SERVER[""]

  数据库操作函数

  mysql_query()

  数字型注入防范:

  1.is_numeric() ctype_digit() intval()

  2.str_length()确定长度

  字符型注入防范:

  1.mysql_real_escape_string()

  2.数据库查询语句前加@防爆错

  3.str_length()确定长度

  -----------------------------------------------

  [2].Command-Execution

  函数:

  system(),passthru(),popen(),exec()

  数据库操作函数:

  exec,system,popen,passthru,proc_open,shell_exec

  执行命令管道符 % | >

  测试如0 | dir c:

  || 双竖线的作用,前面语句执行错误则执行后面语句

  如xx"+||+whoami+||+echo

  -----------------------------------------------

  [3].File-Inclusion

  函数:

  include(),require(),include_once(),require_once()

  远程文件包含漏洞要求

  allow_url_fopen() allow_url_include() file_get_contents()

  绕过:zlib://和ogg://

  5.2.0之后版本

  data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+ //

  @eval(file_get_contents('php://input')); //POST phpinfo();

  配合%00截断,新版本自动转义\0

  -----------------------------------------------

  [4].CSRF

  CSRF防范策略

  1>验证http-referer字段

  安全性低,易被伪造

  2>在请求地址中添加token并验证

  token可在用户登录后存放在session中,每次请求时将token从session中取出,去请求的token对比以防范CSRF

  GET方式:http://url/?=token

  如果一个网站接受请求地方比较多,则在每次页面加载时遍历整个dom树,在dom中每个a和form标签后加入token

  但在动态页面加载后产生的html代码,则需要以硬编码的形式手工添加

  这种方式安全性弱点在于,如在论坛等交互比较频繁的地方hacker可构造环境盗取token并进而构造csrf攻击

  故手工关闭referer

  3>在HTTP头中自定义属性并进行验证。通过XMLHttpRequest类。

  通常用于Ajax方法对页面局部的异步刷新

  但适应性一般,对已有的网站架构局限性较大

  -----------------------------------------------

  [5].XSS(Cross Site Script)

  反射型与存储型

  控制$_GET,$_POST,$_COOKIE 各种传入的变量

  使用htmlspecialchars()函数进行基础过滤

  结合CSRF实现自动化利用

  -----------------------------------------------

  [6].File_Upload

  函数:move_uploaded_file()

  变量:$_FILES

  php文件上传利用form表单进行文件上传时必须为post使用multipart/form-data才能完整的传递文件数据

  php利用$_FILES系统函数的相关参数与函数move_upload_file函数来实例把由$_FILES全局变量生成的临时文件移动到指定目录完成文件的上传

  $_FILES['files']['name']客户端文件的原名称

  $_FILES['files']['type']文件的MIME类型

  $_FILES['files']['size']已上传文件的大小

  $_FILES['files']['tmp_name']储存的临时文件名,一般为系统默认

  $_FILES['files']['error']该文件上传到相关的错误代码

  防范方式:

  1>判断MIME TYPE文件类型如$_FILES['files']['type']=="image/jpeg",判断文件大小,如$_FILES['files']['size']<10000 &&$_FILES['files']['size']>100

  2>指定上传文件名,如依赖时间生成hash(time).jpg等方式

  3>根据文件后缀名判断文件

  如file_ext=substr($filename,$strrpos($filename,'.')+1);

  注意是否可能有双扩展名,二次上传突破等逻辑问题

  4>服务器尝试渲染文件等方式判断是否为图片

  5>不依赖于客户端js脚本限制上传文件类型

  6>白名单规则

  apache服务器常见上传安全问题

  1>配合.htaccess利用上传

  AllOverride ALL 允许子规则覆盖父规则

  .htaccess添加AddType Application/x-httpd-php .jpg

  2>文件名解析漏洞

  *.php.123

  在.htaccess添加AddHandler php5-script .php,文件名中包含php扩展名可以php脚本执行,如x.php.jpg

  .php3 .php4扩展名

  四、配置篇

  1>关注漏洞信息,及时更新版本

  2>php.ini httpd.conf .htaccess文件配置

  1)safe_mode相关配置

  2)register_globals关闭

  3)open_basedir配置,防范目录遍历

  4)allow_url_fopen关闭

  5)disable_functions配置

  6)magic_quotes_gpc打开

  7)error_reporting=E_ALL &~E_NOTICE

  8)display_errors=Off避免攻击者获取更多信息

  9)expose_php=Off隐藏版本信息

  3>最小化服务器其他账户权限

  4>第三方安全加固软件安装

  5>调用第三方安全防护文件,配置php.ini

  include_path=".:/php/includes"

  auto_pretend_file="anti-inj.php"

  auto_appent_file=

  五、思路篇

  刚开始练习审计时,拿到一套源码,马上做的事情就是,丢到工具里,去扫敏感的函数,然后去一个一个的回溯它,找到入口点。但是,这样审计很浪费时间,每次都要在回溯过程中,不断的去寻找源码中定义的一些通用函数。由于不了解整个源码的流程,导致在找这些通用函数的过 程中浪费了很多的时间与精力。

  所以,我重新调整了我的审计流程。在拿到源码之后,先从它开始的地方(一般是根目录下的index文件)按照执行的顺序去读代码,一直到它的初始化内容, 和基本功能实现完毕为止。这样,可以明确的了解整套源码的结构,哪一种函数文件放在哪个文件夹下;知道通用函数放在哪个文件中。这对我们在之后阅读“疑似”有问题的代码时,有很好的帮助,例如,在看到一个通用函数时,我们可以快速的切换到通用函数文件,查找这个函数的实现代码。

  注:此处引用修改唐门三少文章《PHP代码审计学习总结》

  六、小结

  代码审计一如逆向工程,均需要耐心与细心。

  此外,关注漏洞发布平台上最新漏洞并跟踪加以分析也是一个很快提升自己能力的方法。

0 0