http方法_技术分享 | 拦截内网 HTTP 请求方法

概述

运维管理过程中,有时需要对网络中访问站点做限制,比如禁止访问某个网站。接下来给大家介绍一种方法 :拦截 HTTP 访问请求。本文以 http://www.demo.com 为例进行说明。

环境依赖

为了拦截 HTTP 请求,我们需要在网关处抓取数据包,并且发送我们自己的包给用户。这里我们使用 scapy,这是一个 Python 包,用来抓取和构造数据包很方便。在装 scapy 之前需要先安装 libpcap,之后即可使用如下命令安装:

# pip install scapy

通信过程分析

对于 TCP 通信,最基本的便是三次握手。为了拦截用户的 HTTP 请求,我们需要详细了解一下 HTTP 的通信过程,如图 1 为一次完整的 HTTP 请求:

d9b061c576fb3f0ef1ac03eb79c5b528.png

图 1:HTTP通信过程

从图 1 可看出前 3 个包为三次握手过程,第 4 个包为客户端发往服务端的请求。详细构成如图 2 所示:

e00440d37d4e0f6f2b0ed8a9d342d6b0.png

图 2:HTTP请求

紧接着服务端发送了 5、6 两个包。从图 2 和图 3 红色框标记的地方可知,其中第 5 个包是服务端对客户端发送的第 4 个包的应答。

62c78106e3fa7cbde425874370fb5e65.png

图3:服务端应答报文

第 6 个包为服务端对客户端 HTTP 请求的应答。详细构成如图 4 所示:

ca72ba7b2c7185c7853b26cb6abd4006.png

图4:HTTP应答报文

接着第 7 个包是客户端对第 6 个包的应答报文,最后第 8-10 个包为四次挥手过程,断开连接。

拦截实现

通过上面对 HTTP 通信过程的分析,不难发现为了拦截用户特定的 HTTP 请求,需要从第 5 和第 6 个包入手。

判断一个包是否为 HTTP 请求报文

首先我们需要判断抓到的包是否为 HTTP 请求报文,因为 HTTP 报文是通过明文传输的,如图5所示。一个简单的方法是判断数据包中是否存在\r\nHost: www.demo.com\r\n 即可。

ea9200e987b2291171c4eae9498c155e.png

图 5:HTTP报文明文

构造自己的数据包

当收到一个数据包匹配到了我们指定的域名时,我们需要构建自己的数据包发给用户。通过上面的分析,我们至少需要构造 2 个包,图 1 中的第 5 和第 6 个包。另外,还有两点需要注意,其一:因为 HTTP 请求还是会到达服务端的,为了阻止服务端继续返回数据,我们需要再构造一个 RST 报文,断开该 TCP 连接。其二:为了阻止用户继续访问该域名,我们同样需要构造一个发往客户端的 RST 报文。最终结果如图 6 所示。

0b986f786f50c2ab458db5be6897a8b8.png

图 6:拦截结果

下面对图六中部分数据包说明:

第 5-6 个 RST 包对应第 3 个包,第 7-9 个 RST 包对应第 4 个包,为了防止丢失,我们多发了几次。

第 10 和第 15 个包是我们构造的对应第 4 个包的 HTTP 请求的应答(发送了两次)。

第 11 个包是客户端对第 10 个包的应答。

第 12 和第 14 个包是我们构造的发给客户端的RST报文。

第 13 个包则是服务端发送的FIN报文。

第 16-19 个包则是客户端和服务端产生的额外控制报文。

运行结果

图7是在我们没有拦截的情况下返回的输出,为 WWW.DEMO.COM ,图 8 是在拦截的情况下的输出,为 HELLO WORLD

bcbd3aece3a395515ce9c287c9bdee86.png

图 7:正常情况下输出

197f2973941a84bc84f3c41e3c13fb3d.png

图 8:拦截情况下输出

部分代码

最后,给出部分代码(两个用来构造包的函数),仅供大家参考。

from scapy.all import * content = '''HELLO WORLD '''

HTTP404 = '''HTTP/1.1 404 Not Found\r Server: 127.0.0.1\r Cache-Control: no-cache\r Content-Type: text/html\r Content-Length: %s\r %s ''' % (len(content), content) DF = 2      # Don't Fragment

RST = 4     # Reset

PA = 24     # Push, Acknowledgment

def genServerRstPack(pkg):

   pkgs = []    eDst = pkg.dst               # 以太网包 目的MAC地址    eSrc = pkg.src               # 以太网包 源MAC地址    dst = pkg.payload.dst        # IP包 目的IP    src = pkg.payload.src        # IP包 源IP    idn = pkg.id                 # IP包 id字段    tcpPayload = pkg.payload.payload    sport = tcpPayload.sport     # TCP报文 sport字段    dport = tcpPayload.dport     # TCP报文 dport字段    seq = tcpPayload.seq         # TCP报文 seq字段    nSeq = seq + len(tcpPayload)-4*tcpPayload.dataofs  

                                # 计算下一个包的 seq字段    for i in range(2):           # 通过上面的那些字段构造要发送的包                                 # TCP 的 flags 字段设置为 RST = 4         p = Ether(dst=eDst, src=eSrc)/IP(dst=dst, src=src, id=idn+1, flags=DF)/TCP(sport=sport, dport=dport, seq=seq,            window=0, flags=RST)/Padding("\x00\x00\x00\x00\x00\x00")        pkgs.append(p)    

   for i in range(3):        p = Ether(dst=eDst, src=eSrc)/IP(dst=dst, src=src, id=idn+2, flags=DF)/TCP(sport=sport, dport=dport, seq=nSeq,            window=0, flags=RST)/Padding("\x00\x00\x00\x00\x00\x00")        pkgs.append(p)    

   return pkgs

def genClientRstPack(pkg):    pkgs = []    eDst = pkg.dst    eSrc = pkg.src    dst = pkg.payload.dst    src = pkg.payload.src    tcpPayload = pkg.payload.payload    sport = tcpPayload.sport    dport = tcpPayload.dport    ack = tcpPayload.ack    seq = tcpPayload.seq    nSeq = seq + len(tcpPayload)-4*tcpPayload.dataofs       # tcpPayload.dataofs 为TCP首部长度,单位为4字节    p = Ether(dst=eSrc,src=eDst)/IP(dst=src,src=dst,flags=DF)/TCP(sport=dport,dport=sport,seq=ack,ack=nSeq,        window=65535, flags=PA)/Raw(HTTP404)    pkgs.append(p)    pkgs.append(p)    p = Ether(dst=eSrc,src=eDst)/IP(dst=src,src=dst,flags=DF)/TCP(sport=dport,dport=sport,seq=ack+len(HTTP404),        window=0, flags=RST)/Padding("\x00\x00\x00\x00\x00\x00")    pkgs.append(p)    pkgs.append(p)    

   return pkgs

524d110e65820f0a43d8f2144e8c7a01.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值