通过智能TTL锁定防止ISP广告、DNS劫持的一些想法

最近思考两个事情,一个是DNS劫持的升级,以往的方法失效了,另一个是铁通劫持了百度统计(hm.baidu.com)造成几乎全网的页面都被插广告。

劫持的原理都是抢先发送IP包,这样TTL一般会与正确的网站发来的数据不同。据抓包观察,几乎所有的劫持都没智能到使用伪造的正确TTL。大部分都是没有改TTL,抓到的是固定的,高级一点的如目前的DNS劫持会使用随机TTL。

很容易想到ping一下目标服务器,拿到正确的TTL,然后在路由器里用iptables的ttl模块做限制。但这样需要针对每个ip手动去ping出一个TTL。并且有时候TCP与UDP走的路由数并不一样,不是一个固定的值,而是一个范围,那么想要得到相对准确的范围就要手动去抓包。

所以我的想法是通过编程自动地进行这个工作,比如写一个iptables内核模块。

由于TTL是一个范围而不是定值,这个模块应该能足够地聪明,能够通过收集各个TTL出现的频率来计算出一个最佳的范围。这个算法的设计还是有点意(jiu1)思(jie2)的。

目前实验中遇到与解决或未解决的典型问题记录如下:

1、初始范围如何设定,不限制还是给一个+-5的范围。+-5的话在优酷的案例会失败(优酷的TCP第四个包会有两台主机重复发送,TTL初始值不一样,下面会详解)

2、线路路由调整。如果TTL在之前的计算中已经锁定,则路由调整后则没法再访问,除非重新计算。

3、如果为了解决问题2而增加计算频率,如何平衡性能损失。

4、大型网站具有网关结构,表现在IMCP、UDP、TCP的SYN等无连接数据由防火墙处理,建立连接后的数据包转发至真正的服务器处理。例如百度,SYN包的TTL会比数据包大2-4,这个还可以通过范围设定解决,更蛋疼的是例如优酷的案例,防火墙设定的TTL初值是256,而数据包的TTL初值是64,相差这么大但是哪个都不能被过滤,目前我的做法是区分无连接与有连接。

5、正常的随机TTL情况,例如114.114.114.114。如何与劫持包的区分。

开发内核模块比较麻烦,openwrt自带一个iptables-mod-lua模块,可以在内核里写lua脚本处理封包,可以先用这个做实验。不过似乎有一些限制,例如我写的时候没法使用table.insert、table.sort这些库函数(阅读了一下源代码,虽然PacketScript有编译string lib与table lib,但是在初始化state时没有(忘了?)调用lua open把这些库注册到全局变量中)。

贴出实验代码,以后还会更新:

iplist = {}

