-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.go
129 lines (105 loc) · 3.23 KB
/
main.go
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
// cve-2022-44268-detector is a Go program that consumes PNGs and reports
// whether they appear to be malicious images generated by an exploit for
// CVE-2022-44268, an ImageMagick vulnerability that allows attackers to
// read arbitrary file contents using specially crafted images.
package main
import (
"errors"
"flag"
"io"
"log"
"os"
"regexp"
"github.com/jnschaeffer/cve-2022-44268-detector/internal/image/png"
"github.com/jnschaeffer/cve-2022-44268-detector/internal/imutil"
)
const (
// profileKeyword is the string that imagetragic appears to use for
// the png chunk keyword containing leaked data. The code path is
// not immediately obvious in the codebase, but dynamic analysis
// appears to indicate it uses this string.
//
// $ git status
// HEAD detached at 7.0.9-17
// nothing to commit, working tree clean
// $ git grep 'Raw profile type'
// coders/png.c: We have already read "Raw profile type.
// coders/png.c: memcmp(text[i].key, "Raw profile type ",17) == 0)
// coders/png.c: "Raw profile type ",MagickPathExtent);
profileKeyword = "Raw profile type"
)
var (
filename string
printContents string
keywordRegexS string
debug bool
errNoIOCFound = errors.New("no potential indicators of compromise found")
)
func init() {
flag.StringVar(&filename, "filename", "", "filename to parse (uses stdin if not set)")
flag.StringVar(&printContents, "print", "",
"if set, write each suspicious png chunk's value to stdout.\n"+
"May specify: '"+imutil.WRaw+"', '"+imutil.WDecompress+"', '"+imutil.WDecompressHexdecode+"'")
flag.StringVar(&keywordRegexS, "keyword", "^"+profileKeyword+"$", "regex to match png chunk keyword")
flag.BoolVar(&debug, "debug", false, "enable debug logging if set. The program will emit information about\n"+
"unknown png chunk types")
}
func main() {
flag.Parse()
log.SetFlags(0)
if debug {
png.Debug = log.New(log.Writer(), "[debug] ", log.Flags()|log.Lmsgprefix)
}
switch printContents {
case "", imutil.WRaw, imutil.WDecompress, imutil.WDecompressHexdecode:
// Permitted output values.
default:
log.Fatalf("unknown output mode: '%s'", printContents)
}
keywordRegex, err := regexp.Compile(keywordRegexS)
if err != nil {
log.Fatalf("failed to parse png chunk keyword regex - %s", err)
}
var r io.Reader = os.Stdin
if filename != "" {
f, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
defer f.Close()
r = f
}
chunks, err := png.FindTextChunks(r)
if err != nil {
log.Fatal(err)
}
err = findChunks(keywordRegex, chunks)
if err != nil {
log.Println(err)
if errors.Is(err, errNoIOCFound) {
os.Exit(10)
}
os.Exit(1)
}
}
func findChunks(keywordRegex *regexp.Regexp, chunks []png.TextChunk) error {
numFound := 0
for _, chunk := range chunks {
if keywordRegex.MatchString(chunk.Keyword) {
log.Printf("***POTENTIAL INDICATOR OF COMPROMISE*** - "+
"keyword: '%s' | compressed: %t | id: %d | offset: 0x%x | len: %d",
chunk.Keyword, chunk.Compressed, numFound, chunk.Offset, len(chunk.Value))
if printContents != "" {
err := imutil.WriteChunkTo(&chunk, os.Stdout, printContents)
if err != nil {
return err
}
}
numFound++
}
}
if numFound == 0 {
return errNoIOCFound
}
return nil
}