Skip to content

Instantly share code, notes, and snippets.

@raylee
Last active October 23, 2020 19:51
Show Gist options
  • Save raylee/8aa3cc04d60d728eefdbce51f2b9f9ee to your computer and use it in GitHub Desktop.
Save raylee/8aa3cc04d60d728eefdbce51f2b9f9ee to your computer and use it in GitHub Desktop.
guesses passwords for zip files
package main
// zipguess.go
// reads a tsv file, one set of possibilities per line
// one possibile segment is chosen from each line in order
// and concatenated together into a guess
import (
"flag"
"fmt"
"io/ioutil"
"os"
"strings"
ezip "github.com/olegpolukhin/zip"
)
type Guesser struct {
atoms [][]string
}
func NewGuesses(filename string) (*Guesser, error) {
c, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
g := Guesser{make([][]string, 0)}
for _, line := range strings.Split(string(c), "\n") {
atoms := strings.FieldsFunc(line, func(r rune) bool {
if r == '\t' {
return true
} else {
return false
}
})
if len(atoms) > 0 {
g.atoms = append(g.atoms, atoms)
}
}
return &g, nil
}
func Inc(indices, boundaries []int) (more bool) {
// if a complete rollover, all values back to zero, then return false, otherwise true
for i := len(indices) - 1; i >= 0; i-- {
indices[i]++
if indices[i] == boundaries[i] {
indices[i] = 0
} else {
return true
}
}
return false
}
func (g *Guesser) Guesses(root int) <-chan string {
results := make(chan string, 1000)
indicies := make([]int, len(g.atoms))
boundaries := make([]int, len(g.atoms))
base := 0
if root >= 0 {
indicies[0] = root
base = 1
}
for i := 0; i < len(boundaries); i++ {
boundaries[i] = len(g.atoms[i])
}
gen := func(g *Guesser) {
for Inc(indicies[base:], boundaries[base:]) {
var guess string
for i := 0; i < len(indicies); i++ {
guess += g.atoms[i][indicies[i]]
}
results <- guess
}
close(results)
}
go gen(g)
return results
}
func (g *Guesser) String() string {
var s string
for i := 0; i < len(g.atoms); i++ {
s += fmt.Sprintf("atom %2d: %s\n", i, g.atoms[i])
}
return s
}
var (
atomfile = flag.String("atoms", "", "a tab-separated-value file containing the atoms used in a guess. One atom is selected from each row and concatenated together.")
zipfile = flag.String("zip", "", "filename of the zip file to guess at")
)
func Usage() string {
return `Usage:
zipguess -atoms <atomfile.tsv> -zip <zipfile.zip>
<atomfile.tsv> is a tab-separated value (TSV) file. Each row is
used as a set of guesses for that position. One guess is taken from
each row in order, and concatenated into a password to attempt
against <zipfile.zip>. It's assumed all files contained in the zip
use the same password. Only the first password for the first
password protected file is returned.
`
}
func main() {
flag.Parse()
if *atomfile == "" || *zipfile == "" {
fmt.Println(Usage())
os.Exit(1)
}
g, err := NewGuesses(*atomfile)
if err != nil {
fmt.Println("atomfile error:", err)
os.Exit(1)
}
fmt.Printf("Atoms in use:\n%s", g)
checkPasswords(g)
}
func checkPasswords(g *Guesser) string {
r, err := ezip.OpenReader(*zipfile)
if err != nil {
fmt.Printf("Could not open %s: %v", *zipfile, err)
return ""
}
defer r.Close()
if len(r.File) < 1 {
fmt.Printf("Zip file contains no files?")
return ""
}
f := r.File[0]
if !f.IsEncrypted() {
fmt.Printf("Files are not encrypted: %s", f.Name)
return ""
}
for guess := range g.Guesses(-1) {
f.SetPassword(guess)
rc, err := f.Open()
if err != nil {
fmt.Println(err)
continue
}
_, err = ioutil.ReadAll(rc)
rc.Close()
if err != nil {
continue
}
fmt.Println("Success:", guess)
return guess
}
return ""
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment