跟 UMU 一起玩 OpenWRT(入门篇19):检测 WiFi 入侵

问题

我怀疑有人在用工具穷举我的 WiFi 密码,我该怎么确认?

解决

运行 iw event,如果看到频繁出现 new stationdel station 的 log,说明有设备在频繁连接和断开。

如果您的路由器是小米路由器 Pro,则可以用 iwevent 代替 iw event,密码不对的 log 是 had deauthenticated,断开是 had disassociated

安全建议

设置密码时,应该检查一下您的密码是否在“字典”里。字典参考:

rockyou.txt contains 14,341,564 unique passwords, used in 32,603,388 accounts.

举个例子吧!稣打算用 10 个 0 做密码,先查一下……嗯哼!

valentine idontknow pikachu little diamond1 iloveu1 babyphat peanut1 kittens goddess ballet damien nascar 171717 rangers1 winston 0000000000 rocky1 coolgirl maymay charlene caramelo selena lucero wendy volcom 1435254 copper cindy baby123

地球真危险!稣回月球了……

adduser 和 useradd 的区别

需求

在 armbian 系统里新建个账号。

这当然是个简单的任务,问题是发现居然同时存在 adduser 和 useradd 两个命令。

解决选择恐惧症

  1. adduser 不是可执行程序。

1
2
3
4
5
which adduser
/usr/sbin/adduser

ldd /usr/sbin/adduser
not a dynamic executable

  1. useradd 是可执行程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
which useradd
/usr/sbin/useradd

ldd /usr/sbin/useradd
linux-vdso.so.1 (0xbea59000)
libaudit.so.1 => /lib/arm-linux-gnueabihf/libaudit.so.1 (0xb6ea1000)
libselinux.so.1 => /lib/arm-linux-gnueabihf/libselinux.so.1 (0xb6e77000)
libsemanage.so.1 => /usr/lib/arm-linux-gnueabihf/libsemanage.so.1 (0xb6e3f000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6d45000)
/lib/ld-linux-armhf.so.3 (0xb6efb000)
libcap-ng.so.0 => /lib/arm-linux-gnueabihf/libcap-ng.so.0 (0xb6d31000)
libpcre.so.3 => /lib/arm-linux-gnueabihf/libpcre.so.3 (0xb6cd4000)
libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0xb6cc1000)
libsepol.so.1 => /lib/arm-linux-gnueabihf/libsepol.so.1 (0xb6c44000)
libbz2.so.1.0 => /lib/arm-linux-gnueabihf/libbz2.so.1.0 (0xb6c28000)
libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb6c03000)

  1. 推测 adduser 是脚本,内部调用 useradd。求证之!

1
2
3
4
5
6
7
8
9
head -1 /usr/sbin/adduser
#!/usr/bin/perl

