1// Copyright (c) 2017, Google Inc. 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 14 15// break-hash parses an ELF binary containing the FIPS module and corrupts the 16// first byte of the module. This should cause the integrity check to fail. 17package main 18 19import ( 20 "bytes" 21 "crypto/hmac" 22 "crypto/sha512" 23 "debug/elf" 24 "encoding/hex" 25 "errors" 26 "fmt" 27 "io/ioutil" 28 "os" 29) 30 31func do(outPath, inPath string) error { 32 objectBytes, err := ioutil.ReadFile(inPath) 33 if err != nil { 34 return err 35 } 36 37 object, err := elf.NewFile(bytes.NewReader(objectBytes)) 38 if err != nil { 39 return errors.New("failed to parse object: " + err.Error()) 40 } 41 42 // Find the .text section. 43 var textSection *elf.Section 44 var textSectionIndex elf.SectionIndex 45 for i, section := range object.Sections { 46 if section.Name == ".text" { 47 textSectionIndex = elf.SectionIndex(i) 48 textSection = section 49 break 50 } 51 } 52 53 if textSection == nil { 54 return errors.New("failed to find .text section in object") 55 } 56 57 symbols, err := object.Symbols() 58 if err != nil { 59 fmt.Fprintf(os.Stderr, "%s\nTrying dynamic symbols\n", err) 60 symbols, err = object.DynamicSymbols() 61 } 62 if err != nil { 63 return errors.New("failed to parse symbols: " + err.Error()) 64 } 65 66 // Find the start and end markers of the module. 67 var startSeen, endSeen bool 68 var start, end uint64 69 70 for _, symbol := range symbols { 71 if symbol.Section != textSectionIndex { 72 continue 73 } 74 75 switch symbol.Name { 76 case "BORINGSSL_bcm_text_start": 77 if startSeen { 78 return errors.New("duplicate start symbol found") 79 } 80 startSeen = true 81 start = symbol.Value 82 case "BORINGSSL_bcm_text_end": 83 if endSeen { 84 return errors.New("duplicate end symbol found") 85 } 86 endSeen = true 87 end = symbol.Value 88 default: 89 continue 90 } 91 } 92 93 if !startSeen || !endSeen { 94 return errors.New("could not find module in object") 95 } 96 97 moduleText := make([]byte, end-start) 98 if n, err := textSection.ReadAt(moduleText, int64(start-textSection.Addr)); err != nil { 99 return fmt.Errorf("failed to read from module start (at %d of %d) in .text: %s", start, textSection.Size, err) 100 } else if n != len(moduleText) { 101 return fmt.Errorf("short read from .text: wanted %d, got %d", len(moduleText), n) 102 } 103 104 // In order to match up the module start with the raw ELF contents, 105 // search for the first 256 bytes and assume that will be unique. 106 offset := bytes.Index(objectBytes, moduleText[:256]) 107 if offset < 0 { 108 return errors.New("did not find module prefix in object file") 109 } 110 111 if bytes.Index(objectBytes[offset+1:], moduleText[:256]) >= 0 { 112 return errors.New("found two occurrences of prefix in object file") 113 } 114 115 // Corrupt the module in the ELF. 116 objectBytes[offset] ^= 1 117 118 // Calculate the before and after hash of the module. 119 var zeroKey [64]byte 120 mac := hmac.New(sha512.New, zeroKey[:]) 121 mac.Write(moduleText) 122 hashWas := mac.Sum(nil) 123 124 moduleText[0] ^= 1 125 mac.Reset() 126 mac.Write(moduleText) 127 newHash := mac.Sum(nil) 128 129 fmt.Printf("Found start of module at offset 0x%x (VMA 0x%x):\n", start-textSection.Addr, start) 130 fmt.Println(hex.Dump(moduleText[:128])) 131 fmt.Printf("\nHash of module was: %x\n", hashWas) 132 fmt.Printf("Hash of corrupted module is: %x\n", newHash) 133 134 return ioutil.WriteFile(outPath, objectBytes, 0755) 135} 136 137func main() { 138 if len(os.Args) != 3 { 139 usage() 140 os.Exit(1) 141 } 142 143 if err := do(os.Args[2], os.Args[1]); err != nil { 144 fmt.Fprintf(os.Stderr, "%s\n", err) 145 os.Exit(1) 146 } 147} 148 149func usage() { 150 fmt.Fprintf(os.Stderr, "Usage: %s <input binary> <output path>\n", os.Args[0]) 151} 152