一、代码审查方法
(1)从进入点开始追踪用户向应用程序提交的数据,审查负责处理这些数据的代码;
(2)在代码中搜索表示存在常见漏洞的签名,并审查这些签名,确定某个漏洞是否确实存在;
(3)对内在危险的代码进行逐行审查,理解应用程序的逻辑,并确定其中存在的所有问题
(4)应用程序可能扩展了类库与接口,在进行审查之前应了解这些定制的范围 并相应调整审查方法
需要进行仔细审查的组件包括:关键安全机制(验证、会话管理、访问控制、输入确认)、外部组件接口以及任何使用本地代码的情况 。
二、常见漏洞签名
(1)XSS
String link = “<a href=” + HttpUtility.UrlDecode(Request.QueryString
[“refURL”]) + “&SiteID=” + SiteId + “&Path=” + HttpUtility.UrlEncode
(Request.QueryString[“Path”]) + “</a>”;
objCell.InnerHtml = link;
(2)SQL注入
直接使用用户输入
StringBuilder SqlQuery = newStringBuilder(“SELECT name, accno FROM
TblCustomers WHERE “ + SqlWhere);
if(Request.QueryString[“CID”] != null &&
Request.QueryString[“PageId”] == “2”)
{
SqlQuery.Append(“ AND CustomerID = “);
}
...
在代码中搜索关键字:
“SELECT
“INSERT
“DELETE
“ AND
“ OR
“ WHERE
“ ORDER BY
(3)路径遍历
通过在代码中搜索任何与文件名有关的查询字符串参数(如AttachName等)以及在相关语言中搜寻所有文件API并检查提交它们的参数,就可以迅速确定相关的功能。
public byte[] GetAttachment(HttpRequest Request)
{
FileStream fsAttachment = new FileStream(SpreadsheetPath +
HttpUtility.UrlDecode(Request.QueryString[“AttachName”]),
FileMode.Open, FileAccess.Read, FileShare.Read);
byte[] bAttachment = new byte[fsAttachment.Length];
fsAttachment.Read(FileContent, 0,
Convert.ToInt32(fsAttachment.Length,
CultureInfo.CurrentCulture));
fsAttachment.Close();
(4)任意重定向
private void handleCancel()
{
httpResponse.Redirect(HttpUtility.UrlDecode(Request.QueryString[
“refURL”]) + “&SiteCode=” +
Request.QueryString[“SiteCode”].ToString() +
“&UserId=” + Request.QueryString[“UserId”].ToString());
}
JS:
url = document.URL;
index = url.indexOf(‘?redir=’);
target = unescape(url.substring(index + 7, url.length));
target = unescape(target);
if ((index = target.indexOf(‘//’)) > 0) {
target = target.substring (index + 2, target.length);
index = target.indexOf(‘/’);
target = target.substring(index, target.length);
}
target = unescape(target);
document.location = target;
(5)OS命令注入
void send_mail(const char *message, const char *addr)
{
char sendMailCmd[4096];
snprintf(sendMailCmd, 4096, “echo ‘%s’ | sendmail %s”, message, addr);
system(sendMailCmd);
return;
}
(6)后门密码
private UserProfile validateUser(String username, String password)
{
UserProfile up = getUserProfile(username);
if (checkCredentials(up, password) ||
“oculiomnium”.equals(password))
return up;
return null;
}
(7)本地代码漏洞
a. 缓冲区溢出
BOOL CALLBACK CFiles::EnumNameProc(LPTSTR pszName)
{
char strFileName[MAX_PATH];
strcpy(strFileName, pszName);
...
}
b. 整数漏洞
有符号的整数与无符号的整数进行比较,就会有问题
BOOL CALLBACK CFiles::EnumNameProc(LPTSTR pszName, int len)
{
char strFileName[MAX_PATH];
if (len < sizeof(strFileName))
strcpy(strFileName, pszName);
...
}
c. 格式化字符串漏洞
通过检查printf与FormatMessage系列函数的用法,如果发现格式化字符串参数并未硬编码,而是由用户控制,就可以确定这类漏洞。
(8)源代码注释
关键词:bug、 problem、 bad、 hope、 todo、 fix、 overflow、 crash、 inject、 xss、 trust
三、JAVA平台
(1)确定用户提交的数据
JAVA应用程序通过javax.servlet.http.HttpServletRequest接口获取用户提交的输入,该接口对javax.servlet.servletRequest接口进行了扩展。API如下:
(2)会话交互
javax.servlet.http.HttpSession接口保存和检索当前会话中的信息,每会话存储是字符串名称与对象值之间的一个映射。
(3)潜在危险的API
1、文件访问
主要的类为有:
java.io.File
java.io.FileInputStream
java.io.FileOutputStream
java.io.FileReader
java.io.FileWriter
2、数据库访问
java.sql.Connection.createStatement
java.sql.Statement.execute
java.sql.Statement.executeQuery
以下API更可靠,允许应用程序创建一个预先编译的SQL语句,并以可靠且类型安全的方式指定它的参数占位符的值:
java.sql.Connection.prepareStatement
java.sql.PreparedStatement.setString
java.sql.PreparedStatement.setInt
java.sql.PreparedStatement.setBoolean
java.sql.PreparedStatement.setObject
java.sql.PreparedStatement.execute
java.sql.PreparedStatement.executeQuery
3、动态代码执行
4、OS命令执行
java.lang.runtime.Runtime.getRuntime
java.lang.runtime.Runtime.exec
攻击成功:
String userinput = “calc”;
Runtime.getRuntime.exec(userinput);
攻击失败:
String userinput = “| calc”;
Runtime.getRuntime.exec(“notepad “ + userinput); 【notepad后有空格】
攻击成功 :
String userinput = “\\..\\system32\\calc”;
Runtime.getRuntime().exec(“notepad” + userinput); 【notepad后无空格】
5、URL重定向
javax.servlet.http.HttpServletResponse.sendRedirect
javax.servlet.http.HttpServletResponse.setStatus
javax.servlet.http.HttpServletResponse.addHeader
6、套接字
java.net.Socket
(4)配置Java环境
web.xml是配置文件。主要配置项有:
login-config:配置认证细节,两类验证分别是基于表单的(页面由form-login-page元素指定)与在auth-method元素中指定的Basic-Auth或Client-Cert,如果使用基于表单的认证,指定的表单必须有j_security_check动作,须提交j_username与j_password参数。
security-constraint:如果定义了login-config,就可以使用security-constraint元素限定资源。这个元素可用于定义受保护的资源,可以使用url-pattern元素定义资源集。如:<url-pattern>/admin/*</url-pattern>,分别在role-name与principal-name元素中定义的角色与主要用户可以访问的资源
session-config:会话超时时间 (分钟)
error-page:error-code与exception-type分别处理HTTP错误代码与Java异常
init-param:配置初始化参数,listings应设置为false; debug应设置为0
除web.xml文件外,可能还包含次要配置文件weblogic.xml文件等,都应检查
四、ASP.NET
(1)确定用户提交的数据
ASP.NET通过System.Web.HttpRequest类获取用户提交的输入。
(2)会话交互
使用session属性 保存和检索当前会话中的信息。
Session["MyName"] = txtMyName.Text;
lblWelcome.Text = "Welcome "+Session["MyName"];
用户个性化配置中,数据以下列方式保存和检索:
Profile.MyName = txtMyName.Text;
lblWelcome.Text = "Welcome "+Profile.MyName;
System.Web.SessionStage.HttpSessionState 类也用于保存和检索会话中的信息。
(3)潜在危险的API
1、 文件访问
System.IO.File类
下面的类常用于读取与写入文件内容:
System.IO.FileStream
System.IO.StreamReader
System.IO.StreamWriter
以下两个直接拼接用户输入 的操作都会造成路径遍历漏洞。
string userinput = "..\\boot.ini";
FileStream fs = File.Open("C:\\temp\\" + userinput,FileMode.OpenOrCreate);
string userinput = “..\\foo.txt”;
FileStream fs = new FileStream(“F:\\tmp\\” + userinput,FileMode.OpenOrCreate);
2、数据库访问
System.Data.SqlClient.SqlCommand
System.Data.SqlClient.SqlDataAdapter
System.Data.Oledb.OleDbCommand
System.Data.Odbc.OdbcCommand
System.Data.SqlServerCe.SqlCeCommand
通过它们的Parameters属性支持预处理语句,允许应用程序创建一个包含参数占位符的SQL语句,并以可靠且类型安全的方式设定这些占位符的值。
3、动态代码执行
Eval函数接受一个包含VBScript表达式的字符串自变量。
Execute和ExecuteGlobal接受一个包含ASP代码的字符串。
4、OS命令执行
System.Diagnostics.Start.Process
System.Diagnostics.Start.ProcessStartInfo
5、URL重定向
System.Web.HttpResponse.Redirect
System.Web.HttpResponse.Status
System.Web.HttpResponse.StatusCode
System.Web.HttpResponse.AddHeader
System.Web.HttpResponse.AppendHeader
Server.Transfer
6、套接字
System.Net.Sockets.Socket
(4)配置 ASP.NET环境
we.config XML文件包含ASP.NET环境的配置设置。
反常情况:
a. $GLOBALS是一个包含在脚本全局范围内定义的所有变量的引用的数组。使用它可以根据名称访问其他变量
b. 如果配置 指令 register_globals被激活,PHP会为所有请求参数(即$_REQUEST数组中的全部数据)建立全局变量。这表示,应用程序可通过与相关参数相同的名称引用一个变量,从而访问用户输入。
c. PHP还在$_SERVER数组中增加了一个数据,用于处理在请求中收到的任何定制HTTP消息 头。如提交消息头Foo: Bar,则生成:$_SERVER['HTTP_FOO'] = "Bar"
d. 名称包含下标的输入参数被自动转换为数组。如:https://xxx/xxx.php?query[a]=foo&query[b]=bar,将使$_GET['query'] 变量的值转换成一个包含两个成员 的数组 。
(2)会话交互
PHP使用$_SESSION数组保存和检索用户会话中的信息。如:
$_SESSION[‘MyName’] = $_GET[‘username’]; // store user’s name
echo “Welcome “ . $_SESSION[‘MyName’]; // retrieve user’s name
$HTTP_SESSION_VARS数组的用法与上面的数组相同。如果register_globals被激活,那么全局变量将通过以下方式保存在当前会话中:
$MyName = $_GET[‘username’];
session_register(“MyName”);
PS: register_globals 在PHP 6已经删除。
(3)潜在危险的API
1、文件访问
fopen、readfile、 file、 fpassthru、 gzopen、gzfile、gzpassthru、 readgzfile、 copy、rename、rmdir、mkdir、unlink、file_get_contents、file_put_contents、parse_ini_file
下面的函数可用于包含并执行一个指定的PHP脚本:
include、include_once、require、require_once、virtual
远程文件检索协议 :
PHP 5.2以后引入了一个新选项allow_url_include,这个默认的配置 防止前面提到的方法在调用 文件包含函数时用于指定一个远程文件。
2、数据库访问
mysql_query、mssql_query、pg_query
下面的函数用于创建预处理语句,允许应用程序建立一个包含参数占位符的SQL查询,并以可靠而且类型安全的方式设定这些占位符的值:
mysqli->prepare、stmt->prepare、stmt->bind_param、stmt->execute、odbc_prepare
3、动态代码执行
eval、call_user_func、call_user_func_array、call_user_method、call_user_method_array、create_function
分号可以连接几个语句。
搜索与替代正则表达式的preg_replace函数,如果以/e选项调用,可用于运行一段特殊的PHP代码。
PHP可以通过一个包含函数名称的变量动态调用该函数,下面的代码将调用在查询字符串func参数中指定的函数:<?php $var=$_GET[‘func’]; $var();?> 用户可以通过修改func参数的值,使应用程序调用任意一个没有参数的函数,如phpinfo.
4、OS命令执行
exec、passthru、popen、proc_open、shell_exec、system、反单引号 (`)
| 可以连接命令
5、URL重定向
http_redirect、header、HttpMessage::setResponseCode、HttpMessage::setHeaders
一般用http_redirect,但是header也可以,如header("Location:/target.php");
6、套接字
socket_create、socket_connect、socket_write、socket_send、socket_recv、fsockopen、 pfsockopen
fsockopen与 pfsockopen 可用于打开连接指定主机与端口的套接字,并返回一个可用在fwrite和fgets等标准文件函数中的文件指针。
7、配置 PHP环境(php.ini)
(1)使用全局变量注册
如果register_globals被激活,PHP会为所有请求参数建立全局变量,如果PHP不要求变量在使用前被初始化,就会导致安全漏洞 。
PS: register_globals 在PHP 6已经删除。
(2)安全模式
safe_mode被激活后,PHP会对一些危险函数做限制:
a. shell_exec被禁用
b. mail函数的additional_parameters参数被禁用
c. exec函数仅能执行safe_mode_exec_dir指定目标下的可执行程序,命令字符串中的元字符被自动转义
PS: 安全模式在PHP 6已经删除。
(3)magic quotes
虽然激活magic_quotes_gpc指令会将请求中包含的任何单引号、双引号、反斜线和空字符都会有生个反斜线自动 转义,但是由于其局限性与不规则性,建议禁用该 选项。
PS: 在PHP 6已经删除。
(4)其他选项:
六、Perl
CGI.pm 是最常用于创建Web应用程序的Perl模块
(1)确定用户提交的数据
(2)会话交互
Perl模块CGISession.pm对模块CGI.pm进行扩展,为会话追踪与数据存储提供支持:
$q->session_data(“MyName”=>param(“username”)); // store user’s name
print “Welcome “ . $q->session_data(“MyName”); // retrieve user’s name
(3)潜在危险的API
1、文件访问
open与sysopen
如果文件名参数的开头或结尾为管道符(|),就可以执行任意命令。
2、数据库访问
selectall_arrayref函数 用于向数据库发送一个查询,并以一系列数组的形式检索查询结果。
do 函数 用于执行一个查询,并返回受影响的行的数量。
prepare与execute用于创建预处理语句,同样的会添加占位符,以防止SQL注入。
3、动态代码执行
eval,分号分隔符连接语句
4、OS命令执行
system、exec、qx、反单引号(`)
5、URL 重定向
redirect函数
6、套接字
socket与connect
7、配置Perl环境
污染模式(taint mode):#!/usr/bin/perl -T
当以taint 模式运行时,解释器会追踪该程序以外提交的每一个输入,并把它当做被污染的输入处理。如果另一个变量根据一个受污染的数据分配它的值,那么Perl也认为它受到污染。
这个模式旨在防止许多类型的漏洞,但只有当开发者使用适当的正则表达式从被污染的输入中提取“清洁“的数据时它才会有效。
七、JavaScript
重点审查以下API:
八、数据库代码组件
1、SQL注入
不同的数据库平台使用不同的方法动态执行包含SQL语句的字符串:
MS-SQL — EXEC
Oracle — EXECUTE IMMEDIATE
Sybase — EXEC
DB2 — EXEC SQL
在Oracle中,存储过程默认在定义者权限而非调用者权限下运行,因此,如果应用程序使用一个低权限帐户访问数据库,并且使用DBA建立存储过程,攻击者就可以利用某个过程中存在的SQL注入漏洞提升自己的权限,并执行任意数据库查询。
2、调用危险函数
a. Powerful default stored procedures in MS-SQL and Sybase that allow execution of commands, registry access, and so on
b. Functions that provide access to the file system
c. User-defi ned functions that link to libraries outside the database
d. Functions that result in network access, such as through OpenRowSet in MS-SQL or a database link in Oracle
九、代码浏览工具
Source Insight