CWE-129: Improper Validation of Array Index(数组索引验证不当)

 

 ID: 129

类型:基础
结构:简单

状态:草稿

 

描述

产品在计算或使用数组索引时使用不受信任的输入,但产品不会验证或错误地验证索引,以确保索引引用数组中的有效位置。

关联视图

与“研究层面”视图(CWE-1000)相关

 “公开弱点简图”视图(CWE-1003)相关

与“开发层面”视图(CWE-699)相关

引入模式

阶段

说明

实现

 

应用平台

 语言

C (经常出现)

C++ (经常出现)

Class: 语言独立 (出现的可能性不确定)

后果

范围

冲击

可能性

完整性
可利用性

技术冲击: DoS: 崩溃、退出或重启

如果值超出有效内存区域,使用数组边界之外的索引很可能导致相关内存和指令损坏,从而导致崩溃。

 

完整性

技术冲击: 修改内存

如果损坏的内存是数据,而不是指令,系统将继续以不正确的值运行。

 

保密性
完整性

技术冲击: 修改内存; 读内存

使用数组边界之外的索引也可以触发边界外的读或写操作,或者对错误对象执行操作;例如,“缓冲区溢出”并不总是结果。这可能导致敏感数据暴露或修改。

 

完整性
保密性
可利用性

技术冲击: 执行未获授权的代码或命令

.如果攻击者可以有效地控制可访问的内存,则可以执行任意代码,就像标准缓冲区溢出一样,如果可以控制精确索引,则可能不使用大输入。

 

完整性
可利用性
保密性

技术冲击: DoS: 崩溃、推出或者重启: 执行未获授权的代码或命令; Read Memory; 修改内存

单个故障可能允许数组索引溢出(CWE-788)或下溢(CWE-786)。接下来会发生什么取决于超出界限执行的操作类型,但可能暴露敏感信息、导致系统崩溃或可能导致任意代码执行。

 

被利用的可能性:

示例

例1

在下面的代码段中,使用一个不受信任的整数值引用数组中的对象。

(问题代码)

Example Language: Java 

public String getValue(int index) {

return array[index];

}

如果索引超出数组的范围,则可能导致引发arrayIndexOutofBounds异常。

例2

下面的示例采用用户提供的值来分配对象数组,然后对该数组进行操作。

(问题代码)

Example Language: Java 

private void buildList ( int untrustedListSize ){

if ( 0 > untrustedListSize ){

die("Negative value supplied for list size, die evil hacker!");

}
Widget[] list = new Widget [ untrustedListSize ];
list[0] = new Widget();

}

此示例尝试从用户指定的值构建列表,甚至检查以确保提供非负值。但是,如果提供了一个0值,代码将构建一个大小为0的数组,然后尝试在第一个位置存储一个新的小部件,从而引发异常。

例3

在下面的代码中,该方法从特定数组索引位置的数组中检索一个值,该值作为该方法的输入参数给定。

(问题代码)

Example Language:

int getValueFromArray(int *array, int len, int index) {


int value;

// check that the array index is less than the maximum

// length of the array
if (index < len) {


// get the value at the specified index of the array
value = array[index];

}
// if array index is invalid then output error message

// and return value indicating error
else {

printf("Value is: %d\n", array[index]);
value = -1;

}

return value;

}

但是,此方法只验证给定的数组索引是否小于数组的最大长度,但不检查最小值(CWE-839)。这将允许接受负值作为输入数组索引,这将导致越界读取(CWE-125),并允许访问敏感内存。应检查输入数组索引,以验证是否在数组所需的最大和最小范围内(CWE-129)。在这个例子中,if语句应该修改为包含一个最小范围检查,如下所示。

(正确代码)

Example Language:


...

// check that the array index is within the correct

