Zeka 的记事本

2021 年 1 月
红米 AC2100 开启 SSH 权限
2021129 日说: 刷机 路由器

TL;DR 看这里,成功版本为 2.0.23 稳定版

之前按照 这篇 想开启路由器的 telnet 权限。

dalao 写了一些脚本,但是遇到了很多问题,包括但不限于:

  • 由于忘关火绒导致 nc 被删除,弹出 nc.exe不是内部或外部命令,也不是可运行的程序或批处理文件。8081端口被占用,请检查。解决方法为进入火狐的恢复区恢复文件,并添加信任,以免误删。
  • 获取包的时候脚本报错,但还是提示 PPPOE 获取成功。这个问题原因为演示版本为 1.0.14,而新路由器被官方自动升级为了 2.0.23,方法已不再适用。解决方法为从 官方救砖工具以及原厂固件备份 文件夹中手动上传 1.0.14 包升级 ROM。
  • 第一次失败,无法继续。解决方法为断开 WAN LAN 口相连线,然后重试。

简单看了一下,dalao 应该是开启了一个 http 服务器(python -m http.server),然后在反弹 shell 那几个阶段从本地服强制覆盖官方文件。然后之所以破解包那么大是因为居然有整个 python,,,

爪巴了。

突然看到 帖子说 有 XSS 注入漏洞可以钻,试了下直接拿下了 SSH 的 root 权限:

首先注销登陆的账户,然后重新登陆,这时观察浏览器顶上的 url,会有 stok 的参数,这就是 token 啦。有了 token 按照帖子里的办法就办法注入了,只需要两次就行。一次开启 SSH 功能,第二次是将密码修改为 admin。第二次是可选的,但是不这样的话需要找到无序的默认密码,不方便我们后续操作。

去掉 # 开头的注释,把链接复制到浏览器里面,注意把 <STOK> 替换为自己的。

# enable SSH

http://192.168.31.1/cgi-bin/luci/;stok=<STOK>/api/misystem/set_config_iotdev?bssid=Xiaomi&user_id=longdike&ssid=-h%3B%20nvram%20set%20ssh_en%3D1%3B%20nvram%20commit%3B%20sed%20-i%20's%2Fchannel%3D.*%2Fchannel%3D%5C%22debug%5C%22%2Fg'%20%2Fetc%2Finit.d%2Fdropbear%3B%20%2Fetc%2Finit.d%2Fdropbear%20start%3B

# (optional) change password to `admin`
http://192.168.31.1/cgi-bin/luci/;stok=<STOK>/api/misystem/set_config_iotdev?bssid=Xiaomi&user_id=longdike&ssid=-h%3B%20echo%20-e%20'admin%5Cnadmin'%20%7C%20passwd%20root%3B

