Module
Improper Validation of Specified Index, Position, or Offset in Input
Summary
The product, such as a program receives input that is expected to specify an index, position, or offset into an indexable resource such as an array, a list, a buffer or file, but it does not validate or incorrectly validates that the specified index/position/offset has the required properties.
Description
Often, indexable resources, such as collections, memory buffers, files can be accessed using a specific position, index, or offset, such as an index for an array or a position for a file. When untrusted input is not properly validated before it is used as an index, attackers could access (or attempt to access) unauthorized portions of these resources. This could be used to cause buffer overflows, excessive resource allocation, or trigger unexpected failures.
Risk
How Can It Happen? What are the security implications? Consider the following: (DoS: Crash, Exit, or Restart; Execute Unauthorized Code or Commands; Read Memory; Modify Memory Scope: Integrity, Availability, Confidentiality) A single fault could allow either an overflow (CWE-788) or underflow (CWE-786) of the array index. What happens next will depend on the type of operation being performed out of bounds, but can expose sensitive information, cause a system crash, or possibly lead to arbitrary code execution.
-
(DoS: Crash, Exit, or Restart; Scope: Integrity, Availability) Use of an index that is outside the bounds of an array will very likely result in the corruption of relevant memory and perhaps instructions, leading to a crash, if the values are outside of the valid memory area. Thus, this weakness can be leveraged to cause a denial of service (DoS) attack. As such, this weakness is also relevant to memory-safe languages, such as Java, C#, and Python.
-
(Modify Memory; Scope: Integrity) If the memory corrupted is data, rather than instructions, the system will continue to function with improper values. This is particularly relevant to languages that do not have a concept of memory safety, such as C and C++.
-
(Modify Memory; Read Memory; Scope: Confidentiality, Integrity) Use of an index that is outside the bounds of an array can also trigger out-of-bounds read or write operations, or operations on the wrong objects; i.e., “buffer overflows” are not always the result. This may result in the exposure or modification of sensitive data. This is particularly relevant to languages that do not have a concept of memory safety, such as C and C++.
-
(Execute Unauthorized Code or Commands; Scope: Integrity, Confidentiality, Availability) If the memory accessible by the attacker can be effectively controlled, it may be possible to execute arbitrary code, as with a standard buffer overflow and possibly without the use of large inputs if a precise index can be controlled. This is particularly relevant to languages that do not have a concept of memory safety, such as C and C++.
Example
As indicated in CVE-2024-5680, some versions of the Foxboro.sys driver from Schneider Electric cause local denial-of-service when a malicious actor with local user access crafts a script/program using an IOCTL call when use an invalid index.
Example Code (Bad Code)
Below are a few examples that demonstrate this weakness.
-
The following example retrieves the sizes of messages for a pop3 mail server. The message sizes are retrieved from a socket that returns in a buffer the message number and the message size, the message number (num) and size (size) are extracted from the buffer and the message size is placed into an array using the message number for the array index.
// BAD CODE /* 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; } } } ... }
In this example the message number retrieved from the buffer could be a value that is outside the allowable range of indices for the array and could possibly be a negative number. Without proper validation of the value to be used for the array index an array overflow could occur and could potentially lead to unauthorized access to memory addresses and system crashes. The value of the array index should be validated to ensure that it is within the allowable range of indices for the array as in the following code.
// GOOD CODE /* 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"); } } } ... }
-
In the following example the method displayProductSummary is called from a Web service servlet to retrieve product summary information for display to the user. The servlet obtains the integer value of the product number from the user and passes it to the displayProductSummary method. The displayProductSummary method passes the integer value of the product number to the getProductSummary method which obtains the product summary from the array object containing the project summaries using the integer value of the product number as the array index.
// BAD CODE // 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]; }
-
In this example the integer value used as the array index that is provided by the user may be outside the allowable range of indices for the array which may provide unexpected results or cause the application to fail. The integer value used for the array index should be validated to ensure that it is within the allowable range of indices for the array as in the following code.
// GOOD CODE // 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; }
An alternative in Java would be to use one of the collection objects such as ArrayList that will automatically generate an exception if an attempt is made to access an array index that is out of bounds.
// GOOD CODE ArrayList productArray = new ArrayList(MAX_PRODUCTS); ... try { productSummary = (String) productArray.get(index); } catch (IndexOutOfBoundsException ex) {...}
-
The following example asks a user for an offset into an array to select an item.
// BAD CODE int main (int argc, char **argv) { char *items[] = {"boat", "car", "truck", "train"}; int index = GetUntrustedOffset(); printf("User selected %s\n", items[index-1]); }
The programmer allows the user to specify which element in the list to select, however an attacker can provide an out-of-bounds offset, resulting in a buffer over-read (CWE-126).
Addressing Improper Validation of Specified Index, Position, or Offset in Input
Here, we focus on memory-safe languages, such as Java, C#, and Go, where the language runtime or compiler can help prevent out-of-bounds access.
Using unvalidated input as part of an index into the array can cause the array access to throw an ArrayIndexOutOfBoundsException. This is because there is no guarantee that the index provided is within the bounds of the array.
This problem occurs when user input is used as an array index, either directly
or following one or more calculations. If the user input is unsanitized, it may
be any value, which could result in either a negative index, or an index which
is larger than the size of the array, either of which would result in an
ArrayIndexOutOfBoundsException.
Thus, it is recommended that The index used in the array access should be checked against the bounds of the array before being used. The index should be smaller than the array size, and it should not be negative. This recommendation is also applicable to other data structures, such as collections (including lists, maps, and sets) and strings.
The following program accesses an element from a fixed size constant array:
// BAD CODE
public class ImproperValidationOfArrayIndex extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String[] productDescriptions = new String[] { "Chocolate bar", "Fizzy drink" };
// User provided value
String productID = request.getParameter("productID");
try {
int productID = Integer.parseInt(userProperty.trim());
/*
* BAD Array is accessed without checking if the user provided value is out of
* bounds.
*/
String productDescription = productDescriptions[productID];
response.getWriter().write(productDescription);
} catch (NumberFormatException e) { }
}
}
The first access of the productDescriptions
array uses the user-provided
value as the index without performing any checks. If the user provides a
negative value, or a value larger than the size of the array, then an
ArrayIndexOutOfBoundsException
may be thrown. As discussed before,
this can be leveraged to launch a denial of service attack, or to bypass
security checks.
To address this problem, we use a conditional expression that verifies the
user-provided value to the productDescriptions
array is a valid index into
the array. This ensures that the access operation never throws an
ArrayIndexOutOfBoundsException
.
// GOOD CODE
public class ImproperValidationOfArrayIndex extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String[] productDescriptions = new String[] { "Chocolate bar", "Fizzy drink" };
// User provided value
String productID = request.getParameter("productID");
try {
int productID = Integer.parseInt(userProperty.trim());
String productDescription = "";
// GOOD We have checked that the array index is valid first
if (productID >= 0 && productID < productDescriptions.length) {
productDescription = productDescriptions[productID];
} else {
productDescription = "No product for that ID";
}
response.getWriter().write(productDescription);
} catch (NumberFormatException e) { }
}
}
Acknowledgement
This page is inspired from the CodeQL project, CWE-1285, and CWE-129.