Статья Пишем simple-кейлогер с передачей дампа по TCP на C#. Часть II

В первой части было показан вариант серверной части простого кейлогера с передачей дампа по сети используя Socket. Сегодня мы доработаем серверную часть, немного расширив функционал и напишем клиента: переделаем функции для работы с сокетами и добавим несложный удаленный shell, использующий возможности Windows CMD.

Начнем с серверной части. Для начала вынесем функцию screenshot за пределы Main, оформим её отдельно. В дальнейшем мы будем обращаться к функции через команду Screen:
upload_2016-11-28_9-57-23.png


Кстати, если кто смотрел исходник, то мог заметить, что помимо нажатий клавиш, при запуске программы в лог записывается некоторая информация о системе и есть сохранение последнего содержимого буфера (text) через функцию GetBuff:
upload_2016-11-28_9-57-52.png

В дальнейшем можно также вынести функцию в отдельную команду, чтобы получать содержимое буфера по запросу. Очень часто в буфер попадают copy-pastу пароли.

Далее напишем пару функций-помощников. Для начала нам нужно научить пересылать данные между сервером и клиентом. Для начала напишем функцию, которая будет отправлять данные от сервера к клиенту:
upload_2016-11-28_9-59-53.png


Функция приминает два параметра: непосредственно данные и сокет, который будем использовать.
Использовать в дальнейшем будем таким образом:
upload_2016-11-28_10-1-3.png


Поскольку мы планируем сделать небольшой shell, необходимо каким-то образом заставить сервер запускать процессы на удаленном компьютере, а клиенту - пересылать данные.
Поможет нам в этом метод Process из пространства using System.Diagnostics.
Функция, написанная на основе этого метода, принимает два параметра: название команды, ее аргументы и сокет, через который будем работать.

upload_2016-11-28_10-7-27.png


Обращаем внимание на строку process.StartInfo.RedirectStandartOutput = true; она нам поможет формировать output для клиента. process.Start() запускает нашу команду, переданную через string s, string fl. Для формирования output используем StreamReader, читающий весь вывод до конца методом ReadToEnd(), и то, что получилось, отправляем через SSend(output,client).
process.WaitForExit() означает, что мы ждем завершения команды.
ВАЖНО! Например, если вы запустите Блокнот, он откроется на сервере и будет висеть до тех пор, пока кто-нибудь не закроет его. Пока на клиент не поступит сигнал о завершении, другую команду провести не выйдет. Вызов SSend как раз означает, что команда завершилась.

Теперь о сложном. Необходимо полностью переписать функцию ServerSocket():
upload_2016-11-28_10-13-55.png


Создаем точку подключения ipep, создаем сокет newsock, делаем bind и дальше слушаем на порту 9050. Все дальнейшие операции проводим в цикле while(true), внутри которого все будем обрабатывать через try-catch, чтобы иметь возможность отлавливать ошибки.
Далее ждем подключения клиента:
upload_2016-11-28_10-16-9.png

Готовим переменные под входящие данные, создаем просторный массив байтов.
Дальше начинается магия. Получать команды от клиента мы будем через client.Receive(bytes) и в переменную data направлять результат:
upload_2016-11-28_10-17-0.png


Дальше нам предстоит получить команду и ее аргументы. Считаем, что сначала идет команда, пробел, аргументы:
upload_2016-11-28_10-18-58.png


Отделяем команду от аргументов через data.Split(splitChars,2); - таким образом мы делим весь ввод на 2 части. Пробел - разделитель. На выходе получаем одномерный массив string a[].

Но что будет, если команда будет передана без аргументов, а в функцию мы NewProcess мы передаем аж три параметра? Ничего хорошего. Программа вылетит с ошибкой, т.к. не сможет найти a[1], поскольку его просто не существует. В сети есть куча примеров, как определить наличие индекса в массиве, от маленьких до больших. Но всегда есть способ проще:
upload_2016-11-28_10-32-14.png

Просто да? А то. Но ведь в NewProcess по-прежнему передается три параметра, скажете вы. Конечно, но теперь я покажу, как мы будем действовать. Пишем модуль для отправки команд.
Использовать будем switch(fileName). А fileName = a[0];
Полностью описывать не станем, всё есть в исходнике. Покажу основные моменты:
upload_2016-11-28_10-41-44.png


