Создание сервиса уведомлений для Cobalt-Strike

Создание сервиса уведомлений для Cobalt-Strike

@Leakinfo

Не хотите проверять свой сервер Cobalt-Strike каждые 5 минут с надеждой на новое уведомление? Представляем сценарий службы уведомлений, доступный по адресу

https://github.com/NVISOsecurity/blogposts/tree/master/cobalt-strike-notifier


Если изображение выше вам по душе, вы знаете, что момент между отправкой фишинга и получением вашего первого уведомления - очень напряженное время. Вам в голову приходят всевозможные сценарии… «Достаточно ли я проверил свою полезную нагрузку?», «Моя электронная почта была заблокирована где-то?», «Моя цель обратила внимание и сообщила об этом как о фишинге?» Вы можете решить некоторые из этих проблем, добавив, например, изображение, которое уведомляет при открытии электронной почты, произвольный HTTP (или DNS) запрос к серверу, который вы контролируете.


Однако есть одна вещь, которую вы не можете контролировать, даже если действительно хотели: когда пользователь нажмет на вашу полезную нагрузку. Если вы используете Cobalt-Strike из коробки, это приведет к тому, что вам придется проверять свой графический интерфейс каждые x минут/часов/дней.


К счастью, Cobalt-Strike позволяет нам изменять или расширять его поведение по умолчанию с помощью «сценариев». Эти сценарии разрабатываются "Sleep" - это язык сценариев на основе java, разработанный и изобретенный Рафаэлем Маджем (создателем Cobalt-Strike). Уже существует масса сценариев-агрессоров, и даже был один, очень похожий на наш вариант использования, разработанный FortyNorthSecurity:

https://fortynorthsecurity.com/blog/aggressor-get-text-messages-for-your-incoming-beacons /


Несмотря на то, что это было близко к тому, что мы хотели реализовать, это не помогло нам поставить сделать все, что хотели. Вышеупомянутый агрессор полагался на старый код Python и использовал службу преобразования электронной почты в текст, доступную только в США. Этот агрессор также не предоставил «отказ», что означает, что вам придется убить агрессора на командном сервере, если вы хотите отказаться от получения уведомлений. Поэтому мы решили, что хватит это терпеть, и сами начали исследовать мир агрессивного кодирования.


Решение двух проблем по цене одной.

Скрипты Aggressor зависят от профиля. Это означает, что они загружаются, когда вы устанавливаете сеанс пользователя, и выгружаются, когда вы отключаетесь от сервера группы.

Это не проблема для нормальной работы, но для сервера уведомлений это может быть не то, что нам нужно. К счастью, Раффи (именно так часто называет себя Рафаэль Мадж) подумал об этом и представил двоичный файл, упакованный с Cobalt-Strike, под названием «agscript».

Познакомьтесь с нашим графическим интерфейсом уведомлений

Давайте сначала посмотрим на наш графический скрипт-агрессор:

Как вы можете видеть здесь, сила графического агрессора действительно проявляется, так как вы можете включать и выключать свои уведомления и детализировать их, как хотите. Давайте заглянем под капот, как это на самом деле работает, я объясню по ходу дела ... 🙂


Прежде всего, мы определяем глобальные переменные, которые затем можно использовать из любой функции, которую мы захотим.

Затем мы устанавливаем уровень отладки на 57, для получения дополнительной информации об уровнях отладки.

global ('$emailaddress');
global ('$email2textaddress');
global ('$signalphonenumber');
global ('$receivesignalmessages');
global ('$receivemails');
global ('$receivetexts');
global ('$scriptlocation');
debug(57);

Затем мы определяем функцию обратного вызова. Эта функция обратного вызова вызывается при нажатии кнопки установки предпочтений и в основном занимается анализом графического интерфейса и установкой всех глобальных переменных на их соответствующие значения. Это также гарантирует, что они сохранятся при закрытии и повторном открытии окна. Функция обратного вызова также заботится об обработке ошибок в графическом интерфейсе. Например, если вы включили сигнальное сообщение, но не указываете номер сигнала.

sub callback {
  $receivemails = $3["emailchkbox"];
  $emailaddress = $3["email"];
  $email2textaddress = $3["txt2email"];
  $receivetexts = $3["textschkbox"];
  $signalphonenumber = $3["signalnumber"];
  $receivesignalmessages = $3["signalchkbox"];
  $scriptlocation = $3["script_location"];
  if(($receivemails eq 'true') && (strlen($emailaddress) == 0))
  {
    show_message("You won't receive emails because you did not input an email address!");
  }  
  else if(($receivetexts eq 'true') && (strlen($email2textaddress) == 0))
  {
    show_message("mail to text field is empty, you will not receive text messages");
  }
   
  else if (($receivesignalmessages eq 'true') && (strlen($signalphonenumber) == 0))
  {
    show_message("You won't receive signal messages because you did not input a phone number");
  }
   
  else
  {
    show_message("preferences saved successfully!");
  }
   
  if (checkError($error)) 
  {
    warn("$error");
  }  
 
}


