| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- package main
- import (
- "fmt"
- "log"
- "net/url"
- "os"
- "os/user"
- "runtime"
- "strconv"
- "strings"
- "gopkg.in/ini.v1"
- )
- // WinSCP password encryption/decryption salts.
- const (
- PasswordMagic = 0xA3
- PasswordFlag = 0xFF
- )
- func main() {
- args := os.Args[1:]
- if len(args) != 3 && len(args) != 2 {
- // In case provided arguments doesn't match the
- // application usage, print application usage message.
- PrintHelp()
- return
- }
- if args[0] == "ini" {
- // In case 'ini' argument was provided,
- // start the decryption of ini file.
- var iniPath string
- if len(args) == 2 {
- iniPath = args[1]
- } else {
- iniPath = GetDefaultWinSCPIniFilePath()
- }
- DecryptIni(iniPath)
- return
- }
- // In case any argument matches a different operation,
- // perform the default decryption operation.
- fmt.Println(Decrypt(args[0], args[1], args[2]))
- }
- // PrintHelp prints a help message with instructions about the application usage.
- func PrintHelp() {
- fmt.Println("WinSCP stored password finder")
- // WinSCP's password manual decryption mode.
- if runtime.GOOS == "windows" {
- fmt.Println("Registry:")
- fmt.Println(" Open regedit and navigate to [HKEY_CURRENT_USER\\Software\\Martin Prikryl\\WinSCP 2\\Sessions] to get the hostname, username and encrypted password")
- fmt.Println(" Usage winscppasswd.exe <host> <username> <encrypted_password>")
- } else {
- fmt.Println(" Usage ./winscppasswd <host> <username> <encrypted_password>")
- }
- // WinSCP's ini file mode.
- fmt.Println("\nWinSCP.ini:")
- if runtime.GOOS == "windows" {
- fmt.Println(" Usage winscppasswd.exe ini [<filepath>]")
- fmt.Printf(" Default value <filepath>: %s\n", GetDefaultWinSCPIniFilePath())
- } else {
- fmt.Println(" Usage ./winscppasswd ini [<filepath>]")
- }
- }
- // GetDefaultWinSCPIniFilePath obtains default WinSCP configuration file.
- func GetDefaultWinSCPIniFilePath() string {
- usr, err := user.Current()
- if err != nil {
- log.Fatal(err)
- }
- return usr.HomeDir + "\\AppData\\Roaming\\winSCP.ini"
- }
- // DecryptIni decrypts all entries from a WinSCP's ini file.
- func DecryptIni(filepath string) {
- cfg, err := ini.InsensitiveLoad(filepath)
- if err != nil {
- panic(err)
- }
- // Print every entry of the configuration that has password field.
- for _, c := range cfg.Sections() {
- if c.HasKey("Password") {
- name, _ := url.PathUnescape(strings.TrimPrefix(c.Name(), "sessions\\"))
- fmt.Printf("%s\n", name)
- fmt.Printf(" Hostname: %s\n", c.Key("HostName").Value())
- fmt.Printf(" Username: %s\n", c.Key("UserName").Value())
- fmt.Printf(" Password: %s\n", Decrypt(c.Key("HostName").Value(), c.Key("UserName").Value(), c.Key("Password").Value()))
- fmt.Println("========================")
- }
- }
- }
- // Decrypt decripts a specific server password.
- func Decrypt(host, username, password string) string {
- // Build 'encryptedPasswordBytes' variable.
- encryptedPasswordBytes := GetCryptedPasswordBytes(password)
- // Extract 'flag' and 'cryptedPasswordlength' variables
- flag, encryptedPasswordBytes := DecryptNextCharacter(encryptedPasswordBytes) // decryptNextCharacter alters the encryptedPasswordBytes variable to remove already parsed characters.
- cryptedPasswordlength, encryptedPasswordBytes := GetCryptedPasswordLength(flag, encryptedPasswordBytes)
- // Build 'clearpass' variable
- clearpass := GetPassword(cryptedPasswordlength, encryptedPasswordBytes)
- // Apply correction to the 'clearpass' variable.
- if flag == PasswordFlag {
- // The clearpass will contians the username, host and password.
- // Substring username and host from the result password.
- key := username + host
- clearpass = clearpass[len(key):]
- }
- return clearpass
- }
- // GetCryptedPasswordBytes obtains the crypted password byte array.
- func GetCryptedPasswordBytes(password string) []byte {
- encryptedPasswordBytes := []byte{}
- for i := 0; i < len(password); i++ {
- val, _ := strconv.ParseInt(string(password[i]), 16, 8)
- encryptedPasswordBytes = append(encryptedPasswordBytes, byte(val))
- }
- return encryptedPasswordBytes
- }
- // GetCryptedPasswordLength obtains crypted password length from crypted password byte array.
- func GetCryptedPasswordLength(flag byte, encryptedPasswordBytes []byte) (byte, []byte) {
- var cryptedPasswordlength byte = 0
- if flag == PasswordFlag {
- _, encryptedPasswordBytes = DecryptNextCharacter(encryptedPasswordBytes) // Ignore two characters of the encryptedPasswordBytes.
- cryptedPasswordlength, encryptedPasswordBytes = DecryptNextCharacter(encryptedPasswordBytes) // decryptNextCharacter alters the encryptedPasswordBytes variable to remove already parsed characters.
- } else {
- cryptedPasswordlength = flag
- }
- toBeDeleted, encryptedPasswordBytes := DecryptNextCharacter(encryptedPasswordBytes) // decryptNextCharacter alters the encryptedPasswordBytes variable to remove already parsed characters.
- encryptedPasswordBytes = encryptedPasswordBytes[toBeDeleted*2:]
- return cryptedPasswordlength, encryptedPasswordBytes
- }
- // GetPassword obtains clear password from crypted password byte array
- func GetPassword(cryptedPasswordlength byte, encryptedPasswordBytes []byte) string {
- var i, character byte
- var decryptedPassword string
- for i = 0; i < cryptedPasswordlength; i++ {
- character, encryptedPasswordBytes = DecryptNextCharacter(encryptedPasswordBytes) // decryptNextCharacter alters the encryptedPasswordBytes variable to remove already parsed characters.
- decryptedPassword += string(character) // Add decrypted character to the result variable.
- }
- return decryptedPassword
- }
- // DecryptNextCharacter decrypts next character from byte array.
- // Alters the byte array to remove already parsed bytes.
- func DecryptNextCharacter(encryptedPasswordBytes []byte) (byte, []byte) {
- if len(encryptedPasswordBytes) <= 0 {
- // In case encryptedPasswordBytes param was empty,
- // stop the flow here returning '0'.
- return 0, encryptedPasswordBytes
- }
- a := encryptedPasswordBytes[0] // Obtain first character to parse.
- b := encryptedPasswordBytes[1] // Obtain second character to parse.
- encryptedPasswordBytes = encryptedPasswordBytes[2:] // Remove already parsed characters.
- return DecryptCharacter(a, b), encryptedPasswordBytes
- }
- // DecryptCharacter decrypts character from two bytes.
- func DecryptCharacter(a, b byte) byte {
- return ^(((a << 4) + b) ^ PasswordMagic) & PasswordFlag
- }
|