Fortify SCA运用了Fortify Security研究小组开发的Fortify Source Code Analyzers的相关规则(语意规则、配置规则、数据流规则、控制流规则、结构化规则)和Code Modeling规则类型(Alias rules、Allocation rules、Buffer Copy rules、Non-Returning rules、String Length rules)去分析安全漏洞中的源代码,本篇文章将对Foritify用户自定义规则的创建和使用进行简单介绍
假设我们现在有如下JAVA源代码文件:
package org.example;
public class HardPassword {
public String iamUser="Admin";
public String iamPassword="123456";
public String iampassword="123456";
public String iamPass="1234567";
public String iam="12345678";
public String password="123456789";
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return Password;
}
public void setPassword(String password) {
Password = password;
}
}
从上面我们可以看到这里对IAM的用户密码使用了硬编码,下面我们通过Fortify内置的规则编辑器CustomRulesEditor自定义规则来对源代码中的硬编码问题进行排查扫描,首先我们进入到Fortify的bin目录中运行CustomRulesEditor规则编辑器:
打开规则编辑器之后我们可以看到规则分类有以下四类:
我们直接保持默认的"Taint Flags/Rule Type"并选择"File->Generate Rule"来创建规则:
随后会显示自定义规则的引用模板,目前主要按照漏洞类型(Category)和规则类型(Rule Type)进行分类,但是不管是何种分类都可以大致分为数据污染源Tainted规则、数据控制流规则、数据传递规则、漏洞缺陷爆发的Sink规则
在这里我们选择漏洞类型(Category)中Password Management下的"Structural Rule for Password Management"模板
随后选择规则适用的编程语言:
随后填写用于匹配密码的正则表达式(备注:在这里要多做验证测试):
(?i)iampass(|wd|word)
配置规则存储路径:
随后可以看到根据模板生成的规则,其中总计6条匹配项,其中2项为匹配硬编码密码,另外4项为匹配空密码操作:
根据我们当前的规则目的我们无需去处理空密码,所以我们直接删除后面的四项,最后留下以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<RulePack xmlns="xmlns://www.fortifysoftware.com/schema/rules">
<RulePackID>86AF47A5-E7EF-4779-AB49-123A3EC90AE2</RulePackID>
<SKU>SKU-D:\Environment\FortifySCA\Core\config\customrules\IAM-HandCoded-rule</SKU>
<Name><![CDATA[D:\Environment\FortifySCA\Core\config\customrules\IAM-HandCoded-rule]]></Name>
<Version>1.0</Version>
<Description><![CDATA[]]></Description>
<Rules version="22.1.0">
<RuleDefinitions>
<StructuralRule formatVersion="22.1.0" language="java">
<RuleID>668C7FF9-A407-4EFD-B5E7-04A70E56A611</RuleID>
<VulnKingdom>Security Features</VulnKingdom>
<VulnCategory>Password Management</VulnCategory>
<VulnSubcategory>Hardcoded Password</VulnSubcategory>
<DefaultSeverity>4.0</DefaultSeverity>
<Description ref="desc.semantic.java.password_management_hardcoded_password">
<Explanation append="true"><![CDATA[This issue is being reported by a custom rule.]]></Explanation>
</Description>
<Predicate><![CDATA[
FieldAccess fa: fa.field.name matches "(?i)iampass(|wd|word)" and
fa in [AssignmentStatement: lhs.location is fa and not rhs.constantValue.null and not rhs.constantValue is [Null:] and not rhs.constantValue == ""] and fa.field is [Field f:]*
]]></Predicate>
</StructuralRule>
<StructuralRule formatVersion="22.1.0" language="java">
<RuleID>668C7FF9-A407-4EFD-B5E7-04A70E56A6111</RuleID>
<VulnKingdom>Security Features</VulnKingdom>
<VulnCategory>Password Management</VulnCategory>
<VulnSubcategory>Hardcoded Password</VulnSubcategory>
<DefaultSeverity>4.0</DefaultSeverity>
<Description ref="desc.semantic.java.password_management_hardcoded_password">
<Explanation append="true"><![CDATA[This issue is being reported by a custom rule.]]></Explanation>
</Description>
<Predicate><![CDATA[
VariableAccess va: va.variable.name matches "(?i)iampass(|wd|word)" and
va in [AssignmentStatement: lhs.location is va and not rhs.constantValue.null and not rhs.constantValue is [Null:] and not rhs.constantValue == ""] and va.variable is [Variable v:]*
]]></Predicate>
</StructuralRule>
</RuleDefinitions>
</Rules>
</RulePack>
首先将自定义规则保存到用户自定义规则目录中去(上面我们在创建时就直接保存到了自定义规则目录)——FortifySCA\Core\config\customrules
当然你也可以在${FortifyInstall}/Core/config/fortify-sca.properties进行配置自定义路径
随后启动Fortify代码扫描工具并配置加载自定义规则:
选择工程执行静态代码扫描:
随后开始执行扫描:
扫描结果如下:
在这里由于我们扫描的时候加载了默认的扫描规则,其中也包含了HardCoded Password规则所以有一部分是重复的,在验证的时候我们需要特别留意以下这里的RuleID是否和我们自定义的规则中的一致来确保我们自己定义的规则是有被加载且保证正常扫描执行到:
在这里我们为了规避默认的规则带来的影响我们可以对项目执行"重新扫描"并只勾选自定义规则:
随后得到如下结果,从中可以看到这里的大小写以及关键字的匹配都是我们预期想要的内容,所以至此规则测试完成且符合我们的预期
在完成上面的规则的测试之后我们还需要对规则进行进一步的优化处理,包括:规则名称、修复建议、
前面我们说过在加载规则的时候的那个路径是规则的名称而不是规则的路径,这里为了后期规则的区分我们对规则名称进行一次更改,变更的方式为更改规则中的"Name"标签属性:
随后可以看到再次加载规则的时候规则名称被成功更改:
在创建自定义规则时我们有两种选择来生成关于漏洞的描述:
A、引用Foritify官方的描述
首先我们需要确定要使用的描述的标识符,描述标识符位于https://vulncat.fortify.com/en/weakness,找到要使用的描述的标识符后将自定义规则的ref属性设置为Fortify描述的标识符
描述有点偏,位于每个描述的最下面部分,例如:
desc.semantic.java.password_management_hardcoded_password
随后变更规则,加入漏洞描述:
但是并没有什么效果:
<?xml version="1.0" encoding="UTF-8"?>
<RulePack xmlns="xmlns://www.fortifysoftware.com/schema/rules">
<RulePackID>86AF47A5-E7EF-4779-AB49-123A3EC90AE2</RulePackID>
<SKU>SKU-D:\Environment\FortifySCA\Core\config\customrules\IAM-HandCoded-rule</SKU>
<Name><![CDATA[IAM-HandCoded Password]]></Name>
<Version>1.0</Version>
<Description><![CDATA[]]></Description>
<Rules version="22.1.0">
<RuleDefinitions>
<StructuralRule formatVersion="22.1.0" language="java">
<RuleID>668C7FF9-A407-4EFD-B5E7-04A70E56A611</RuleID>
<VulnKingdom>Security Features</VulnKingdom>
<VulnCategory>Password Management</VulnCategory>
<VulnSubcategory>Hardcoded Password</VulnSubcategory>
<DefaultSeverity>4.0</DefaultSeverity>
<Description ref="desc.semantic.java.password_management_hardcoded_password">
</Description>
<Predicate><![CDATA[
FieldAccess fa: fa.field.name matches "(?i)iampass(|wd|word)" and
fa in [AssignmentStatement: lhs.location is fa and not rhs.constantValue.null and not rhs.constantValue is [Null:] and not rhs.constantValue == ""] and fa.field is [Field f:]*
]]></Predicate>
</StructuralRule>
<StructuralRule formatVersion="22.1.0" language="java">
<RuleID>668C7FF9-A407-4EFD-B5E7-04A70E56A6111</RuleID>
<VulnKingdom>Security Features</VulnKingdom>
<VulnCategory>Password Management</VulnCategory>
<VulnSubcategory>Hardcoded Password</VulnSubcategory>
<DefaultSeverity>4.0</DefaultSeverity>
<Description ref="desc.semantic.java.password_management_hardcoded_password">
<Explanation append="true"><![CDATA[This issue is being reported by a custom rule.]]></Explanation>
</Description>
<Predicate><![CDATA[
VariableAccess va: va.variable.name matches "(?i)iampass(|wd|word)" and
va in [AssignmentStatement: lhs.location is va and not rhs.constantValue.null and not rhs.constantValue is [Null:] and not rhs.constantValue == ""] and va.variable is [Variable v:]*
]]></Predicate>
</StructuralRule>
</RuleDefinitions>
</Rules>
</RulePack>
B、用户自定义描述
关于用户的自定义描述我们可以直接借助自定义规则编辑器来实现:
<Abstract>:漏洞摘要
<Explanation>:漏洞描述
<Recommendations>:修复建议
随后可以看到如下效果:
变更后的规则如下:
<?xml version="1.0" encoding="UTF-8"?>
<RulePack xmlns="xmlns://www.fortifysoftware.com/schema/rules">
<RulePackID>86AF47A5-E7EF-4779-AB49-123A3EC90AE2</RulePackID>
<SKU>SKU-D:\Environment\FortifySCA\Core\config\customrules\IAM-HandCoded-rule</SKU>
<Name><![CDATA[IAM-HandCoded Password]]></Name>
<Version>1.0</Version>
<Description><![CDATA[]]></Description>
<Rules version="22.1.0">
<RuleDefinitions>
<StructuralRule formatVersion="22.1.0" language="java">
<RuleID>668C7FF9-A407-4EFD-B5E7-04A70E56A611</RuleID>
<VulnKingdom>Security Features</VulnKingdom>
<VulnCategory>Password Management</VulnCategory>
<VulnSubcategory>Hardcoded Password</VulnSubcategory>
<DefaultSeverity>4.0</DefaultSeverity>
<Description>
<Abstract><![CDATA[IAM账号密码硬编码]]></Abstract>
<Explanation><![CDATA[IAM账号密码硬编码在源代码文件中存在安全风险]]></Explanation>
<Recommendations><![CDATA[禁止将IAM账号密码硬编码在源代码文件中]]></Recommendations>
</Description>
<Predicate><![CDATA[
FieldAccess fa: fa.field.name matches "(?i)iampass(|wd|word)" and
fa in [AssignmentStatement: lhs.location is fa and not rhs.constantValue.null and not rhs.constantValue is [Null:] and not rhs.constantValue == ""] and fa.field is [Field f:]*
]]></Predicate>
</StructuralRule>
<StructuralRule formatVersion="22.1.0" language="java">
<RuleID>668C7FF9-A407-4EFD-B5E7-04A70E56A6111</RuleID>
<VulnKingdom>Security Features</VulnKingdom>
<VulnCategory>Password Management</VulnCategory>
<VulnSubcategory>Hardcoded Password</VulnSubcategory>
<DefaultSeverity>4.0</DefaultSeverity>
<Description >
<Abstract><![CDATA[IAM账号密码硬编码]]></Abstract>
<Explanation><![CDATA[IAM账号密码硬编码在源代码文件中存在安全风险]]></Explanation>
<Recommendations><![CDATA[禁止将IAM账号密码硬编码在源代码文件中]]></Recommendations>
</Description>
<Predicate><![CDATA[
VariableAccess va: va.variable.name matches "(?i)iampass(|wd|word)" and
va in [AssignmentStatement: lhs.location is va and not rhs.constantValue.null and not rhs.constantValue is [Null:] and not rhs.constantValue == ""] and va.variable is [Variable v:]*
]]></Predicate>
</StructuralRule>
</RuleDefinitions>
</Rules>
</RulePack>
场景描述:代码审计过程中发现Foritify的某一条规则频繁错误提报,此时我们可以通过自定义规则对原规则进行覆盖处理
具体实现:
Step 1:首先记录当前规则ID
C204F020-1CA1-4c25-A6CB-BAA69CA2DA0B
Step 2:新建一个规则覆盖当前规则ID
随便胡乱输入匹配规则
生成规则如下:
更改当前Rule ID:
随后导入规则执行扫描操作:
随后可以看到记录被移除
场景描述:代码审计过程中发现存在具备特征的函数名称时我们可以为函数名称制定规则来实现全量检索,在检索时我们的终极目的时检索定位处所有调用该函数的位置点
具体实现:
示例代码:
package org.example;
public class SecTest {
public int evil(int a, int b) {
//do something
return a+b;
}
public int verify() {
//do something
return evil(1,2);
}
}
新建扫描规则
选择语言:
(?i)evil
最终规则如下:
<?xml version="1.0" encoding="UTF-8"?>
<RulePack xmlns="xmlns://www.fortifysoftware.com/schema/rules">
<RulePackID>96F34CB0-3E78-4410-B6FE-E4D87859E682</RulePackID>
<SKU>SKU-D:\Environment\FortifySCA\Core\config\customrules\evil-rule</SKU>
<Name><![CDATA[D:\Environment\FortifySCA\Core\config\customrules\evil-rule]]></Name>
<Version>1.0</Version>
<Description><![CDATA[]]></Description>
<Rules version="22.1.0">
<RuleDefinitions>
<SemanticRule formatVersion="22.1.0" language="java">
<MetaInfo>
<Group name="Accuracy">5.0</Group>
<Group name="Impact">5.0</Group>
<Group name="RemediationEffort">15.0</Group>
<Group name="Probability">5.0</Group>
</MetaInfo>
<RuleID>5C3B2AC3-9F40-4CF3-9942-88E7C76ED780</RuleID>
<VulnCategory>FunSecScan</VulnCategory>
<DefaultSeverity>4.0</DefaultSeverity>
<Description/>
<Type>default</Type>
<FunctionIdentifier>
<NamespaceName>
<Pattern>.*</Pattern>
</NamespaceName>
<ClassName>
<Pattern>.*</Pattern>
</ClassName>
<FunctionName>
<Pattern>(?i)evil</Pattern>
</FunctionName>
<ApplyTo implements="true" overrides="true" extends="true"/>
</FunctionIdentifier>
</SemanticRule>
</RuleDefinitions>
</Rules>
</RulePack>
随后进行扫描
紧接着可以看到扫描结果如下:
备注:这里一定一定要切记,测试代码一定要符合JAVA标准语法且能够再IDEA中正常编译,不要随意乱写,否则直接扫描不出来东西,笔者这里就是在测试的时候文件名称和类名称没写一致导致始终死活出不来后期才发现这个问题,主要的原因是在使用Fortify进行扫描的时候会将源代码进行一次编译处理,如果你代码有问题,那么在编译阶段就会直接挂,根本走不到所谓的代码规则的匹配阶段,更不用说扫描出漏洞来了~
场景描述:企业为了对用户输入的数据进行统一处理大多数情况下回制定统一的过滤函数或者验证函数来进行验证,在这种情况下我们需要对此类函数加白
具体实现:
这一步就有点意思了,我们在这里可以对调用的返回值加白或者函数整体加白使其调用点加白,另外也可以指定参数加白来适配各类场景,在这里我们选择返回值加白:
最终规则如下,数据污染溯源中如果遇到evil则会直接跳过不再报相关问题
<?xml version="1.0" encoding="UTF-8"?>
<RulePack xmlns="xmlns://www.fortifysoftware.com/schema/rules">
<RulePackID>4F7E0448-CB78-4B38-BB9C-CD99204A9DF0</RulePackID>
<SKU>SKU-D:\Environment\FortifySCA\Core\config\customrules\custom-rule</SKU>
<Name><![CDATA[D:\Environment\FortifySCA\Core\config\customrules\custom-rule]]></Name>
<Version>1.0</Version>
<Description><![CDATA[]]></Description>
<Rules version="22.1.0">
<RuleDefinitions>
<DataflowCleanseRule formatVersion="22.1.0" language="java">
<RuleID>937B7C98-A88E-4D73-84B9-09A02D8D3534</RuleID>
<FunctionIdentifier>
<NamespaceName>
<Pattern>.*</Pattern>
</NamespaceName>
<ClassName>
<Pattern>.*</Pattern>
</ClassName>
<FunctionName>
<Pattern>verify</Pattern>
</FunctionName>
<ApplyTo implements="true" overrides="true" extends="true"/>
</FunctionIdentifier>
<OutArguments>return</OutArguments>
</DataflowCleanseRule>
</RuleDefinitions>
</Rules>
</RulePack>
Foritify自定义规则适用频次最多的应该属于SemanticRule,如果要使用更为复杂的数据流规则、控制流规则、内容规则等则自行进行配置测试即可,建议结合具体的漏洞代码展开为好~