LIMB
Вход Регистрация

тема

(Статья) Сказ о том, как я ботнет делал

limmoner · · 8 ответов · 654 просмотров

ВАЖНО: Данный материал предназначен исключительно для образовательных целей в рамках легитимной деятельности Red Team. Использование данных из этой статьи может быть незаконным, так что используйте на свой страх и риск. Статья написана специально для форума LIMB.cc.

Когда ты пилишь ботнет, тебе надо пройти как минимум 4 этапа. Это как строительство дома — от фундамента, до крыши

Этап 1. Выбор C2
Это мой выбор, ты можешь сделать любой иной. Я лично, так как немного понимаю в Go, решил просто написать серверную часть. Почему Go? Потому что это быстро и отлично подходит для сетевых приложений. 

Мой C2 сервер получился простым и эффективным. Он умеет принимать подключения ботов, отдавать команды, собирать статистику и самое главное — мониторить ресурсы каждого роутера. 
  • Если статья зайдет, то возможно будет развитие в сторону:
  • - Шифрования трафика
    - P2P архитектуры 
    - Domain Fronting
    - Автоматической смены IP

    Не удалось прогрузить медиа













  • Вот и скрин работы сервера с красивым баннером и статистикой в реальном времени.

    Этап 2. Роутеры для ботнета


  • Тут множество вариантов, но так как мне необходимо было быстро и без особых затрат получить какое-то количество клиентов, я остановился на старых роутерах. Почему именно они? 

    Во-первых, их много. Очень много. Люди покупают новые роутеры. Во-вторых, их редко обновляют. Прошивка 2015 года? Да без проблем!

    Моя стратегия заражения включала два основных вектора:

    1. Дефолтные учетки — использовал стандартные логины и пароли, по типу Admin/admin, admin/password, root/12345 — классика, которая до сих пор работает.

    2. Эксплойты — использовал несколько CVE, а именно:
       - Zyxel: CVE-2023-28771 (RCE), CVE-2020-9054 (RCE)
       - D-Link: CVE-2021-45382 (RCE), CVE-2019-17621 (RCE)

    Логика проста — мне просто надо доставить бинарник на роутер используя оба варианта заражения. Как курьерская служба, только посылки не совсем легальные.

    Этап 3. OSINT

  • Без хорошей разведки никакой атаки не получится. 
    Мой товарищ собирал старые роутеры с помощью самописного Python скрипта, который:
    - Сканировал диапазоны IP на предмет открытых портов 
    - Определял роутер и уточнял d-link или zyxel
    - Проверял наличие CVE
    - Сохраняет результаты и доп.инфу

  • Спойлер: Код прилагаю. Он не идеален, но для старта сойдет.
    [*]
    Python:
    import requests
    import socket
    import threading
    import time
    import json
    import sys
    import argparse
    from concurrent.futures import ThreadPoolExecutor, as_completed
    from urllib3.exceptions import InsecureRequestWarning
    
    
    requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
    
    class colors:
        RED = '\033[91m'
        GREEN = '\033[92m'
        YELLOW = '\033[93m'
        BLUE = '\033[94m'
        MAGENTA = '\033[95m'
        CYAN = '\033[96m'
        WHITE = '\033[97m'
        RESET = '\033[0m'
        BOLD = '\033[1m'
    
    BANNER = f"""
    {colors.BOLD}{colors.CYAN}
    ╔══════════════════════════════════════════════════════════════╗
    ║               D-Link & Zyxel Router Scanner                  ║
    ╚══════════════════════════════════════════════════════════════╝
    {colors.RESET}
    """
    
    def print_banner():
        print(BANNER)
        print(f"{colors.BOLD}Scan started at: {time.strftime('%Y-%m-%d %H:%M:%S')}{colors.RESET}")
        print()
    
    def is_port_open(host, port, timeout=2):
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(timeout)
            result = sock.connect_ex((host, port))
            sock.close()
            return result == 0
        except:
            return False
    
    def check_http_service(host, port, timeout=5):
        protocols = ['http', 'https']
        
        for proto in protocols:
            url = f"{proto}://{host}:{port}"
            try:
                response = requests.get(
                    url,
                    timeout=timeout,
                    verify=False,
                    allow_redirects=True,
                    headers={
                        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
                        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
                    }
                )
                if response.status_code < 500:
                    return proto, port, response
            except requests.exceptions.RequestException:
                continue
            except Exception as e:
                continue
        
        return None, None, None
    
    def detect_router_type(response):
        if not response:
            return None
        
        text = response.text.lower()
        headers = str(response.headers).lower()
    
        dlink_indicators = [
            'd-link', 'dlink', 'realm="dlink"', 'realm="d-link"',
            'd-link systems', 'dlinkrouter', 'dir-', 'dsl-'
        ]
        
        for indicator in dlink_indicators:
            if indicator in text or indicator in headers:
                return 'D-Link'
    
        zyxel_indicators = [
            'zyxel', 'realm="zyxel"', 'zywall', 'p-', 'vmg', 'emg',
            'zyxel communications', 'zyxel router'
        ]
        
        for indicator in zyxel_indicators:
            if indicator in text or indicator in headers:
                return 'Zyxel'
    
        server_header = response.headers.get('Server', '').lower()
        if any(indicator in server_header for indicator in dlink_indicators):
            return 'D-Link'
        if any(indicator in server_header for indicator in zyxel_indicators):
            return 'Zyxel'
        
        if '<title>' in text and '</title>' in text:
            title_start = text.find('<title>') + 7
            title_end = text.find('</title>')
            title = text[title_start:title_end].lower()
            
            if any(indicator in title for indicator in dlink_indicators):
                return 'D-Link'
            if any(indicator in title for indicator in zyxel_indicators):
                return 'Zyxel'
        
        return None
    
    def check_common_ports(host):
        common_ports = [80, 443, 8080, 8081, 8888, 23, 22, 21]
        open_ports = []
        
        for port in common_ports:
            if is_port_open(host, port, 1):
                open_ports.append(port)
        
        return open_ports
    
    def scan_single_ip(ip, timeout=5):
        results = {
            'ip': ip,
            'router_type': None,
            'open_ports': [],
            'http_ports': [],
            'services': [],
            'details': {}
        }
        
        try:
            open_ports = check_common_ports(ip)
            results['open_ports'] = open_ports
            
            if not open_ports:
                return results
            
            http_ports = [port for port in open_ports if port in [80, 443, 8080, 8081, 8888]]
            results['http_ports'] = http_ports
            
            for port in http_ports:
                proto, http_port, response = check_http_service(ip, port, timeout)
                if proto and response:
                    results['services'].append({
                        'protocol': proto,
                        'port': port,
                        'status_code': response.status_code
                    })
    
                    router_type = detect_router_type(response)
                    if router_type:
                        results['router_type'] = router_type
                        results['details'] = {
                            'server': response.headers.get('Server', 'Unknown'),
                            'title': extract_title(response.text),
                            'auth_type': detect_auth_type(response)
                        }
                        break
            
            for port in open_ports:
                if port == 23:
                    results['services'].append({'protocol': 'telnet', 'port': 23})
                elif port == 22:
                    results['services'].append({'protocol': 'ssh', 'port': 22})
                    
        except Exception as e:
            results['error'] = str(e)
        
        return results
    
    def extract_title(html):
        try:
            text = html.lower()
            if '<title>' in text and '</title>' in text:
                title_start = text.find('<title>') + 7
                title_end = text.find('</title>')
                return html[title_start:title_end].strip()
        except:
            pass
        return "Unknown"
    
    def detect_auth_type(response):
        headers = str(response.headers).lower()
        
        if 'www-authenticate' in headers:
            if 'basic' in headers:
                return 'HTTP Basic Auth'
            elif 'digest' in headers:
                return 'HTTP Digest Auth'
        
        if 'login' in response.text.lower() or 'password' in response.text.lower():
            return 'Web Form Auth'
        
        return 'No Auth'
    
    def load_ips_from_file(filename):
        ips = []
        
        try:
            with open(filename, 'r') as f:
                for line in f:
                    line = line.strip()
                    if not line or line.startswith('#'):
                        continue
    
                    if '/' in line:
                        base_ip = line.split('/')[0]
                        ips.append(base_ip)
                    elif '-' in line:
                        base_ip = line.split('-')[0]
                        ips.append(base_ip)
                    else:
                        ips.append(line)
                        
        except FileNotFoundError:
            print(f"{colors.RED}[!] File not found: {filename}{colors.RESET}")
            sys.exit(1)
        
        return list(set(ips))
    
    def save_targets(targets, filename):
        try:
            with open(filename, 'w') as f:
                for target in targets:
                    if target['router_type']:
                        for port in target['http_ports']:
                            f.write(f"{target['ip']}:{port}\n")
    
                        if 23 in target['open_ports']:
                            f.write(f"{target['ip']}:23\n")
                        
                        if 22 in target['open_ports']:
                            f.write(f"{target['ip']}:22\n")
            
            print(f"{colors.GREEN}[+] Saved {len(targets)} targets to {filename}{colors.RESET}")
            
        except Exception as e:
            print(f"{colors.RED}[!] Error saving targets: {e}{colors.RESET}")
    
    def save_detailed_results(results, filename):
        try:
            with open(filename, 'w') as f:
                json.dump(results, f, indent=2, ensure_ascii=False)
            print(f"{colors.GREEN}[+] Detailed results saved to {filename}{colors.RESET}")
        except Exception as e:
            print(f"{colors.RED}[!] Error saving detailed results: {e}{colors.RESET}")
    
    def print_results_summary(results):
        router_count = sum(1 for r in results if r['router_type'])
        dlink_count = sum(1 for r in results if r['router_type'] == 'D-Link')
        zyxel_count = sum(1 for r in results if r['router_type'] == 'Zyxel')
        
        print(f"\n{colors.BOLD}=== SCAN SUMMARY ==={colors.RESET}")
        print(f"Total IPs scanned: {len(results)}")
        print(f"Routers found: {router_count}")
        print(f"D-Link routers: {dlink_count}")
        print(f"Zyxel routers: {zyxel_count}")
        print(f"Targets saved to: targets.txt")
    
    def print_router_details(results):
        routers = [r for r in results if r['router_type']]
        
        if not routers:
            print(f"{colors.YELLOW}[!] No routers found{colors.RESET}")
            return
        
        print(f"\n{colors.BOLD}=== FOUND ROUTERS ==={colors.RESET}")
        
        for router in routers:
            print(f"\n{colors.GREEN}[+] {router['router_type']} at {router['ip']}{colors.RESET}")
            print(f"   Open ports: {', '.join(map(str, router['open_ports']))}")
            print(f"   HTTP ports: {', '.join(map(str, router['http_ports']))}")
            
            if router['details']:
                if router['details'].get('title'):
                    print(f"   Title: {router['details']['title']}")
                if router['details'].get('server'):
                    print(f"   Server: {router['details']['server']}")
                if router['details'].get('auth_type'):
                    print(f"   Auth: {router['details']['auth_type']}")
    
    def main():
        parser = argparse.ArgumentParser(description='D-Link & Zyxel Router Scanner')
        parser.add_argument('-i', '--input', default='ip.txt', help='Input file with IP addresses (default: ip.txt)')
        parser.add_argument('-o', '--output', default='targets.txt', help='Output file for targets (default: targets.txt)')
        parser.add_argument('-j', '--json', default='scan_results.json', help='JSON output file (default: scan_results.json)')
        parser.add_argument('-t', '--threads', type=int, default=20, help='Number of threads (default: 20)')
        parser.add_argument('-to', '--timeout', type=int, default=3, help='Timeout in seconds (default: 3)')
        parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
        
        args = parser.parse_args()
        
        print_banner()
        
        print(f"{colors.BLUE}[*] Loading IP addresses from {args.input}{colors.RESET}")
        ips = load_ips_from_file(args.input)
        
        if not ips:
            print(f"{colors.RED}[!] No IP addresses found in {args.input}{colors.RESET}")
            sys.exit(1)
        
        print(f"{colors.BLUE}[*] Loaded {len(ips)} unique IP addresses{colors.RESET}")
        print(f"{colors.BLUE}[*] Starting scan with {args.threads} threads...{colors.RESET}")
        
        all_results = []
        found_routers = []
        
        with ThreadPoolExecutor(max_workers=args.threads) as executor:
            future_to_ip = {
                executor.submit(scan_single_ip, ip, args.timeout): ip
                for ip in ips
            }
            
            for i, future in enumerate(as_completed(future_to_ip), 1):
                ip = future_to_ip[future]
                try:
                    result = future.result()
                    all_results.append(result)
                    
                    if args.verbose:
                        if result['router_type']:
                            print(f"{colors.GREEN}[+] Found {result['router_type']} at {ip}{colors.RESET}")
                        else:
                            print(f"{colors.BLUE}[*] Scanned {ip} ({i}/{len(ips)}){colors.RESET}")
                    else:
                        if result['router_type']:
                            print(f"{colors.GREEN}[+] Found {result['router_type']} at {ip}{colors.RESET}")
                    
                    if result['router_type']:
                        found_routers.append(result)
                        
                except Exception as e:
                    print(f"{colors.RED}[!] Error scanning {ip}: {e}{colors.RESET}")
                    all_results.append({'ip': ip, 'error': str(e)})
        
    
        save_targets(found_routers, args.output)
        save_detailed_results(all_results, args.json)
        print_results_summary(all_results)
        print_router_details(all_results)
        
        print(f"\n{colors.GREEN}[+] Scan completed!{colors.RESET}")
    
    if __name__ == "__main__":
        main()
    [*]
    Мой знакомый с помощью данного скрипта собрал огромное количество устройств, так что пользуемся и говорим спасибо дяде Лиммонеру. 

    Этап 4. Доставка бинарника
  • В ходе разработки я пришел к выводу, что лучше всего использовать Go для написания приложения, которое будет:

    - Проверять креды
    - Эксплуатировать CVE
    - Доставлять полезную нагрузку
    - Вести логи

  • Это как швейцарский нож — много функций в одной обертке. Софт многопоточен, работает быстро и главное — стабильно.
    Правда самое сложное это все-таки обеспечить живучесть ботнета, вот что умеет мой клиент
  • - Автоматически прописываться в автозагрузку
    - Уметь переподключаться при обрыве связи
    - Тихо ждет команд от сервера. 
    Тут конечно тоже есть место для развития, но все зависит от Вашей реакции и реакции админстрации форума. 
    Весь софт можно запросить у меня в личке, но даже по инфе из статьи, хоть сколько-то опытный кодер сможет восстановить весь софт. 
    Удачи в пентесте и создании стрессеров. 

👍46 ❤️11 🔥14 👏11
Как буд-то ИИ генерировало.
👍2
А что ты сделал в итоге в статье?
Чел другой скриптом прошелся по устройствам эт мы увидели.
Как создавал с2 - хз.
Как заражать - хз.
Почему выбрал эти цве - хз.
Чему тут учится - хз.
👍3
Ответskeet

Как буд-то ИИ генерировало.

Не то что твои статьи с рерайтом мануала, правда?)
👍3
Ответakulapera

А что ты сделал в итоге в статье? Чел другой скриптом прошелся по устройствам эт мы увидели. Как создавал с2 - хз. Как заражать - хз. Почему выбрал эти цве - хз. Чему тут учится - хз.

запроси в личке инструменты и можешь проверить сам, можно ли собрать ботнет или нет. Выкладывать данный инструментарий, не очень правильно.
Ответlimmoner

Если статья зайдет, то возможно будет развитие в сторону: - Шифрования трафика - P2P архитектуры - Domain Fronting - Автоматической смены IP

Ждем этого! ибо под дедос утилизировать не интересно!
Ответlimmoner

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

и пожалуйста можно инструменты в личку? я бы проверил
👍3
Ответlimmoner

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

При чем здесь твои инструменты?
Речь про статью; практческую нагрузку.
Как добавлять бинарник; как ты писал серв; как отработает парсинг.
Этого всего нет.
Есть просто описание концепта и скрипт товарища.
👍5 ❤️1
Ответakulapera

При чем здесь твои инструменты? Речь про статью; практческую нагрузку. Как добавлять бинарник; как ты писал серв; как отработает парсинг. Этого всего нет. Есть просто описание концепта и скрипт товарища.

Так всё понятно, арендовал дедик, хуяк-хуяк, дедик подготовил. Потом кофе попил, еще бинарик-туда, логи-сюда и ты уже миллионер :)
👍4
Ответyngbeaver

Так всё понятно, арендовал дедик, хуяк-хуяк, дедик подготовил. Потом кофе попил, еще бинарик-туда, логи-сюда и ты уже миллионер :)

Интырнет туда сюда.
Ботнет тоси боси.
И ты в дамках. 
Не пойму что сложного.
По этой статье даже новокек повторит.
👍2

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.