-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[feat] Add image, directory, pipe functionalities
- Loading branch information
Yunus AYDIN
committed
Jul 30, 2022
1 parent
871fceb
commit f86b6df
Showing
12 changed files
with
379 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
package cmd | ||
|
||
import ( | ||
"bufio" | ||
"github.com/fatih/color" | ||
"github.com/spf13/cobra" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"net/http" | ||
"net/url" | ||
"os" | ||
"os/exec" | ||
"strconv" | ||
"strings" | ||
"time" | ||
) | ||
|
||
var directory string | ||
var image string | ||
|
||
// rootCmd represents the base command when called without any subcommands | ||
var rootCmd = &cobra.Command{ | ||
Use: "exifLooter", | ||
Short: "ExifLooter finds GeoLocation Metadata and display", | ||
Long: `ExifLooter finds GeoLocation Metadata and display. You can use with pipe and flags`, | ||
Run: analyzeFlags, | ||
} | ||
|
||
// Execute adds all child commands to the root command and sets flags appropriately. | ||
// This is called by main.main(). It only needs to happen once to the rootCmd. | ||
func Execute() { | ||
err := rootCmd.Execute() | ||
if err != nil { | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func init() { | ||
|
||
rootCmd.PersistentFlags().BoolP("pipe", "p", false, "Pipe with other scripts") | ||
|
||
rootCmd.PersistentFlags().StringP("image", "i", "", "Specify a image for Analyzing") | ||
|
||
rootCmd.PersistentFlags().StringP("directory", "d", "", "Specify a directory for Analyzing") | ||
} | ||
|
||
func analyzeFlags(cmd *cobra.Command, _ []string) { | ||
i, err := cmd.Flags().GetString("image") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
image = i | ||
|
||
d, err := cmd.Flags().GetString("directory") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
directory = d | ||
|
||
p, err := cmd.Flags().GetBool("pipe") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
if len(directory) != 0 { | ||
analyzeDirectory(cmd) | ||
} else if len(image) != 0 { | ||
analyzeImages(cmd, image, false) | ||
} else if p { | ||
pipeImages() | ||
} | ||
} | ||
|
||
func analyzeImages(cmd *cobra.Command, args string, inDir bool) { | ||
if !inDir { | ||
img, err := cmd.Flags().GetString("image") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
out, err := exec.Command("exiftool", img).Output() | ||
|
||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// fmt.Println(string(out)) | ||
parseOutput(string(out)) | ||
} else { | ||
out, err := exec.Command("exiftool", directory+args).Output() | ||
|
||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// fmt.Println(string(out)) | ||
parseOutput(string(out)) | ||
} | ||
|
||
} | ||
|
||
func analyzeDirectory(cmd *cobra.Command) { | ||
files, err := ioutil.ReadDir(directory) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
for _, file := range files { | ||
if file.IsDir() { | ||
continue | ||
} else { | ||
analyzeImages(cmd, file.Name(), true) | ||
} | ||
} | ||
} | ||
|
||
func parseOutput(out string) { | ||
scanner := bufio.NewScanner(strings.NewReader(out)) | ||
var flag bool | ||
for scanner.Scan() { | ||
txt := scanner.Text() | ||
key := strings.Split(txt, ":") | ||
key[0] = standardizeSpaces(key[0]) | ||
if strings.Contains(key[0], "GPS") { | ||
flag = true | ||
color.Yellow(key[0] + ":" + key[1]) | ||
|
||
} | ||
} | ||
|
||
if !flag{ | ||
color.Green("These image/images not Vulnerable") | ||
} else{ | ||
color.Red("EXIF Geolocation Data Not Stripped From Uploaded Images") | ||
} | ||
} | ||
|
||
func parseOutputPipe(out string, fname string) { | ||
vuln := false | ||
scanner := bufio.NewScanner(strings.NewReader(out)) | ||
for scanner.Scan() { | ||
txt := scanner.Text() | ||
key := strings.Split(txt, ":") | ||
key[0] = standardizeSpaces(key[0]) | ||
if strings.Contains(key[0], "GPS") { | ||
color.Red("EXIF Geolocation Data Not Stripped From Uploaded Images") | ||
color.Yellow(key[0] + ":" + key[1]) | ||
vuln = true | ||
} | ||
} | ||
if !vuln{ | ||
color.Green(fname + " is not Vulnerable") | ||
} | ||
} | ||
|
||
func pipeImages() { | ||
if _, err := os.Stat("images/"); os.IsNotExist(err) { | ||
err := os.Mkdir("images/", 777) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
// Check for stdin input | ||
stat, _ := os.Stdin.Stat() | ||
if (stat.Mode() & os.ModeCharDevice) != 0 { | ||
color.Red("No urls detected. Hint: cat urls.txt | exifLooter -p") | ||
os.Exit(1) | ||
} | ||
|
||
results := make(chan string, 8) | ||
|
||
go func() { | ||
// get each line of stdin, push it to the work channel | ||
s := bufio.NewScanner(os.Stdin) | ||
for s.Scan() { | ||
line := s.Text() | ||
//fmt.Println(line) | ||
|
||
response, e := http.Get(line) | ||
if e != nil { | ||
log.Fatal(e) | ||
} | ||
|
||
defer func(Body io.ReadCloser) { | ||
err := Body.Close() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
}(response.Body) | ||
|
||
//open a file for writing | ||
//fmt.Println(line) | ||
|
||
file, err := os.Create("images/" + strconv.Itoa(int(getTimestamp())) + "." + getFileExtensionFromUrl(line)) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
defer func(file *os.File) { | ||
err := file.Close() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
}(file) | ||
|
||
// Use io.Copy to just dump the response body to the file. This supports huge files | ||
|
||
_, err = io.Copy(file, response.Body) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
//fmt.Println(b) | ||
|
||
//fmt.Println("Success!") | ||
|
||
checkValidImage() | ||
|
||
} | ||
if err := s.Err(); err != nil { | ||
log.Println("reading standard input:", err) | ||
} | ||
close(results) | ||
}() | ||
|
||
w := bufio.NewWriter(os.Stdout) | ||
defer func(w *bufio.Writer) { | ||
err := w.Flush() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
}(w) | ||
|
||
for res := range results { | ||
log.Println(w, res) | ||
} | ||
|
||
files, err := ioutil.ReadDir("images/") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
for _, f := range files { | ||
_, err := os.ReadFile("images/" + f.Name()) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
out, err := exec.Command("exiftool", "images/"+f.Name()).Output() | ||
|
||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// fmt.Println(string(out)) | ||
parseOutputPipe(string(out), f.Name()) | ||
|
||
} | ||
|
||
} | ||
|
||
func getFileExtensionFromUrl(rawUrl string) string { | ||
u, err := url.Parse(rawUrl) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
pos := strings.LastIndex(u.Path, ".") | ||
if pos == -1 { | ||
return "" | ||
} | ||
return u.Path[pos+1 : len(u.Path)] | ||
} | ||
|
||
func standardizeSpaces(s string) string { | ||
return strings.Join(strings.Fields(s), " ") | ||
} | ||
|
||
func getTimestamp() int64 { | ||
return time.Now().UnixNano() / int64(time.Millisecond) | ||
} | ||
|
||
func checkValidImage() bool { | ||
files, err := ioutil.ReadDir("images/") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
for _, f := range files { | ||
file, err := os.ReadFile("images/" + f.Name()) | ||
if err != nil { | ||
return false | ||
} | ||
|
||
if !strings.Contains(http.DetectContentType(file), "image/") { | ||
err := os.Remove("images/" + f.Name()) | ||
if err != nil { | ||
return false | ||
} | ||
} | ||
} | ||
|
||
return false | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module exifLooter | ||
|
||
go 1.17 | ||
|
||
require ( | ||
github.com/fatih/color v1.13.0 | ||
github.com/spf13/cobra v1.5.0 | ||
) | ||
|
||
require ( | ||
github.com/inconshreveable/mousetrap v1.0.0 // indirect | ||
github.com/mattn/go-colorable v0.1.9 // indirect | ||
github.com/mattn/go-isatty v0.0.14 // indirect | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= | ||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= | ||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= | ||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= | ||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= | ||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= | ||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | ||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= | ||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= | ||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= | ||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= | ||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= | ||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* | ||
Copyright © 2022 NAME HERE <EMAIL ADDRESS> | ||
*/ | ||
package main | ||
|
||
import "exifLooter/cmd" | ||
|
||
func main() { | ||
cmd.Execute() | ||
} |