Improper Validation of Specified Index, Position, or Offset in Input
Description
Improper Validation of Specified Index, Position, or Offset in Input occurs when a product fails to properly validate indices, positions, or offsets provided in user input before using them to access indexable resources like buffers or files. When untrusted input specifies an index without validation, attackers may access unauthorized resource portions, potentially causing buffer overflows, buffer over-reads, excessive resource allocation, or trigger unexpected failures.
Risk
Unvalidated index inputs have severe security implications. Buffer overflows may occur. Out-of-bounds reads possible. Information disclosure may happen. Memory corruption can occur. Denial of service attacks enabled. Arbitrary code execution possible. Sensitive data may be accessed. System crashes may occur.
Solution
Implement "accept known good" input validation strategy, requiring strict conformance to specifications and rejection of non-compliant inputs. Validation should consider acceptable value ranges, bounds against actual data size, and both signed and unsigned considerations. Always check indices against actual array/buffer bounds before use.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Impact varies by context - from crashes to data leakage. |
| Confidentiality | Scope: Confidentiality Read Memory - Out-of-bounds read can leak data. |
| Integrity | Scope: Integrity Modify Memory - Out-of-bounds write can corrupt data. |
| Availability | Scope: Availability DoS - Invalid indices can crash application. |
Example Code
Vulnerable Code
// Vulnerable: No validation of array index
#include <stdint.h>
#include <stdlib.h>
#define MAX_MESSAGES 100
typedef struct {
char subject[64];
char body[1024];
size_t size;
} message_t;
message_t messages[MAX_MESSAGES];
int message_count = 0;
// VULNERABLE: POP3 server - no bounds check on message number
size_t vulnerable_get_message_size(int msg_num) {
// VULNERABLE: No validation of msg_num
// User input directly used as index
return messages[msg_num].size;
// If msg_num < 0 or msg_num >= MAX_MESSAGES:
// Out-of-bounds read - undefined behavior
// Could read sensitive data from adjacent memory
}
// VULNERABLE: Buffer access with user-provided offset
void vulnerable_read_buffer(const uint8_t* buffer, size_t buffer_size,
int offset) {
// VULNERABLE: No validation of offset
uint8_t value = buffer[offset]; // Out-of-bounds if offset invalid
process_byte(value);
}
// VULNERABLE: User-controlled array index
typedef struct {
char name[32];
double price;
int quantity;
} product_t;
product_t products[1000];
product_t* vulnerable_get_product(int product_id) {
// VULNERABLE: No bounds check
// User-provided product_id used directly
return &products[product_id];
// Attacker can read/write outside products array
}
// VULNERABLE: File seek with user offset
void vulnerable_file_read(FILE* file, long offset, size_t length,
uint8_t* output) {
// VULNERABLE: No validation of offset
fseek(file, offset, SEEK_SET); // Could seek beyond file
fread(output, 1, length, file); // Undefined behavior
}
// VULNERABLE: Negative index not checked
void vulnerable_negative_index(int* array, int index, int value) {
// VULNERABLE: Negative index can access memory before array
array[index] = value;
// array[-1] accesses memory before array start
// Can corrupt adjacent data structures
}
// Vulnerable: Java with unvalidated indices
public class VulnerableProductService {
private Product[] products = new Product[1000];
// VULNERABLE: No bounds check on index
public Product getProduct(int index) {
// VULNERABLE: ArrayIndexOutOfBoundsException or unexpected access
return products[index];
}
// VULNERABLE: Servlet with user-provided index
public void handleRequest(HttpServletRequest request,
HttpServletResponse response) {
// VULNERABLE: User input directly used as index
int productId = Integer.parseInt(request.getParameter("id"));
Product product = products[productId]; // No validation!
response.getWriter().write(product.toString());
}
// VULNERABLE: List access without bounds check
private List<String> messages = new ArrayList<>();
public String getMessage(int index) {
// VULNERABLE: Throws exception or accesses wrong element
return messages.get(index);
}
}
# Vulnerable: Python with unvalidated indices
messages = ["msg1", "msg2", "msg3"]
products = [{"name": "A", "price": 10}, {"name": "B", "price": 20}]
def vulnerable_get_message(index):
# VULNERABLE: No bounds check
# Negative indices in Python wrap around!
return messages[index]
# index = -1 returns last message (might not be intended)
# index = 100 raises IndexError (DoS)
def vulnerable_slice_buffer(data, start, end):
# VULNERABLE: No validation of start/end
return data[start:end]
# Negative indices behave unexpectedly
# Very large indices could cause issues
def vulnerable_dict_array_access(index):
# VULNERABLE: No bounds check
return products[index]
class VulnerableBuffer:
def __init__(self, size):
self.data = bytearray(size)
def read_at(self, offset):
# VULNERABLE: No bounds check
return self.data[offset]
def write_at(self, offset, value):
# VULNERABLE: No bounds check
self.data[offset] = value
Fixed Code
// Fixed: Proper validation of array indices
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_MESSAGES 100
typedef struct {
char subject[64];
char body[1024];
size_t size;
} message_t;
message_t messages[MAX_MESSAGES];
int message_count = 0;
// FIXED: Bounds check on message number
bool secure_get_message_size(int msg_num, size_t* size_out) {
// FIXED: Validate index range
if (msg_num < 0) {
log_error("Invalid message number: %d (negative)", msg_num);
return false;
}
if (msg_num >= message_count) {
log_error("Message number %d out of range (count=%d)",
msg_num, message_count);
return false;
}
*size_out = messages[msg_num].size;
return true;
}
// FIXED: Validate offset before buffer access
bool secure_read_buffer(const uint8_t* buffer, size_t buffer_size,
size_t offset, uint8_t* value_out) {
// FIXED: Validate offset is within bounds
if (offset >= buffer_size) {
log_error("Offset %zu out of bounds (size=%zu)", offset, buffer_size);
return false;
}
*value_out = buffer[offset];
return true;
}
// FIXED: Validate array index with bounds
typedef struct {
char name[32];
double price;
int quantity;
} product_t;
#define MAX_PRODUCTS 1000
product_t products[MAX_PRODUCTS];
int product_count = 0;
product_t* secure_get_product(int product_id) {
// FIXED: Comprehensive validation
if (product_id < 0) {
log_error("Invalid product ID: %d (negative)", product_id);
return NULL;
}
if (product_id >= product_count) {
log_error("Product ID %d not found (count=%d)",
product_id, product_count);
return NULL;
}
return &products[product_id];
}
// FIXED: Validate file offset
bool secure_file_read(FILE* file, long offset, size_t length,
uint8_t* output, size_t output_size) {
if (output == NULL || file == NULL) {
return false;
}
// FIXED: Get file size for validation
long current_pos = ftell(file);
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
fseek(file, current_pos, SEEK_SET);
// FIXED: Validate offset
if (offset < 0) {
log_error("Negative file offset: %ld", offset);
return false;
}
if (offset >= file_size) {
log_error("Offset %ld beyond file size %ld", offset, file_size);
return false;
}
// FIXED: Validate length
if (length > output_size) {
log_error("Length %zu exceeds output buffer", length);
return false;
}
if (offset + (long)length > file_size) {
log_error("Read would exceed file size");
return false;
}
fseek(file, offset, SEEK_SET);
size_t read = fread(output, 1, length, file);
return (read == length);
}
// FIXED: Safe array access with explicit size
bool secure_array_write(int* array, size_t array_size,
int index, int value) {
// FIXED: Check for negative index
if (index < 0) {
log_error("Negative index: %d", index);
return false;
}
// FIXED: Check against actual size
if ((size_t)index >= array_size) {
log_error("Index %d out of bounds (size=%zu)", index, array_size);
return false;
}
array[index] = value;
return true;
}
// FIXED: Helper function for safe array access
#define SAFE_ARRAY_ACCESS(array, index, default_value) \
((index) >= 0 && (size_t)(index) < ARRAY_SIZE(array) ? \
(array)[index] : (default_value))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
// Fixed: Java with validated indices
public class SecureProductService {
private Product[] products = new Product[1000];
private int productCount = 0;
// FIXED: Bounds check on index
public Product getProduct(int index) throws ValidationException {
// FIXED: Validate index
if (index < 0) {
throw new ValidationException("Invalid index: " + index + " (negative)");
}
if (index >= productCount) {
throw new ValidationException(
"Product index " + index + " out of range (count=" + productCount + ")");
}
return products[index];
}
// FIXED: Servlet with validated index
public void handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ValidationException {
String idParam = request.getParameter("id");
// FIXED: Validate parameter exists
if (idParam == null || idParam.isEmpty()) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing id parameter");
return;
}
int productId;
try {
productId = Integer.parseInt(idParam);
} catch (NumberFormatException e) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid id format");
return;
}
// FIXED: Use validated getter
try {
Product product = getProduct(productId);
response.getWriter().write(product.toString());
} catch (ValidationException e) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
}
}
// FIXED: Safe list access
private List<String> messages = new ArrayList<>();
public Optional<String> getMessage(int index) {
// FIXED: Bounds check
if (index < 0 || index >= messages.size()) {
return Optional.empty();
}
return Optional.of(messages.get(index));
}
}
# Fixed: Python with validated indices
from typing import Optional, List, Any
messages = ["msg1", "msg2", "msg3"]
products = [{"name": "A", "price": 10}, {"name": "B", "price": 20}]
def secure_get_message(index: int) -> Optional[str]:
"""Get message at index with bounds checking."""
# FIXED: Validate index type
if not isinstance(index, int):
raise TypeError(f"Index must be integer, got {type(index)}")
# FIXED: Validate index range
if index < 0:
raise ValueError(f"Index cannot be negative: {index}")
if index >= len(messages):
raise IndexError(f"Index {index} out of range (size={len(messages)})")
return messages[index]
def secure_slice_buffer(data: bytes, start: int, end: int) -> bytes:
"""Slice buffer with validated indices."""
# FIXED: Validate types
if not isinstance(start, int) or not isinstance(end, int):
raise TypeError("Start and end must be integers")
# FIXED: Validate range
if start < 0:
raise ValueError(f"Start cannot be negative: {start}")
if end < start:
raise ValueError(f"End {end} cannot be less than start {start}")
if end > len(data):
raise ValueError(f"End {end} exceeds data length {len(data)}")
return data[start:end]
def secure_get_product(index: int) -> Optional[dict]:
"""Get product at index with validation."""
# FIXED: Comprehensive validation
if not isinstance(index, int):
raise TypeError(f"Index must be integer, got {type(index)}")
if index < 0:
return None # Or raise ValueError
if index >= len(products):
return None # Or raise IndexError
return products[index]
class SecureBuffer:
def __init__(self, size: int):
if size <= 0:
raise ValueError(f"Size must be positive: {size}")
self.data = bytearray(size)
def read_at(self, offset: int) -> Optional[int]:
"""Read byte at offset with bounds checking."""
# FIXED: Validate offset
if not isinstance(offset, int):
raise TypeError(f"Offset must be integer, got {type(offset)}")
if offset < 0:
raise ValueError(f"Offset cannot be negative: {offset}")
if offset >= len(self.data):
raise IndexError(f"Offset {offset} out of bounds (size={len(self.data)})")
return self.data[offset]
def write_at(self, offset: int, value: int) -> bool:
"""Write byte at offset with bounds checking."""
# FIXED: Validate offset
if not isinstance(offset, int):
raise TypeError(f"Offset must be integer")
if offset < 0 or offset >= len(self.data):
return False
# FIXED: Validate value
if not isinstance(value, int) or value < 0 or value > 255:
raise ValueError(f"Value must be byte (0-255): {value}")
self.data[offset] = value
return True
CVE Examples
- CVE-2005-0369: Large packet ID used as array index in network protocol.
- CVE-2001-1009: Negative array index in POP LIST command caused out-of-bounds access.
Related CWEs
- CWE-20: Improper Input Validation (parent)
- CWE-129: Improper Validation of Array Index (child)
- CWE-781: Improper Address Validation in IOCTL (child)
- CWE-787: Out-of-bounds Write (related)
- CWE-125: Out-of-bounds Read (related)
References
- MITRE Corporation. "CWE-1285: Improper Validation of Specified Index, Position, or Offset in Input." https://cwe.mitre.org/data/definitions/1285.html
- CERT C. "ARR30-C: Do not form or use out-of-bounds pointers or array subscripts"
- OWASP. "Input Validation Cheat Sheet"