// range of values for the array
if (index >= 0 && index < len) {

...

例4

以下示例检索POP3邮件服务器的邮件大小。消息大小从一个套接字中检索,该套接字在缓冲区中返回消息编号和消息大小、消息编号(num)和大小(size),并使用数组索引的消息编号将消息大小放入数组中。

(问题代码)

Example Language:


/* capture the sizes of all messages */
int getsizes(int sock, int count, int *sizes) {

...
char buf[BUFFER_SIZE];
int ok;
int num, size;

// read values from socket and added to sizes array
while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
{


// continue read from socket until buf only contains '.'
if (DOTLINE(buf))

break;


else if (sscanf(buf, "%d %d", &num, &size) == 2)

sizes[num - 1] = size;

}

...

}

在本例中,从缓冲区中检索到的消息编号可能是一个超出数组允许索引范围的值,也可能是一个负数。如果不正确验证用于数组索引的值,可能会发生数组溢出,并可能导致对内存地址的未授权访问和系统崩溃。应验证数组索引的值,以确保它在数组的允许索引范围内,如下面的代码所示。

(正确代码)

Example Language:


/* capture the sizes of all messages */
int getsizes(int sock, int count, int *sizes) {

...
char buf[BUFFER_SIZE];
int ok;
int num, size;

// read values from socket and added to sizes array
while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
{


// continue read from socket until buf only contains '.'
if (DOTLINE(buf))

break;


else if (sscanf(buf, "%d %d", &num, &size) == 2) {

if (num > 0 && num <= (unsigned)count)

sizes[num - 1] = size;


else


/* warn about possible attempt to induce buffer overflow */
report(stderr, "Warning: ignoring bogus data for message sizes returned by server.\n");

}

}

...

}

例5

在下面的示例中,从Web服务servlet调用displayProductSummary方法,以检索要显示给用户的产品摘要信息。servlet从用户获取产品编号的整数值,并将其传递给displayProductSummary方法。displayProductSummary方法将产品编号的整数值传递给getProductSummary方法,该方法使用产品编号的整数值作为数组索引,从包含项目摘要的数组对象中获取产品摘要。

(问题代码)

Example Language: Java 


// Method called from servlet to obtain product information
public String displayProductSummary(int index) {

String productSummary = new String("");

try {

String productSummary = getProductSummary(index);



} catch (Exception ex) {...}

return productSummary;

}

public String getProductSummary(int index) {

return products[index];

}

在此示例中,用户提供的用作数组索引的整数值可能超出数组的允许索引范围,这可能会提供意外结果或导致应用程序失败。应验证用于数组索引的整数值,以确保它在以下代码中所允许的数组索引范围内。

(正确代码)

Example Language: Java 


// Method called from servlet to obtain product information
public String displayProductSummary(int index) {

String productSummary = new String("");

try {

String productSummary = getProductSummary(index);



} catch (Exception ex) {...}

return productSummary;

}

public String getProductSummary(int index) {

String productSummary = "";

if ((index >= 0) && (index < MAX_PRODUCTS)) {

productSummary = products[index];

}
else {

System.err.println("index is out of bounds");
throw new IndexOutOfBoundsException();

}

return productSummary;

}

Java中的另一种选择是使用一个集合对象,例如ARARYLIST,如果试图访问超出界限的数组索引,将自动生成异常。

(正确代码)

Example Language: Java 

ArrayList productArray = new ArrayList(MAX_PRODUCTS);
...
try {

productSummary = (String) productArray.get(index);

} catch (IndexOutOfBoundsException ex) {...}

例6

下面的示例要求用户在数组中输入偏移量以选择项。

(问题代码)

Example Language:


int main (int argc, char **argv) {

char *items[] = {"boat", "car", "truck", "train"};
int index = GetUntrustedOffset();
printf("You selected %s\n", items[index-1]);

}

程序员允许用户在列表中指定要选择的元素,但是攻击者可以提供越界偏移量,从而导致缓冲区过度读取(CWE-126)。

应对措施

阶段: 架构与设计

策略: 验证输入

使用验证输入框架,如struts或owasp esapi validation api。如果您使用Struts,请注意CWE-101类别所涵盖的弱点。

阶段: 架构与设计

策略: 库或者框架

使用验证输入框架,如struts或owasp esapi validation api。如果您使用Struts,请注意CWE-101类别所涵盖的弱点。

阶段: 架构与设计

对于在客户端执行的任何安全检查,请确保这些检查在服务器端重复,以避免CWE-602。攻击者可以通过在执行检查后修改值或更改客户端以完全删除客户端检查来绕过客户端检查。然后,这些修改后的值将提交给服务器。

尽管客户端检查在服务器端安全性方面提供的好处微乎其微,但它们仍然有用。首先,它们可以支持入侵检测。如果服务器接收到的输入本应被客户机拒绝,那么它可能表示有攻击。第二,客户端错误检查可以向用户提供关于有效输入预期的有用反馈。第三,对于意外的输入错误,服务器端处理时间可能会减少,尽管这通常是一个很小的节省。

阶段: 需求

策略: 语言选择

使用不允许出现这种弱点的语言,或提供使这种弱点更容易避免的结构。

例如,ada允许程序员约束变量的值,如Java和rubi将允许程序员在访问越界索引时处理异常。

阶段: 操作

策略: 强化环境

使用随机排列程序可执行文件和库在内存中的位置的特性或扩展来运行或编译软件。因为这使得地址不可预测,所以可以防止攻击者可靠地跳到可利用的代码上。

 

示例包括地址空间布局随机化(ASLR)[Ref-58][Ref-60]和位置独立可执行文件(Pie)[Ref-64]。

有效性: 深度防御

注意:这不是一个完整的解决方案。但是,它强制攻击者猜测一个未知值,该值会改变每个程序的执行。此外,攻击仍然可能导致拒绝服务,因为典型的响应是退出应用程序。

阶段: 操作

策略: 强化环境

使用提供数据执行保护(NX)或其等效设备[Ref-60][Ref-61]的CPU和操作系统。

有效性: 深度防御

注意:这不是一个完整的解决方案,因为缓冲区溢出可以用来覆盖附近的变量,以危险的方式修改软件的状态。此外,它不能用于需要自我修改代码的情况。最后,攻击仍然可能导致拒绝服务,因为典型的响应是退出应用程序。

阶段: 实现

策略: 验证输入

假设所有输入都是恶意的。使用“接受已知良好”验证输入策略,即使用严格符合规范的可接受输入白名单。拒绝任何不严格符合规范的输入,或者将其转换为符合规范的输入。

在执行验证输入时,考虑所有潜在的相关属性,包括长度、输入类型、可接受值的完整范围、丢失或额外输入、语法、相关字段的一致性以及对业务规则的一致性。作为业务规则逻辑的一个例子,“boat”在语法上可能有效,因为它只包含字母数字字符,但如果输入只包含诸如“red”或“blue”之类的颜色,则它是无效的。

不要完全依赖于查找恶意或格式错误的输入(即,不要依赖黑名单)。黑名单可能会遗漏至少一个不需要的输入,特别是当代码的环境发生变化时。这可以给攻击者足够的空间来绕过预期的验证。然而,黑名单对于检测潜在的攻击或者确定哪些输入是如此畸形以至于应该直接拒绝它们是有用的。

访问用户控制的数组索引时,请使用目标数组中的严格值范围。确保不允许使用负值。也就是说,验证可接受值范围的最小值和最大值。

阶段: 实现

当调用跨越语言边界的代码(如从解释语言到本机代码)时,要特别小心地验证所有输入。这可能会在语言边界之间创建一个意外的交互。确保您没有违反与之交互的语言的任何期望。例如,即使Java可能不易受缓冲区溢出的影响,但在调用本机代码时提供大量参数可能会引发溢出。

Phases: 架构与设计; 操作

策略: 强化环境

使用完成必要任务所需的最低权限运行代码[Ref-76]。如果可能,请创建仅用于单个任务的具有有限权限的独立帐户。这样,成功的攻击不会立即让攻击者访问软件的其余部分或其环境。例如,数据库应用程序很少需要以数据库管理员的身份运行,特别是在日常操作中。

阶段: 架构与设计; 操作

策略: 沙箱或牢房

在“监狱”或类似的沙盒环境中运行代码,该环境强制在进程和操作系统之间建立严格的边界。这可能有效地限制哪些文件可以在特定目录中访问,或者哪些命令可以由软件执行。

操作系统级的例子包括unix chrot-guille、apparmor和selinux。一般来说,托管代码可能提供一些保护。例如,Java安全管理器中的java. Io.FielEn允许允许软件指定对文件操作的限制。

这可能不是一个可行的解决方案,它只限制了对操作系统的影响;应用程序的其余部分仍然可能受到损害。

 

小心避免与监狱有关的CWE-243和其他弱点。

有效性: 有限

注意:这种缓解措施的有效性取决于所使用的特定沙箱或坚固房的预防能力,可能只有助于减少攻击的范围,例如限制攻击者进行某些系统调用或限制可访问的文件系统部分。

种属

关系

类型

ID

名称

属于

738

CERT C Secure Coding Standard (2008) Chapter 5 - Integers (INT)

属于

740

CERT C Secure Coding Standard (2008) Chapter 7 - Arrays (ARR)

属于

802

2010 Top 25 - Risky Resource Management

属于

867

2011 Top 25 - Weaknesses On the Cusp

属于

872

CERT C++ Secure Coding Section 04 - Integers (INT)

属于

874

CERT C++ Secure Coding Section 06 - Arrays and the STL (ARR)

属于

884

CWE Cross-section

属于

970

SFP Secondary Cluster: Faulty Buffer Access

属于

1131

CISQ Quality Measures - Security

属于

1160

SEI CERT C Coding Standard - Guidelines 06. Arrays (ARR)

说明

相关弱点

这种弱点可以先于不受控制的内存分配(CWE-789),即使用大于数组大小的索引(如javascript)时自动扩展数组。

理论

验证不正确的数组索引可能直接导致“使用越界索引访问数组”的行为始终不正确。

你可能感兴趣的:(漏洞检查)