Функция shownotificationdialog отвечает за отрисовку нашего графического интерфейса пользователя и установку некоторых значений по умолчанию:

sub shownotificationdialog{
  $dialog = dialog("notification preferences",%(email => $emailaddress, txt2email => $txt2email,signalnumber => $signalphonenumber,script_location => "/home/kali/aggressors/mailer.py", emailchkbox => $receivemails,textschkbox => $receivetexts, signalchkbox => $receivesignalmessages),&callback);
  dialog_description($dialog, "Get notified when a new beacon calls home.");
  drow_text($dialog,"email","Your email address:");
  drow_text($dialog,"txt2email","Email address of the mail-to-text provider:");
  drow_text($dialog,"signalnumber","Your signal phone number in internation notation(+countrycode):");
  drow_text($dialog,"script_location","The location of the mail script on YOUR LOCAL HOST:");
  drow_checkbox($dialog,"emailchkbox","Do you want email notifications?");
  drow_checkbox($dialog,"textschkbox","Do you want text messages?");
  drow_checkbox($dialog,"signalchkbox","Do you want signal messages?");
  dbutton_action($dialog,"set preferences");
  dialog_show($dialog);
}


Всплывающий агрессор подключается к кнопке меню Cobalt-Strike в графическом интерфейсе Cobalt-Strike. Обычно эта функция запускает функцию показанного диалогового окна при нажатии кнопки:


popup aggressor {
item "Notification preferences" {shownotificationdialog();}
}

Однако настоящая «магия» заключается в обратном вызове on beacon_initial, этот метод проанализирует имя хоста и внутренний IP-адрес маяка и вызовет скрипт python, используя встроенную функцию exec Sleeps.


on beacon_initial {
local('$computer');
local('$internal');
$computer = beacon_info($1, "computer");
$internal = beacon_info($1, "internal");
if(($receivemails eq 'true') && (strlen($emailaddress) != 0)){
     println("executing python $scriptlocation --ip $internal --computer $computer --receive-emails $emailaddress");
     $handle = exec("python $scriptlocation --ip $internal --computer $computer --receive-emails --email-address $emailaddress");
  }
   
if(($receivetexts eq 'true') && (strlen($email2textaddress) != 0))
{
    println("executing python $scriptlocation --ip $internal --computer $computer --receive-texts --mail_totext $email2textaddress ");
    $handle = exec("python $scriptlocation --ip $internal --computer $computer --receive-texts --mail_totext $email2textaddress");
}
   
if (($receivesignalmessages eq 'true') && (strlen($signalphonenumber) != 0))
{
  println("executing python $scriptlocation --ip $internal --computer $computer --receive-signalmessage --signal-number $signalphonenumber");
   $handle = exec("python $scriptlocation --ip $internal --computer $computer --receive-signalmessage --signal-number $signalphonenumber");
}
   
 
if (checkError($error)) 
  {
    warn("$error");
  }  
 
};


Важная ошибка! Как я уже упоминал, ваш скрипт-агрессор привязан к вашему профилю, он не привязан к командному серверу. В результате «exec» выполнит команду на ВАШЕМ компьютере, а НЕ КОМАНДНЫЙ СЕРВЕР. Сценарий уведомителя имеет некоторые зависимости, основной из которых, очевидно, является python3, поскольку это сценарий python. Для интеграции сигнала он полагается на signal-cli.

Наш сценарий агрессора без заголовка почтовой программы во многом похож на наш графический сценарий агрессор


