由于看到不少讲web安全的地方都会推荐一本书叫 《代码审计:企业级web代码安全架构》,于是从图书馆找来这本书读了一下,虽然有些内容可能有点老了,但整体上还是挺不错的,下面做一点总结。

一、通用代码审计思路

常见的代码审计思路有以下四种:

1、根据敏感关键词(敏感函数)回溯参数传递过程。

2、查找可控变量,正向追踪变量传递过程。

3、寻找敏感功能点,通读功能点代码。
(1)文件上传功能:文件上传漏洞、文件名SQL注入漏洞
(2)文件管理功能:任意文件操作漏洞、XSS漏洞
(3)登陆认证功能:任意用户登陆漏洞、越权漏洞
(4)找回密码功能

4、直接通读全文代码

通读全文代码时,首先应看程序的代码结构,如主目录有哪些文件,模块目录有哪些文件,插件目录有哪些文件,除了关注有哪些文件,还要注意文件的大小、创建时间。在看程序目录结构时候,注意以下几个文件:
(1)函数集文件:通常命名中包含functions或者common等关键字,一般可以在index.php或者一些功能性文件的头部可以找到。
(2)配置文件:通常命名中包含config关键字,配置文件包括web程序运行必须的功能性配置选项以及数据库等配置信息。
(3)安全过滤文件:通常文件名中包含filtersafecheck等关键字,这类文件主要是对参数进行过滤,关系到我们找到的可疑点能否利用。
(4)index文件:index是一个程序的入口文件,通常读一遍index文件就可以大致了解整个程序的架构、运行的流程、包含到的文件,其中核心文件有哪些。


二、SQL注入漏洞

1.普通注入:int型和string型

2.编码注入:宽字节注入和二次urldecode注入

(1)宽字节注入:
当php连接mysql时如果 set character_set_client =gbk时会导致该注入的发生。提交

`/1.php?id= -1' and 1=1%23`

时,单引号会被\转义.
这时如果提交?id=-1%df' and 1=1#的话,过滤用的\ (%5c)会与%df%5c组合,mysql语句就成了

select * from user where id='-1運' and 1=1#`

(2)urldecode二次解码
在web中通常使用addslashes()mysql_real_escape_string()mysql_escape_string函数或者开启GPC的方式来防止注入,原理就是给预定义字符也就是单引号、双引号、反斜杠和NULL进行转义,但是如果某处使用了urldecode或者rawurldecode函数,如果开启了GPC来过滤,那么提交?id=1%25%27,%25经过url解码为%%27解码为单引号,那么成功引发注入,在代码审计的过程中要注意这两个函数。

3.防护

(1)魔术引号 gpc/rutime
在数据处理中主要有两条路线,一种是用户主动提交的,另外一种是用户被动接受的,
GPC主要对用户的POST、GET、cookie的值进行过滤,runtimer对从数据库或者文件中获取的数据进行过滤,但是对Int类型注入作用不大。

(2)过滤函数
addslashes与GPC的作用差不多。

mysql_real_esca## 二、SQL注入漏洞pe_string()、mysql_escape_string():主要对字符串进行过滤。

intval()、floatval()等:这类是将string类型转化为int类型的函数,将 1' union select强制转化为1,


三、XSS漏洞

推荐书籍:《XSS跨站脚本攻击剖析与防御》、《Web前端黑客技术揭秘》

1.分类

(1)反射型XSS
(2)存储型XSS

2.防御

过滤掉特殊字符即可:

  • 单引号(’)
  • 双引号(”)
  • 尖括号(<>)
  • 反斜杠(\)
  • 冒号(:)
  • and符(&)
  • #号(#)

四、CSRF漏洞

1.判断方法

(1)黑盒
在挖掘CSRF的时候可以先搭建好环境,打开几个有非静态操作的页面,抓包看看有没有token,如果没有token的话,在直接请求这个页面,不带referer。如果返回的数据还是一样的,那说明有可能有CSRF漏洞。
(2)白盒
看看几个核心文件里面有没有验证token和referer相关的代码。

2.防御

(1)Token验证
(2)验证码验证


五、文件操作漏洞

1.文件包含漏洞

文件包含函数:

  • include()
  • include_once()
  • require()
  • require_once()

2.文件读取(下载漏洞)

文件读取函数:

  • file_get_contents()
  • highlight_file()
  • fopen()
  • readfile()
  • fread()
  • fgetss()
  • fgets()
  • parse_ini_file()
  • show_source()
  • file()
  • 文件包含函数include以及php输入输出流php://filter

3.文件上传漏洞

  • 对文件名或扩展名未过滤或过滤不全
  • 黑名单存在绕过
  • 文件头、content-type验证绕过
  • 解析漏洞

4.文件删除漏洞

文件删除函数:

  • unlink()
  • 老版本下session_destroy()

5.防御

(1)对文件操作的权限管理要合理
(2)有些文件不需要直接传入文件名
(3)过滤掉文件跳转的一些符号
(4)文件上传尽量使用白名单过滤扩展名,保存上传的文件时利用时间戳+随机数MD5重命名