Таким образом вызовем ipconfig. В этом случае будет существовать только a[0].
Если наберем ipconfig /all - будет и a[0] и a[1] и они будут аргументами для NewProcess.
А вот если мы наберем cmd без параметров, то на сервер откроется cmd-сессия, но поскольку мы не передали аргументов, мы ничего не увидим, клиент останется в неведении и вполне возможно, мы просто потеряем контроль через Socket.
Чтобы этого не произошло, делаем так:
upload_2016-11-28_10-45-21.png


Таким простым образом защитили себя от невнимательности. Обратим внимание на вызов NewProcess: "cmd.exe" передается с ключем "/C" - это позволяет выполнить команду с последующим ее завершением. Без завершения нужен ключ "/K". Можете поэкспериментировать. Это позволяет проводить довольно большой список команд через CMD сервера, но не весь. Например переход в каталог через cd работать не будет. Кстати, работа cmd cmd не проверялась.

Для разнообразия я добавил еще функцию Delete. В ней также присутствует защита от использования без аргументов. А вот для удаления мы используем не cmd del, а метод FileInfo.Delete(). Но если вы дадите команду серверу cmd del path_to_file/file - это тоже сработает.
upload_2016-11-28_10-48-38.png


Как вы могли заметить, весь вывод идет через SSend. Функция в две строки оказалась чрезвычайно полезной.
Если вы наберете всякую белиберду, или вовсе пустую команду - есть case default:
upload_2016-11-28_10-51-52.png


Все что не нашлось в switch, будет распознано как мусор и выполняться не будет (почти, кое-что будет).Осталось совсем немного, дописать вызов функции отправляющей нам лог-файл и функцию, которая создает и отправляет по запросу скриншот удаленного компьютера.
За пределами switch пишем следующее:

upload_2016-11-28_10-54-39.png


Это вызов для функции скриншота. Заметили функцию SocketWorker? О ней чуть ниже.
Для лог-файла делаем аналогично:
upload_2016-11-28_10-55-33.png


В интернете есть много примеров передачи файлов через сокеты, что-то кривое, что-то нет.
Некоторые делают FTP-клиент(многие упираются в ошибку 500 при работе), некоторые пишут сервер.
Мы пойдем проще: останемся верными сокету. Функция принимает только имя файла, который нужен и текущий сокет:
upload_2016-11-28_10-57-6.png


Первые 5 строк должны быть понятны: берем входные параметры и переводим их в массивы байтов, с учетом смещений и выравнивания. А вот что за CopyTo?
Это весьма полезная вещь для работы с массивами. Метод позволяет копировать все элементы текущего одномерного массива в заданный одномерный массив начиная с указанного индекса в массиве назначения. Таким образом все содержимое нужного нам файла будет упаковано в массив byte[] и отправлено нам через... Socket.Send, т.е. тем же самым методом, которым мы отправляем и ответы на команды. Весьма удобно, можно протолкнуть слона в игольное ушко.

На этом моменте с серверной частью покончено. Базис задан, желающие могут пилить под себя. Это легче, чем кажется.

Приступим к клиенту. Задача клиента - соединяться с сервером, отправлять команды и получать ответы, а также принимать файлы по запросу.
Для клиента подключаем необходимые адресные пространства:
upload_2016-11-28_13-26-30.png

Больше для работы не требуется. В функции Main клиента пишем функционал для подключения к серверу:
upload_2016-11-28_13-26-55.png

В Main все просто. Главное в нем - функция SendMessageFromSocket(host). Функция принимает в качестве аргумента имя хоста или ip-адрес:
upload_2016-11-28_13-28-48.png

Первые три строки уже должны быть понятны: преобразовываем имя хоста, устанавливаем конечную точку подключения. Обратите внимание на AddressFamily. Метод InterNetwork означает использовать IP-адрес версии 4. Там же можно указать ATM, AppleTalk,Chaos и пр. Материал тут:

Сам метод AddressFamily возвращает семейство адресов IP-адреса.

