内容

隐藏

一、引言

二、初探ssh

三、再探ssh

三、终探ssh

四、参考链接

一、引言

手里有台极路由,型号HC5861,感觉做工不错,全铝合金外壳,散热功能良好,功能挺多,支持插件,官方宣称的去视频无广告功能、出国加速等也是收到外界一致好评。

可惜正是由于某些插件的原因,导致极路由遇上了它有史以来最大的危机,至于后来虽然经过一些改变,但最终由于某些原因,失去了用户口碑,以至于走下坡路,下图是插件功能的界面,已经无法使用了,同时官网也被其他服务商使用。

二、初探ssh

虽然极路由官网已经无法连接了,但是玩家们还是有很多方式进行设备的改良,如更换固件,但极路由的固件与普通的固件格式是不一致的,所以通常玩家们先获取root权限,通过更换通用BootLoader,如Breed,然后再进行刷机操作。那么如何获取ssh就是首先要解决的问题,当初官网能够使用的情况下,只需要绑定小极账号,申请开发者模式即可使用。

可是现在官网已经无法连接了,想要使用这个方式不太容易,经过互联网搜索,有大神做了一个开启ssh的工具http://www.hiwifi.wtf/ ,免费提供给用户使用,真的很感谢!

根据网站的说明,很快就能开启ssh

最终能够获取到root权限。

三、再探ssh

获取到root权限之后,我们就能拿到设备内的文件系统的内容,通过搜索关键字local-ssh,可以定位到一些文件,其中有nginx的配置文件vh.tw.conf

分析配置文件,可以知晓处理逻辑在local_ssh.lua中

使用文本编辑器打开lua文件,显示为乱码,通过起始的字符串LuaQ可以确定lua代码为编译后的lua,那么需要找到对应的lua版本进行反汇编。

通过搜索liblua.so字符串可以知道lua的版本为5.1.5

经过多次测试,在github上能够找到对应luadec反编译工具,使用对应的安装方式进行安装

git clone https://github.com/HandsomeYingyan/luadec-openwrt.git

cd luadec-openwrt

cd lua-5.1

make linux

sudo make install

cd ../luadec

make LUAVER=5.1

sudo cp luadec /usr/local/bin/

首先试试local_ssh.lua是否能够正常反编译,可以看出来能够正常反编译,虽然里面有一些错误,但基本不影响逻辑的分析:

$ luadec local_ssh.lua

......

local l_0_0 = require("hiwifi.json")

local l_0_1 = require("openapi.utils.utils")

local l_0_2 = string

local l_0_3 = tostring

module("luci.local_ssh.local_ssh")

dispatcher = function(l_1_0)

-- function num : 0_0 , upvalues : l_0_1, l_0_3, l_0_2, l_0_0

local l_1_1 = ""

local l_1_2 = {}

if not l_1_0 then

l_1_0 = {}

end

if l_1_0.method == "get" then

l_1_2.data = (l_0_1.exec_cmd)("sudo /usr/lib/lua/luci/local_ssh/local_ssh_util.lua get")

else

-- DECOMPILER ERROR at PC32: Unhandled construct in 'MakeBoolean' P1

do

if not (l_0_1.exec_cmd)("sudo /usr/lib/lua/luci/local_ssh/local_ssh_util.lua valid " .. l_0_3(l_1_0.data)) then

local l_1_3 = l_1_0.method ~= "valid" or l_1_0.data == nil or ""

end

-- DECOMPILER ERROR at PC34: Confused about usage of register: R3 in 'UnsetPending'

do

local l_1_4 = l_0_3(l_1_3)

if (l_0_2.find)(l_1_4, "Success:") ~= nil then

l_1_2.code = Unknown_Type_Error

l_1_2.data = l_1_4

else

l_1_2.code = Unknown_Type_Error

l_1_2.data = l_1_4

end

if l_1_0.method == "stop" then

l_1_2.data = (l_0_1.exec_cmd)("sudo /usr/lib/lua/luci/local_ssh/local_ssh_util.lua stop")

else

l_1_2.code = Unknown_Type_Error