grep useradd /usr/sbin/adduser
my $useradd = &which('useradd');
&systemcall($useradd, '-d', $home_dir, '-g', $ingroup_name, '-s',
my $useradd = &which('useradd');
&systemcall($useradd, '-d', $home_dir, '-g', $ingroup_name, '-s',
# useradd without -p has left the account disabled (password string is '!')

这说明 adduser 是 perl 脚本,内部确实调用 useradd。

  1. 直觉告诉 UMU,应该用 adduser,如果 useradd 很好用,不会有 adduser 存在的必要。

优化思维【6】安全性

前情

前五篇,主要考虑性能优化,只有第二篇与安全性相关。

其实区块链业界一直不缺乏黑客,最近看过不少安全事故导致惨重代价,所以想总结点安全性方面的优化思路。(本篇比较务虚,只是大体思路。)总的来说,为了安全是必须付出实现或者性能代价的。实现代价是开发、测试阶段就要投入更多精力,性能代价是因为考虑更多,有可能消耗更大运行资源。但从长期来看,这些代价都是必须的。

产品价值与安全意识

开发者可能有能力做一定安全防范,但如果他认为产品没有价值,没必要防范,就可能明明有能力防住,实际却被黑翻车。要不要注重安全性,是设计阶段就应该交代清楚的。

夫兵久而国利者,未之有也。故不能尽知用兵之害者,则不能尽知用兵之利也。——《孙子兵法》

做任何事情先考虑失败。——李嘉诚

程序员版解读:安全怎么能大意,甚至忽视?那都是侥幸心理,只要您的产品、服务有价值,长期看都会被破解、攻击。开发者如果不能知悉黑客可能的攻击点,并衡量被攻破的代价,他必然也不清楚自己写的代码的真正价值。

实际开发过程中,有些领导者会故意隐藏关于产品价值的信息,这实际上可能导致安全考虑不到位。这种情况就应该配备一个在安全方面经验丰富的审查者。

一句话总结:越有价值的东西,就越应该注重安全。

知识深度

一般黑客都是上层、底层皆通,尤其擅长底层。很少听说只做增删查改业务的人能够黑掉什么东西、偷到数字货币,因为同样只做增删查改业务的人就具备防止这种级别的攻击手段。

比如古老的 SQL 注入漏洞,即便是入门级的 Web 开发也能理解并防护,用预编译语句、存储过程、改用 ORM 就天然免疫。他们无法防护的往往来自更底层的 Web Server 的漏洞,比如 Apache、Nginx 某个版本有 bug,刚好中枪。

再举个例子,用 C/C++ 写 UDP 服务程序,“先把它实现,能用就行”,“不就 socket 嘛?很容易!”于是没有考虑 socket 等资源的生存周期,没料到黑客可以伪造 UDP 包源地址,实现出来的就可能有拒绝服务攻击 (Denial of Service,DoS) 漏洞

总之,为了性能或安全的优化,开发者往往需要往底层钻。为性能,主要是研究底层模块与之配合,达到消除瓶颈目的;为安全,则是不让对底层设计的不了解,导致实现不够严谨周密而产生漏洞。

知识广度

经常听到这样的段子:

千万不要跟程序员说,你的代码有 bug。

他的第一反应是你的环境有问题,第二就是你是傻逼不会用吧!

你要跟他这么说:这个程序运行的怎么运行的跟预期不一样,是我操作有问题吗?

这货就会第一反应,我擦,这是不是出 bug 了?

这段子里其实间接反映一个程序员经常遇到的问题:自己测试没问题,一到用户侧或者线上就莫名出问题。环境不同,是最大原因。比如 Linux 的发行版众多;著名的 Android 碎片化;iPhone 型号随时间推移也越来越多了……

另一个大原因则是依赖。比如古老的 DLL Hell。类似的问题在 macOS、Linux 上也都存在。有一次 UMU 把 macOS 的 OpenSSL 升级到 1.1,结果 1.0 居然被删掉,导致原来编译的依赖 1.0 的 eos 就无法运行了。

再以 eos 为例,它依赖不少库,这些依赖库本身也可能有 bug,也要升级。又比如 ipfs,熟悉下来,发现其依赖树很广。作为开发者,引入一个依赖时,肯定需要操心会不会同时引入 bug。一般解决方式是:采用被大量验证的著名库,尽快跟进最新稳定版本。

总之,为了安全,设计者可能需要了解更多方面的知识,并不仅限于表面上需要的那些。开发者还要与时俱进,积极消灭潜在的漏洞。

开发语言

高级语言程序员可能很少听说缓冲区溢出,即使有,多半也是这门语言的宿主、解释器的 bug。而 C/C++ 等能直接操作指针的语言,就可能听到栈溢出、空指针、野指针等。采用高级语言确实会在程序执行时的安全性上省心不少,能把更多精力放在流程安全、业务安全上。

从架构上说,应该让不同语言只用于它擅长的领域。比如用 Rust 实现底层模块,用 Go 实现上层业务。这种分层选择语言的方式,充分利用 Rust 的安全性和底层开发能力,还可以让分工更清晰、沟通更愉快。

为什么 EOS 私钥有不同长度?

问题

这里有两个 EOS 私钥,它们长度居然不一样?

  • PVT_K1_1EEr5aW5162skbocDSMDgoWn9jna6HPSr1TwEMR6PNXbPtRky

  • PVT_K1_2bfGi9rYsXQSXXTvJbDAPhHLQUojjaNLomdm3cEJ1XTzMqUt3V

为什么私钥有不同长度?而公钥就都是固定长度呢?

知识点

  • 私钥是一个大型随机数,而公钥则是私钥乘以椭圆曲线上的基点后对应的点。对于 secp256k1 来说,是 256 位,并且 < n 的整数。

  • n 须为质数,Order of G,是使得 n * G = 0 的最⼩正整数,n 是安全性最⼤的决定因素。对于 secp256k1 来说,n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141。

  • 不是每个数都安全,比如小的数肯定是不安全的,黑客可以从 1 开始枚举,不够大的数很快就被找到对应关系,也可以从 n 倒着枚举,所以太大的也不安全。(PS:临近一些特别数的数也不安全……)一般来说,私钥的安全范围是 [0x0080000000000000000000000000000000000000000000000000000000000000, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff]。

工具

https://github.com/UMU618/secp256k1-tools

使用 private-2-public.js 可以把私钥转为它代表的数字:

1
2
3
4
5
6
7
8
9
10
11
12
13
DEBUG=* node private-2-public.js PVT_K1_1EEr5aW5162skbocDSMDgoWn9jna6HPSr1TwEMR6PNXbPtRky
secp256k1-tools:key-util pvt = 84ade57e2b35cca8972562fcc6d1f6f2fbf078c4f2cfb532eb4d740767c5a8 +0ms
secp256k1-tools:key-util x = 2110b8d675240f5d548d166cc06b22f44c671d762711a3a67027b74cd166ab76 +9ms
secp256k1-tools:key-util y = 20ac68b75ad8b0e4bc3ec5705ebaf57c69d2d8268504d6aa95fdebfd0b7db831 +0ms
PVT_K1_1EEr5aW5162skbocDSMDgoWn9jna6HPSr1TwEMR6PNXbPtRky
PUB_K1_75o92oRgWSgx3XzTDYPj1e3hFSRhMnKaUdW8ZZpxJXkhfiGBHS

DEBUG=* node private-2-public.js PVT_K1_2bfGi9rYsXQSXXTvJbDAPhHLQUojjaNLomdm3cEJ1XTzMqUt3V
secp256k1-tools:key-util pvt = d2653ff7cbb2d8ff129ac27ef5781ce68b2558c41a74af1f2ddca635cbeef07d +0ms
secp256k1-tools:key-util x = c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf +8ms
secp256k1-tools:key-util y = eeceff7130fd352c698d2279967e2397f045479940bb4e7fb178fd9212fca8c0 +1ms
PVT_K1_2bfGi9rYsXQSXXTvJbDAPhHLQUojjaNLomdm3cEJ1XTzMqUt3V
PUB_K1_6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5BoDq63

分析

1. 数值比较

  • PVT_K1_1EEr5aW5162skbocDSMDgoWn9jna6HPSr1TwEMR6PNXbPtRky,有 56 个字符,去掉前缀和校验码后有 45 个字符,它代表 0x84ade57e2b35cca8972562fcc6d1f6f2fbf078c4f2cfb532eb4d740767c5a8;

  • PVT_K1_2bfGi9rYsXQSXXTvJbDAPhHLQUojjaNLomdm3cEJ1XTzMqUt3V,有 57 个字符,去掉前缀和校验码后有 46 个字符,它代表 d2653ff7cbb2d8ff129ac27ef5781ce68b2558c41a74af1f2ddca635cbeef07d。

可以清楚地看出前者短一个字符,数值也相应比较小。

2. BASE58 编码的原理

BASE58 的字符集:123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz,其中 '1' 代表 0,'z' 代表 57。把待编码的数字不断除以 58,并将余数用对应的字符表示。举个小点的数字做例子:618

1
2
3
618 / 58 = 10 .. 38 -> f

10 / 58 = 0 .. 10 -> B

拼接余数得 fB,再反转得 Bf。

3. BASE58 编码位数关系

一个数编码后,应该长于或等于比它小的数。我们可以通过简单的数学计算得出 45 个字符的 BASE58 编码可以表示的最大数:

zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = 0xc33ed2d1fbdd3bfe9c22b96164d38cf0d640e1c0ee8b61c39c57899fffffffffff

所以 <= 0xc33ed2d1fbdd3bfe9c22b96164d38cf0d640e1c0ee8b61c39c57899fffffffffff 的私钥编码后是 56 个字符;大于者 59 个字符。

4. 旧格式私钥

  • 旧格式私钥:5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3

  • 对应新格式:PVT_K1_2bfGi9rYsXQSXXTvJbDAPhHLQUojjaNLomdm3cEJ1XTzMqUt3V

同理,只是格式不同罢了。不再展开。

5. 为什么公钥是固定长度呢?

因为公钥有个表示奇偶性的前缀,0x02 或者 0x03,所以它的大小范围被限定,没能相差一个 BASE58 字符。

相关文章

基于 ECC 的私钥转为公钥的过程

ECC Node.js

nfs-ganesha 端口绑定

需求

前文《在 Armbian 安装 NFS 服务端》介绍 nfs-ganesha 的安装配置。经过几天使用,发现效果还可以,但一直都是在局域网内使用,突然有一天想在公司访问家里的 NFS 共享……

问题

直接 mount 会一直卡着。

开放 111、2049 端口,再 mount,还是卡着。

通过反复重启 nfs-ganesha 并 netstat -nalp | grep ganesha.nfsd 观测,发现 mountd 端口不固定!给开放端口配置带来困难。

解决

Armbian 配置

将 mountd 端口绑定,比如 2618,但配置的方法和常规 Debian 服务器的内核级 NFS Server 不同。修改 /etc/services 添加 mountd 绑定是无用的,应该编辑 /etc/ganesha/ganesha.conf,添加以下配置:

1
2
3
4
NFS_CORE_PARAM
{
MNT_Port = 2618;
}

改完重启服务:

1
systemctl restart nfs-ganesha.service

防护墙配置

UMU 用的是 OpenWRT 路由器作为家庭网络出口,firewall 配置文件是 /etc/config/firewall,添加以下几行:

1
2
3
4
5
6
7
8
9
10
config rule
option src 'wan'
option name 'Allow-NFS'
option dest '*'
option target 'ACCEPT'
option dest_port '111 2049 2618'
option family 'ipv6'
option start_time '00:09:00'
option stop_time '00:21:00'
option enabled '0'

以上配置开启了 111、2049、2618 三个端口的转发,其中 udp/tcp 111 是 portmap 端口,udp/tcp 2049 是 nfsd 端口,udp/tcp 2618 是上一步绑定的 mountd 端口。

/etc/init.d/firewall restart 重启后就可以在办公室通过 IPv6 访问家里的 NFS 共享了。

参考

https://github.com/nfs-ganesha/nfs-ganesha/blob/next/src/config_samples/config.txt

在 Armbian 安装 NFS 服务端

需求

有个刷了 Armbian 的玩客云想当文件共享服务器。

问题

某些版本的 Armbian 内核不支持 nfsd,刚好稣就刷到!如果按照 debian 服务器玩法——安装 kernel 版服务端,是无法正常工作的:

1
apt install nfs-common nfs-kernel-server

启动时会提示:

1
2
3
4
5
6
mount: /proc/fs/nfsd: unknown filesystem type 'nfsd'.
proc-fs-nfsd.mount: Mount process exited, code=exited, status=32/n/a
proc-fs-nfsd.mount: Failed with result 'exit-code'.
nfs-mountd.service: Job nfs-mountd.service/start failed with result 'dependency'.
nfs-idmapd.service: Job nfs-idmapd.service/start failed with result 'dependency'.
nfs-server.service: Job nfs-server.service/start failed with result 'dependency'.

解决

服务端

使用用户态的 nfs-ganesha。

1
apt install nfs-ganesha nfs-ganesha-vfs

配置文件为 /etc/ganesh/ganesha.conf。nfs-ganesha-vfs 包另带一个 vfs.conf 参考模板。以下配置创建读写共享 /root/share 和只读共享 /opt:

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
EXPORT_DEFAULTS
{
Protocols = 4;
}

EXPORT
{
Export_Id = 77;
Protocols = 3, 4;
Path = /root/share;
Pseudo = /root/share;
Access_Type = RW;
FSAL {
Name = VFS;
}
}

EXPORT
{
Export_Id = 78;
Path = /opt;
Pseudo = /opt;
Access_Type = RO;
FSAL {
Name = VFS;
}
}

改完重启服务:

1
systemctl restart nfs-ganesha.service

客户端

  • Debian

1
2
3
4
5
6
7
8
9
sudo apt install nfs-common

showmount -e u1
Export list for u1:
/root/share (everyone)

sudo mkdir /mnt/share
# sudo mount -t nfs u1:/root/share /mnt/share
sudo mount.nfs u1:/root/share /mnt/share

注意:如果提示 mount.nfs: No such device,说明内核没有 NFS 模块,洗洗睡了,换 Windows 10 吧!

  • Windows 10

安装 NFS 客户端

浏览 NFS 共享目录

NFS 属性

注意:Windows 10 目前只有 NFS v3 客户端。服务端如果只开 v4 协议,则 Windows 10 将无法访问。

参考

https://github.com/nfs-ganesha/nfs-ganesha/blob/next/src/config_samples/config.txt

https://github.com/nfs-ganesha/nfs-ganesha/blob/next/src/config_samples/export.txt

解决通过 TTL 登陆 Armbian 时的 Login incorrect

问题

用 TTL 连接刷 Armbian buster 的盒子,然后用 putty 和 plink 登陆,一输入 root 回车,就报 Login incorrect!但通过 ssh 远程登陆却没任何问题。

原因

ttyAML0 不在 /etc/securetty 里。

分析过程

一开始听运维小伙伴说:最可能的原因是键盘 Caps 开启了。轻松排除。

后来发现输入大写的 ROOT,反而提示输入 password,这让稣想到“枚举用户”攻击。开始思考,是不是通过 TTL 登陆被 Armbian 认为是不安全的?

于是学习 securetty 相关知识,发现确实在某些不安全场合 root 是不被允许登陆的,因为系统管理员一旦通过不安全渠道输入密码,那么密码就可能被盗取,所以一输入 root,就应该立刻报错,而不该继续让输入密码。而输入其它不存在的用户时(比如大写的 ROOT),反而应该让继续输入密码,最后再提示登陆失败,因为如果提示用户不存在,会让黑客穷举出系统里有什么账号。

1
2
3
grep securetty /etc/pam.d/login
# Disallows root logins except on tty's listed in /etc/securetty
auth [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] pam_securetty.so

后来注意到 TTL 用的 tty 名字是 ttyAML0,grep ttyAML0 /etc/securetty 果然不存在。

解决

echo ttyAML0 >> /etc/securetty 搞定。

让 git 使用 Windows 10 OpenSSH

问题

在 Windows 10 安装 git 的同时,开启系统自带的 OpenSSH,则系统里存在两套 ssh,git 会默认使用它自己的那套。

分析

由于 Windows 10 的 sshd、ssh-agent 做成服务,比较容易管理,而且微软改造的版本会更注重安全,所以 UMU 决定舍弃 git 带的那套。

解决

思路:把 git 那套 ssh 指向 Windows 10 OpenSSH。

用管理员权限运行 cmd,输入:

1
2
3
4
5
6
cd "%ProgramFiles%\git\usr\bin"
ren ssh.exe -ssh.exe
mklink ssh.exe %windir%\System32\OpenSSH\ssh.exe

ren scp.exe -scp.exe
mklink scp.exe %windir%\System32\OpenSSH\scp.exe

git ssh 链接到 OpenSSH

TTL 线连接树莓派

需求

有以下物品:

  • PL2303 串口线(TTL 线)

  • Windows 10 PC

  • 树莓派 Model B

求:PL2303 串口线是好是坏?

解决

1. 使 Windows 10 正常驱动 PL2303

把 TTL 线插入 PC USB 口,Windows 10 会自动安装驱动,然而 Prolific USB-to-Serial Comm Port 版本 3.8.31.0 [2019/7/30]Prolific USB-to-Serial Comm Port 版本 3.8.18.0 [2017/10/17] 都不能正常工作。这说明 PL2303 芯片已经被淘汰……买 TTL 线请选当下流行的其它芯片。

Prolific USB-to-Serial Comm Port 版本 3.8.31.0 [2019/7/30] 不能正常工作

Prolific USB-to-Serial Comm Port 版本 3.8.18.0 [2017/10/17] 不能正常工作

搜“PL2303_Prolific_GPS_1013_20090319”,装“Prolific USB-to-Serial Comm Port 版本 3.3.2.105 [2008/10/27]”,可正常工作。

Prolific USB-to-Serial Comm Port 版本 3.3.2.105 [2008/10/27] 可正常工作

2. 配置串口

波特率设为 115200,其它默认,最终参数如图:

串口参数

高级

3. TTL 线连树莓派 GPIO

如果树莓派有用 MicroUSB 供电,则 VCC(红)可以不接,只把 GND(黑)、RX(白)、TX(绿)分别接到树莓派的 P6、P8、P10;如果需要直接用 GPIO 供电,则把 VCC(红)插到 P2 或 P4。

PL2303 接线颜色 树莓派 GPIO 树莓派针脚
VCC 5V P2, P4
RX GPIO14(UART_TXD) P8
TX 绿 GPIO15(UART_RXD) P10
GND GND P6, P39

GPIO

TTL 接 GPIO:红接 1 或 2,黑接 3,白接 4,绿接 5

树莓派 GPIO 供电

4. 用 PuTTY 连接串口

使用 plink 或 putty 皆可,注意:需使用管理员权限运行。

1
plink -serial \\.\COM1 -sercfg 115200,8,n,1,n

plink

PuTTY

COM1 - PuTTY

相关

在树莓派上编译 go-ipfs

跟 UMU 一起玩 OpenWRT(入门篇18):更换 opkg 源

问题

默认源在国内访问速度普遍比较慢。

PS: 本篇理论上应该几年前就写的……以前经常用台湾省的网络,没发现,现在补一篇。

解决

  1. 更换清华大学源

1
2
sed -i 's/downloads\.openwrt\.org/mirrors\.tuna\.tsinghua\.edu\.cn\/openwrt/g' /etc/opkg/distfeeds.conf
opkg update

  1. 使用 https

1
2
opkg install libustream-mbedtls
sed -i 's/http:/https:/g' /etc/opkg/distfeeds.conf

相关

如果您想把整个软件源下载到本地,可以参考:https://github.com/UMU618/openwrt-opkg-cache