
什么是源代码审查?
输入/输出的验证:比如sql注入,未对用户提交的内容做验证和未对服务器的返回做验证
安全功能:请求的参数没有做范围限制导致信息泄露,cookie泄露等
程序异常处理:异常处理不恰当等
MVC架构
MVC是一种使用MVC(ModelViewController模型-视图-控制器)设计创建Web应用程序的模式。MVC模式同时提供了对HTML、CSS和JavaScript的完全控制。
•Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。表示应用程序核心(比如数据库记录列表),通常模型对象负责在数据库中存取数据。
•View(视图)是应用程序中处理数据显示的部分。显示数据(数据库记录),通常视图是依据模型数据创建的。
•Controller(控制器)是应用程序中处理用户交互的部分。处理输入(写入数据库记录),通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
可以理解为:Model模型 对应数据库,Controller控制器 对应访问的路径,View视图 对应渲染的html页面
数据处理找Controller
回显数据找view
处理流程
获取请求-》全局过滤-》模块文件-》c函数内容-》M函数内容-》v显示
入口是找index.php里的define('APP_PATH',_DIR_.control)
MVC架构:
比如访问admin目录下的wp_login.class.php的login方法的话,通常为
/index.php?m=admin&c=wp_login&f=login,通过index.php(控制器)路由转发
thinkphp架构:
比如在网址的index.php里路由转到其他目录(找入口文件)
想访问applicaton目录下的index目录下控制器目录下的的wp_login.php的login方法的id参数的话
?index.php/index/wp_login/login/id/116
思路:
先通读原文,分析函数集文件、配置文件、安全过滤文件、index文件,找到入口文件,看包含了哪些文件,层层递进
敏感关键字回溯参数
优点:可以快速挖出想要漏洞、判断敏感触发点的位置以及上下文以便追踪参数源头
查找可控变量
先可以进行黑盒测试,再去进行功能点定向审计,程序安装、文件上传、文件管理、登录验证、备份恢复、找回密码
对功能点进行进行审计
CMS-》关键字搜索对应框架-》查看框架对应版本-》网上寻找是否存在该框架版本的漏洞-》直接利用
php.ini配置文件
因为要了解该cms有哪些配置条件是开启或关闭的
基本配置-语法
•大小写敏感
directive = value(指令= 值)
foo=bar ≠ FOO=bar
•运算符
|、&、~、!
•空值的表达方式
foo = ;
foo = none;
foo = "none“;
基本配置—安全模式
•安全模式
safe_mode= off
安全模式,用来限制文档的存取、限制环境变量的存取,控制外部程序的执行。
本特性已在PHP5.4.0被移除
•限制环境变量存取
safe_mode_allowed_env_vars= string
指定PHP 程序可以改变的环境变量的前缀,当这个选项的值为空时,那么php 可以改变任何环境变量。
如:safe_mode_allowed_env_vars= PHP_ ,
当这个选项的值为空时,那么php 可以改变任何环境变量
•外部程序执行目录
safe_mode_exec_dir=“E:\Local Test\WWW”
基本配置—安全模式
•禁用函数
disable_functions
为了更安全的运行PHP,可以用此指令来禁止一些敏感函数的用,当你想用本指令禁止一些危险函数时,切忌把dl()函数也加到禁止列表,攻击者可以利用dl()函数加载自定义的php扩展来突破disable_function。配置禁用函数时可以使用逗号分隔函数名
•com组件
com.allow_dcom= false
PHP设置在安全模式下(safe_mode),仍旧允许攻击者使用COM()函数来创建系统组件来执行任意命令,我推荐关闭这个函数来防止出现此漏洞
使用COM()函数需要在PHP.ini中配置extension=php_com_dotnet.dll,如果PHP VERSION<5.4.5 则不需要
基本配置—控制变量
•全局变量注册开关
register_globals= off
php.ini的register_globals选项的默认值预设为Off,在4.2版本之前是默认开启的,当register_globals的设定为On 时,程序可以接收来自服务器的各种环境变量,包括表单提交的量,这是对服务器来讲是非常不安全的,所以我们不能让它注册为全局变量。register_globals=Off时,服务器端获取数据的时候用$_GET['name']来获取数据
<?php
if(isset($_SESSION['name'])){
echo'yes';
}
else{
echo'no'
}
?>
//url=http://127.0.0.1?_SESSION['name']=to时页面返回no,因为虽然是get的,但是变量不存在,只能接受规定的传参方式
register_globals=On时,服务端使用POST或者GET提交的变量,都将自动使用全局变量的值来接受值
<?php
if(isset($_SESSION['name'])){
echo'yes';
}
else{
echo'no'
}
?>
/*url=http://127.0.0.1?_SESSION['name']=to时页面显示yes,因为这个选项开启,它会接受使用POST或者GET提交的变量,然后把它变成全局变量,所有此时这个$_SESSION['name']是存在的*/
•魔术引号自动过滤
magic_quotes_gpc= on 本特性已在PHP5.4.0被移除magic_quotes_gpc= Off 在php.ini里是默认关闭的,如果它打开后将自动把用户提交对sql的查询语句进行转换,如果设置成ON的话,php会把所有的单引号(“'”),双引号(“"”),反斜杠(“\”)和空字符(NULL)加上反斜杠进行转义,它会影响HTTP 请求的数据有(GET、POST、Cookies),开启它会提高网站的安全性,当然,您也可以使用addslashes来转义提交的HTTP 请求数据,或者用stripslashes来删除转义
基本配置—远程文件
•是否允许包含远程文件
allow_url_include= off该配置为on的情况下,可以直接包含远程文件,若包含的变量为可控的情下,可以直接控制变量来执行PHP代码
•是否允许打开远程文件
allow_url_open= on允许本地PHP文件通过调用URL重写来打开和关闭写权限,默认的封装协议提供的ftp和http协议来访问文件。
基本配置—目录权限
•HTTP头部版本信息
expose_php= off
防止了通过http头部泄露的php版本信息
•文件上传临时目录
upload_tmp_dir=
上传文件临时保存的目录,如果不设置的话,则采用系统的临时目录
•用户可访问目录
open_basedir= E:\Local Test\WWW
能够控制PHP脚本只能访问指定的目录,这样能够避免PHP脚本访问,不应该访问的文件,一定程度上限制了phpshell的危害
基本配置—错误信息
•内部错误选项
display_errors= on
表明显示PHP脚本的内部错误,网站发布后建议关闭PHP的错误回显,在调试的时候通常把PHP错误显示打开
•错误报告级别
error_reporting= E_ALL & ~E_NOTICE
这个设置的作用是将错误级别调到最高,显示所有问题,方便排错
审计技巧
1、动态调试技术
phpStudy + PhpStorm + XDebug调试,了解变量在传递的过程
2、文件对比技术
可采用两个不同版本的cms进行对比,快速锁定上一个版本出现漏洞的变量等
SQL注入
1.拿到源码搭建在本地,通过访问自建网站,配合seay审计工具的数据库监控功能读数据库进行监控,在某页面刷新时看它执行了哪些语句,并判断这些sql语句里面有哪个可疑参数是可控的,再定位到对应文件查看上下文等
2.搜索关于sql注入的关键字,可以利用正则表达式搜索。如:
(update|select|insert|delete|).*?where.*=\ ......
character_set_connect='gbk'宽字节注入
寻找可能与数据库交互的语句等来判断是否存在可控参数,再进行定位判断是否存在过滤等,技巧:可以利用工具进行监控或者输出进行调试
XSS
寻找有html实体编码的地方大概率会有htmlspecialchars(),还要在SQL注入点也会有xss,比如sql注入的拼接参数没有单引号包裹的话,可以在后面拼接xss语句,或者在sql语句插入或更新数据时也可以有xss
文件操作漏洞
任意文件上传
关键字:
$FILES,type="file",上传,move_upload_file( )等
寻找上传点,头像上传为最多,或者上传商品图片等等,还要判断过滤是黑名单还是白名单
任意文件删除
搜索关键函数:
unlink()利用回溯变量的方式(基本删除文件的功能都会在一个文件里)
老版本下的session_destroy(),可以删除文件,现已基本被修复
1.搜索php的删除文件函数如unlink(),查看被删除的变量是从哪里传过来的,定位到该变量的具体实现,并查看对应删除的地方是否做好文件过滤操作
2.如果传进来被删除的变量被指定了目录,如只能删除adimn目录下的文件,查看是否能进行目录跨越来绕过限制
3.如果可以进行目录跨越,可以选择删除敏感文件导致网站不能运行,还可以删除覆盖文件可导致能重新安装(重新安装可以自己选择数据库账户名密码等,危害性大)
任意文件读取
搜索关键字
file_get_contents()
highlight_file()
fopen()
read file()
fread()
fgetss(),
?fgets()
parse_ini_file()
show_source()
file()等
1.搜索文件读取函数关键字判断在哪里进行了文件读取,并看在文件读取函数里的变量是否可控,如果为文件读取固定文件,则可能性不大
2.定位文件读取函数的可控变量是怎么实现的,追踪从什么地方来,是否被经历过滤等,可以在变量实现的地方进行动态调试,输出目录路径进行判断
3.可控变量是否被固定,如只能读admin/目录下的文件,这时候考虑是否可跨目录读取
任意文件下载
没有特定函数,需要寻找文件下载相关功能点或者自定义函数,如download(),或者寻找有路径拼接的地方,也可以部署网站后看网站功能点的url来寻找源码里文件下载的点进行判断是否存在任意文件下载
任意文件包含
(1) 本地文件包含:
一般存在于模块加载,模板加载,cache调用
包括函数:include()/include_once(),require()/require_once()寻找可控变量
如:
//1.php
<?php
dirfine("ROOT",dirname(_FILE_).'/');
$mod=$GET['mod'];
include(ROOT.$mod.'php');
?>
//2.php
<?php
phpinfo();
?>
(2) 远程文件包含:
前提条件:allow_url_include = on
出现频率不如本地包含
(3) 文件包含截断:
%00截断(php版本小于5.3),当后缀名固定时可以用,还可以用包含临时文件(P神文章)
问号截断(问号后面相当于请求的参数,伪截断)
英文(.) 反斜杠(/) 截断
路径长度截断,Linux 需要文件名长于 4096,Windows 需要长于 256
RCE
命令执行:
system(),exec(),shell_exec(),passthru(),pcntl_exec(),popen(),proc_open()
代码执行:
eval(),assert(),preg_replace(),call_user_func(),call_user_func_array(),array_map()
preg_replace()函数:
mixedpreg_replace( mixed$pattern, mixed$replacement, mixed$subject[, int$limit=-1[, int&$count]] )
当$pattern处存在e修饰符时,$replacement会被当做php代码执行。
变量跟踪
反序列化
关键函数:
serialize()
unserialize()
__construct() 当创建对象时触发,一般用于初始化对象,对变量赋初值,构造函数
__sleep() 使用serialize()时自动触发
__wakeup() 使用unserialize()时自动触发
__destruct() 当一个对象被销毁时触发,析构函数
__toString() 当一个类被当成字符串使用时触发
__invoke() 当尝试以调用函数的方式调用一个对象时触发
__call() 在对象上下文中调用不可访问的方法时触发
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发xxxxxxxxxxunserlize__construct() 当创建对象时触发,一般用于初始化对象,对变量赋初值,构造函数__sleep() 使用serialize()时自动触发__wakeup() 使用unserialize()时自动触发__destruct() 当一个对象被销毁时触发,析构函数__toString() 当一个类被当成字符串使用时触发__invoke() 当尝试以调用函数的方式调用一个对象时触发__call() 在对象上下文中调用不可访问的方法时触发__callStatic() 在静态上下文中调用不可访问的方法时触发__get() 用于从不可访问的属性读取数据__set() 用于将数据写入不可访问的属性__isset() 在不可访问的属性上调用isset()或empty()触发__unset() 在不可访问的属性上使用unset()时触发
1.通过关键函数去找触发点,寻找在关键函数里的代码执行或者命令执行函数来层次分析构造pop链,恶意执行效果
2.通过函数方法
注:新人总结,轻喷
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)