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 return errors.New("failed to parse symbols: " + err.Error()) 60 } 61 62 // Find the start and end markers of the module. 63 var startSeen, endSeen bool 64 var start, end uint64 65 66 for _, symbol := range symbols { 67 if symbol.Section != textSectionIndex { 68 continue 69 } 70 71 switch symbol.Name { 72 case "BORINGSSL_bcm_text_start": 73 if startSeen { 74 return errors.New("duplicate start symbol found") 75 } 76 startSeen = true 77 start = symbol.Value 78 case "BORINGSSL_bcm_text_end": 79 if endSeen { 80 return errors.New("duplicate end symbol found") 81 } 82 endSeen = true 83 end = symbol.Value 84 default: 85 continue 86 } 87 } 88 89 if !startSeen || !endSeen { 90 return errors.New("could not find module in object") 91 } 92 93 moduleText := make([]byte, end-start) 94 if n, err := textSection.ReadAt(moduleText, int64(start-textSection.Addr)); err != nil { 95 return fmt.Errorf("failed to read from module start (at %d of %d) in .text: %s", start, textSection.Size, err) 96 } else if n != len(moduleText) { 97 return fmt.Errorf("short read from .text: wanted %d, got %d", len(moduleText), n) 98 } 99 100 // In order to match up the module start with the raw ELF contents, 101 // search for the first 256 bytes and assume that will be unique. 102 offset := bytes.Index(objectBytes, moduleText[:256]) 103 if offset < 0 { 104 return errors.New("did not find module prefix in object file") 105 } 106 107 if bytes.Index(objectBytes[offset+1:], moduleText[:256]) >= 0 { 108 return errors.New("found two occurrences of prefix in object file") 109 } 110 111 // Corrupt the module in the ELF. 112 objectBytes[offset] ^= 1 113 114 // Calculate the before and after hash of the module. 115 var zeroKey [64]byte 116 mac := hmac.New(sha512.New, zeroKey[:]) 117 mac.Write(moduleText) 118 hashWas := mac.Sum(nil) 119 120 moduleText[0] ^= 1 121 mac.Reset() 122 mac.Write(moduleText) 123 newHash := mac.Sum(nil) 124 125 fmt.Printf("Found start of module at offset 0x%x (VMA 0x%x):\n", start-textSection.Addr, start) 126 fmt.Printf(hex.Dump(moduleText[:128])) 127 fmt.Printf("\nHash of module was: %x\n", hashWas) 128 fmt.Printf("Hash of corrupted module is: %x\n", newHash) 129 130 return ioutil.WriteFile(outPath, objectBytes, 0755) 131} 132 133func main() { 134 if len(os.Args) != 3 { 135 usage() 136 os.Exit(1) 137 } 138 139 if err := do(os.Args[2], os.Args[1]); err != nil { 140 fmt.Fprintf(os.Stderr, "%s\n", err) 141 os.Exit(1) 142 } 143} 144 145func usage() { 146 fmt.Fprintf(os.Stderr, "Usage: %s <input binary> <output path>\n", os.Args[0]) 147} 148