Далее готовим буфер под принимаемые данные и соединяемся с удаленной точкой:
upload_2016-11-28_13-35-19.png


После того, как соединение установлено, нужно как-то послать команду серверу. Делаем это таким образом:
upload_2016-11-28_13-36-18.png


3 строка - проверка на пустую строку или Null. Наша команда это string message. Чтобы его отправить, его нужно преобразовать в байты - применим Encoding.
И дальше снова магия:
upload_2016-11-28_13-38-34.png

Отправляем команды, поддерживаемые сервером и получаем ответ.
Теперь оформим блок для приема двух файлов: скриншота и лог-файла с сервера.
upload_2016-11-28_13-39-34.png


Чуть позже напишем функцию SocketReceive, принимающая в качестве параметров Socket клиента и выходной файл. Желающие могу дописать функцию для сохранения в произвольное имя файла.

upload_2016-11-28_13-41-44.png


Чтобы клиент не закрывался после каждой введенной команды, используем переход в начало функции. Теоретически можно объявить метку и делать goto.

Обратите внимание, в серверной части нет функций для закрытия сокета, чтобы мы могли подключаться к серверу постоянно, если на клиенте вдруг произойдет сбой, либо мы прервем его выполнение (клиент можно прервать по CTRL+Z. В этом случае сервер напишет у себя в логе Bye!, а клиент завершиться с выводом строки "Передача завершена. Завершите соединение вручную")

Осталось малое - написать функцию SocketReceive, для приема файлов.
upload_2016-11-28_13-45-49.png

На вход подаем сокет и имя файла, создается буфер под принятые данные.
После подготовки к приему, сообщаем что мы готовы и открываем BinaryWriter для записи потока битов напрямую в файл на клиенте.
int receivedBytesLen = sender.Receive(clientData) - принимает данные от сервера.
Файлы сохраняются там же, где лежит клиент.
upload_2016-11-28_13-47-55.png

После приема сообщаем об этом и закрываем поток BinaryWriter.

На этом шаге написание клиента завершено. Осталось написать отдельный инструмент для работы с шифрованным логом, расшифровать его, проанализировать, восстановить все нажатия.
Но об этом - в следующей статье.
 

Вложения

  • upload_2016-11-28_13-53-18.png
    upload_2016-11-28_13-53-18.png
    8,4 КБ · Просмотры: 540
  • upload_2016-11-28_13-53-52.png
    upload_2016-11-28_13-53-52.png
    7,8 КБ · Просмотры: 505
  • upload_2016-11-28_13-54-47.png
    upload_2016-11-28_13-54-47.png
    1,1 КБ · Просмотры: 707
Последнее редактирование модератором:
I

Ishikawa

Немного скриншотов, как это работает:
Прием файла (команда screen):
upload_2016-11-28_14-15-0.png

Не смотрите что пишет "невыполнимая команда", так и должно быть, позже поправим).

Пример команды Help:
upload_2016-11-28_14-16-21.png


Белиберда:
upload_2016-11-28_14-16-43.png


Команда без аргументов:
upload_2016-11-28_14-17-11.png


Пустая команда:
upload_2016-11-28_14-19-58.png


Пробуем taskkill:
upload_2016-11-28_14-18-28.png


Ну и конечно, исходники сервера и клиента ищите во вложении к этому сообщению :)
И не забываем, что неправомерный доступ к информации карается законом.
УК РФ, Статья 272. Неправомерный доступ к компьютерной информации
 

Вложения

  • upload_2016-11-28_14-14-2.png
    upload_2016-11-28_14-14-2.png
    1,7 КБ · Просмотры: 474
  • upload_2016-11-28_14-17-36.png
    upload_2016-11-28_14-17-36.png
    898 байт · Просмотры: 625
  • Server.txt
    19,1 КБ · Просмотры: 618
  • SocketClient.txt
    3,6 КБ · Просмотры: 840
Последнее редактирование модератором:
P

poxys

клиент не соединяется! а консоль сервера не скрыта и плюс бредятина высвечивается!
 