六、代码执行漏洞

1.常见函数

  • eval()
  • assert()
  • preg_replace()
  • call_user_func()
  • call_user_func_array()
  • array_map()

(1)eval 和 assert 函数

动态执行代码,参数直接就是php代码。

1
2
3
4
5
6
7
8
<?php
$a='aaa';
$b='bbb';
eval('$a=$b;');
echo $a;
?>

结果输出: bbb

(2)preg_replace函数
该函数作用是对字符串进行正则处理,它的参数和返回如下:
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit=-1 [, int &$count ] ] )
这段代码的含义是搜索$subject中匹配$pattern的部分,以$replacement进行代替,而当$pattern处即第一个参数存在e修饰符时,$replacement的值会被当作PHP代码来执行。

1
2
3
4
5
6
<?php
preg_replace("/\[(.*)\]/e", '\\1', $_GET['str']);
?>

当传入?str=[phpinfo()]
代码执行成功,回显phpinfo页面

(3)调用函数过滤不严
call_user_fun()和araay_map()等函数有调用其他函数的功能,其中一个参数作为要调用的函数名,如果这个传入的函数名可控, 就可以调用意外的函数来执行代码。

1
2
3
4
5
6
<?php
$b='phpinfo()';
call_user_func($_GET['a'], $b);
?>

当请求?a=assert时候,调用了assert函数,并将phpinfo()作为参数传入,执行了该代码。

(4)动态函数执行
php动态函数的写法为“变量(参数)”,如下例:

1
2
3
4
5
6
<?php
$_GET['a']($GET_['b']);
?>

上述代码意思时a的参数作为函数,b的参数作为函数的参数
即当传入?a=assert&b=phpinfo()时,相当于执行assert('phpinfo()')

2.防御

采用白名单过滤,或者结合正则表达式来进行白名单限制。


七、命令执行漏洞

1.命令执行函数:

  • system()
  • exec()
  • shell_exec()
  • passthru()
  • pcntl_exec()
  • popen()
  • proc_open()
  • 反引号(`),相当于调用shell_exec()

(1)system()、exec()、shell_exec()、passthru()以及反引号(`)可以直接传入命令并且函数会返回执行结果,其中system()函数会直接回显打印输出,不需要ehco也可以。

1
2
3
4
5
<?php
system('whoami');
?>

结果输出当前的WebServer用户

(2)popen()、proc_open()不会直接返回执行结果,而是返回一个文件指针,但是命令已经执行了。

1
2
3
4
5
<?php
popen('whoami >>D://2.txt', 'r');
?>

执行完可以在D盘根目录看到2.txt这个文件,内容为WebServer用户名

2.防御

(1)命令防注入函数

  • escapeshellcmd() 过滤整体命令,参数是一整条命令
  • escapeshellarg() 过滤参数,将参数限制在一对双引号里,确保参数为一个字符串,因此它会把双引号替换为空格

八、变量覆盖漏洞

1.引发函数:

  • extract()
  • parse_str()
  • import_request_variables()

(1)extract 函数
简单来说就是将数组中的键值对注册成变量。

1
2
3
4
5
6
7
8
<?php
$b=3;
$a=array('b'=>'1');
extract($a);
print_r($b);
?>

结果输出:1

原本变量$b的值为3,经过extract()函数对变量$a处理后,变量$b的值被覆盖为1。

(2)parse_str 函数
parse_str()函数作用是解析字符串并注册成变量,它在注册变量之前不会验证当前变量是否已经存在,所以会覆盖掉已有变量。

1
2
3
4
5
6
7
<?php
$b=1;
parse_str('b=2');
print_r($b);
?>

结果输出:2

(3)import_request_variables 函数
import_request_variables()函数是把GET、POST、COOKIE的参数注册成变量,用在register_globals被禁止的时候,需要PHP4.1至5.4之间的版本。

(4)$$ 变量覆盖

2.防御
(1)使用原始变量
(2)验证变量存在


九、逻辑处理漏洞

1.等于与存在判断绕过
(1)in_array()函数
(2)is_numeric函数
(3)双等于(==)和三等于( ===)

2.账户体系中的越权漏洞

3.未 exit 或 return 引发的安全问题

4.常见支付漏洞

5.防御
(1)深入熟悉业务逻辑
(2)熟悉函数的功能和差异


十、会话认证漏洞

1.cookie认证安全
(1)cookie的SQL注入
(2)伪造cookie

2.防御
(1)严格限制输入的异常字符已经避免用客户端提交的内容直接操作
(2)将cookie与session结合起来使用,并保证客户端不能操作敏感session参数
(3)敏感数据不要放在cookie中


十一、二次漏洞

1.什么是二次漏洞
需要先构造好利用代码写入网站保存,在第二次或多次请求后调用攻击代码触发或者修改配置触发的漏洞叫做而此漏洞。

(此漏洞较复杂,需之后进一步学习研究)