然后就可以愉快的登陆啦(密码为 admin

当然可以直接拿这个注入漏洞修改很多东西www,不过这样还是太麻烦了,所以直接拿到 SSH 权限上去吧。

img 标签底下的一点点空白
2021127 日说: CSS 前端

最近突然发现站点的 img 标签不能占满全部空间,而是留下了一层空。指定 height:100% 没有效果。

搜了下,在 这里 找到了答案。只需将图片的 display 设置成 block 就好了。

简单来说,图片默认的 display 属性为 inline 而不是 block。这导致了其展示与文字类似(联想图片可以通过浮动设置文字环绕)。

默认的文字排版方式为 baseline,这个排版的影响一般只体现在 西文 中。因此图片多出来的部分恰恰就是它的小尾巴。因此另一种解决方式为设置 vertical-alignbottom

图片没有占用全部空间,留出了一层空隙
从 Nuxt 查看 CORS 行为
2021126 日说: 前端 网络

CORS 是浏览器为了避免恶意请求做出的一项保护措施。其行为由浏览器控制,而决定是否放行则由服务器添加相应请求头,由浏览器检查完成。

换句话说,如果没有浏览器的限制,正常的请求是不可能出现 CORS 问题的。

Nuxt 恰好是 SSR 框架,也就是说部分请求是在服务端完成的,部分请求是在浏览器完成的。可以考察这一限制。

在服务端的请求不受到浏览器影响,因此只要接口无误,就能保证渲染出来。但是在浏览器就没有那么简单了。

要弄明白的一点是,对于前后端开发,CORS 基本是由后端配置的,前端只需要关心能否请求到数据。前端不需要关系的原因为浏览器自动完成了所有操作。而后端则往往由对应模块完成。

步骤大概是这样的(阮老师的教程 写的很详细):

前端发起请求,浏览器检查请求头,查看是否为简单请求(简单来说,header 足够简单,请求局限于 POST 和 GET,默认是不带 cookie 的)。

如果是简单请求,则浏览器在 header 添加 origin,并说明 origin(域名)。之后再检查服务器的回应,不管服务器响应的 body 是什么,只检测 header,如果没有出现 Access-control-allow-origin 字段,或者不合法,那么此次请求就会被丢弃。也就是说数据到了浏览器,但是被浏览器拦截了,不允许 JS 拿到数据并进一步动作。如果存在请求头,则数据正常到达,表现和没有跨域一样。

如果是非简单请求,则浏览器首先发出的不是我们的请求,而是一个 OPTIONS 类型的请求(请求自然不带响应内容)。对于有 CORS 模块的后端,会返回几个字段:

  • Access-Control-Request-Method
  • Access-Control-Request-Headers
  • Access-Control-Allow-Origin

非简单请求与简单请求的区别就是方法与 header,这也就是预检请求的意义。预检请求检查方法与响应头是否允许。如果不被许可自然就拦截了。如果通过,就再发起请求。也就是说需要请求两次。为了不频繁发送请求,浏览器会对预检请求进行缓存,从而避免多次 OPTIONS 请求。

此外不论是简单请求还是非简单请求,对 cookie 都有更严格限制,比如 Access-Control-Allow-Origin 必须详细指定域名,不能是宽泛的 *,还需要加上额外的 Access-Control-Allow-Credentials = true 的 header。

对于正确配置了 CORS 的后端,会按照期望进行响应。接下来分析下没有配置 CORS 的后端服务器行为。

对于没有配置 CORS 的后端,有什么请求就会有什么响应。最大的特点是一定不会有 CORS headers。对于简单请求,即使数据送到了浏览器,浏览器也会拦截并丢弃。对于非简单请求,OPTIONS 请求后端无法进行正确答复,请求无法继续,即使后端进行了正确响应,浏览器也会拦截。

CORS 只能阻止接口被滥用。假设有一台正确配置了 CORS 的银行服务器 A ,那么恶意页面 B 就无论如何无法对 A 发送合法的转帐请求,从而避免了安全事故。

但是 CORS 不是万能的。比如它无法防范信息收集。毕竟信息收集的服务器是由坏人控制的,他知道要配置好 CORS 从而窃取用户信息。此外,如果对于本站点发动了 XSS 攻击,那么请求是不会触发 CORS 的,浏览器无法拦截。

对于不能让其他页面随随便便调用的 API(比如用户数据),CORS 应该尽可能阻止请求,只运行在自己的域名上使用。而对于一些开放接口(比如 一言 的接口),它们需要能够在不同页面展示,就需要设置所有域名允许访问了。

上面是在应用层次。随着前后端分离的影响,数据接口与页面接口渐渐分离,导致了跨域问题频发。对于简单页面,虽然部署时可以通过 nginx 统一,但是开发的时候还是经常遇到。对此的解决方案是后端安装并正确配置 CORS 即可。此外,虽然是开发环境的解决方案,但是完善 CORS 总归是没错的,因此开发完成后不需要移除 CORS(当然还是需要合理配置)。

替换 node-sass 为 dart-sass
2021125 日说: 前端

在这里 曾提到过 node-sass 兼容性问题,对此官方的做法是换用 dart-sass。

替换只需要两行就行了:

yarn remove node-sass
# dart-sass package name 
yarn add sass 

安装会丝滑很多。

修复 KDE 主题 WhiteSur Aurorae 底部边界透明问题
2021124 日说: KDE Linux

对于 4k 屏幕来说,各种主题兼容性都不太好,往往存在缩放问题。为此 WhiteSur Aurorae 主题提供了不同的缩放比(1.25, 1.5, 2)。

对于我的屏幕,1.25 倍缩放比是合适的,但是出现了透明的边框。而作者提供的另一种主题 Sharp 消去了底部边框,所以没有这个问题。但是很可惜的是 Sharp 主题没有提供不同的缩放比,也就是说要么把 Sharp 改造成 1.25 倍的,要么将 1.25 倍的白边修复。

打开主题文件夹,发现主要装饰是一个矢量图(decoration.svg)。不知道是出于疏忽还是怎么,这个矢量图底部是透明的,于是一下子就找到了问题,只要修改填充颜色即可。

由于手头没有 SVG 编辑工具,于是暴力拿浏览器打开,找到相应的样式:

opacity: 0.6;
fill: #f5f5f5;
fill-opacity: 1;
stroke: none;
stroke-width: 1.41421;

不知为何设置了 opacity,以及与主题不同的颜色,修改成如下即可:

opacity: 1;
fill: #ffffff;
fill-opacity: 1;
stroke: none;
stroke-width: 1.41421;

这样的地方一共需要修改三处,修改完成后重启就完成了。

但是这样还是有一个 bug,当处于 deactivate 模式时,透明度又来了。

主题透明底部
/static/55c7fe5411ff902ffadd1dfae2dc8ea7/7902e/screenshot_20210124_101025.jpg
Ubuntu 配置 dhcp
2021118 日说: 网络 Linux

Ubuntu Server 18 LTS

试了试刀片服务器,由于安装的时候没有链接网络,所以安装的时候没有进行网络配置。安装好后虽然能上去,但是不能正常上网。

在 Ubuntu 17 之后,配置网络的文件改为了 netplan

# automatically generate config file
# file locate at /etc/netplan/*.yml
sudo netplan generate

生成的配置就是 .yml 文件。 在修改这个文件前,首先要检查网络接口名称,使用 ifconfig -a 指令可以看到所有网络接口,直接运行 ifconfig 可以看到正在运行的接口。

这台服务器有四个网口,所以在使用的时候需要注意区分。在使用网口一(ifconfig 得到名称为 eno1)的时候需要配置网口一。

network:
  ethernets:
    eno1:
      addresses: []
      dhcp4: true # 开启 dhcp,使用动态 ip 分配
    # other configurations
  version: 2

之后就是检查与应用了:

sudo netplan try
# success
sudo netplan apply

之后就能配置完毕了。

使用 mutt wizard 配置 gmail
2021117 日说: Linux

参考的是 Mutt 终端邮箱,gmail 流程稍有不同。

使用顺利,但是在生成完密钥,输入帐号密码登陆 gmail 的时候出现了问题:

mw -a [email protected] -x "mypasswd"

提示密码错误,按照某个 issue 提到的,密码中的特殊字符需要转义,如 ! 需要转化为 \!。测试后失败。

再依据 这个 issue 提到的,由于我的 gmail 开启了多步验证,所以不允许任意 app 登陆。mutt 作为一个 app,需要使用 google 生成一个 app token 登陆(也就是以 app 形式登陆,参考 OAuth 模型,不直接使用主密码有利于防止第三方客户端滥用帐号的全部权限)。

这里 下面的 “App passwords” 申请一个密钥,中间要求输入帐号密码确认,确认后点击生成就能获取一个全新的密钥。

再使用这个密钥登陆就可以啦:

mw -a [email protected] -x "apppasswd"
Nginx 反代 jupyter
202118 日说: Nginx 网络

Nginx 反代理 jupyter 可以让控制更灵活(并不。

为了安全,jupyter 有一些基础的限制,比如默认开在本地,不允许远端访问等等。要想使用 nginx 反代,必须加以设置。

Nginx 的设置文件 参考这篇

添加所需 header 就没有问题了。

此外,jupyter 需要打开远端访问设置。首先初始化配置:

jupyter notebook --generate-config

初始化后的文件默认保存在 home 目录下的 jupyter_notebook_config.py 中。文件里面有很详细的注释了。对于开启 Nginx 访问,需要允许远端访问:

c.NotebookApp.allow_remote_access = True

默认是通过 token 访问的,这样会在 url 后跟随一长串的 token,不美观,访问也不方便。为此设置密码访问:

jupyter notebook password

确认密码就好了。之后只要将域名,证书配置正确就能正常运行了。

记一次 crontab 配置错误引发的灾难
202114 日说: Linux

书写 cron 定时同步脚本时,时间参数设置出现了错误:

* * 17 * * *
# except:
# 0 0 17 * * *
# or:
# 0 17 * * *

由于第一位(秒钟)与第二位(分钟)设置为了 *。导致 crontab 在 17 点时每过一秒执行一次任务。刚好同步脚本用到向 telegram 发送信息,于是出现了对 telegram 的小 d。在我还没意识到发生什么的 51 分钟里,telegram channel 已经出现了 1k+ 条消息(由于本地程序执行也需要时间,所以实际上不可能一秒执行一次)。

换作其他平台(比如莫名其妙把我的开发者号封了的 twitter),这个 bot 肯定是废了。但是 telegram 仍然正常运行。

也许这次没有触碰到 telegram 的上限?毕竟换算下来频率大概 3s 请求一次。

不过不管怎样,也提醒了我 cron 时间设置一定要检查好。毕竟 cron 执行本身是无声的,很有可能出现错误配置却没有发现的情况。

找了找,果然发现了 cron 可视化网站(crontab.guru)。它支持将书写好的时间转化为人类可读的格式,从而帮助你发现错误。

举例来说,将我的错误时间放上去,会提示:

# * 17 * * *
At every minute past hour 17.

这样一下就知道出错了。

/static/3cb9644c58ebb7c8c97cdfcfe20f79d2/03908/screenshot_20210104_115457.jpg
Deno 与 top level await
202112 日说: 前端 Deno

单线程 JavaScript 能获得成功的一大原因在于其强大的异步能力。而作为 Node 的 “纠错者”,Deno 提供了更完善的异步支持。async await 满天飞

一个特征是 Deno 支持 top level await。

top level await 特性似乎仍然处于提案阶段,各大浏览器似乎除了最新版 Chrome 没有支持的。

相信学习 JS 的人一开始对于 JS 没有 sleep 函数感到困惑。你无法直接让程序休眠一段时间,JS 只提供了 setTimeoutsetInterval 方法。这是为了防止主线程阻塞。对于单线程程序,运行在浏览器里的 JS 主线程发生阻塞将会发生无法预知的效果。为此,浏览器只支持回调形式的延时,Top level 的执行在没错误的情况下是不会阻塞的。

但是有了而 top level await 之后,sleep 就变得顺理成章了:

// define sleep function
const sleep = (sec: number) => {
    return new Promise((reslove) => {
        setTimeout(reslove, sec);
    });
};

// use in top level
console.log('execute at time 0');
await sleep(3);
console.log('execute at time 3');

有人说 Top-level await is a footgun,理由也很简单,阻塞了主线程执行,可能一个再正常不过的请求就会导致执行阻塞。

当然,只要加个 wrapper,就能实现伪 top level await 了 机智的地球人

(async()=>{
    // everthing here!!!
    // I am top level :)
    await sleep(3);
})();

使用自然见仁见智了,但是如果运行环境不是浏览器,那么 Deno 的这个特性将会带来不少便利。比如上面提到的 sleep 函数,以及异步迭代器(for await),请求发送等等。