Skip to content

Commit

Permalink
[feat] Add image, directory, pipe functionalities
Browse files Browse the repository at this point in the history
  • Loading branch information
Yunus AYDIN committed Jul 30, 2022
1 parent 871fceb commit f86b6df
Show file tree
Hide file tree
Showing 12 changed files with 379 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/exifLooter.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added 036ad8d7-d949-4108-899b-1d2dfae2df92.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added DSCN0010.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added LICENSE
Empty file.
305 changes: 305 additions & 0 deletions cmd/root.go
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 added exifLooter
Binary file not shown.
16 changes: 16 additions & 0 deletions go.mod
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
)
21 changes: 21 additions & 0 deletions go.sum
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=
11 changes: 11 additions & 0 deletions main.go
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()
}

0 comments on commit f86b6df

Please sign in to comment.