某网站的极验验证码实战
字符型验证码
SimGAN-Captcha代码阅读与复现 关于SimGAN-Captcha的扩展实验 全都得死:GAN掉字符验证码
小试牛刀
先尝试模拟,使用 puppteer 稍加计算就能成功绕过极验
请求依次为
- POST https://passport.bilibili.com/x/passport-login/sms/send 设备信息为 body,返回 recaptcha_url 随即向其发起请求,注意该 url 中的 gt 和 challenge 将用于后续一系列请求
- GET https://www.bilibili.com/h5/project-msg-auth/verify?ct=geetest&recaptcha_token=&gee_gt=&gee_challenge=&hash= 即向之前获得的 recaptcha_url 发送 GET 请求跳转到页面
- GET https://api.geetest.com/gettype.php?gt=&callback=geetest_{13位毫秒时间戳} 返回一些配置参数如静态js文件的位置(即相对路径)
- GET https://api.geetest.com/get.php?gt=&challenge=&lang=zh-cn&pt=3&client_type=web_mobile&w={一长串}&callback= 仍然返回一些配置如验证开始前显示的i18n字符串
- GET https://api.geetest.com/ajax.php?gt=&challenge=&lang=zh-cn&pt=3&client_type=web_mobile&w=&callback= 第一次请求 ajax.php,返回
callback值({"status": "success", "data": {"result": "slide"}})
- GET https://api.geetest.com/get.php 此时刚加载了 slide.js,和第一次请求 get.php 相比多了一些 params 如
is_next=true&type=slide3
,返回结果中有滑块验证时会显示的i18n字符串,以及滑块和图片的位置 - GET https://api.geetest.com/ajax.php?gt=&challenge=&lang=zh-cn&%24_BBF=3&client_type=web_mobile&w=&callback= 最终的请求,返回 success 及 score
工程化探索
将验证码填充作为通用服务运行,让爬虫客户端无感绕过验证码,考虑在客户端和服务端(比如 puppeteer)之间使用 RPC,客户端先调起服务端,服务端进入验证码流程,但将所有请求拦截并通过 RPC 传递给客户端,客户端代为请求,响应结果作为 RPC 的返回值,服务端再强行将其作为响应,继续之后的动作,从而在验证方看来客户端正常完成了验证。
真正开发过程中,很多时间浪费在了数据类型造成的错误中,在 proto 中我把除状态码外的所有字段定为 string,但用 axios 等库发起请求时,header 为 object,且若不在请求时指定 responseType,所获响应默认用 json 解析成 object,否则才是 text。更坑的是图片等二进制数据,获得为文本时已经铸成大错,需要先指定 responseType 为 arraybuffer
(在 Node 中 blob
实际还是以文本返回,因为 blob
是 browser only),然后 res.data.toString('base64')
转成 base64 字符串通过 RPC 传递,接收方再 Buffer.from(str, 'base64')
来转成 buffer。
SEEDLab Network
SEED Labs 2.0 - Network Security
Firewall
使用 NetFilter 自制防火墙 LKM
Netfilter 是 Linux 内核中一个用于管理网络数据包的软件框架,可以使用它自制 Linux Kernel Module,实现简易的防火墙。
Task1 只是练习如何编译内核模块,即在 module_init(fn)
, module_exit(fn)
处初始化及退出。
使用 Netfilter 搭建防火墙的步骤:
- 定义 nf_hook_ops 结构体,给 hook(hook函数) 和 hooknum(hook点类型) 赋值
struct nf_hook_ops {
/* User fills in from here down. */
nf_hookfn *hook;
struct net_device *dev;
void *priv;
u8 pf;
enum nf_hook_ops_type hook_ops_type:8;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
-
模块加载时
nf_register_net_hook(&init_net, &hook1)
,卸载时nf_unregister_net_hook(&init_net, &hook1)
-
nf_hookfn
函数签名如下,实验中只需ip_hdr(skb)
获得 iphdr 结构体(类似有 tcphdr/udphdr),再从 iph 获得协议类型、源/目标地址,从 tcph/udph 获得端口号,比较决定是否 DROP 即可。
typedef unsigned int nf_hookfn(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state);
- 注意每个结构体只能赋值一个
hooknum
,想在多个点上 hook 需定义多个nf_hook_ops
,分别设置不同的 hooknum,枚举类型如下:
enum nf_inet_hooks {
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS,
NF_INET_INGRESS = NF_INET_NUMHOOKS,
};
使用 iptables
基本命令 iptables -A {chain} -j {rule}
,-i/o {dev}
指定入/出接口,-s/d
指定源/目标地址,-sport/dport
指定源/目标端口
对于 TCP 连接,使用 conntrack
模块搭建有状态防火墙,只允许已经建立的 TCP 连接和内部发起新连接
限流使用 limit
模块,--limit
指定设置最大频率(即如10次/分钟),--limit-burst
指定最大连续次数
负载均衡使用 statistic
模块,--mode
指定模式为 random
或 nth
,random
模式下 --probability
指定概率,nth
模式下 --every n
指定轮转周期,--packet p
指定初始计数值(即从[0,n-1]中某处开始计数),一般配合 -j DNAT --to-destination {ip:port}
使用
VPN_Tunnel
实验基于 TUN/TAP 技术,TUN 模拟网络层设备,TAP 模拟数据链路层设备,用户程序和操作系统可以通过 TUN/TAP 接口互相传递数据包。
Client Program send(ip) ->
Client TUN read(ip) ->
Client Socket send(udp/ip) ->
Server Socket recv(udp/ip) ->
Server TUN write(ip) ->
...
route to dst then got reply routed back
...
Server TUN read(ip) ->
Server Socket send(udp/ip) ->
Client Socket recv(udp/ip) ->
Client TUN write(ip) ->
Client Program recv(ip)