Вложения

  • Screenshot_2.jpg
    Screenshot_2.jpg
    35,8 КБ · Просмотры: 687
  • Screenshot_3.jpg
    Screenshot_3.jpg
    12,5 КБ · Просмотры: 645
P

poxys

А то что Visual Studio предлагает удалить лишние, это надо делать или нет?
 

Вложения

  • Screenshot_4.jpg
    Screenshot_4.jpg
    28,8 КБ · Просмотры: 682
I

Ishikawa

клиент не соединяется! а консоль сервера не скрыта и плюс бредятина высвечивается!
Консоль сервера сам скроешь, я писал в первой части какой параметр отвечает за стелс. Высвечивается не бредятина - а последнее содержимое буфера, бро.

Клиент и сервер соединяться - если у обоих будет один порт. В моем случае 9050.
Ко всему этот порт не должен быть закрыт фаерволом.
Пиши в личку настройки фаера, проверь комп Nmap на наличие открытого порта 9050 на стороне сервера.

p.s. и как бы по коду программы:
// Соединяем сокет с удаленной точкой
sender.Connect(ipEndPoint);
Console.Write("Введите сообщение: ");
т.о. клиент и сервер все же соединяются. Так что нужно дебажить :)
Отследить можно через сетевой монитор:
upload_2016-11-29_9-11-14.png

Как видишь, у меня клиент соединяется с сервером, находящийся в вообще другой подсети.
 
Последнее редактирование модератором:
I

Ishikawa

а как все это относится к
Информационная безопасность (New)>Этичный хакинг и тестирование на проникновение
? тут же просто сишарповские какието примеры
Можете переместить в нужный раздел, я не против.
А как metsvc относится ко взлому, бро?
 
M

-master-

Я же не говорю что эту информацию можно/нужно/попробуй-только-не ипользовать в хакинге/тестах/...
речь только про то что тут явно куча того что можно просто переметить и красиво оформить в соотв. разделе, а здесь красивый линк туда.
типа вот мы начинаем обалденно тестировать чего то там, но как прогеры мы можем наваять неслабые штуки зырь тут -> линк
и погналово дальше ..

там можно будет вети свое обсуждение/дополнение/ а тут свое, и все в шоколаде

вот както так, я конечно не спец по хакингу, и ели не прав то с честью выдержу прямое попадание))
 
P

poxys

Фаер выключен локальными политиками. Все заработало со второго раза, консоль тупит, надо энтер несколько раз нажать, короче итоги: при команде screen загружается скриншот, в папку и если еще раз сделать это, то screen.jpg не заменяется, или не создается новый, cmd лист неудобный, нельзя сделать что бы был полноценный cmd shell? Без команд cmd ifconfig (сделать меню в нем две-три команды, первая при нажатии цифры 1 и энтер подгружает логи с сервера, вторая запускает полноценный shell cmd оболочку в которой можно работать как в обычной командной строке, третья показывает инфу о системе, например выпоняет команду sysinfo и выводит содержимое) как у нормальных ратах. Clipboard один раз делается, а неплохо бы было что бы это записывалось в логи когда этот процесс вызывает пользователь. На сервере не нужно выводить команды которые отправляет управляющий клиент.

Это просто советы по улучшению.
Я бы сам это попробовал реализовать, но нет знаний:(
 
P

poxys

с cmd наверно погорячился это можно сказать полноценный шел, но вот cd ../ переходы не работают,
 
I

Ishikawa

Я же не говорю что эту информацию можно/нужно/попробуй-только-не ипользовать в хакинге/тестах/...
речь только про то что тут явно куча того что можно просто переметить и красиво оформить в соотв. разделе, а здесь красивый линк туда.
типа вот мы начинаем обалденно тестировать чего то там, но как прогеры мы можем наваять неслабые штуки зырь тут -> линк
и погналово дальше ..

там можно будет вети свое обсуждение/дополнение/ а тут свое, и все в шоколаде

вот както так, я конечно не спец по хакингу, и ели не прав то с честью выдержу прямое попадание))

Я с тобой согласен бро. В момент написания первой части я не нашел куда лучше положить тему. Первую часть никто перемещать не стал, я решил в этом же разделе накатать вторую. Перемещайте тему на свое усмотрение, главное чтобы инфу нашли :)
 