l_1_2.data = "Error: Method does not exist"

end

l_1_1 = (l_0_0.encode)(l_1_2)

return l_1_1

end

end

end

end

通过分析可以知道主要的逻辑实际上在另外一个lua脚本local_ssh_util.lua,那么使用同样的方式反编译:

$ luadec local_ssh_util.lua

......

local l_0_0 = require("tw")

local l_0_1 = require("auth")

local l_0_2 = require("socket")

local l_0_3 = require("luci.util")

local l_0_4 = require("nixio")

local l_0_5 = require("luci.http.protocol")

local l_0_6 = (math.floor)((l_0_2.gettime)() * Unknown_Type_Error)

local l_0_7 = (l_0_0.get_mac)()

local l_0_8 = "ssh"

local l_0_9 = ""

local l_0_10 = tostring(l_0_7) .. "," .. l_0_8 .. "," .. tostring(l_0_6)

local l_0_11 = ""

local l_0_12 = "/tmp/local_ssh_ms"

local l_0_13 = (io.open)(l_0_12, "r")

local l_0_14 = Unknown_Type_Error

local l_0_15 = arg[Unknown_Type_Error]

do

local l_0_16 = arg[Unknown_Type_Error] or ""

local l_0_17, l_0_18 = , nil

if not l_0_13:read("*n") then

l_0_13:close()

-- DECOMPILER ERROR at PC71: Overwrote pending register: R14 in 'AssignReg'

l_0_13:close()

-- DECOMPILER ERROR at PC83: Overwrote pending register: R13 in 'AssignReg'

if l_0_15 == "get" then

l_0_13:write(l_0_6)

l_0_13:close()

-- DECOMPILER ERROR at PC106: Overwrote pending register: R11 in 'AssignReg'

print(l_0_11)

;

(os.exit)(Unknown_Type_Error)

else

-- DECOMPILER ERROR at PC136: Overwrote pending register: R11 in 'AssignReg'

-- DECOMPILER ERROR at PC141: Overwrote pending register: R11 in 'AssignReg'

if l_0_15 == "valid" then

if (l_0_1.lua_hmac_sha1_with_uuid)(l_0_10, (string.len)(l_0_10)) or l_0_17 == l_0_11 then

(os.execute)("/etc/init.d/dropbear restart;hwf-at 10 /usr/lib/lua/luci/local_ssh/local_ssh_util.lua check_connected")

;

(os.remove)(l_0_12)

if l_0_18 == nil then

print("Error: port file does not exist")

;

(os.exit)(Unknown_Type_Error)

end

-- DECOMPILER ERROR at PC169: Overwrote pending register: R18 in 'AssignReg'

if nil == nil then

print("Error: port does not exist")

;

(os.exit)(Unknown_Type_Error)

end

l_0_18:close()

-- DECOMPILER ERROR at PC183: Confused about usage of register: R18 in 'UnsetPending'

print("Success: ssh port is " .. nil)

;

(os.exit)(Unknown_Type_Error)

else

print("Error: valid token error")

;

(os.exit)(Unknown_Type_Error)

end

else

if l_0_15 == "stop" then

local l_0_19 = nil

local l_0_20 = nil

l_0_20:close()

if ((io.popen)("pidof dropbear | wc -w")):read("*n") <= Unknown_Type_Error then

(os.execute)("/etc/init.d/dropbear stop")

else

;

(os.execute)("hwf-at 5 /usr/lib/lua/luci/local_ssh/local_ssh_util.lua stop")

end

else

do

if l_0_15 == "check_connected" then

local l_0_21 = nil

local l_0_22 = nil

l_0_22:close()

if ((io.popen)("pidof dropbear | wc -w")):read("*n") <= Unknown_Type_Error then

(os.execute)("hwf-at 10 /usr/lib/lua/luci/local_ssh/local_ssh_util.lua check_connected")

else

;

(os.execute)("hwf-at 180 /usr/lib/lua/luci/local_ssh/local_ssh_util.lua stop")

end

else

do

print("Error: method does not exist")

;