function process_packet(p)
	local saddr = p:data(packet_ip):saddr():get()
	local ttl = p:data(packet_ip):ttl():get()
	local tcp = p:data(packet_ip):data(packet_tcp)
	if tcp and not tcp:flags():get(15) then saddr = saddr .. ':tcp' end -- i dont know why syn become bit 15
	if not iplist[saddr] then
		print(saddr .. ',' .. ttl)
		iplist[saddr] = {count = 0, lb = 0, ub = 255, map = {}}
	end
	local data = iplist[saddr]
	if not data.map[ttl] then data.map[ttl] = 0 end
	data.map[ttl] = data.map[ttl] + 1
	data.count = data.count + 1
	if data.count == 100 or data.count % 1000 == 0 then
		if (data.count > 1000000) then
			data.count = 0
			for i, v in pairs(data.map) do
				data.map[i] = data.map[i] / 10
				data.count = data.count + data.map[i]
			end
		end
		
		local zero = 0
		local flag = true
		local block
		function ttlval(i)
			if not data.map[i] then return 0 end
			if data.map[i] < zero then return 0 end
			return data.map[i] - zero
		end
		while flag == true do
			local blocks = {}
			local i = 0
			flag = false
			while i <= 255 do
				while i <= 255 and ttlval(i) == 0 do i = i + 1 end
				if i > 255 then break end
				block = {lb = i, ub = i, count = ttlval(i)}
				i = i + 1

				while ttlval(i) > 0 or ttlval(i + 1) > 0 or ttlval(i + 2) > 0 do	--连续3个ttl为空认为可分块
					block.count = block.count + ttlval(i)
					i = i + 1
				end
				block.ub = i - 1
				blocks[#blocks + 1] = block
			end
			block = nil
			for i = 1, #blocks do
				if not block then block = blocks[i] end
				if blocks[i].count > block.count then block = blocks[i] end
			end
			if block.ub - block.lb > 9 then
				local av = block.count / (block.ub - block.lb + 1)
				for i = block.lb, block.ub do
					if ttlval(i) > av * 2 then
						zero = av
						flag = true
						break
					end
				end
			end
		end
		data.lb = block.lb
		data.ub = block.ub

		print(saddr .. ',' .. data.lb .. '-' .. data.ub)
	end
	if (ttl < data.lb or ttl > data.ub) then return NF_DROP end
	return XT_CONTINUE
end

使用很简单:iptables -t mangle -A PREROUTING -i pppoe-wan -j LUA –script ttl.lua

Win7下借助VirtualBox虚拟机和承载网络构建无线软路由

为什么需要Linux路由呢,因为那啥啥啥的原因,需要进行那啥啥啥,只有使用iptables、ipset这些工具才能做到完美透明那啥。

为什么要用虚拟机做软路由呢,因为有时候需要将电脑带出去,显然不能就为了个那啥还要在书包里放个路由器。尽管自己组装了个U盘大小的WR703N模块美名曰那啥神器,但没有精力去专门为那啥设计固件,也没设计reset键,万一配置的时候玩坏就把自己墙了。

关于那啥的配置之前已有多篇文章详细写,请自行翻阅。这里只写虚拟机和Windows的配置。

关于虚拟机,首先你需要装个Linux。当然你可以直接用OpenWrt的x86版,但是笔者因为本身就经常使用Linux,所以就直接在常用的发行版上配置了。

为了实现路由功能,我们需要给虚拟机装两个网卡。我们设计第一个网卡eth0用来当wan,第二个网卡eth1用来当lan。在VirtualBox的网络设置里,将网卡1设为桥接网络桥接到电脑用来上网的网卡上(本地连接或无线网络连接),将网卡2设为仅主机(Host-Only)网卡。关于网卡1设为桥接网络而不是NAT网络,因为我们一会儿要让电脑通过虚拟机上网,而让虚拟机直接从上级路由分配IP,NAT的话就反了。关于网卡2,Host-Only就相当于一根网线的两端,一会儿我们就让电脑通过这个网卡连接到虚拟机。

在虚拟机里首先设置两个网卡,例如debian的配置文件是/etc/network/interfaces,设置后/etc/init.d/networking restart。本例中我的配置为:
auto eth0
iface eth0 inet dhcp
auto eth1
iface eth1 inet static
address 192.168.56.1
netmask 255.255.255.0

在通常的桌面发行版中,linux默认是没有开启ip转发功能的,编辑/etc/sysctl.conf将net.ipv4.ip_forward=1取消注释,重启后生效。若想即时生效可以执行一句echo 1 >/proc/sys/net/ipv4/ip_forward。另外,我们使用的路由实际上是NAT网关,需要添加iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE规则以开启NAT转发。

在windows网络设置里,需要将本来用于上网的网卡(本地连接或无线网络连接)的IP地址设为无法上网的地址(如169.254.x.x,默认网关空),否则本机会从上级路由分配到IP与默认网关,这会让windows路由表困惑。然后,由于本例中我没有在虚拟机里安装DHCP服务,故将Host-Only网卡设为固定IP地址192.168.56.2,默认网关192.168.56.1,DNS可设为到常用的8.8.8.8、223.5.5.5、114.114.114.114等,如果虚拟机有安装DNS服务也可设到192.168.56.1。

此时电脑应当可以通过虚拟机上网。随后开启win7的无线承载网络以实现无线AP,注意有些老旧的无线网卡可能不支持承载网络,或不支持同时用作上网和开启AP。
netsh wlan set hostednetwork allow SSID KEY
netsh wlan start hostednetwork

最后打开Host-Only网卡的属性,在共享选项卡里将该连接共享到开启无线承载网络时添加的虚拟无线网卡上(一般为无线网络连接2),此时该虚拟网卡的IP会被自动设置为192.168.137.1,连接的设备会由windows分配到这个网段的IP并进行一次NAT。由于这个虚拟无线网卡不能设置桥接,所以只能通过windows internet共享接入网络。

实际的效果相当于设备经过192.168.137.1、192.168.56.1、192.168.1.1三次路由后到达外网。设备访问电脑可以用192.168.137.1这个IP,电脑和linux可用192.168.56.1、56.2互相访问。

WRT54G的河南联通PPPoE拨号设置手记

由于实在忍受不了家里无线路由的丢包率,抱着赌博的心态在TB上淘了个WRT54G V4。WRT54G这款路由器不用多做介绍,一代经典,堪称“机王”,尤其是V2到V4版本。此机早在07年就已停产,但远远不能满足人们对cisco一代经典的追求,于是万能的天朝商人便打起了山寨的主意,之所以说是赌博便因为目前TB上WRT54G与WRT54GS买到山寨的概率远远高于买到二手真货的概率。我本来就赌它是个假的,只要比我现在用的无线好并且能编程折腾就行,来了就拆机看电路板,结果居然是真货!并且是WRT54G-CN V4的大陆行货!!!恩,恭喜我中奖。。。

因为用不上太多功能,所以目前不急着刷系统。先弄上网再说。。。

结果我悲催地发现我把宽带的密码忘了!而且我爸妈都忘了!

不过这个难不倒,旧的Tenda路由器里记录的有,恩,用Firefox的Inspect Element功能看一般密码毫无压力的说,于是我拿到了一个6位的数字密码。

然后我发现linksys拨不上号!提示pppoe验证失败!我一度以为是tenda记录的不对,比如说只显示了前6位等等。于是这两天就先用两层路由,tenda拨号,linksys的wan接到tenda的lan上,因为tenda只是无线丢包,有线还没怎么坏。

今天我爸找到了用纸记录的密码,居然就是我两天前试的那个!!!我倒!!!

LZ已死,有事烧纸。。。

==============华丽丽的分割线================

天朝互联网寓言故事之小鬼大战路由器拨号

第一回合:躺枪阶段

若干次试验后,linksys依旧无法拨号,而插到tenda上立即就能拨上,所以可以否定是刚下线不能立即拨的看法。为了验证是不是isp做了mac地址绑定之类的动作,直接用电脑接猫,新建pppoe连接,输入帐号和密码,结果失败,提示用户名或密码错误。于是推断isp做了mac地址绑定。

如果判断为MAC地址绑定,那么很简单,修改电脑网卡(本机拨号情况)或路由器wan的mac地址即可。于是将linksys的mac地址修改为tenda的,失败!将电脑网卡的mac地址修改为tenda的,失败!将tenda的mac地址修改为linksys的,成功??!!!

也就是说isp并没有检测mac地址。那怎么tenda怎么弄都能上,其它设备怎么弄都弄不上。难道拨号时还有其他方法可能判断客户端设备?于是我翻了遍ppp协议,似乎并没有什么新的发现,并且最最重要的是,网络上讲,识别设备的唯一办法就是mac地址。

我艹!!!!!!这不科学!!!!!!!!!!!!!!!!!!!!

到了这个时候,问题的诡异性已经超出了人类的智商。

不信?让我们把思路整理一下:

条件一,密码真的真的真的真的真的是对的。

条件二,tenda能拨上linksys和电脑拨不上,设置上没有任何纰漏。

条件三,isp并没有通过绑定mac地址来限制拨号设备。

苍天啊,大地啊,这三个条件真的真的是无解啊!!!!!!!!!!!

小鬼给跪了。。。。。。。。。

 

第二回合:逆袭阶段

正在一筹莫展之际,我想起了前些天被豆子叫去给某妹纸装电脑的一个细节。

当时是我配置的tp的路由器,等了半天也没拨上号,后来又突然好了。我点开了路由器的日志记录,看到一共拨了3次好像,前两次都是用户名密码认证失败,最后一次成功了。

妹纸家是网通,也就是现在的新联通。而我用网通也有十二年的历史了,我知道河南网通一直用的自己的拨号客户端,从原来的dhcp+到后来的pppoe。我也听说网通的拨号客户端对用户的帐号和密码有一些加密处理。

失败了两次之后才成功,我当时以为是isp意识到用户在使用直接的拨号,所以最后就给放行了。现在看来是我太天真了。这应该是tp在拨号失败后主动尝试了针对isp的拨号客户端,如电信的星空极速,亦或河南网通的racer这些定制的特殊拨号方式,tp的手动拨号配置中是有这个选项的。

那么同是国产的tenda是不是也在固件里内置了河南联通的特殊处理,只不过它也是自动尝试,在设置界面上并不体现出来。想到这点,问题终于有了转机。

stfw得知,河南网通客户端将用户名进行了加密处理,用加密的用户名与未加密的密码进行拨号。有人通过对racer软件的反汇编得出了加密算法并写出了计算拨号用真实用户名的软件,但新联通成立时对客户端进行了升级,结束了dhcp+的时代并更新了加密算法,目前这个算法还没人做现成的软件出来。

下载河南联通356版客户端安装,拨号,成功!

下面就可以考虑找到真正的用户名填入linksys的设置里,目前推荐的做法是打开windows的事件查看器查看RemoteAccess来源的事件。

image

等等。。。3冒号后面是个嘛。。。。。。。。。。what the fxxing。。。

是的,您没有看错,356版河南联通算法即3冒号开头的用户名,表示第三版算法,在用户名中加了个回车符让您没法填到路由器的设置里。这里的用户名实际上是3:\r\n5q3F9zN6lF4。这个回车符遵循的是windows的crlf习惯。

不过没法填到路由器的设置页面里可不一定代表路由器就不支持这玩意,直觉告诉我我可以使用HTTP封包工具将这个回车直接post到路由器的设置页面。打开HTTPAnalyzer,截一个linksys设置的包,然后将post的用户名字段手动改为3:%0D%0A5q3F9zN6lF4,发送。

试着让linksys拨号,成功获取了ip地址!此时是西元2012年7月25日19时12分。经过三个小时的折腾,某小鬼终于成功让“机王”上上了网。

截图一张。注意那里其实是有个\r\n的,看源代码可以看到。

image

 

后记:

这则寓言告诉我们两个道理。

一、河南网通被评为全国最差isp(没有之一)是正确的,科学的,名符其实的。

二、没有在中国的互联网江湖上混过十年八年的,不要买WRT54G之类的传说中的古董神机来装逼,上不了网坑死你个XX的。