极路由安全设计分析姐妹篇

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

开篇想说两件事情:

一、非常感谢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验证机制结合进来后,即使你破解了本地算法,云算法你不了解也白搭。

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

相关文章
|
3月前
|
网络协议 安全 网络虚拟化
"揭秘!网工爱不释手的Wireshark八大绝技,让复杂网络问题无所遁形,你掌握了吗?"
【8月更文挑战第19天】Wireshark是网络工程师不可或缺的工具,以其卓越的抓包与分析能力闻名,在网络故障排除、性能优化和安全审查方面作用显著。本文精选八大实用技巧,包括精准数据包过滤、序列号排序、时间格式调整、数据包解码、混杂模式使用、数据包标记与导出、自定义显示列以及过滤器的高效应用,助您在网络分析中得心应手,成为解决复杂问题的高手。
50 0
|
6月前
|
搜索推荐 计算机视觉
赠人玫瑰,手有余香,5款实用软件推荐
今天推荐5款十分小众的软件,知道的人不多,但是每个都是非常非常好用的,有兴趣的小伙伴可以自行搜索下载。
49 0
|
6月前
|
监控 安全 网络安全
宝鸡陇县中学弱电系统集成设计方案_kaic
宝鸡陇县中学弱电系统集成设计方案_kaic
|
6月前
|
存储 安全 数据库
推荐我最近刚发现的5款实用软件
我喜欢发现和分享一些好用的软件,我觉得它们可以让我们的工作和生活更加轻松和快乐。今天给大家介绍五款我最近发现的软件。
58 1
分享5款小而精的实用软件
分享是一种神奇的东西,它使快乐增大,它使悲伤减小。分享好用软件给大家的同时,我自己也能获得愉悦的心情。
79 0
|
Web App开发 Linux 数据安全/隐私保护
亮个相吧小宝贝儿,五款压箱底的软件
今天要给大家推荐5款压箱底的宝贝软件了,百度搜索一下就能找到下载链接了。
144 0
亮个相吧小宝贝儿,五款压箱底的软件
大小双轨公排互助开发逻辑丨大小双轨公排互助系统开发(开发详细)丨大小双轨公排互助源码及功能
  The basis of the big public bus belongs to the single network body,also known as the whole network public bus.The so-called whole network public bus means that all people on the service platform are ranked above the same big network body.The people you develop are not necessarily ranked under your
stepn跑鞋链游开发功能丨stepn跑鞋链游系统开发(详细及方案)
This article is compiled and released by WeChaT: kaifa873, which is only for reference of project development requirements! telegram @ sleu88
82 0
stepn跑鞋链游开发功能丨stepn跑鞋链游系统开发(详细及方案)丨stepn跑鞋链游源码部署
amountIn = max && exactIn ? amountIn : SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true);
|
安全
马蹄(波场)佛萨奇2.0项目系统开发技术介绍讲解方案
马蹄(波场)佛萨奇2.0项目系统开发技术介绍讲解方案
188 0