(os.exit)(Unknown_Type_Error)

end

end

end

end

end

end

end

end

通过浏览器抓取开启ssh的请求包,可以知道method=get的时候会生成一串字符串local token,需要将它发送到官方客服,然后通过这段字符串进行计算得到cloud token,当method=valid的时候,输入token进行校验,就能开启dropbear,也就是ssh服务了。通过简单的逻辑分析,总结如下:

method值作用get写入当前时间戳存入/tmp/local_ssh_ms,并且生成local token字符串valid校验cloud token,如果正确则重新启动ssh服务stop停止ssh服务check_connected检查ssh服务状态,实际上这个method是无法调用到的那么method=valid的校验方法就是关键,然而反编译的结果有一些不完整,整个逻辑无法连贯起来,那么就得分析原始的lua字节码了:

$ luac -l local_ssh_util.lua

1 [-] GETGLOBAL 0 -1 ; require

2 [-] LOADK 1 -2 ; "tw"

3 [-] CALL 0 2 2

4 [-] GETGLOBAL 1 -1 ; require

5 [-] LOADK 2 -3 ; "auth"

6 [-] CALL 1 2 2

7 [-] GETGLOBAL 2 -1 ; require

8 [-] LOADK 3 -4 ; "socket"

9 [-] CALL 2 2 2

10 [-] GETGLOBAL 3 -1 ; require

11 [-] LOADK 4 -5 ; "luci.util"

12 [-] CALL 3 2 2

13 [-] GETGLOBAL 4 -1 ; require

14 [-] LOADK 5 -6 ; "nixio"

15 [-] CALL 4 2 2

16 [-] GETGLOBAL 5 -1 ; require

17 [-] LOADK 6 -7 ; "luci.http.protocol"

18 [-] CALL 5 2 2

19 [-] GETGLOBAL 6 -8 ; math

20 [-] GETTABLE 6 6 -9 ; "floor"

21 [-] GETTABLE 7 2 -10 ; "gettime"

22 [-] CALL 7 1 2

23 [-] MUL 7 7 -11 ; - 1000

24 [-] CALL 6 2 2

25 [-] GETTABLE 7 0 -12 ; "get_mac"

26 [-] CALL 7 1 2

27 [-] LOADK 8 -13 ; "ssh"

28 [-] LOADK 9 -14 ; ""

29 [-] GETGLOBAL 10 -15 ; tostring

30 [-] MOVE 11 7

31 [-] CALL 10 2 2

32 [-] LOADK 11 -16 ; ","

33 [-] MOVE 12 8

34 [-] LOADK 13 -16 ; ","

35 [-] GETGLOBAL 14 -15 ; tostring

36 [-] MOVE 15 6

37 [-] CALL 14 2 2

38 [-] CONCAT 10 10 14

39 [-] LOADK 11 -14 ; ""

40 [-] LOADK 12 -17 ; "/tmp/local_ssh_ms"

41 [-] GETGLOBAL 13 -18 ; io

42 [-] GETTABLE 13 13 -19 ; "open"

43 [-] MOVE 14 12

44 [-] LOADK 15 -20 ; "r"

45 [-] CALL 13 3 2

46 [-] LOADK 14 -21 ; 0

47 [-] GETGLOBAL 15 -22 ; arg

48 [-] GETTABLE 15 15 -23 ; 1

49 [-] GETGLOBAL 16 -22 ; arg

50 [-] GETTABLE 16 16 -24 ; 2

51 [-] TEST 16 0 1

52 [-] JMP 1 ; to 54

53 [-] LOADK 16 -14 ; ""

54 [-] LOADNIL 17 18

55 [-] EQ 1 13 -25 ; - nil

56 [-] JMP 9 ; to 66

57 [-] SELF 19 13 -26 ; "read"

58 [-] LOADK 21 -27 ; "*n"

59 [-] CALL 19 3 2

60 [-] TESTSET 14 19 1

61 [-] JMP 1 ; to 63

62 [-] LOADK 14 -21 ; 0

63 [-] SELF 19 13 -28 ; "close"

