Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Malformed unreal4 crashdump file can cause OOM when being parsed #476

Closed
5225225 opened this issue Jan 3, 2022 · 0 comments · Fixed by #480
Closed

Malformed unreal4 crashdump file can cause OOM when being parsed #476

5225225 opened this issue Jan 3, 2022 · 0 comments · Fixed by #480

Comments

@5225225
Copy link

5225225 commented Jan 3, 2022

I'm using the following fuzzer harness

#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    drop(symbolic::unreal::Unreal4Crash::parse_with_limit(data, 1024*1024));
});

And 1MB should be enough to never OOM. But the issue is that

fn gread_files(
bytes: &[u8],
count: usize,
offset: &mut usize,
) -> Result<Vec<Unreal4FileMeta>, Unreal4Error> {
let mut files = Vec::with_capacity(count);
for _ in 0..count {
let file_offset = *offset;
files.push(bytes.gread_with(offset, file_offset)?);
}
Ok(files)
}
here, line 116 is an unbounded allocation. count is read directly from the file, and not validated in any way. Therefore, a malicious file can cause the Vec to be allocated with an arbitrarily large count.

I suggest just capping the capacity here to something small. (Say, 64?). That would imply extra allocations if the file actually has more than that, but that's cheap enough, and most definitely better than an OOM. The capacity here is just a hint, anyways.

A sample program that shows off the vulnerability is

use flate2::write::ZlibEncoder;
use std::io::Write;

fn main() {
    let mut bytes = Vec::new();
    bytes.extend_from_slice(b"\x00\x00\x00\x00"); // Directory Name (AnsiString)
    bytes.extend_from_slice(b"\x00\x00\x00\x00"); // File Name (AnsiString)
    bytes.extend_from_slice(b"\x00\x00\x00\x00"); // Uncompressed Size (i32 LE)
    bytes.extend_from_slice(b"\xff\xff\xff\x40"); // File Count (i32 LE)

    // File Count is what we're targeting, but we need the bits at the front.
    
    let mut enc = ZlibEncoder::new(Vec::new(), flate2::Compression::default());
    enc.write_all(&bytes).unwrap();
    let compressed = enc.finish().unwrap();
    drop(dbg!(symbolic::unreal::Unreal4Crash::parse_with_limit(&compressed, 1024*1024)));
    // drop is just to ignore the unused Result warning
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants