zoukankan      html  css  js  c++  java
  • 极路由安全设计分析姐妹篇

    开篇想说两件事情:

    一、非常感谢Freebuf大牛们,在其提供的网站上找到了HiWiFi固件、其中9003版本squashfs文件系统上的lua代码没有经过预编译处理,这对我们基于源码分析极路由提供了可能。地址

    二、经过修炼发现HiWiFi固件解压问题,其实可以使用Windows操作系统下面的开源软件7zip解压。

    那么,本期的重点是分析HiWiFi lua源码安全设计部分。

    0×01 分析思路

    一、了解OpenWRT Web认证过程。

    二、了解HiWiFi web认证过程和HiWiFi Cloud 之间的通讯认证分析。

    0×02 分析过程

    测试工具:源代码阅读使用luaEdit。连接openwrt查看目录软件winscp ,因为有些文件是认证后才产生的。


    一、了解OpenWRT Web认证过程。

      1.1、搭建OpenWRT虚拟运行环境

      1.2、网络分析结合lua源代码分析,了解其认证过程。

    搭建OpenWRT虚拟机运行环境,下载x86架构的openWRT.vmdk文件,就强调一点如果你只使用一块无线网卡搭建环境,会失败,因为OpenWRT需要两个不同的网络LAN、WAN。

    OpenWRT主要是通过内建的web服务器uHttpd配合lua脚本语言实现B/S互交。

    通过抓包软件抓取交互数据包,如下:

    POST http://192.168.1.10/cgi-bin/luci HTTP/1.1
    Host: 192.168.1.10
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
    Accept-Encoding: gzip, deflate
    Referer: http://192.168.1.10/cgi-bin/luci
    Connection: keep-alive
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 29
    username=root&password=123qwe
    GET http://192.168.1.10/cgi-bin/luci/;stok=f10e9261c036d0c97db82c5eee568b34?status=1&_=0.4118332080369933 HTTP/1.1
    Host: 192.168.1.10
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
    Accept-Encoding: gzip, deflate
    Referer: http://192.168.1.10/cgi-bin/luci
    Cookie: sysauth=ae896241b40cf93dbf079c08acaeffcd
    Connection: keep-alive

    通过web界面提供的账号和密码,返回用户在web端保持通讯的token.

    stok=f10e9261c036d0c97db82c5eee568b34

    和客户端认证的cookie。

    Cookie: sysauth=ae896241b40cf93dbf079c08acaeffcd 时间产生cookies.

    然后使用winscp登录到OpenWRT上查看文件,会发现整个登录调用文件流程如下:

    通过ccache.lua处理,发现服务器端变化:/tmp/luci-sessions 产生f10e9261c036d0c97db82c5eee568b34 一个文件(session)

    该文件存储一个预编译lua脚本,主要是映射的登录用户名和token的对应关系。

    Json数据形式(保存的数据库形式):

    return { ["secret"] = "bb8d42ed6c097b6f16ea698b22f7b0e1",
    ["token"] = "f10e9261c036d0c97db82c5eee568b34",
    ["user"] = "root" }
    /usr/lib/lua/luci/ccache.lua

    涉及到加密算法 16进制输出。encoded = encoded .. ("%2X" % string.byte(name, i))

    由于源码太多没全部写出来,大家可以参考相关的bin文件中的代码。

    然后查看:/usr/lib/lua/luci/dispatcher.lua 是怎么处理登录的:发现authenticator.htmlauth 函数。

    function authenticator.htmlauth(validator, accs, default)
        local user = luci.http.formvalue("username")
        local pass = luci.http.formvalue("password")
     
        if user and validator(user, pass) then
             return user
        end
     
        require("luci.i18n")
        require("luci.template")
        context.path = {}
        luci.template.render("sysauth", {duser=default, fuser=user})
        return false
     
    end

    validator{},主要是通过对提交的密码做MD5对比校验,如果匹配则返回真。

    Sysauth具体怎么产生的呢?

    通过查询/usr/lib/lua/luci/sauth.lua

    关键代码:

    sessiontime = tonumber(luci.config.sauth.sessiontime) or 15 * 60
     
    local function _checkid(id)
        return not not (id and #id == 32 and id:match("^[a-fA-F0-9]+$"))
    end
     
     
    --- Write session data to a session file.
    -- @param id Session identifier
    -- @param data    Session data table
    function write(id, data)
        if not sane() then
             prepare()
        end
     
        assert(_checkid(id), "Security Exception: Session ID is invalid!")
        assert(type(data) == "table", "Security Exception: Session data invalid!")
     
        data.atime = luci.sys.uptime()
     
        _write(id, luci.util.get_bytecode(data))
    end

    从上面代码可以看出session生成和时间有关,要想破解这个需要通过系统时间遍历。有点难度呀。

    当然在极路由上 我发现在这之前会有一个函数调用   authenticator{}; 但是在lua代码中没有找到具体的函数编写内容,后来通过luaedit工具做全文内容检索发现。在libauth.so文件中。这个在最后章节讲解。

    小结:可以看出,只有认证通过了才会在openwrt上产生服务器端token,保存在uhttpd服务器的本地文件中。整个过程基本上无法伪造,登录验证体系比较安全,当然也有漏洞,包括遍历密码尝试等。

    二、了解HiWiFi web认证过程和HiWiFi Cloud 之间的通讯认证分析。

    我申请了极路由的root权限通过winscp登录查看其目录新产生的文件。本来想逆向lua最新的源代码unlua 和luadec 两个开源的反编译工具都无法通过,所以只好看H5661-9003版本软件。

    GET /cgi-bin/turbo/;stok=7523cc581c1bccca1db1ea1866c90b95/api/system/check_network_connect?_=1440172033296 HTTP/1.1
    Host: 192.168.199.1
    Connection: keep-alive
    Accept: application/json, text/javascript, */*; q=0.01
    X-Requested-With: XMLHttpRequest
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.103 Safari/537.36
    Referer: http://192.168.199.1/cgi-bin/turbo/admin_web
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4
    Cookie: sysauth=d4b90df6bee107655b1672c244dd8b75; is_mobile=0

    整个过程和openwrt类似,但是做了一些改动。把路径改变成:/cgi-bin/turbo/admin_web,然后推送ajax 脚本检测相关内容。

    改进部分:

    (1)通过云端参与密钥生成。

    LOCAL_KEY='oLKmbg1g'
    APP_LOGIN_AUTH_FILE='/etc/app/appcloudkey'
     
    putkey() {
      echo "$1" | grep -q -E "^[a-z0-9_]+$"
      if [ $(echo $?) != 0 ]; then
        return 1
      fi
      touch "${APP_LOGIN_AUTH_FILE}"
      echo "$1" >"${APP_LOGIN_AUTH_FILE}"
      return 0
    }
     
    validate() {
      userkey="$1"
      randkey="$2"
      echo "${userkey}" | grep -q -E "^[a-f0-9]{32}$"
      if [ $(echo $?) != 0 ]; then
        echo false
        return 1
      fi
      echo "${randkey}" | grep -q -E "^[a-f0-9]{8,}$"
      if [ $(echo $?) != 0 ]; then
        echo false
        return 1
      fi
      touch "${APP_LOGIN_AUTH_FILE}"
      key=$(cat "$APP_LOGIN_AUTH_FILE")
      echo "${key}" | grep -q -E "^[a-z0-9]+$"
      if [ $(echo $?) != 0 ]; then
        echo false
        return 1
      fi
      sign=$(echo -n "$LOCAL_KEY""${key}""${randkey}"|md5sum|awk '{print $1}')
      if [ ${userkey} == "${sign}" ] 2>/dev/null; then
        echo true
        return 0
      fi
      echo false
      return 1
    }

    (2)增加登录重试次数限定(10次)。通过校验/tmp/loginerrnum 存储的次数

    local loginlock_time = 10
    function up_loginlock()
    local num = fs.readfile("/tmp/loginerrnum") or 0
    num=num+1
    fs.writefile("/tmp/loginerrnum", num)
    end
    function unset_loginlock()
    fs.writefile("/tmp/loginerrnum", 0)
    end
    function get_loginlock()
    local num = fs.readfile("/tmp/loginerrnum") or 0
    return num
    end
     
    function authenticator.jsonauth(validator, accs, default)
    if user and validator(user, pass) and user~="root" then
    luci.util.unset_loginlock()
    return user
    end
    context.path = {}
    local json_msg = '{"code":"99999","msg":"not auth."}'
    luci.http.write(json_msg)
    return false
    end

    (3)对openapi调用设置沙盒权限,以及更为严格的token获取方式。

    在 /usr/lib/lua/luci/dispatcher.lua 中

    authenticator{};没找到实现的方法。

    通过查询目录发现usr/lib/libauth.so 可能存在认证信息。然后把它丢到IDA中。选择MIPS little endian

    先通过cache_load_token_v3读取本地存储token。如果没有那么申请token

    https://auth.hiwifi.com/tokenv2?app=%s&checksum=%s&name=%s&cnonce=%d&nonce=%s
    checksum(校验和)、name(设备MAC地址)、
    cnonce(本地产生的随机数) Cnonce  时间和加盐处理

    nonce是通过云平台自动产生的,算法只有云自己知道。

    这里要找的就是checksum值。

    查找tw_get_uuid,通过查找发现在tw.so文件中

    当然你也可以使用python语言调用so库,来测试其加密算法。

    UUID是通过设备mac地址,外加一个常量123456789123 等再加上复杂的算法生成,那么由于每台设备的uuid并不相同,所以即使得到对方的MAC地址,也无法通过伪造请求来进行利用。这种多因素校验的机制,极大的保障了云平台用户的安全。

    沙盒部分,其实就是现在通过openapi登录取得密钥的访问目录限制,防止二次开发人员开发恶意程序,然后数以百万路由器。

    0×03 安全设计总结

    可以看出,极路由从openwrt->hiwifi 9003->hiwifi 9008(目前极1s用的最多的版本),整个固件的软件安全部分设计越来越复杂。

    变态的安全设计也是符合业务需求:

    (1)基本安全的保障:开原版lua源代码可见,必须在发布设备前要做预编译处理。核心的算法和认证库放到so文件中。

    (2)由于要做路由器软件market,那么,把云的key验证机制结合进来后,即使你破解了本地算法,云算法你不了解也白搭。

    最后,为这种信息安全的工匠精神点个赞。

  • 相关阅读:
    linux安装nodejs
    linux系统执行.exe文件
    linux部署php网页
    S: WARNING: Could not write to (C:UsersAdministratorAppDataLocalapktoolframework), using C:UsersADMINI~1AppDataLocalTemp instead...
    Exception in thread "main" brut.androlib.AndrolibException: Could not decode arsc file at brut.androlib.res.decoder.ARSCDecoder.decode
    .frm文件怎么导入到数据库
    layui时间控件闪退的问题
    spring boot集成Swagger2
    java.io.IOException: Connection reset by peer at sun.nio.ch.FileDispatcherImpl.read0(Native Method) at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39)
    spring Securicty入门(一)
  • 原文地址:https://www.cnblogs.com/k1two2/p/4760794.html
Copyright © 2011-2022 走看看