PHP代码审计系列(一) 基础:方法、思路、流程
免责声明
本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。
只供对已授权的目标使用测试,对未授权目标的测试作者不承担责任,均由使用本人自行承担。
文章正文
工具
Fotify|代码审计静态扫描工具,商业化静态代码扫描工具,误报率相对较低。
seay|源代码审计工具
PHPStorm|是PHP编程语言开发的集成环境。
chrome & burp & HackerBar 插件 & xdebug插件
Xcheck |Xcheck是一个由腾讯公司CSIG质量部代码安全检查团队自研的静态应用安全测试工具。Xcheck在基于成熟的污点分析技术与对抽象语法树的精准剖解上,通过巧妙优雅的实现来达到对污点的传递和跟踪的目的,更精准地发现隐藏在代码中的安全风险。同时赋予了Xcheck两大优势:快!低误报!
框架
确定目标是否使用的框架、MCV设计模式。
熟悉常见的框架:ThinkPHP、Laravel、Codeigniter、Yii等
MVC
既然是MVC框架的,那么我们真正关心的是其中的控制器(C),因为功能点大部分都在C上,我们能找到的大部分漏洞也都在C上
ThinkPHP
一般如果是审计基于框架的cms,我不会去看框架系统目录(对于TP框架就是ThinkPHP文件夹下的东西),第三方类库vendor也不会去先看,除非是在审计过程中流向了这些文件中,才会大概看一看,而重点在Application文件夹下做文章。
Laravel
目录怎么变,MVC架构的重点还是在Controllers里
无框架
先弄清目标的目录结构。
审计过程中需要关注几个点:(在我们后面开始审计的过程中,自己要注意这些地方,经常想一想)
1)函数集文件,通常命名包含function或者common等关键字,这些文件里面是一些公共的函数,提供其他文件统一调用,所以大多数文件都会在文件头部包含到其他文件。寻找这些文件一个非常好用的技巧就是去打开index.php或者一些功能性文件,在头部一般都能找到。
2)配置文件,通常命名中包括config关键字,配置文件包括web程序运行必须的功能性配置选项以及数据库等配置信息。从这个文件中可以了解程序的小部分功能,另外看这个文件的时候注意观察配置文件中参数值是单引号还是用双引号括起来,如果是双引号可能就存在代码执行的问题了。
3)安全过滤文件,安全过滤文件对代码审计至关重要,这关系到我们挖掘到的可以点能否直接利用,通常命名中带有filter、safe、check等关键字,这类文件主要是对参数进行过滤,大多数的应用其实会在参数的输入做一下addslashes()函数的过滤。
4)index文件,index是一个程序的入口,所以通常我们只要读一读index文件就可以大致了解整个程序的架构、运行的流程、包含到的文件,其中核心的文件有哪些。而不同目录的index文件也有不同的实现方式,建议最好将几个核心目录的index文件都通读一遍。
路由
以ThinkPHP为例,一般有几种形式:普通模式、PATHINFO模式、REWRITE模式、兼容模式
普通模式:
http://localhost/?m=home&c=user&a=login&var1=value1&var2=value2
pathinfo模式:
http://serverName/index.php(或者其他应用入口文件)/模块/控制器/操作/[参数名/参数值...]
REWRITE模式:
在PATHINFO的基础上去掉入口文件
http://serverName/模块/控制器/操作/[参数名/参数值...]
兼容模式:
http://servername/index.php?s=/index/Index/index
其中变量`s`的名称的可以配置的。
审计
流程
1、先全局总览:配置文件、路由等2、定向功能审计:黑盒(找到敏感功能)+白盒(定位到代码进行审计)3、敏感函数回溯
比较完整的审计流程:
先黑盒+白盒看敏感功能,再用自动化审计工具跑一遍并验证,最后再根据漏洞危险函数去回溯
常见漏洞类型审计(危险函数)
这里先仅列出函数,后面会详细展开
SQL注入
如果使用了框架,可以分辨一下框架名称以及版本,去搜索一下该版本的框架是否存在漏洞,如果存在再去cms中验证。
如果没有使用框架,则需要仔细的观察数据库函数,一般来说,cms是将select、insert等函数进行了封装的,比如$db->table(‘test’)->where(“name=admin”)便是 select * from test where name=admin这种格式,而此时若是发现cms使用的是过滤+拼接,那么很有可能会出现问题,而如果使用了PDO,则继续跟进涉及到table,order by等字段的拼接去,因为这些字段是无法使用PDO的。
审计要素:
参数是否用户可控
是否使用了预编译
那么首先,如果没有使用框架封装的sql语句,那么全局搜索insert、select等sql语句关键词,然后定位到具体的语句,然后查看里面有没有拼接的变量,回溯可不可控。如果可控并且存在字符串拼接,很有可能就存在漏洞。
使用了框架的就是搜索的关键词不一样,还是得看是否存在字符串拼接,可不可控。
即使使用了预编译,但是如果在预编译之前字符串拼接了,那照样没有鸟用,该注入还是能注入。
关键词:
insert
create
delete
update
order by
group by
where
from
limit
desc
asc
union
select
宽字节注入
SET NAMES 'gbk' => set character_set_connection='gbk',character_set_results='gbk',character_set_client='gbk'
检索关键字:
1、SET NAMES
2、character_set_client = gbk
3、mysql_set_charset('gbk') //后面没有 mysql_real_escape_string函数
GPC
magic_quotes_gpc=on,会自动在GET、POST、COOKIE变量中的' 、"、 \、 NULL的前面加上反斜杠(\),但是在PHP5中magic_quotes_gpc并不会过滤$_SERVER变量,导致很多类似client-ip,referer一类的漏洞能够利用。PHP5.4取消这个参数。
二次urldecode注入
我们提交参数到web服务器,web服务器会自动解码一次。1%2527 解码两次=》1'
检索关键字:
urldecode
rawurldecode
XSS
审计要素
是否存在全局参数过滤器,过滤规则是否符合安全要求,是否存在需过滤和不需过滤两种输出,页面是否控制恰当。
输出时是否进行编码(HTML、JS等)。
前端是否采用了Angularjs、React、vue.js等具有XSS防护功能的前端框架进行数据输出
寻找带有参数的输出函数,然后根据输出函数对输出内容回溯输入参数,观察有没有经过过滤常用的输出函数:
print、print_r、echo、printf、sprintf、die、var_dump、var_export
CSRF
先黑盒:打开几个有非静态操作的页面,抓包看看有没有token,如果没有token的话,再直接请求这个页面,不带referer再白盒:读代码的时候看看几个核心文件里面有没有验证token和referer相关的代码
可以尝试全局搜索
csrf-token
csrf_token
csrftoken
csrf
SSRF
审计要素:
是否存在可以产生SSRF漏洞的函数。
是否存在限制请求的方式只能为HTTP或者HTTPS。
关键词:
file_get_contents
fsockopen
curl_exec
get_headers
fopen
readfile
文件包含漏洞
文件包含算是拿shell最快的方法了,所以一般要重点关注。
大多数出现在模块加载、模板加载以及cache调用的地方先跟踪一下程序运行流程,看看里面模块加载时包含的文件是否可控,另外就是直接搜索
关键词:
include、include_once、require、require_once
回溯看看有没有可控的变量
文件读取(下载)漏洞
先黑盒看看功能点对应的文件,再去读文件搜索文件读取函数,回溯看看有没有直接或者间接控制的变量:
关键词:
file_get_contents()
highlight_file()
fopen()
readfile()
fread()
fgetss()
fgets()
parse_ini_file()
show_source()
file()
文件包含函数include等
文件上传
审计要素
是否检查了上传文件的文件类型
是否限制了文件上传路径
是否对文件进行了重命名
文件大小是否限制
是否返回了文件路径或文件路径很好猜测
有的项目,会对文件上传下载进行分装,所以可以全局搜索有关upload、file的函数,看看是不是封装了
function uploadfunction file
如果封装了,那么就看这些封装好的函数,有没有上面提到的审计要素的漏洞。
如果没封装,一般是move_uploaded_file这个函数,全局搜索这个函数,回溯查看这些漏洞存不存在。(白盒黑盒一起搞比较好。)
文件删除
可以先去找相应的功能点,直接黑盒测试一下看能不能删除某个文件,如果删除不了,再去执行流程去追提交的文件名参数的传递过程。
unlink
rmdir
代码执行
审计要素
php.ini文件中的disable_function是否有禁用函数。
是否存在代码执行的敏感函数。
是否输入变量可控。
关键词:
eval()、assert()、preg_replace()、create_function()、array_map()、call_user_func()、call_user_func_array()、array_filter、 usort()、动态函数
命令执行
审计要素
参数是否用户可控
是否配置了全局过滤器,过滤规则是否符合安全规范
是否所有的命令执行参数都经过了过滤器,或受白名单限制
关键词:
exec()、passthru()、system()、 shell_exec()、popen()、反引号`、proc_open()、pcntl_exec()
变量覆盖
变量覆盖漏洞通常要结合应用其他功能代码来实现完整攻击,所以挖掘一个可用的变量覆盖漏洞不仅仅要考虑的是能够实现变量覆盖,还要考虑后面的代码能不能让这个漏洞利用起来。要挖可用的变量覆盖漏洞,一定要看漏洞代码行之前存在哪些变量可用覆盖并且有被使用到。extract()
、parse_str()
这两个函数比较好挖掘,直接检索关键字,然后回溯import_request_variables()
函数则相当于开了全局变量注册,这时候只要找哪些变量没有初始化并且操作之前没有赋值的,然后就去提交这个变量作为参数。PHP4-5.4.0 才有该函数。$$ 变量覆盖,经典代码:
以上代码,可以用从G P C获得的 '参数=值',来覆盖之前的所有变量
XXE
就关键函数:
SimpleXMLElement
simplexml_load_string
loadxml
直接搜索 回溯
逻辑漏洞
通读功能点源码。值得关注的点是程序是否可以重复安装、修改密码处是否可越权修改其他用户密码、找回密码验证码是否可暴力破解以及修改其他用户密码、cookie是否可预测或者说cookie验证是否可绕过等。
危险函数: php黑魔法
in_array : 在比较之前会自动做类型转换
is_numeric:任何参数做16进制编码传入,会直接通过(true)
双等于(==):弱等于,判断之前会先做变量类型转换
$_REQUEST获取变量,进行过滤,可能存在绕过:GET方式传危险数据,POST方式传正常数据,$REQUEST默认获取的是POST方式传入的数据。所有如果存在类似,**$GET传入的参数,通过$_REQUEST进行过滤**,就可能存在问题
技术交流
交流群
知识星球
星球不定时更新最新漏洞复现,手把手教你,同时不定时更新POC、内外网渗透测试骚操作。涉及方向包括Web渗透、免杀绕过、内网攻防、代码审计、应急响应、云安全等
往期文章:
点一下爱心再走吧!