Out Of Bounds Write in Actix
How Out Of Bounds Write Manifests in Actix
Out Of Bounds Write vulnerabilities in Actix applications typically occur when handling HTTP request data without proper bounds checking. In Actix, these vulnerabilities often manifest through improper handling of JSON payloads, form data, or path parameters that can lead to memory corruption or data exposure.
A common scenario involves extracting values from JSON bodies without validating their size or structure. For example, an Actix handler might directly deserialize a JSON array into a fixed-size Rust array without checking the input length:
async fn process_data(mut payload: web::Json<Vec<i32>>) -> impl Responder {
let data = &*payload;
let fixed_array: [i32; 10] = data.try_into().unwrap_or_default();
// ... processing logic
}
This code appears safe but can panic or cause undefined behavior if the input array has more than 10 elements. The try_into() call will panic on size mismatch, potentially crashing the service.
Another Actix-specific manifestation occurs with query parameters and path extraction. Consider this route handler:
async fn get_item(
web::Path((id, index)): web::Path<(i32, usize)>,
data: web::Data<AppState>
) -> impl Responder {
let items = &data.items[id as usize];
let item = items[index];
HttpResponse::Ok().json(item)
}
If id or index exceed the bounds of the underlying data structures, this can lead to out-of-bounds reads or writes, potentially exposing sensitive data or causing panics.
Actix's extractor system can also introduce vulnerabilities when handling multipart form data. An attacker might craft a multipart request with excessive field counts or oversized content, overwhelming server resources or causing buffer overflows in custom processing logic.
Actix-Specific Detection
Detecting Out Of Bounds Write vulnerabilities in Actix applications requires both static analysis and runtime testing. Static analysis tools can examine the codebase for patterns like unchecked array indexing, unsafe Rust code, and improper use of get_unchecked() or similar methods.
For runtime detection, middleBrick's black-box scanning approach is particularly effective for Actix applications. The scanner tests unauthenticated endpoints by sending crafted payloads designed to trigger out-of-bounds conditions. For JSON endpoints, middleBrick sends arrays of varying sizes, including empty arrays, single-element arrays, and arrays with hundreds of elements to test for proper bounds checking.
The scanner also tests path parameter handling by sending extremely large numeric values, negative numbers, and non-numeric strings where numeric parameters are expected. For example, testing /api/items/9999999999999 when the application only has 100 items can reveal whether proper bounds checking exists.
middleBrick's LLM/AI security module adds another layer of detection for Actix applications that use AI features. It tests for system prompt leakage and prompt injection vulnerabilities that could be exploited to cause out-of-bounds writes in AI-powered Actix endpoints.
Key detection patterns include:
- Sending JSON arrays larger than expected sizes to test for proper validation
- Testing path parameters with extreme values to check array indexing safety
- Submitting multipart forms with excessive field counts
- Testing for proper error handling when invalid data is provided
- Checking for information disclosure through error messages that reveal array sizes or internal structure
The Actix framework itself provides some built-in protections, but custom business logic often introduces vulnerabilities. middleBrick's scanning identifies these custom vulnerabilities by testing the actual runtime behavior rather than just the code structure.
Actix-Specific Remediation
Remediating Out Of Bounds Write vulnerabilities in Actix requires a combination of proper input validation, safe Rust practices, and leveraging Actix's built-in safety features. The most effective approach is to validate all input data before processing and use Rust's type system to enforce safety at compile time.
For JSON payload handling, always validate the input size before processing:
async fn process_data(
payload: web::Json<Vec<i32>>
) -> Result<impl Responder, HttpResponse> {
let data = &*payload;
if data.len() > 100 {
return Err(HttpResponse::BadRequest().body("Payload too large"));
}
let fixed_array: [i32; 100] = data.try_into()
.map_err(|_| HttpResponse::BadRequest().body("Invalid payload size"))?;
// Safe processing
Ok(HttpResponse::Ok().json(fixed_array))
}
This approach validates the input length before attempting to convert it to a fixed-size array, returning a proper error response instead of panicking.
For path parameter extraction, use comprehensive validation:
async fn get_item(
web::Path((id, index)): web::Path<(i32, usize)>,
data: web::Data<AppState>
) -> Result<impl Responder, HttpResponse> {
let id = id as usize;
// Validate bounds before accessing
if id >= data.items.len() {
return Err(HttpResponse::NotFound().body("Item not found"));
}
let items = &data.items[id];
if index >= items.len() {
return Err(HttpResponse::BadRequest().body("Invalid index"));
}
Ok(HttpResponse::Ok().json(items[index]))
}
This pattern ensures that all array accesses are validated before use, preventing out-of-bounds access.
Actix's extractor system can be extended with custom validation logic:
use actix_web::{FromRequest, HttpRequest, HttpResponse};
pub struct ValidatedArray(Vec<i32>);
impl<'de> Deserialize<'de> for ValidatedArray {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let vec = Vec::deserialize(deserializer)?;
if vec.len() > 100 {
return Err(serde::de::Error::custom("Array too large"));
}
Ok(ValidatedArray(vec))
}
}
impl FromRequest for ValidatedArray {
type Config = ();
type Error = actix_web::Error;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, payload: &mut actix_web::dev::Payload) -> Self::Future {
web::Json::>::from_request(req, payload)
.map(|res| {
let vec = res.map(|v| ValidatedArray(v)).unwrap_or_default();
Ok(vec)
})
.into_future()
}
}
This custom extractor validates array size during deserialization, providing a reusable validation component.
For comprehensive protection, combine these techniques with Actix's built-in validation features and Rust's ownership system to ensure memory safety throughout your application.