InfoSect, Canberra's hackerspace, regularly runs public group sessions to perform code review and vulnerability discovery. Over the next 30 days, I'll highlight the source code of 30 unknown vulnerabilities.
Bonus Bug #32
In the package abootimg, a program for working with android boot images, it's possible to craft a boot image to trigger a buffer overflow and other issues when abootimg accesses it.
struct boot_img_hdr
{
unsigned char magic[BOOT_MAGIC_SIZE];
unsigned kernel_size; /* size in bytes */
unsigned kernel_addr; /* physical load addr */
unsigned ramdisk_size; /* size in bytes */
unsigned ramdisk_addr; /* physical load addr */
unsigned second_size; /* size in bytes */
unsigned second_addr; /* physical load addr */
...
case extract:
open_bootimg(bootimg, "r");
read_header(bootimg);
write_bootimg_config(bootimg);
extract_kernel(bootimg);
extract_ramdisk(bootimg);
extract_second(bootimg);
break;
...
void read_header(t_abootimg* img)
{
size_t rb = fread(&img->header, sizeof(boot_img_hdr), 1, img->stream);
if ((rb!=1) || ferror(img->stream))
...
if (check_boot_img_header(img))
abort_printf("%s: not a valid Android Boot Image.\n", img->fname);
}
If we can bypass check_boot_img_header we can do some things..
int check_boot_img_header(t_abootimg* img)
{
if (strncmp(img->header.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
fprintf(stderr, "%s: no Android Magic Value\n", img->fname);
return 1;
}
...
unsigned n = (img->header.kernel_size + page_size - 1) / page_size;
unsigned m = (img->header.ramdisk_size + page_size - 1) / page_size;
unsigned o = (img->header.second_size + page_size - 1) / page_size;
unsigned total_size = (1+n+m+o)*page_size;
if (total_size > img->size) {
fprintf(stderr, "%s: sizes mismatches in boot image\n", img->fname);
return 1;
}
This is a simple integer overflow to calculate total_size. Lets find some potential places for heap overflows..
void extract_second(t_abootimg* img)
{
unsigned psize = img->header.page_size;
unsigned ksize = img->header.kernel_size;
unsigned rsize = img->header.ramdisk_size;
unsigned ssize = img->header.second_size;
if (!ssize) // Second Stage not present
return;
unsigned n = (rsize + ksize + psize - 1) / psize;
unsigned soffset = (1+n)*psize;
printf ("extracting second stage image in %s\n", img->second_fname);
void* s = malloc(ksize);
if (!s)
abort_perror(NULL);
if (fseek(img->stream, soffset, SEEK_SET))
abort_perror(img->fname);
size_t rb = fread(s, ssize, 1, img->stream);
if ((rb!=1) || ferror(img->stream))
abort_perror(img->fname);
This is bad code. It's using malloc(ksize) but using ssize for the read. If we use the boot img header check bypass, we should be able to do bad things.