
本文对 https://ping0.cc/ 的前端代码进行抓包与逆向分析,重点关注其是否存在收集用户真实 IP 及浏览器指纹的行为。
使用 Reqable 工具抓包,过滤请求体中含有 IP 地址的请求,发现如下可疑端点:
POST https://ping0.cc/ip/peer HTTP/2
{
"ip": "xxx.xxx.xxx.xxx", // 用户真实公网 IP
"size": "879-769" // 浏览器窗口尺寸
}
HTTP/2 204
通过 Chrome DevTools 的 Initiator 追踪,请求来源为:
https://cdn.ping0.cc/js/check.js
下载 check.js,发现经过 jsjiami.com.v7 混淆处理。以下为混淆后的原始关键方法片段:
'peer'() {
const _0x240196 = _0x35e62e,
_0x4a0fc3 = {
'qnArj': function(_0x1b0c8c, _0x599205) { return _0x1b0c8c === _0x599205; },
'CfeTI': _0x240196(0x2a1, '&]9^'),
'uxcbk': function(_0x47aa75, _0xb926d0) { return _0x47aa75 === _0xb926d0; },
'saKfi': _0x240196(0x1bf, 'l5GL'),
'KxNbH': function(_0x3ad146, _0x30d0ae) { return _0x3ad146 + _0x30d0ae; },
'Ahaer': _0x240196(0x208, 'EHcf'),
'FCfGW': _0x240196(0x23f, 'p7Wb'),
'gpjRN': function(_0x1758b5, _0x5b3737) { return _0x1758b5 === _0x5b3737; },
'DrvIp': _0x240196(0x2a8, 'H@6r')
},
_0x213f70 = window[_0x4a0fc3[_0x240196(0x2b7, 'H)5x')]];
if (_0x4a0fc3[_0x240196(0x1ef, '2RVF')](_0x213f70, undefined)) {
this[_0x240196(0x1c9, 'nGax')] = window[_0x240196(0x1a3, '[1jt')];
return;
}
const _0x475906 = new _0x213f70({ 'iceServers': [{ 'urls': _0x4a0fc3[_0x240196(0x2b6, 'fr9d')] }] });
_0x475906[_0x240196(0x239, 'M0YP')] = _0x365e93 => {
const _0x507ee3 = _0x240196;
if (_0x365e93[_0x507ee3(0x1ad, 'YS0[')]) {
if (_0x4a0fc3[_0x507ee3(0x1f1, '4Qn*')](_0x4a0fc3[_0x507ee3(0x2a7, 'tqEz')], _0x4a0fc3[_0x507ee3(0x29a, 'J^P0')])) {
const _0x22ec76 = _0x365e93[_0x507ee3(0x1c7, 'nGax')][_0x507ee3(0x2b1, 'H)5x')][_0x507ee3(0x1ff, 'ni%P')](' ')[0x4];
if (_0x4a0fc3[_0x507ee3(0x252, '3LK)')](_0x22ec76[_0x507ee3(0x1a5, '[1jt')](_0x4a0fc3[_0x507ee3(0x248, '#IR7')]), -0x1) && !this[_0x507ee3(0x2a3, '$aHF')](_0x22ec76)) {
const _0x13b720 = _0x4a0fc3[_0x507ee3(0x1e0, '*qsc')](_0x4a0fc3[_0x507ee3(0x28c, 'l5GL')](window[_0x507ee3(0x1e1, '[1jt')], '-'), window[_0x507ee3(0x2b3, 'iMDv')]);
axios[_0x507ee3(0x1d3, '4Qn*')](_0x4a0fc3[_0x507ee3(0x23e, 'ZAzr')], { 'ip': _0x22ec76, 'size': _0x13b720 });
}
} else _0x2588e5[_0x507ee3(0x213, 'SZ(Q')](_0x390073);
}
};
_0x475906[_0x240196(0x1b6, 'f01U')]({ 'offerToReceiveAudio': !![] })[_0x240196(0x1b2, 'H)5x')](_0x2c7a44 => _0x475906[_0x240196(0x24b, 'J^P0')](_0x2c7a44));
}
借助 AI 辅助反混淆,还原后的逻辑如下:
peer() {
// 获取浏览器 RTCPeerConnection API
const RTCPeerConnection = window.RTCPeerConnection;
// 若浏览器不支持 WebRTC,则 fallback 到 window.ip
if (RTCPeerConnection === undefined) {
this.newaddr = window.ip;
return;
}
// 创建 RTCPeerConnection,使用 Google 公共 STUN 服务器
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
// 监听 ICE 候选事件
pc.onicecandidate = (event) => {
if (event.candidate) {
// 从 SDP candidate 字符串中提取第 5 个字段(即 IP 地址)
const ip = event.candidate.candidate.split(' ')[4];
// 过滤条件:必须包含点号(IPv4),且不属于保留地址
if (ip.indexOf('.') !== -1 && !this.isreserve(ip)) {
// 构造 size 参数(窗口宽 x 高)
const size = window.innerWidth + '-' + window.innerHeight;
// 上报至服务端
axios.post('/ip/peer', { ip: ip, size: size });
}
}
};
// 触发 ICE 候选收集流程(必须先创建 Offer)
pc.createOffer({ offerToReceiveAudio: true })
.then(offer => pc.setLocalDescription(offer));
}
该方法挂载于 Vue 组件的 created() 生命周期钩子中,即页面加载完成后立即静默执行,无需用户任何交互操作。
WebRTC(Web Real-Time Communication)是浏览器内置的点对点通信协议。在建立连接时,浏览器需要通过 ICE(Interactive Connectivity Establishment)协议收集本地候选地址,其中包括:
该站点利用 onicecandidate 事件回调截获 srflx 类型候选,从 SDP 字符串中提取出真实公网 IP,绕过了用户的代理设置(仅限非 TUN 模式的软件级代理)。
ICE Candidate SDP 字段结构示意:
candidate:xxx 1 udp 2122260223 203.0.113.45 54321 typ srflx ...
↑ 第5字段 = 真实公网 IP
| 字段 | 内容 | 推测用途 |
|---|---|---|
ip |
用户真实公网 IP | 验证代理是否泄漏、判断用户地区 |
size |
浏览器窗口尺寸 | 浏览器指纹的组成部分 |
浏览器窗口尺寸本身是常见的指纹识别维度之一。结合真实 IP,可用于:
完全防护方案:
浏览器级防护:
about:config 中设置 media.peerconnection.enabled = false 直接禁用 WebRTC注意: 仅使用 HTTP/SOCKS 代理(非 TUN 模式)无法阻止此类泄漏,因为 WebRTC 走的是系统底层网络,不经过代理软件。
本文内容基于对公开 JS 文件的技术分析,所有观点仅代表作者个人理解,不构成对该网站的任何法律指控。读者可自行审阅源码进行验证。