You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Here's an extremely rudimentary naive fuzzer for docx :
#!/usr/bin/env ruby#################################################### ----------------------------------------------- ## Fuzz docx Ruby gem with mutated DOCX files ## ----------------------------------------------- ## ## Each test case is written to 'fuzz.docx' in the ## current working directory. ## ## Crashes and the associated backtrace are saved ## in the 'crashes' directory in the current ## working directory. ## ##################################################### ~ bcolesrequire'date'require'docx'require'colorize'require'fileutils'require'timeout'require'securerandom'VERBOSE=falseOUTPUT_DIR="#{Dir.pwd}/crashes".freeze## Show usage#defusageputs'Usage: ./fuzz.rb <FILE1> [FILE2] [FILE3] [...]'puts'Example: ./fuzz.rb spec/fixtures/**.docx'exit1end## Print status message## @param [String] msg message to print#defprint_status(msg='')puts'[*] '.blue + msgifVERBOSEend## Print progress messages## @param [String] msg message to print#defprint_good(msg='')puts'[+] '.green + msgifVERBOSEend## Print error message## @param [String] msg message to print#defprint_error(msg='')puts'[-] '.red + msgend## Setup environment#defsetupFileUtils.mkdir_pOUTPUT_DIRunlessFile.directory?OUTPUT_DIRrescue=>eprint_error"Could not create output directory '#{OUTPUT_DIR}': #{e}"exit1end## Generate a mutated DOCX file with a single mitated byte## @param [Path] f path to DOCX file#defmutate_byte(f)data=IO.binreadfposition=SecureRandom.random_numberdata.sizenew_byte=SecureRandom.random_number256new_data=data.dup.tap{ |s| s.setbyte(position,new_byte)}File.open(@fuzz_outfile,'w')do |file|
file.writenew_dataendend## Generate a mutated DOCX file with multiple mutated bytes## @param [Path] f path to DOCX file#defmutate_bytes(f)data=IO.binreadffuzz_factor=200num_writes=rand((data.size / fuzz_factor.to_f).ceil) + 1new_data=data.dupnum_writes.timesdoposition=SecureRandom.random_numberdata.sizenew_byte=SecureRandom.random_number256new_data.tap{ |stream| stream.setbyteposition,new_byte}endFile.open(@fuzz_outfile,'w')do |file|
file.writenew_dataendend## Generate a mutated DOCX file with all integers replaced by '-1'## @param [Path] f path to DOCX file#defclobber_integers(f)data=IO.binreadfnew_data=data.dup.gsub(/\d/,'-1')File.open(@fuzz_outfile,'w')do |file|
file.writenew_dataendend## Generate a mutated DOCX file with all strings 3 characters or longer# replaced with 2000 'A' characters## @param [Path] f path to DOCX file#defclobber_strings(f)data=IO.binreadfnew_data=data.dup.gsub(/[a-zA-Z]{3,}/,'A' * 2000)File.open(@fuzz_outfile,'w')do |file|
file.writenew_dataendend## Read a DOCX file## @param [String] f path to DOCX file#defread(f)print_status"Processing '#{f}'"beginreader=Docx::Document.open(f)rescue=>eife.message == 'zlib error while inflating'print_status"Could not parse DOCX '#{f}': #{e.message}"returnendife.message == 'No such file or directory'print_status"Could not parse DOCX '#{f}': #{e.message}"returnendraiseendprint_good'Processing complete'print_status"Parsing '#{f}'"parse(reader)print_good'Parsing complete'end## Parse DOCX#defparse(reader)print_status'Parsing DOCX...'print_statusreader.document_propertiesprint_statusreader.paragraphsprint_statusreader.bookmarksprint_statusreader.to_xmlprint_statusreader.tablesprint_statusreader.font_sizeprint_statusreader.hyperlinksprint_statusreader.hyperlink_relationshipsprint_statusreader.to_sprint_statusreader.to_htmlprint_statusreader.streamprint_status'Parsing DOCX contents...'contents=''reader.bookmarks.each_pairdo |bookmark_name,bookmark_object|
contents << bookmark_object.to_sendreader.tables.eachdo |table|
table.rows.eachdo |row|
row.cells.eachdo |cell|
contents << cell.textendendend# puts contents if VERBOSEend## Show summary of crashes#defsummaryputsputs"Complete! Crashes saved to '#{OUTPUT_DIR}'"putsputs`/usr/bin/head -n1 #{OUTPUT_DIR}/*.trace`ifFile.exist?'/usr/bin/head'end## Report error message to STDOUT# and save fuzz test case and backtrace to OUTPUT_DIR#defreport_crash(e)puts" - #{e.message}"putse.backtrace.firstfname="#{DateTime.now.strftime('%Y%m%d%H%M%S%N')}_crash_#{rand(1000)}"FileUtils.mv@fuzz_outfile,"#{OUTPUT_DIR}/#{fname}.docx"File.open("#{OUTPUT_DIR}/#{fname}.docx.trace",'w')do |file|
file.write"#{e.message}\n#{e.backtrace.join"\n"}"endend## Test docx with the mutated file#deftestTimeout.timeout(@timeout)doread@fuzz_outfileendrescueSystemStackError=>ereport_crasherescueTimeout::Error=>ereport_crasherescueSyntaxError=>ereport_crasherescue=>eraiseeunlesse.backtrace.join("\n") =~ %r{docx}report_crasheend## Generate random byte mutations and run test## @param [String] f path to DOCX file#deffuzz_bytes(f)iterations=10001.upto(iterations)do |i|
print"\r#{(i * 100) / iterations} % (#{i} / #{iterations})"mutate_bytesftestendend## Generate integer mutations and run tests## @param [String] f path to DOCX file#deffuzz_integers(f)clobber_integersftestend## Generate string mutations and run tests## @param [String] f path to DOCX file#deffuzz_strings(f)clobber_stringsftestendputs'-' * 60puts'% Fuzzer for docx Ruby gem'puts'-' * 60putsusageifARGV[0].nil?setup@timeout=15@fuzz_outfile='fuzz.docx'trap'SIGINT'doputsputs'Caught interrupt. Exiting...'summaryexit130endARGV.eachdo |f|
unlessFile.exist?fprint_error"Could not find file '#{f}'"nextendfuzz_integersffuzz_stringsffuzz_bytesfputs'-' * 60endsummary
Here's the stack traces for the latest version on master using test data from ./spec/fixtures as input.
Here's an extremely rudimentary naive fuzzer for
docx
:Here's the stack traces for the latest version on master using test data from
./spec/fixtures
as input.crashes.zip
Unique crash messages:
Several of these are from underlying libraries.
Most interesting are:
undefined method `close' for nil:NilClass
- likely fixed by Only close zip file if set #115.undefined method `value' for nil:NilClass
undefined method `xpath' for nil:NilClass
The text was updated successfully, but these errors were encountered: