本地文件读取漏洞 | Electron 安全

0x01 简介

大家好,今天和大家讨论一下关于本地文件读取漏洞,我们都知道,在浏览器里直接输入本地地址,例如 file:///etc/hosts 会在浏览器里直接加载,最常见的可能就是用浏览器打开 PDF 文件

本地文件读取漏洞 | Electron 安全

既然浏览器窗口可以读取本地文件,那页面中的这些标签是不是也可以呢?

本地文件读取漏洞 | Electron 安全

当页面不是 file:// 协议时加载时,内部的元素不可以加载本地文件,当页面是 file://协议加载时,内部元素可以加载本地文件

本地文件读取漏洞 | Electron 安全

所以在 Web 领域,这不是什么问题,但到了 Electron 领域就不一样了,如果 Electron 配置了禁止 Node.js 执行,上下文隔离,此时还能造成的较大的危害就是读取本地的文件了

毕竟,大部分 Electron 程序都是通过加载本地文件创建窗口的,如何配合 JavaScript ,是不是可以直接打到窃取文件的效果呢?

我们今天针对这件事展开探究


公众号开启了留言功能,欢迎大家留言讨论~

这篇文章也提供了 PDF 版本及 Github ,见文末


  • 0x01 简介

  • 0x02 通过 iframe 窃取文件

  • 0x02 哪些标签可以利用

    • 1. iframe

    • 2. objetc

    • 3. embed

    • 4. 其他标签

  • 0x03 fetch

  • 0x04 问题本质是什么?

  • 0x05 漏洞案例

  • 0x06 总结

  • 0x07 PDF 版 & Github

  • 往期文章


0x02 通过 iframe 窃取文件

场景为当 Electron 开发的程序出现 XSS 或者说就是允许 JavaScript 在前端执行,此时我们测试是否可以将 /etc/hosts 文件偷走

本地文件读取漏洞 | Electron 安全

正常加载应该没问题

本地文件读取漏洞 | Electron 安全

插入 JavaScript 尝试窃取其内容

本地文件读取漏洞 | Electron 安全

因为是在标签内联事件执行 JavaScript,所以需要 CSP 允许,我们直接把它关闭了测试

本地文件读取漏洞 | Electron 安全

JavaScript 成功获取到内容,现在我们尝试将其传输到远程服务器 192.168.31.26

本地文件读取漏洞 | Electron 安全
本地文件读取漏洞 | Electron 安全

执行测试

本地文件读取漏洞 | Electron 安全
本地文件读取漏洞 | Electron 安全

成功窃取 /etc/hosts 文件

0x02 哪些标签可以利用

1. iframe

iframe 标签肯定是可以了

2. objetc

本地文件读取漏洞 | Electron 安全

object 标签可以

3. embed

https://developer.mozilla.org/en-US/docs/Web/API/HTMLEmbedElement

https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement

可以加载,但并没有找到可以获取加载的内容的 DOM 属性

4. 其他标签

这么多标签,测试起来时间耗费不起,直接上大杀器,在 renderer.js 中,执行以下代码

// 定义一个数组,包含所有HTML5标签名
const allTags = [
  'a''abbr''address''area''article''aside''audio''b''base''bdi''bdo''blockquote''body''br''button',
  'canvas''caption''cite''code''col''colgroup''data''datalist''dd''del''details''dfn''dialog''div''dl',
  'dt''em''embed''fieldset''figcaption''figure''footer''form''h1''h2''h3''h4''h5''h6''head''header',
  'hr''html''i''iframe''img''input''ins''kbd''label''legend''li''link''main''map''mark''meta''meter',
  'nav''noscript''object''ol''optgroup''option''output''p''param''picture''pre''progress''q''rp''rt''ruby',
  's''samp''script''section''select''slot''small''source''span''strong''style''sub''summary''sup''table',
  'tbody''td''template''textarea''tfoot''th''thead''time''title''tr''track''u''ul''var''video''wbr',
  // ... 其余标签省略以保持示例简洁,实际应包含所有HTML5标签
];

// const allTags = ['iframe', 'object']

// 支持加载事件的标签列表,这里简化列举了一些常见的
const loadSupportedTags = ['iframe''img''script''audio''video''object''embed'];

// 遍历所有标签
allTags.forEach(tagName => {
    // 创建一个新的标签元素
    const element = document.createElement(tagName);
    
    // 设置属性
    element.setAttribute('src''file:///etc/hosts');
    element.setAttribute('href''file:///etc/hosts');
    element.setAttribute('data''file:///etc/hosts');
    element.setAttribute('rel''file:///etc/hosts');
//   element.setAttribute('id', tagName + '-test');
  
  // 检查并设置onload事件
    element.onload = function({
        if (this.contentDocument && this.contentDocument.body) { // 主要针对iframe
            console.log(`${tagName}:`this.contentDocument.body.innerText);
            console.log(`${tagName}:`this.contentDocument.body.innerHTML);
            console.log(`${tagName}:`this.contentDocument.body.textContent);
        } else if(this.textContent) {
            console.log(`${tagName}:`this.textContent);
        } else { 
            console.log(`${tagName} loaded.`);
        }
    }
    
    // 添加错误处理
    element.onerror = function({
      console.error(`${tagName} failed to load.`);
    };

  
  // 仅为演示,不实际添加到页面
  document.body.appendChild(element);
});

// 请记得,这段代码主要用于教学目的,实际操作本地文件在浏览器中是严格受限的。

直接测试所有标签

本地文件读取漏洞 | Electron 安全
本地文件读取漏洞 | Electron 安全

经过测试,还是只有 iframeobject 标签可以获取内容,source 标签得单独测试一下

0x03 fetch

当然,也可以直接通过 fetch 等发起请求

本地文件读取漏洞 | Electron 安全
本地文件读取漏洞 | Electron 安全

0x04 问题本质是什么?

问题本质在于 file:// 协议权限过大了,可以访问任意地址的文件,如果将 grantFileProtocolExtraPrivileges 设置为 Disabled ,是否可以有效解决呢?

如果大家看了 Fuse 那篇文章,可能大家会知道,上面的配置会导致页面加载 index.html 失败,猜测是因为相对路径的问题,我们修改一下试试

本地文件读取漏洞 | Electron 安全

index.html 中添加 iframefetch 两种读取本地文件的代码

本地文件读取漏洞 | Electron 安全

先用 fiddle 测试一下,此时先用 mainWindow.loadFile('index.html')

本地文件读取漏洞 | Electron 安全

功能可用,现在修改回 mainWindow.loadFile(path.join(asar_path, 'index.html')) ,我们用 Forge 打包,先按照默认设置, grantFileProtocolExtraPrivileges 设置为 Enabled

本地文件读取漏洞 | Electron 安全

页面可以正常加载,同时 iframefetch 也都获取到了 /etc/hosts 的内容,接下来修改 Forge 的配置,添加 grantFileProtocolExtraPrivileges 设置为 Disabled

本地文件读取漏洞 | Electron 安全

再次打包

本地文件读取漏洞 | Electron 安全

加载页面失败,难道要换成 loadURL

本地文件读取漏洞 | Electron 安全

再次打包

本地文件读取漏洞 | Electron 安全

还是不行,看来官方的意思,开启了 grantFileProtocolExtraPrivileges file 协议就像是废了,连 app.asar 里的文件都不能读取了

看来想规避这种风险可能得使用自定义协议了,不用担心,现在”强得可怕”,我们自定义 nop:// 协议

// Modules to control application life and create native browser window
const { app, BrowserWindow, protocol, net, session } = require('electron')
const url  = require('url')
const path = require('node:path')
const fs = require('node:fs')


const asar_path = path.join(process.resourcesPath, 'app.asar')
protocol.registerSchemesAsPrivileged([
  {
    scheme'nop',
    privileges: {
      standardtrue,
      securetrue,
      supportFetchAPItrue
    }
  }
])

function createWindow ({
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width800,
    height600,
    webPreferences: {
      partition'persist:example',
      preload: path.join(__dirname, 'preload.js')
    }
  })

  // and load the index.html of the app.
  // mainWindow.loadFile(path.join(asar_path, 'index.html'))
  mainWindow.loadURL('nop://index.html')
  // mainWindow.loadFile('index.html')

  // Open the DevTools.
  mainWindow.webContents.openDevTools()
}

app.whenReady().then(() => {

  const partition = 'persist:example'
  const ses = session.fromPartition(partition)

  ses.protocol.handle('nop', (request) => {
    const filePath = request.url.slice('nop://'.length)
    return net.fetch(url.pathToFileURL(path.join(__dirname, filePath)).toString())
  })

  createWindow()
  app.on('activate'function ({
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

app.on('window-all-closed'function ({
  if (process.platform !== 'darwin') app.quit()
})

打包后,执行测试

本地文件读取漏洞 | Electron 安全

这回就属于是不同源了,就不允许攻击者通过 file:// 读取文件了,但这并没有解决问题,因为攻击者还是可以通过代码审计的方式,发现这个协议,之后通过 nop://../../../etc/hosts 这种格式进行本地文件读取,所以需要在自定义协议的时候做好安全校验

或者可以通过在本地搭建监听在 127.0.0.1 的 http 服务器来防止本地文件读取

0x05 漏洞案例

Typora 这类的 Markdown 特别容易出现这类漏洞

  • CVE-2023-2316
  • CVE-2023-2971

0x06 总结

本地文件读取漏洞是web攻击在 Electron 中的一种延伸,在 web 中做不到这类攻击,但是在通过加载本地文件创建窗口的应用中就可以实现窃取文件的效果

对于安全措施开得比较全的应用来说,这是一种有效的攻击手法,虽然不能代码执行,但也可以造成很大危害

防御此类攻击可以从以下几个方面考虑

  • 配置合理的 CSP 策略
  • 在本地搭建 web 服务器或自定义协议,对请求路径做白名单
  • 监听部分事件,做有效过滤
  • 最重要的还是防止出现 XSS 漏洞
  • [在深入了解 grantFileProtocolExtraPrivileges后,在不影响正常运行的情况下,将其设置为 disabled]

0x07 PDF 版 & Github

PDF

https://pan.baidu.com/s/1QafwG0Dg7YQWV4OGmOC57g?pwd=1bht


Github

https://github.com/Just-Hack-For-Fun/Electron-Security

往期文章

本地文件读取漏洞 | Electron 安全

有态度,不苟同



原文始发于微信公众号(NOP Team):本地文件读取漏洞 | Electron 安全

版权声明:admin 发表于 2024年5月14日 下午4:14。
转载请注明:本地文件读取漏洞 | Electron 安全 | CTF导航

相关文章