Adam Langley | 21cb074 | 2017-05-17 17:55:00 -0700 | [diff] [blame] | 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. |
| 17 | package main |
| 18 | |
| 19 | import ( |
| 20 | "bytes" |
| 21 | "crypto/hmac" |
| 22 | "crypto/sha512" |
| 23 | "debug/elf" |
| 24 | "encoding/hex" |
| 25 | "errors" |
| 26 | "fmt" |
Adam Langley | 21cb074 | 2017-05-17 17:55:00 -0700 | [diff] [blame] | 27 | "os" |
| 28 | ) |
| 29 | |
| 30 | func do(outPath, inPath string) error { |
David Benjamin | 5511fa8 | 2022-11-12 15:52:28 +0000 | [diff] [blame] | 31 | objectBytes, err := os.ReadFile(inPath) |
Adam Langley | 21cb074 | 2017-05-17 17:55:00 -0700 | [diff] [blame] | 32 | if err != nil { |
| 33 | return err |
| 34 | } |
| 35 | |
| 36 | object, err := elf.NewFile(bytes.NewReader(objectBytes)) |
| 37 | if err != nil { |
| 38 | return errors.New("failed to parse object: " + err.Error()) |
| 39 | } |
| 40 | |
| 41 | // Find the .text section. |
| 42 | var textSection *elf.Section |
| 43 | var textSectionIndex elf.SectionIndex |
| 44 | for i, section := range object.Sections { |
| 45 | if section.Name == ".text" { |
| 46 | textSectionIndex = elf.SectionIndex(i) |
| 47 | textSection = section |
| 48 | break |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | if textSection == nil { |
| 53 | return errors.New("failed to find .text section in object") |
| 54 | } |
| 55 | |
| 56 | symbols, err := object.Symbols() |
| 57 | if err != nil { |
Pete Bentley | 76918d0 | 2019-10-16 16:14:23 +0100 | [diff] [blame] | 58 | fmt.Fprintf(os.Stderr, "%s\nTrying dynamic symbols\n", err) |
| 59 | symbols, err = object.DynamicSymbols() |
| 60 | } |
| 61 | if err != nil { |
Adam Langley | 21cb074 | 2017-05-17 17:55:00 -0700 | [diff] [blame] | 62 | return errors.New("failed to parse symbols: " + err.Error()) |
| 63 | } |
| 64 | |
| 65 | // Find the start and end markers of the module. |
| 66 | var startSeen, endSeen bool |
| 67 | var start, end uint64 |
| 68 | |
| 69 | for _, symbol := range symbols { |
| 70 | if symbol.Section != textSectionIndex { |
| 71 | continue |
| 72 | } |
| 73 | |
| 74 | switch symbol.Name { |
| 75 | case "BORINGSSL_bcm_text_start": |
| 76 | if startSeen { |
| 77 | return errors.New("duplicate start symbol found") |
| 78 | } |
| 79 | startSeen = true |
| 80 | start = symbol.Value |
| 81 | case "BORINGSSL_bcm_text_end": |
| 82 | if endSeen { |
| 83 | return errors.New("duplicate end symbol found") |
| 84 | } |
| 85 | endSeen = true |
| 86 | end = symbol.Value |
| 87 | default: |
| 88 | continue |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | if !startSeen || !endSeen { |
| 93 | return errors.New("could not find module in object") |
| 94 | } |
| 95 | |
| 96 | moduleText := make([]byte, end-start) |
| 97 | if n, err := textSection.ReadAt(moduleText, int64(start-textSection.Addr)); err != nil { |
| 98 | return fmt.Errorf("failed to read from module start (at %d of %d) in .text: %s", start, textSection.Size, err) |
| 99 | } else if n != len(moduleText) { |
| 100 | return fmt.Errorf("short read from .text: wanted %d, got %d", len(moduleText), n) |
| 101 | } |
| 102 | |
| 103 | // In order to match up the module start with the raw ELF contents, |
| 104 | // search for the first 256 bytes and assume that will be unique. |
| 105 | offset := bytes.Index(objectBytes, moduleText[:256]) |
| 106 | if offset < 0 { |
| 107 | return errors.New("did not find module prefix in object file") |
| 108 | } |
| 109 | |
| 110 | if bytes.Index(objectBytes[offset+1:], moduleText[:256]) >= 0 { |
| 111 | return errors.New("found two occurrences of prefix in object file") |
| 112 | } |
| 113 | |
| 114 | // Corrupt the module in the ELF. |
| 115 | objectBytes[offset] ^= 1 |
| 116 | |
| 117 | // Calculate the before and after hash of the module. |
| 118 | var zeroKey [64]byte |
| 119 | mac := hmac.New(sha512.New, zeroKey[:]) |
| 120 | mac.Write(moduleText) |
| 121 | hashWas := mac.Sum(nil) |
| 122 | |
| 123 | moduleText[0] ^= 1 |
| 124 | mac.Reset() |
| 125 | mac.Write(moduleText) |
| 126 | newHash := mac.Sum(nil) |
| 127 | |
| 128 | fmt.Printf("Found start of module at offset 0x%x (VMA 0x%x):\n", start-textSection.Addr, start) |
Pete Bentley | 76918d0 | 2019-10-16 16:14:23 +0100 | [diff] [blame] | 129 | fmt.Println(hex.Dump(moduleText[:128])) |
Adam Langley | 21cb074 | 2017-05-17 17:55:00 -0700 | [diff] [blame] | 130 | fmt.Printf("\nHash of module was: %x\n", hashWas) |
| 131 | fmt.Printf("Hash of corrupted module is: %x\n", newHash) |
| 132 | |
David Benjamin | 5511fa8 | 2022-11-12 15:52:28 +0000 | [diff] [blame] | 133 | return os.WriteFile(outPath, objectBytes, 0755) |
Adam Langley | 21cb074 | 2017-05-17 17:55:00 -0700 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | func main() { |
| 137 | if len(os.Args) != 3 { |
| 138 | usage() |
| 139 | os.Exit(1) |
| 140 | } |
| 141 | |
| 142 | if err := do(os.Args[2], os.Args[1]); err != nil { |
| 143 | fmt.Fprintf(os.Stderr, "%s\n", err) |
| 144 | os.Exit(1) |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | func usage() { |
| 149 | fmt.Fprintf(os.Stderr, "Usage: %s <input binary> <output path>\n", os.Args[0]) |
| 150 | } |