global ('$emailaddress');
global ('$email2textaddress');
global ('$signalphonenumber');
global ('$scriptlocation');
global ('$receivemails');
global ('$receivetexts');
global ('$receivesignalmessages');
 
 
$emailaddress = "";
$txt2emailaddress ="";
$signalphonenumber ="+countrycode";
$scriptlocation = "/some/dir/notifier.py";
$receivemails = "true";
$receivetexts = "false";
$receivesignalmessages = "true";
 
 
on beacon_initial {
local('$computer');
local('$internal');
$computer = beacon_info($1, "computer");
$internal = beacon_info($1, "internal");
if(($receivemails eq 'true') && (strlen($emailaddress) != 0)){
     say("new beacon detected! Emailing $emailaddress");
     println("executing python $scriptlocation --ip $internal --computer $computer --receive-emails $emailaddress");
     $handle = exec("python $scriptlocation --ip $internal --computer $computer --receive-emails --email-address $emailaddress");
  }
   
if(($receivetexts eq 'true') && (strlen($email2textaddress) != 0))
{
    say("new beacon detected! sending an email to the email to text service!");
    println("executing python $scriptlocation --ip $internal --computer $computer --receive-texts --mail_totext $email2textaddress ");
    $handle = exec("python $scriptlocation --ip $internal --computer $computer --receive-texts --mail_totext $email2textaddress");
}
   
if (($receivesignalmessages eq 'true') && (strlen($signalphonenumber) != 0))
{
  say("new beacon detected! sending a signal message to $signalphonenumber");
  println("executing python $scriptlocation --ip $internal --computer $computer --receive-signalmessage --signal-number $signalphonenumber");
   $handle = exec("python $scriptlocation --ip $internal --computer $computer --receive-signalmessage --signal-number $signalphonenumber");
}
}


Как уже упоминалось, вам нужно жестко закодировать свои переменные, чтобы иметь параметры в графическом интерфейсе. После того, как вы заполнили переменные, вы можете запустить agscript для подключения к вашему серверу Cobalt-Strike:


./agscript 127.0.0.1 50050 notification-service demo /home/notSBU/Documents/Tools/Agressors/headless-notifier.cna


Это можно запустить из любого места, но ваш сеанс должен оставаться открытым, поэтому я рекомендую запускать его прямо с вашего командного сервера. Синтаксис: agscript <host> <port> <username> <password> </path/to/cna>. Когда все будет сделано успешно, на ваш сервер войдет новый пользователь:


Теперь ваша служба уведомлений готова к работе! Когда появится что-то новое, служба уведомлений сообщит об этом в окне журнала событий + примет соответствующее действие.

Теперь проверьте свою электронную почту и/или телефон, вас будет ждать новое сообщение!


Настоящая магия заключается в скрипте Python?

Не совсем так, сценарий python довольно тривиален:


import argparse
import os
import smtplib
from email.mime.multipart import MIMEMultipart 
from email.mime.text import MIMEText  
 
#change your smtp login details here.
fromaddr = ""
smtp_password=""
smtp_server =""
smtp_port = 587
 
#change your signal REGISTRATION number here:
signal_registration_number =""
 
#leave these blank,will be dynamically filled through the aggressor.
smsaddr = ""
mailaddr = ""
 
 
parser = argparse.ArgumentParser(description='beacon info')
parser.add_argument('--computer')
parser.add_argument('--ip')
parser.add_argument('--receive-texts', action="store_true")
parser.add_argument('--receive-emails', action="store_true")
parser.add_argument('--receive-signalmessage', action="store_true")
parser.add_argument('--email-address')
parser.add_argument('--mail_totext')
parser.add_argument('--signal-number')
 
args = parser.parse_args()
toaddr = []
 
#take care off email and email2text:
if args.receive_texts and args.mail_totext:
  toaddr.append(smsaddr)
if args.receive_emails and args.email_address:
  toaddr.append(args.email_address)
 
 
#message contents:
hostname = args.computer
internal_ip = args.ip
body = "Check your teamserver! \nHostname - " + str(hostname) + "\nInternal IP - " + str(internal_ip)
 
#email logic
if toaddr:
  print("debug")
  msg = MIMEMultipart()
  msg['From'] = fromaddr
  msg['To'] = ", ".join(toaddr)
  msg['Subject'] = "INCOMING BEACON"
  msg.attach(MIMEText(body, 'plain'))
  server = smtplib.SMTP(smtp_server, smtp_port)
  server.starttls()
  server.login(fromaddr,smtp_password)
  text = msg.as_string()
  server.sendmail(fromaddr, toaddr, text)
  server.quit()
 
#signal-cli
if args.signal_number and args.receive_signalmessage:
  #take care of signal
  print(f"{args.signal_number}")
  os.system(f"signal-cli -u {signal_registration_number} send -m " + "\"" + str(body) + "\"" + f" {args.signal_number}")
Как видите, это не что иное, как простой сценарий электронной почты с одним исполнителем команд ОС для signal-cli.

Это означает, что независимо от того, выполняете ли вы графическое или автономное исполнение, вам необходимо иметь установленный python3 (и доступный как ваш «python» по умолчанию), а также установленный signal-cli в вашем глобальном пути.

Если signal-cli нет в вашем глобальном пути, вы можете адаптировать скрипт python, чтобы учесть это, требуется лишь небольшое изменение. То же самое касается python3, который не является вашей командой «python» по умолчанию.





Report Page