Fix runtime BOSD

Chunk size and tag is check before handle.
Check if heuristics search is not correct, and the try_ptr goes of the bound,
making dereference an invalid address.
This commit is contained in:
nganhkhoa 2020-05-20 00:42:24 +07:00
parent dd16a31984
commit ff53a1a31c
3 changed files with 54 additions and 17 deletions

View File

@ -29,33 +29,45 @@ fn main() -> Result<(), Box<dyn Error>> {
// let eprocess_scan_head = driver.scan_active_head(ntosbase)?; // let eprocess_scan_head = driver.scan_active_head(ntosbase)?;
// let mut eprocess_list: Vec<EprocessPoolChunk> = Vec::new(); // let mut eprocess_list: Vec<EprocessPoolChunk> = Vec::new();
driver.scan_pool(b"Proc", |pool_addr, header, data_addr| { driver.scan_pool(b"Proc", |pool_addr, header, data_addr| {
let chunk_size = (header[2] as u64) * 16u64;
let eprocess_size = driver.pdb_store.get_offset_r("_EPROCESS.struct_size")?;
let eprocess_name_offset = driver.pdb_store.get_offset_r("_EPROCESS.ImageFileName")?; let eprocess_name_offset = driver.pdb_store.get_offset_r("_EPROCESS.ImageFileName")?;
let eprocess_create_time_offset = driver.pdb_store.get_offset_r("_EPROCESS.CreateTime")?; let eprocess_create_time_offset = driver.pdb_store.get_offset_r("_EPROCESS.CreateTime")?;
let eprocess_exit_time_offset = driver.pdb_store.get_offset_r("_EPROCESS.ExitTime")?; let eprocess_exit_time_offset = driver.pdb_store.get_offset_r("_EPROCESS.ExitTime")?;
let eprocess_size = driver.pdb_store.get_offset_r("_EPROCESS.struct_size")?;
let chunk_size = (header[2] as u64) * 16u64;
let eprocess_valid_start = data_addr; let eprocess_valid_start = data_addr;
let eprocess_valid_end = pool_addr + chunk_size - eprocess_size; let eprocess_valid_end = (pool_addr + chunk_size) - eprocess_size;
let mut try_eprocess_ptr = eprocess_valid_start; let mut try_eprocess_ptr = eprocess_valid_start;
let mut create_time = 0u64; let mut create_time = 0u64;
let mut exit_time = 0u64; let mut exit_time = 0u64;
while try_eprocess_ptr <= eprocess_valid_end { while try_eprocess_ptr <= eprocess_valid_end {
driver.deref_addr(try_eprocess_ptr + eprocess_create_time_offset, &mut create_time); driver.deref_addr(try_eprocess_ptr + eprocess_create_time_offset, &mut create_time);
driver.deref_addr(try_eprocess_ptr + eprocess_exit_time_offset, &mut exit_time); // driver.deref_addr(try_eprocess_ptr + eprocess_exit_time_offset, &mut exit_time);
// using heuristics to eliminate false positive // using heuristics to eliminate false positive
if driver.windows_ffi.valid_process_time(create_time) { if driver.windows_ffi.valid_process_time(create_time) {
break; break;
} }
try_eprocess_ptr += 0x4; // search exhaustively try_eprocess_ptr += 0x4; // search exhaustively
} }
if (try_eprocess_ptr > eprocess_valid_end) {
return Ok(false);
}
let mut image_name = [0u8; 15]; let mut image_name = [0u8; 15];
driver.deref_addr(try_eprocess_ptr + eprocess_name_offset, &mut image_name); driver.deref_addr(try_eprocess_ptr + eprocess_name_offset, &mut image_name);
let eprocess_name = from_utf8(&image_name)? if let Ok(name) = from_utf8(&image_name) {
.to_string() let eprocess_name = name
.trim_end_matches(char::from(0)) .to_string()
.to_string(); .trim_end_matches(char::from(0))
.to_string();
println!("pool: 0x{:x} | eprocess: 0x{:x} | {}", pool_addr, try_eprocess_ptr, eprocess_name);
}
else {
println!("pool: 0x{:x} | eprocess: 0x{:x} | {:?}", pool_addr, try_eprocess_ptr, image_name);
}
Ok(true)
// eprocess_list.push(EprocessPoolChunk { // eprocess_list.push(EprocessPoolChunk {
// pool_addr, // pool_addr,
// eprocess_addr: try_eprocess_ptr, // eprocess_addr: try_eprocess_ptr,
@ -63,8 +75,6 @@ fn main() -> Result<(), Box<dyn Error>> {
// create_time: to_epoch(create_time), // create_time: to_epoch(create_time),
// exit_time: to_epoch(exit_time) // exit_time: to_epoch(exit_time)
// }); // });
println!("pool: {} | eprocess: {}: {}", pool_addr, try_eprocess_ptr, eprocess_name);
Ok(try_eprocess_ptr <= eprocess_valid_end)
})?; })?;
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());

View File

@ -163,23 +163,47 @@ impl DriverState {
if ptr >= end_address { if ptr >= end_address {
break; break;
} }
let pool_addr = ptr; let pool_addr = ptr;
let mut header = vec![0u8; pool_header_size as usize]; let mut header = vec![0u8; pool_header_size as usize];
self.deref_addr_ptr(pool_addr, header.as_mut_ptr(), pool_header_size); self.deref_addr_ptr(pool_addr, header.as_mut_ptr(), pool_header_size);
let chunk_size = (header[2] as u64) * 16u64;
let success = handler(ptr, &header, pool_addr + pool_header_size)?; if pool_addr + chunk_size > end_address {
if success { // the chunk found is not a valid chunk for sure
let chunk_size = (header[2] as u64) * 16u64; break;
ptr += chunk_size /* pass this chunk */ }
// TODO: move to another function, with tables mapping fromm tag -> struct
// automatically reject bad chunk
// Proc -> _EPROCESS
// Thre -> _KTHREAD
if tag == b"Proc" {
let eprocess_size = self.pdb_store.get_offset_r("_EPROCESS.struct_size")?;
let minimum_data_size = eprocess_size + pool_header_size;
if chunk_size < minimum_data_size {
ptr += 0x4;
continue;
}
} }
else { else {
ptr += 0x4 /* search next */ println!("Tag unknown");
}; break;
}
let success = handler(pool_addr, &header, pool_addr + pool_header_size)?;
if success {
ptr += chunk_size; /* pass this chunk */
}
else {
ptr += 0x4; /* search next */
}
} }
Ok(true) Ok(true)
} }
pub fn deref_addr<T>(&self, addr: u64, outbuf: &mut T) { pub fn deref_addr<T>(&self, addr: u64, outbuf: &mut T) {
// println!("deref addr: 0x{:x}", addr);
let code = DriverAction::DereferenceAddress.get_code(); let code = DriverAction::DereferenceAddress.get_code();
let size: usize = size_of_val(outbuf); let size: usize = size_of_val(outbuf);
let mut input = InputData { let mut input = InputData {

View File

@ -99,7 +99,10 @@ impl PdbStore {
]; ];
let mut need_structs = HashMap::new(); let mut need_structs = HashMap::new();
need_structs.insert("_POOL_HEADER", vec![]); need_structs.insert("_POOL_HEADER", vec![
"struct_size",
"PoolType", "BlockSize", "PoolTag"
]);
need_structs.insert("_PEB", vec![]); need_structs.insert("_PEB", vec![]);
need_structs.insert("_LIST_ENTRY", vec![ need_structs.insert("_LIST_ENTRY", vec![
"Flink", "Blink" "Flink", "Blink"