// SimpleHashcatRules applies the basic hashcat rules based on delete, insert, replace func SimpleHashcatRules(word []rune, password []rune, operations []EditOp) []string { if string(word) == string(password) { return []string{":"} } temp := make([]rune, len(word)) copy(temp, word) r := []string{} for _, op := range operations { if op.Op == "insert" { r = append(r, fmt.Sprintf("i%d%c", op.P, password[op.P])) temp = rules.InsertAtN(temp, op.P, password[op.P]) } else if op.Op == "delete" { r = append(r, fmt.Sprintf("D%d", op.P)) temp = rules.DeleteN(temp, op.P) } else if op.Op == "replace" { r = append(r, fmt.Sprintf("o%d%c", op.P, password[op.P])) temp = rules.OverwriteAtN(temp, op.P, password[op.P]) } } if string(temp) == string(password) { return r } return nil }
// RuleWorks tests if a rule results in the correct managled word func RuleWorks(word []rune, password []rune, operations []EditOp) bool { temp := make([]rune, len(word)) copy(temp, word) for _, op := range operations { if op.Op == "insert" { rules.InsertAtN(temp, op.P, password[op.P]) } else if op.Op == "delete" { rules.DeleteN(temp, op.P) } else if op.Op == "replace" { rules.OverwriteAtN(temp, op.P, password[op.P]) } } if string(temp) == string(password) { return true } return false }
// AdvancedHashcatRules applies all hashcat rules to a word func AdvancedHashcatRules(passwordString, wordString string, perations []EditOp) []string { // TODO // can we do this earlier not in this function to save a fucntion call if passwordString == wordString { return []string{":"} } password := []rune(passwordString) word := []rune(wordString) needNewName := []string{} // this holds the current mangled as rules are applied wordRules := make([]rune, 0, len(word)) wordRules = append(wordRules, []rune(word)[:]...) var passwordLower int var passwordUpper int for _, r := range password { if unicode.IsLower(r) { passwordLower++ } else if unicode.IsUpper(r) { passwordUpper++ } } for i, op := range perations { if op.Op == "insert" { needNewName = append(needNewName, fmt.Sprintf("i%c%c", rules.ToAlpha(op.P), password[op.P])) wordRules = rules.InsertAtN(wordRules, op.P, password[op.P]) } else if op.Op == "delete" { needNewName = append(needNewName, fmt.Sprintf("D%c", rules.ToAlpha(op.P))) wordRules = rules.DeleteN(wordRules, op.P) } else if op.Op == "replace" { // rule was made obsolete by prior global replacement // test to see if word is greater than password to avoid index error if len(wordRules) >= len(password) && wordRules[op.P] == password[op.P] { if *debug { fmt.Println("obsolete rule") } // Swapping rules } else if op.P < len(password)-1 && op.P < len(word)-1 && word[op.P] == password[op.P+1] && word[op.P+1] == password[op.P] { if op.P == 0 && RuleWorks(word, password, perations[i+1:]) { needNewName = append(needNewName, "k") wordRules = rules.SwapFront(wordRules) } else if op.P == len(wordRules)-2 && RuleWorks(rules.SwapBack(wordRules), password, perations[i+1:]) { needNewName = append(needNewName, "K") wordRules = rules.SwapBack(wordRules) } else if RuleWorks(rules.SwapAtN(wordRules, op.P, op.P+1), password, perations[i+1:]) { // Swap any two characters (only adjacent swapping is supported) needNewName = append(needNewName, fmt.Sprintf("*%c%c", op.P, rules.ToAlpha(op.P+1))) wordRules = rules.SwapAtN(wordRules, op.P, op.P+1) } else { needNewName = append(needNewName, fmt.Sprintf("o%c%c", rules.ToAlpha(op.P), password[op.P])) wordRules = rules.OverwriteAtN(wordRules, op.P, password[op.P]) } // Case Toggle: Uppercased a letter } else if unicode.IsLower(wordRules[op.P]) && unicode.ToUpper(wordRules[op.P]) == password[op.P] { // Toggle the case of all characters in word (mixed cases) if passwordUpper > 0 && passwordLower > 0 && RuleWorks(rules.ToggleCase(wordRules), password, perations[i+1:]) { needNewName = append(needNewName, "t") wordRules = rules.ToggleCase(wordRules) // Capitalize all letters } else if RuleWorks(rules.Uppercase(wordRules), password, perations[i+1:]) { needNewName = append(needNewName, "u") wordRules = rules.Uppercase(wordRules) // Capitalize the first letter } else if op.P == 0 && RuleWorks(rules.Capitalize(wordRules), password, perations[i+1:]) { needNewName = append(needNewName, "c") wordRules = rules.Capitalize(wordRules) // Toggle the case of characters at position N } else { needNewName = append(needNewName, fmt.Sprintf("T%c", rules.ToAlpha(op.P))) wordRules = rules.ToggleAt(wordRules, op.P) } // Case Toggle Lowercased a letter } else if unicode.IsUpper(wordRules[op.P]) && unicode.ToLower(wordRules[op.P]) == password[op.P] { // Toggle the case of all characters in word (mixed cases) if passwordUpper > 0 && passwordLower > 0 && RuleWorks(rules.ToggleCase(wordRules), password, perations[i+1:]) { needNewName = append(needNewName, "t") wordRules = rules.ToggleCase(wordRules) // Lowercase all letters } else if RuleWorks(rules.Lowercase(wordRules), password, perations[i+1:]) { needNewName = append(needNewName, "l") wordRules = rules.Lowercase(wordRules) // Lowercase the first found character, uppercase the rest } else if op.P == 0 && RuleWorks(rules.InvertCapitalize(wordRules), password, perations[i+1:]) { needNewName = append(needNewName, "C") wordRules = rules.InvertCapitalize(wordRules) // Toggle the case of characters at position N } else { needNewName = append(needNewName, fmt.Sprintf("T%c", rules.ToAlpha(op.P))) wordRules = rules.ToggleAt(wordRules, op.P) } // Special case substitution of 'all' instances (1337 $p34k) } else if unicode.IsLetter(wordRules[op.P]) && !unicode.IsLetter(password[op.P]) && RuleWorks(rules.Replace(wordRules[0:], wordRules[op.P], password[op.P]), password, perations[i+1:]) { needNewName = append(needNewName, fmt.Sprintf("s%c%c", wordRules[op.P], password[op.P])) wordRules = rules.Replace(wordRules, wordRules[op.P], password[op.P]) // Replace next character with current } else if op.P < len(password)-1 && op.P < len(wordRules)-1 && password[op.P] == password[op.P+1] && password[op.P] == wordRules[op.P+1] { needNewName = append(needNewName, fmt.Sprintf(".%c", rules.ToAlpha(op.P))) wordRules = rules.ReplaceNPlus(wordRules, op.P) // Replace previous character with current } else if op.P > 0 && op.Word > 0 && password[op.P] == password[op.P-1] && password[op.P] == wordRules[op.P-1] { needNewName = append(needNewName, fmt.Sprintf(",%c", rules.ToAlpha(op.P))) wordRules = rules.ReplaceNMinus(wordRules, op.P) // ASCII increment } else if wordRules[op.P]+1 == password[op.P] { needNewName = append(needNewName, fmt.Sprintf("+%c", rules.ToAlpha(op.P))) wordRules = rules.ASCIIIncrementPlus(wordRules, op.P) // ASCII decrement } else if wordRules[op.P]-1 == password[op.P] { needNewName = append(needNewName, fmt.Sprintf("-%c", rules.ToAlpha(op.P))) wordRules = rules.ASCIIIncrementMinus(wordRules, op.P) // SHIFT left } else if wordRules[op.P]<<1 == password[op.P] { needNewName = append(needNewName, fmt.Sprintf("L%c", rules.ToAlpha(op.P))) wordRules = rules.BitwiseShiftLeft(wordRules, op.P) // SHIFT right } else if wordRules[op.P]>>1 == password[op.P] { needNewName = append(needNewName, fmt.Sprintf("R%c", rules.ToAlpha(op.P))) wordRules = rules.BitwiseShiftRight(wordRules, op.P) // Position based replacements. } else { needNewName = append(needNewName, fmt.Sprintf("o%c%c", rules.ToAlpha(op.P), password[op.P])) wordRules = rules.OverwriteAtN(wordRules, op.P, password[op.P]) } } } // out of for loop // these next things convert rules to append $ and prepend rules // TODO // possibility to have either what the rule is now or // the rule swapped with these replacements // Prefix rules lastPrefix := 0 var prefixRules []string for i, hashcatRule := range needNewName { if hashcatRule[0] == 'i' && rules.ToNumByte(hashcatRule[1]) == lastPrefix { prefixRules = append(prefixRules, fmt.Sprintf("^%c", hashcatRule[2])) lastPrefix++ needNewName[i] = fmt.Sprintf("^%c", hashcatRule[2]) } else { // TODO // dont know about breaking early here break } } // Appendix rules lastAppendix := len(password) - 1 var appendixRules []string for i, hashcatRule := range needNewName { if hashcatRule[0] == 'i' && rules.ToNumByte(hashcatRule[1]) == lastAppendix { appendixRules = append(appendixRules, fmt.Sprintf("$%c", hashcatRule[2])) lastAppendix-- needNewName[i] = fmt.Sprintf("$%c", hashcatRule[2]) } else { break } } // Truncate left rules lastPrecut := 0 for i, hashcatRule := range needNewName { if hashcatRule[0] == 'D' && rules.ToNumByte(hashcatRule[1]) == lastPrecut { needNewName[i] = "[" } else { break } } // Truncate right rules lastPostcut := len(password) for i, hashcatRule := range needNewName { if hashcatRule[0] == 'D' && rules.ToNumByte(hashcatRule[1]) >= lastPostcut { needNewName[i] = "]" } else { break } } /* // naive implementation of OMN // will only work if the first rule is a delete overwrite := 0 for i, hashcatRule := range needNewName { if hashcatRule[0] == 'D' && i < len(password)-1 && needNewName[i+1] == 'D' { overwrite++ needNewName[i] = "" } else { break } } if overwrite > 0 { var temp []string temp = append(temp, fmt.Sprintf("O%c%c", rules.ToAlpha(0), rules.ToAlpha(overwrite))) temp = append(temp, needNewName[:]...) needNewName = temp } */ // Check if rules result in the correct password if string(wordRules) == passwordString { return needNewName } log.Printf("advanced processing failed: P: %s, M: %s, O: %s, %v\n", passwordString, string(wordRules), wordString, needNewName) return nil }