Byte Flipping Tekniği ile Pdf Bulama (Fuzzing)

Bu yazımızda gelin byte flipping (ya da bit flipping) tekniği ile fuzzing konusu üzerinde çalışalım. Byte flipping, örnek bir dosya üzerine ardışık olarak ‘\xFF’ basıp her bir adım için ayrı bir bozuk girdi dosyası üretme yöntemidir. Buradaki örneğimiz örnek bir PDF dokümanı olacak ve öğrenme maksatlı olarak Adobe Reader uygulamasını bulamaya çalışacağız.

Bu yazıda kullandığım dönüştürücü kod parçası, Tobias Klein tarafından yazılmış “A Bug Hunter’s Diary” kitabından alınma. Söz konusu kitabın 8’inci Bölümünü incelemenizi özellikle tavsiye ederim. Evet, hadi başlayalım:

İlk olarak testcase.pdf isimli örnek bir PDF doküman üretelim. Bu bizim ana şablonumuz olacak. Dosyanın boyutunu öğrenmek için aşağıdaki komutu giriniz:

du -b testcase.pdf

Dosya Boyutu

Şimdi aşağıdaki fuzz.c kodunu derleyip çalıştıralım:

gcc fuzz.c -o fuzz


 
//Reference: A Bug Hunter's Diary - Tobias Klein
//fuzz.c
 
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
 
int main (int argc, char *argv[])
{
int fd = 0;
char * p = NULL;
char * name = NULL;
unsigned int file_size = 0;
unsigned int file_offset = 0;
unsigned int file_value = 0;
 
if (argc < 2) {
printf ("[-] Error: not enough arguments\n");
return (1);
} else {
file_size = atol (argv[1]);
file_offset = atol (argv[2]);
file_value = atol (argv[3]);
name = argv[4];
}
 
// open file
fd = open (name, O_RDWR);
if (fd < 0) {
perror ("open");
exit (1);
}
 
// mmap file
p = mmap (0, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if ((int) p == -1) {
perror ("mmap");
close (fd);
exit (1);
}
 
// mutate file
printf ("[+] file offset: 0x%08x (value: 0x%08x)\n", file_offset, file_value);
fflush (stdout);
p[file_offset] = file_value;
 
close (fd);
munmap (p, file_size);
 
return (0);
}


Bu bizim dönüştürücü programımız. Şimdi bu programı testcase.pdf dosyası üzerinde dolaştırmaya başlayabiliriz:



#!/bin/bash
 
# file size
filesize=4201
 
# file offset
off=0
 
# number of files
num=5
 
# fuzz value
val=255
 
# name counter
cnt=0
 
while [ $cnt -lt $num ]
do
cp ./testcase.pdf ./file$cnt.pdf
./fuzz $filesize $off $val ./file$cnt.pdf
let "off+=1"
let "cnt+=1"
done


fuzz.sh kodunu çalıştırdıktan sonra, 5 adet örnek PDF dosyasının üretildiğini göreceğiz. Şimdi aşağıdaki kodları artarda girerek her bir dosyanın nasıl ‘\xFF’ değeri ile basıldığını görelim:

xxd file0.pdf | head -1

xxd file1.pdf | head -1

xxd file2.pdf | head -1

xxd file3.pdf | head -1

xxd file4.pdf | head -1

Fuzz Çalışırken

Çok ilginç değil mi? Şimdi artık bu dosyaları gerçek uygulama ile test edebiliriz. Sözünü ettiğimiz kitapta yazar Safari tarayıcısını fuzz ediyordu, oysa biz Adober Reader uygulamasını fuzz etmek istiyoruz. İşte çözümü:



#!/usr/bin/python
# Reference: https://gist.github.com/Radcliffe/7208419
from os import listdir
from os.path import isfile, join
mypath = 'C:\Python27\pdfs\\'
file_list = [ mypath+f for f in listdir(mypath) if isfile(join(mypath,f)) ]
apps = ['C:\Program Files\Adobe\Reader 11.0\Reader\AcroRd32.exe']
fuzz_output = "fuzz.pdf"
FuzzFactor = 2500
start = 0
stop = 4
import math
import random
import string
import subprocess
import time
random.seed(1337)
out = []
 
out.append("Logging Crashes Started for files:" + " " + str(start) + " " + str(stop))
 
for i in range(start, stop):
    file_choice = file_list[i]
    app = apps[0]
 
    print i
    buf = bytearray(open(file_choice, 'rb').read())
    numwrites = random.randrange(math.ceil((float(len(buf)) / FuzzFactor)))+1
    for j in range(numwrites):
        rbyte = random.randrange(256)
        rn = random.randrange(len(buf))
        buf[rn] = "%c"%(rbyte)
    open(fuzz_output, 'wb').write(buf)
    process = subprocess.Popen([app, fuzz_output])
    time.sleep(1)
    crashed = process.poll()
    if not crashed:
        process.terminate()
    else:
        out.append("Attempt #%d crashed!" % (i+1))
        out.append("Application: %s" % app)
        out.append("Return code: %s" % process.returncode)
        out.append("")
        open(fuzz_output + '_' + str(i+1), 'wb').write(buf)
    time.sleep(1)
out.append("Logging Crashes Finished")
f = open(mypath +'fuzzer'+str(start)+ '_' + str(stop) + '.log', 'w')
f.write('\n'.join(out))
f.close()


Bu basit örneğimiz, yarattığımız 5 adet arızalı dosyanın Adobe Reader tarafından otomatik olarak açılıp kapanmasını sağlarken bu esnada çökme olasılığını takip edip logluyor. (Tabi ki burada çökme anlamında hiçbir şey olmadı). Artık hem örnek sayısını binlerce çoğaltabilir, hem de hem dosya hem de uygulama seçiminde rasgelelik ekleyebilirsiniz.

İyi eğlenceler :)