64 [-] CALL 19 2 1

65 [-] JMP 9 ; to 75

66 [-] GETGLOBAL 19 -18 ; io

67 [-] GETTABLE 19 19 -19 ; "open"

68 [-] MOVE 20 12

69 [-] LOADK 21 -29 ; "a"

70 [-] CALL 19 3 2

71 [-] MOVE 13 19

72 [-] LOADK 14 -21 ; 0

73 [-] SELF 19 13 -28 ; "close"

74 [-] CALL 19 2 1

75 [-] EQ 0 15 -30 ; - "get"

76 [-] JMP 39 ; to 116

77 [-] GETGLOBAL 19 -31 ; assert

78 [-] GETGLOBAL 20 -18 ; io

79 [-] GETTABLE 20 20 -19 ; "open"

80 [-] MOVE 21 12

81 [-] LOADK 22 -32 ; "w"

82 [-] CALL 20 3 0

83 [-] CALL 19 0 2

84 [-] MOVE 13 19

85 [-] SELF 19 13 -33 ; "write"

86 [-] MOVE 21 6

87 [-] CALL 19 3 1

88 [-] SELF 19 13 -28 ; "close"

89 [-] CALL 19 2 1

90 [-] GETTABLE 19 1 -34 ; "lua_hmac_sha1_with_uuid"

91 [-] MOVE 20 10

92 [-] GETGLOBAL 21 -35 ; string

93 [-] GETTABLE 21 21 -36 ; "len"

94 [-] MOVE 22 10

95 [-] CALL 21 2 0

96 [-] CALL 19 0 2

97 [-] TESTSET 11 19 1

98 [-] JMP 1 ; to 100

99 [-] LOADK 11 -14 ; ""

100 [-] GETTABLE 19 4 -37 ; "bin"

101 [-] GETTABLE 19 19 -38 ; "b64encode"

102 [-] MOVE 20 10

103 [-] LOADK 21 -16 ; ","

104 [-] MOVE 22 11

105 [-] CONCAT 20 20 22

106 [-] CALL 19 2 2

107 [-] MOVE 11 19

108 [-] GETGLOBAL 19 -39 ; print

109 [-] MOVE 20 11

110 [-] CALL 19 2 1

111 [-] GETGLOBAL 19 -40 ; os

112 [-] GETTABLE 19 19 -41 ; "exit"

113 [-] LOADK 20 -23 ; 1

114 [-] CALL 19 2 1

115 [-] JMP 137 ; to 253

116 [-] EQ 0 15 -42 ; - "valid"

117 [-] JMP 82 ; to 200

118 [-] GETGLOBAL 19 -15 ; tostring

119 [-] MOVE 20 7

120 [-] CALL 19 2 2

121 [-] LOADK 20 -16 ; ","

122 [-] MOVE 21 8

123 [-] LOADK 22 -16 ; ","

124 [-] GETGLOBAL 23 -15 ; tostring

125 [-] ADD 24 14 -23 ; - 1

126 [-] CALL 23 2 2

127 [-] CONCAT 10 19 23

128 [-] GETTABLE 19 1 -34 ; "lua_hmac_sha1_with_uuid"

129 [-] MOVE 20 10

130 [-] GETGLOBAL 21 -35 ; string

131 [-] GETTABLE 21 21 -36 ; "len"

132 [-] MOVE 22 10

133 [-] CALL 21 2 0

134 [-] CALL 19 0 2

135 [-] TESTSET 11 19 1

136 [-] JMP 1 ; to 138

137 [-] LOADK 11 -14 ; ""

138 [-] GETTABLE 19 4 -37 ; "bin"

139 [-] GETTABLE 19 19 -38 ; "b64encode"

140 [-] MOVE 20 11

141 [-] CALL 19 2 2

142 [-] MOVE 11 19

143 [-] EQ 0 16 11

144 [-] JMP 47 ; to 192

145 [-] GETGLOBAL 19 -40 ; os

146 [-] GETTABLE 19 19 -43 ; "execute"

147 [-] LOADK 20 -44 ; "/etc/init.d/dropbear restart;hwf-at 10 /usr/lib/lua/luci/local_ssh/local_ssh_util.lua check_connected"

148 [-] CALL 19 2 1

149 [-] GETGLOBAL 19 -40 ; os

150 [-] GETTABLE 19 19 -45 ; "remove"

151 [-] MOVE 20 12

152 [-] CALL 19 2 1

153 [-] GETGLOBAL 19 -18 ; io

154 [-] GETTABLE 19 19 -46 ; "popen"

155 [-] LOADK 20 -47 ; "s=`netstat -lntp|grep dropbear | grep '0.0.0.0' | grep -v '127.0.0.1'|awk '{print $4}'`; echo ${s##*:}"

156 [-] CALL 19 2 2

157 [-] MOVE 17 19

158 [-] EQ 0 17 -25 ; - nil

159 [-] JMP 7 ; to 167

160 [-] GETGLOBAL 19 -39 ; print

161 [-] LOADK 20 -48 ; "Error: port file does not exist"

162 [-] CALL 19 2 1

163 [-] GETGLOBAL 19 -40 ; os

164 [-] GETTABLE 19 19 -41 ; "exit"

165 [-] LOADK 20 -23 ; 1

166 [-] CALL 19 2 1

167 [-] SELF 19 17 -26 ; "read"

168 [-] LOADK 21 -27 ; "*n"

169 [-] CALL 19 3 2

170 [-] MOVE 18 19

171 [-] EQ 0 18 -25 ; - nil

172 [-] JMP 7 ; to 180

173 [-] GETGLOBAL 19 -39 ; print

174 [-] LOADK 20 -49 ; "Error: port does not exist"

175 [-] CALL 19 2 1

176 [-] GETGLOBAL 19 -40 ; os

177 [-] GETTABLE 19 19 -41 ; "exit"

178 [-] LOADK 20 -23 ; 1

179 [-] CALL 19 2 1

180 [-] SELF 19 17 -28 ; "close"

181 [-] CALL 19 2 1

182 [-] GETGLOBAL 19 -39 ; print

183 [-] LOADK 20 -50 ; "Success: ssh port is "

184 [-] MOVE 21 18

185 [-] CONCAT 20 20 21

186 [-] CALL 19 2 1

187 [-] GETGLOBAL 19 -40 ; os

188 [-] GETTABLE 19 19 -41 ; "exit"

189 [-] LOADK 20 -23 ; 1

190 [-] CALL 19 2 1

191 [-] JMP 61 ; to 253

192 [-] GETGLOBAL 19 -39 ; print

193 [-] LOADK 20 -51 ; "Error: valid token error"

194 [-] CALL 19 2 1

195 [-] GETGLOBAL 19 -40 ; os

196 [-] GETTABLE 19 19 -41 ; "exit"

197 [-] LOADK 20 -23 ; 1

198 [-] CALL 19 2 1

199 [-] JMP 53 ; to 253

200 [-] EQ 0 15 -52 ; - "stop"

201 [-] JMP 21 ; to 223

202 [-] GETGLOBAL 19 -18 ; io

203 [-] GETTABLE 19 19 -46 ; "popen"

204 [-] LOADK 20 -53 ; "pidof dropbear | wc -w"

205 [-] CALL 19 2 2

206 [-] SELF 20 19 -26 ; "read"

207 [-] LOADK 22 -27 ; "*n"

208 [-] CALL 20 3 2

209 [-] SELF 21 19 -28 ; "close"

210 [-] CALL 21 2 1

211 [-] LE 0 20 -23 ; - 1

212 [-] JMP 5 ; to 218

213 [-] GETGLOBAL 21 -40 ; os

214 [-] GETTABLE 21 21 -43 ; "execute"

215 [-] LOADK 22 -54 ; "/etc/init.d/dropbear stop"

216 [-] CALL 21 2 1

217 [-] JMP 35 ; to 253

218 [-] GETGLOBAL 21 -40 ; os

219 [-] GETTABLE 21 21 -43 ; "execute"

220 [-] LOADK 22 -55 ; "hwf-at 5 /usr/lib/lua/luci/local_ssh/local_ssh_util.lua stop"

221 [-] CALL 21 2 1

222 [-] JMP 30 ; to 253

223 [-] EQ 0 15 -56 ; - "check_connected"

224 [-] JMP 21 ; to 246

225 [-] GETGLOBAL 19 -18 ; io

226 [-] GETTABLE 19 19 -46 ; "popen"

227 [-] LOADK 20 -53 ; "pidof dropbear | wc -w"

228 [-] CALL 19 2 2

229 [-] SELF 20 19 -26 ; "read"

230 [-] LOADK 22 -27 ; "*n"

231 [-] CALL 20 3 2

232 [-] SELF 21 19 -28 ; "close"

233 [-] CALL 21 2 1

234 [-] LE 0 20 -23 ; - 1

235 [-] JMP 5 ; to 241

236 [-] GETGLOBAL 21 -40 ; os

237 [-] GETTABLE 21 21 -43 ; "execute"

238 [-] LOADK 22 -57 ; "hwf-at 10 /usr/lib/lua/luci/local_ssh/local_ssh_util.lua check_connected"

239 [-] CALL 21 2 1

240 [-] JMP 12 ; to 253

241 [-] GETGLOBAL 21 -40 ; os

242 [-] GETTABLE 21 21 -43 ; "execute"

243 [-] LOADK 22 -58 ; "hwf-at 180 /usr/lib/lua/luci/local_ssh/local_ssh_util.lua stop"

244 [-] CALL 21 2 1

245 [-] JMP 7 ; to 253

246 [-] GETGLOBAL 19 -39 ; print

247 [-] LOADK 20 -59 ; "Error: method does not exist"

248 [-] CALL 19 2 1

249 [-] GETGLOBAL 19 -40 ; os

250 [-] GETTABLE 19 19 -41 ; "exit"

251 [-] LOADK 20 -23 ; 1

252 [-] CALL 19 2 1

253 [-] RETURN 0 1

有了汇编的基础,lua字节码还是比较容易看懂的,关键逻辑如下:

local token生成方法,其中时间戳存入/tmp/local_ssh_ms文件:

mac地址,ssh,timestamp时间戳,hmac哈希认证(mac地址,ssh,timestamp时间戳)

cloud token生成方法:

hmac哈希认证(local token的timestamp时间戳+1)

其中hmac哈希认证的密码为uuid的sha1值,具体的细节可以逆向/usr/lib/libauth.so的hmac_sha1_with_uuid函数。

三、终探ssh

根据逻辑我们可以编写代码来实现自动开启ssh功能,以下是python3代码展示,如果成功,则会显示“result >> Success: ssh port is 22”

import base64,hashlib,hmac,json,urllib.request

def urlopen(url):

r = urllib.request.urlopen(url)

j = json.loads(r.read())

return j

def get_hmac_sha1(message, key):

result = hmac.new(key, message, hashlib.sha1).digest()

return base64.b64encode(result).decode()

def sha1(data):

return hashlib.sha1(data).digest()

if __name__ == '__main__':

local_token = urlopen("http://www.4006024680.com/local-ssh/api?method=get")["data"]

print("local token:" + local_token)

mac, ssh, t, hmacstr = base64.b64decode(local_token).split(b",",3)

message = "{},ssh,{}".format(mac.decode(),int(t)+1).encode()

uuid = urlopen("http://www.4006024680.com/cgi-bin/turbo/proxy/router_info")["data"]["uuid"]

print("uuid:" + uuid)

key = sha1(uuid.encode())

h = get_hmac_sha1(message, key)

print("cloud token:" + h)

print("result >> " + urlopen("http://www.4006024680.com/local-ssh/api?method=valid&data=" + h)["data"])

当然,为了方便大家,也可以使用本站的在线工具进行操作:

四、参考链接

https://baijiahao.baidu.com/s?id=1607853490223049939

https://www.cnblogs.com/lsgxeva/p/16376971.html

https://zhuanlan.zhihu.com/p/108934048