旁路劫持攻

旁路劫持攻击简介

在client和server之间的网络通信中,如果它们之间交互的流量被攻击者监听到,那么攻击者就可以监听client端发起的请求,然后伪造一个假的response包发送给client端。假如攻击者与受害主机的网络距离(物理跳数)小于真实服务器与受害者的距离,那么这个伪造的response就会比真实服务器发出的响应提前到达,从而使客户端优先处理了这个假的response,而真的response因为来的晚,最后被client端忽略掉了。

黑客通过旁路劫持攻击,可以达到很多目的。比如在用户访问的网页中插入一段恶意的js代码,骗取账号密码;或者将页面重定向到精心准备好的钓鱼页面;也可以将大量的用户请求重定向到一个第三方的网站,当访问量很大时,可以达到ddos的效果。

从实现的角度来说,可以构造一个假的http页面的response,这个页面里面包含了一段恶意代码,但是其余部分和真实的网站内容一致,这样对于受害者来说,虽然看起来访问的网站和真实的一致,但是恶意代码其实已经在悄悄的运行了。或者利用http协议的302跳转,受害者的浏览器在收到这样的response之后,会自动跳转到攻击者指定的url,这样就能够实现钓鱼攻击,或者用来ddos目标url。当然也可以直接监听客户的dns请求,然后发一假的response过去,这样受害者就会直接访问你指定的那个ip地址,同样可以实现钓鱼攻击,或者ddos攻击。

以http劫持为例,一个简单的劫持流程说明如下:


1.        受害者向真实服务器发起一个http get请求

2.        攻击者通过控制网络中的交换机或其它服务器,监听到了受害者的请求

3.        攻击者构造了一个假的response包,内容为302跳转,目的地址为黑客指定的地址

4.        受害者在收到攻击者发出的响应后,自动跳转到了www.hack.com,攻击成功

5.        因为真实服务器发出的response在这之后才到达,直接被tcp协议丢弃了。

劫持成功的条件:

要劫持成功,首先伪造的response包要满足几个基本条件

1.        伪造包的seq,和ack要与真实的response一致。这两个值可以根据监听到的get请求计算出来。(实测情况下,这两个值其实是可以变动的,也能劫持成功)

2.        伪造包的源/目的ip,源/目的端口号要符合当前会话要求

3.        目的mac地址必须是受害者的mac地址

4.        Tcp校验和之类的东西,否则受害者收到这个包之后,校验失败就丢掉了

5.        伪造包一定要比正常的response先到达


测试环境下,使用scapy模拟旁路劫持:


为了模拟受害者流量被监听的环境,这里使用交换机将交换机的流量镜像一份出来,用来运行监听脚本的为一个有双网卡的ubuntu虚拟机,监听脚本从eth1口读取镜像过来的流量,如果发现有发往知乎的http请求,那么就根据当前的请求生成一个伪造的response从eth0口发出去。这里因为攻击者和受害者都是在一个交换机上,而且受害者要访问的服务器是公网服务器,所以正常情况下伪造包肯定是会比真实的response先到达受害pc的。

功能通过scapy实现起来比较简单,首先是在eth1上监听镜像过来的流量,设置过滤条件,如果发现有发往www.zhihu.com的请求,那么就调用函数构造一个伪造包,然后从eth0口发出去。伪造包的内容为一个302跳转,目的地址为一个内网的url。

import scapy
from scapy.all import *

response = "HTTP/1.1 302 FOUND\r\nServer: nginx/1.1.15\r\nContent-Type: text/html; charset=utf-8\r\nConnection: keep-alive\r\nLocation: http://10.16.66.167/\r\n\r\n"

def gen_res(x):
        p_E = Ether()
        p_E.src = x[Ether].dst
        p_E.dst = x[Ether].src
        p_I = IP()
        p_I.src = x[IP].dst
        p_I.dst = x[IP].src
        p_I.flags = x[IP].flags
        p_I.ttl = 234
        data_len = x[IP].len - 40
        p_T = TCP()
        p_T.sport = x[TCP].dport
        p_T.dport = x[TCP].sport
        p_T.flags = x[TCP].flags
        p_T.seq = x[TCP].ack
        p_T.ack = x[TCP].seq + data_len
        p_R = Raw(response)
        p = Ether(str(p_E/p_I/p_T/p_R))
        print ls(p)
        sendp(p,iface = "eth0")

def pr(x):
        print ls(x)
        gen_res(x)

my_filter = lambda(r): Raw in r and TCP in r and r[TCP].dport == 80 and "Host: www.zhihu.com" in r[Raw].load

pkts = sniff(iface = "eth1",lfilter = my_filter,count = 0, prn = pr)
~                                                                     

伪造包中seq和ack的取值

正常情况下,伪造包中的seq和监听到的get请求中的ack一致,伪造包中的ack等于get请求包中的seq加上这个包的tcp playload长度。实际测试的情况下,攻击者在发送伪造包时,seq和ack并非严格按照这个要求才能成功。

假如我们监听到的get请求的seq和ack,tcppayload长度分别为get_seq,get_ack,get_len

1.        伪造包ack的取值fake_ack:

当fake_ack > get_seq + get_len,这个时候不能劫持成功。大概的原因就是,客户端发出的数据最大序列为get_seq+get_len,这个时候fake_ack大于这个值的话,就相当于服务器告诉客户端,我收到的数据比你真实发出来的内容还多,所以这个情况下,客户端就会认为这是一个异常包,直接丢掉了。

 

当fake_ack <= get_seq + get_len,这个时候是可以劫持成功的。但是没有试过fake_ack小于这个会话最初始的seq的情况,估计是不行的。

2.        伪造包的seq的取值fake_seq:

当fake_seq<get_ack,或者fake_seq>get_ack很多时,同样不能劫持成功,这种情况下有些时候浏览器会直接把http response里面的header直接打印出来。

为了验证为什么这种情况下不能劫持成功,写了一个简单的tcp socket程序来发送给请求,然后把收到的response打印出来。

最后得到的结论是,当伪造的fake_seq != get_ack时,tcp会把这个伪造的response和真实的response合并为一个response返回给浏览器。假的response内容会按照seq序列,覆盖掉真实response内容的一部分。

当fake_seq = get_ack-1时,我们得到的response是这样:

注意红色部分内容实际是我们伪造的response,但是少了第一个字符h,剩下的部分为真实的response,只是前面的数据被覆盖掉了(至于到底谁覆盖谁,好像不同的操作系统上实现方式是不一样的,测试环境下用的是windows)。这种情况下,浏览器收到这样的response,是不会正常解析的。

 

当fake_seq=get_ack+1时,我们得到的response是这样的:


左边是通过wireshark查看到的response,右边是通过tcp socket获得的resonse,而且实际情况是,浏览器跳转到了我指定的地址。所以这里应该以右侧的结果作为参考。至于为什么wireshark呈现的是这样,应该和它对tcp的实现方式,以及本身的配置有关系。我们看到绿色部分多出来了一个h,就是因为伪造包的seq向右偏移了一个字节导致的。这种情况下,浏览器还可以正常解析这个response,够跳转。可是如果伪造包的seq向右偏移太多的话,就会导致最后浏览器收到的response本身格式被破坏,所以就不能正常解析并实现跳转了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值