I

Ishikawa

Фаер выключен локальными политиками. Все заработало со второго раза, консоль тупит, надо энтер несколько раз нажать, короче итоги: при команде screen загружается скриншот, в папку и если еще раз сделать это, то screen.jpg не заменяется, или не создается новый, cmd лист неудобный, нельзя сделать что бы был полноценный cmd shell? Без команд cmd ifconfig (сделать меню в нем две-три команды, первая при нажатии цифры 1 и энтер подгружает логи с сервера, вторая запускает полноценный shell cmd оболочку в которой можно работать как в обычной командной строке, третья показывает инфу о системе, например выпоняет команду sysinfo и выводит содержимое) как у нормальных ратах. Clipboard один раз делается, а неплохо бы было что бы это записывалось в логи когда этот процесс вызывает пользователь. На сервере не нужно выводить команды которые отправляет управляющий клиент.

Это просто советы по улучшению.
Я бы сам это попробовал реализовать, но нет знаний:(

Привет, бро. У меня все с первого раза работает, Enter долбить тоже не приходится. Допускаю, что на медленных соединениях возможно будет задержка при передаче файлов и обмене командами. Скриншот создается новый, если удалить текущий. Если смотрел внимательно - в коде нет функции удаления скриншота или лога. Я в первой части писал (ты по-ходу не читал) что код будет с ограничениями, ибо мы пишем simple-версию. Профессиональные кейлогеры пишут иначе, выносят код в драйвер, используют другие функции.
Cmd-лист явлен как демонстрация возможностей: основные команды, которые доступны в CMD, можно провернуть. То что не работает переход между директориями cd.. я тоже писал.
И писали все же кейлогер, а не шелл. Clipboard делается один раз, да. Я писал, что желающие могут вынести код в отдельную функцию и добавить ей команду (ты видимо вообще не читал). На сервере - всё в твоих руках, бро.
Закомментируй строки Console.WriteLine и будет тебе счастье :)

p.s. я понимаю, что copy-paste рулит, но понимать основы наверное стоит. Начать прежде всего с внимательного чтения, а не бездумной компиляции. Можно ведь и вирус зашить в код или зашифровать всю инфу на компе. Первым делом нужно проверять, что другие дают.
Могу рекомендовать/подарить две хорошие книги по C#, чтобы знания всё же были.
 
Последнее редактирование модератором:
M

MAdDog719

Кому нужны исходники - пишите в личку :)
Кто-то неудачно потер сообщения к теме. В одном из них были исходники.
Привет, скажи, пожалуйста, это уже полностью готовый кейлогер, или еще нужно написать дешифратор файлов которые будет принимать сервер?
 
I

Ishikawa

Привет, скажи, пожалуйста, это уже полностью готовый кейлогер, или еще нужно написать дешифратор файлов которые будет принимать сервер?
Привет, бро. Да, будет еще дешифратор. Он самая сложная часть, т.к.нужно парсить строки, используя всякие Replace и Regex. В дальнейшем можно будет объединить клиент и дешифратор в одну программу.
На текущий момент принцип такой: сервер работает на жертве постоянно, мы подсоединяемся клиентом с целью быстро вытащить логи или скрин и смыться. А награбленное скормить дешифратору.
Время от времени я дописываю еще и серверную часть, сегодня написал как поставить прогу в автозапуск.
 
  • Нравится
Реакции: MAdDog719
M

MAdDog719

Привет, бро. Да, будет еще дешифратор. Он самая сложная часть, т.к.нужно парсить строки, используя всякие Replace и Regex. В дальнейшем можно будет объединить клиент и дешифратор в одну программу.
На текущий момент принцип такой: сервер работает на жертве постоянно, мы подсоединяемся клиентом с целью быстро вытащить логи или скрин и смыться. А награбленное скормить дешифратору.
Время от времени я дописываю еще и серверную часть, сегодня написал как поставить прогу в автозапуск.
Понятно, жду тогда дешифратор. И спасибо за твой труд, реально клевый проект.
 
  • Нравится
Реакции: Ishikawa
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!