万通驿馆的无线网络是需要用户名密码进行认证的。在 Windows 上,只要连接上 “Vantone Inn” 这个接入点,就会弹出浏览器窗口,显示用户认证界面。访问任意 HTTP网址,也会跳转到认证界面。认证成功后,无线网络就畅通无阻了。今日某同学问我,这个接入点怎么这么NB,能强制电脑弹出窗口,莫非是病毒?

这个问题我也不知道,于是就抓了抓包,然后 Google 了一把。为了便于理解,我们先不讨论自动弹出浏览器,先想想如何实现“访问任意 HTTP 网址都会跳转到认证界面”。

访问一个 HTTP 页面有哪些步骤呢?

  1. DNS查询出域名的IP地址
  2. 连接这个IP的80端口
  3. 发送 GET /path/to/page HTTP/1.1 请求
  4. 接收返回的HTML,显示在浏览器上
    我们看到,未认证时访问任何 HTTP 页面,都会跳转到 http://172.16.1.2/webAuth/index.htm?http_referer 这个认证界面(其中 http_referer 是试图访问的URL),输入用户名密码认证成功后,就会跳转到 http_referer 这个之前试图访问的网址。

要劫持正常的浏览器访问,有两种办法:

  1. 劫持DNS解析,对所有DNS请求一律返回认证服务器的IP。这种办法无法应付直接访问IP地址的情况。
  2. 劫持路由,将未认证用户的80端口请求全部路由到认证服务器,认证服务器伪装成目标IP返回认证页面。据猜测,万通驿馆用的是这种方案。
    具体到万通驿馆所用的实现,未认证用户的80端口请求,除了白名单中的IP(当然要包括172.16.1.2),全部返回 HTTP 302 redirect,跳转到 http://172.16.1.2/webAuth/index.htm?http_referer,其中 http_referer 是 HTTP header 中实际请求的URL。

细心的同学可能发现了,未认证时访问 Google、Gmail 等 HTTPS 网页是无法连接的,而不会跳转到用户认证界面。这是因为接入设备只把未认证用户的80端口请求送到了认证服务器,其他端口(除DNS要用的53端口外)的请求一概 drop packet,拒绝访问。HTTPS 是 443 端口,因此无法连接。

有人会问,为什么不劫持 HTTPS 呢?因为 HTTPS 是要验证服务器证书的,上网认证系统显然拿不到 Google 的私有证书,当然就无法劫持了。正是这种证书签名机制,使得中间人攻击成为不可能,从而保证了 HTTPS 连接的可信性。

现在回到开头提出的“弹出浏览器”。事实上,这是操作系统与上网认证系统达成某种“默契”的结果,因为这种被称为 Captive Portal 的 Web 认证技术在公共 wifi 接入点已经被广泛使用了。

我们知道 Windows 会在连接上网络时提示“Internet 连接”、“连接受限”等。Windows 怎么知道是不是 Internet 连接呢?Windows 在连接上一个网络后,会自动访问 www.msftnsci.com,能返回正确结果就是 Internet 连接了。而在使用了 Captive Portal 的公共 wifi 上,返回的是一个302,显然不是正确的结果,因此会显示“连接受限”。在 Windows 7 或 Windows 8 上,遇到这种情况系统会自动弹出默认浏览器,然后用户就看到了上网认证页面。

在 Android 4.0 以上系统中,连接 “Vantone Inn” 接入点也会提示 “网络连接需要验证” 系统信息,点击进入浏览器的上网认证页面。这个原理与 Windows 是类似的。好在 Android 是开源的,我们可以从源码 [1] 里一探究竟:

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
private final static String mWalledGardenUrl = "http://clients3.google.com/generate_204";

private boolean isWalledGardenConnection() {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(mWalledGardenUrl); // "http://clients3.google.com/generate_204"
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
// We got a valid response, but not from the real google
return urlConnection.getResponseCode() != 204;
} catch (IOException e) {
if (DBG) {
log("Walled garden check - probably not a portal: exception "
+ e);
}
return false;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}

Android 访问了 http://clients3.google.com/generate_204 ,如果返回的是 HTTP 204 则认为是正常连接,否则就认为是需要认证的,并在系统消息栏弹出提醒。用户点击这个提醒,访问上述网址,会被上网认证系统重定向到登录页面。Chromium OS 官方文档对这个过程有更详细的描述 [5]。(Chromium OS 跟 Android 都是 Google 的亲儿子)

美中不足的是,登陆成功后返回登录前页面,结果是个 204 No Content,于是浏览器可能就停留在登录页面上了,好像没有反应一样。这点 Windows 比 Android 做得好,访问 www.msftnsci.com/some_url (具体我忘了),会跳转到 bing search,起到了产品推广的效果。最初,我还以为是微软跟万通驿馆有合作呢。某同学甚至抛出了“微软监控实习生住处网络”的阴谋论论调,看来这个证据不成立。

我们还会发现断掉 wifi 重连,只要在一定的时间间隔内,就不用再次进行认证。一种最简单的猜测是,DHCP 服务器在没有冲突的情况下,一般会给相同的 MAC 地址分配相同的 IP 地址,而认证系统的规则是有一定超时的,因此在超时前重连就又用上了已有的规则。当然,也可能采用了更完善的机制,保存认证者的 MAC 地址,断线重连后刷新对应的规则。因为我的 IP 地址一直没变,因此无法验证哪种假设是对的。

顺便说一句,万通驿馆的 wifi 其实有两个漏洞:允许未认证用户到任意 IP 的 ICMP 包和 53 端口的 UDP 包。允许 ICMP 包没有什么道理,可能是开发人员未考虑到;允许 UDP 53 端口是为了 DNS 解析,但它没有限制只允许自己的 DNS 服务器。只要在外面有一台工作在 53 端口的 OpenVPN 服务器,就能连接上 VPN,进而突破 wifi 访问限制。类似地,也有 IP over ICMP 这种利用 ICMP 漏洞的协议。相比之下,CMCC 的无线热点就没有这两个漏洞,国企就是国企啊~

References:

  1. http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/net/wifi/WifiWatchdogStateMachine.java#WifiWatchdogStateMachine.isWalledGardenConnection%28%29
  2. http://stackoverflow.com/questions/13958614/how-to-check-for-unrestricted-internet-access-captive-portal-detection
  3. http://www.cisco.com/en/US/tech/tk722/tk809/technologies_configuration_example09186a008067489f.shtml
  4. http://en.wikipedia.org/wiki/Captive_portal
  5. http://www.chromium.org/chromium-os/chromiumos-design-docs/network-portal-detection

Comments

2013-07-25