之前在 timeline 上看到一篇 2016 年的老文章 Detecting the use of “curl | bash” server side(档案馆备份),指出从服务器端可以检测出客户端是在用 curl
拉脚本还是在用 curl | bash
边拉边执行。如果检测到后者,可以返回不同脚本的内容以达到 “加料” 的效果。整个 blog 都干货满满,就是很久没更新了,站点开了 HSTS,(2023 年 3 月 12 日后)还不更新证书,正好让我学到了 Chrome(v111)在证书错误且不能跳过的警告界面盲敲 “thisisunsafe” 就可以强制跳过…
HTTP 是被 TCP 一块儿一块儿驮着在服务器端和客户端之间传输的,服务器端和客户端各有一块缓冲区,这里服务器端得先把己方发送缓冲区固定住。脚本的第一行可以用一条比较耗(客户端的)时的命令如 ping
、 find
等,文章 demo 的 py 脚本中以 sleep 3
为例,在此之后立即输出一大堆 NUL 空字符给客户端(使用 NUL 的原因是不会把命令行或浏览器弄得乱糟糟,但是可以被抓包或 curl --output
看到),每个 chunk 都与当前 Linux 系统的发送缓冲区大小相当,此时:
- 如果客户端是在用
curl
拉脚本,由于缓冲区大小被定住了,那么每个 chunk 的发送间隔应该差不多,因为不管脚本里面写了什么,curl
只是当文本拉下来; - 如果客户端在用
curl
边拉边交给bash
执行,由于bash
执行东西是一行一行的,上面的sleep 3
被拉下来交给bash
执行要停止传输 3 秒,服务器端就会检测到有那么一个 chunk 传输间隔时间明显超出其他 chunk 。
因为要排除网络波动的原因,demo 脚本中还使用了 numpy
库中的 std
求标准差的方法来进行统计运算,当找到那个偏离平均值很大的传输间隔时则认为客户端在用 curl | bash
。文中也指出客户端可以使用 curl | (sleep 3; cat)
来反制服务器端的上述行为。其实 curl
或 wget
拉下来看清楚再在本地执行就可以应对以上问题。
又是一个连自己的眼睛都不能相信的案例。