3.3.5. 脚本定制

什么是TH-Nebula脚本?

TH-Nebula脚本是TH-Nebula系统配合策略, 触发策略, 参与策略定义变量计算的程序.

为何需要定制TH-Nebula脚本?

因为业务千变万化, TH-Nebula并不能预知到所有的业务场景, 所以需要自定义化, 可以对一个接口写一个, 甚至多个脚本去触发策略.

如何定制TH-Nebula脚本?

TH-Nebula脚本的总体流程

21.脚本定制1

概要:

  1. 首先取得所需要监控网站的接口以及页面(page)
  2. 对接口的参数需要收集起来,并对参数要明确了解
  3. 对接口的类型分类为所属的策略, 根据接口参数所提供的, 在定制脚本上定制相应的类,也就是处理的函数,分配到策略上, 就可以在触发事件的时候, 通过处理函数,将事件分配到策略上, 在详情页面拿到所定制的流量数据.

简要的例子

在需要监控网站的订单页面, 某个用户提交订单, 那么在定制脚本通过监控订单接口, 拿到订单流量, 把所有的订单参数作为策略字段存到事件中去, 那么事件就会根据配备上线的策略去触发策略, 这事件就会在TH-Nebula中例如总览, 风险名单管理, 风险事件管理位置出现, 在详情页面就能拿到这订单的所有信息.

完整的例子

根据之前的例子, 新建了一个事件叫做账号-实名验证的策略, 那么它的类型如图所示它的类型为: ACCOUNT_CERTIFICATION

22.脚本定制2

已经定制好策略, 然后在脚本中定义此策略相应的类, 以下是测试的类
例如在/etc/nebula/sniffer/sniffer.conf中配置脚本名, 以下testparser即是脚本名, 脚本的所在位置在于TH-Nebula_sniffer项目:

sniffer.conf位置:

  1. /etc/nebula/sniffer/sniffer.conf

配置:

  1. sources: [eth0]
  2. en0:
  3. driver: bro
  4. interface: en0
  5. ports: [80, 81, 1080, 3128, 8000, 8080, 8888, 9001, 8081-8083]
  6. start_port: 48880
  7. instances: 1
  8. parser:
  9. name: test
  10. module: testparser

testparser.py位置:

  1. TH-Nebula_sniffer\TH-Nebula_sniffer\customparsers\testparser.py

配置:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import re
  4. import logging
  5. from threathunter_common.util import millis_now
  6. from threathunter_common.event import Event
  7. from ..parser import Parser, extract_common_properties, extract_http_log_event
  8. from ..parserutil import extract_value_from_body, get_md5, get_json_obj
  9. from ..msg import HttpMsg
  10. logger = logging.getLogger("sniffer.parser.{}".format("testparser"))
  11. def arg_from_get(data):
  12. d = data.split('?')[1].split('&')
  13. result = {}
  14. for e in d:
  15. a, b = e.split('=')
  16. result[a] = b
  17. return result
  18. def account_test(httpmsg):
  19. if not isinstance(httpmsg, HttpMsg):
  20. return
  21. if "/loign" not in httpmsg.uri:
  22. return
  23. # get请求通过httpmsg的uri字段获得请求参数
  24. data = httpmsg.get_dict()
  25. data = arg_from_get(data['uri'])
  26. properties = extract_common_properties(httpmsg)
  27. properties["result"] = "T"
  28. properties["new_password"] = data.get('new_password')
  29. properties["old_password"] = data.get('old_password')
  30. properties["register_realname"] = ""
  31. properties["register_channel"] = "not"
  32. properties["email"] = data.get('email')
  33. properties["user_name"] = extract_value_from_body(r_mobile_pattern, httpmsg.req_body)
  34. properties["password"] = data.get('password')
  35. properties["captcha"] = ""
  36. properties["register_verification_token"] = "none"
  37. properties["register_verification_token_type"] = "null"
  38. return Event("nebula", "ACCOUNT_CERTIFICATION", "", millis_now(), properties)
  39. #############Parser############################
  40. class TestParser(Parser):
  41. def __init__(self):
  42. super(TestParser, self).__init__()
  43. self.http_msg_parsers = [
  44. account_test,
  45. ]
  46. def name(self):
  47. return "test customparsers"
  48. def get_logbody_config(self):
  49. return ["login", "user"]
  50. def get_events_from_http_msg(self, http_msg):
  51. if not http_msg:
  52. return []
  53. result = list()
  54. for p in self.http_msg_parsers:
  55. try:
  56. ev = p(http_msg)
  57. if ev:
  58. result.append(ev)
  59. except:
  60. logger.debug("fail to parse with {}".format(p))
  61. return result
  62. def get_events_from_text_msg(self, text_msg):
  63. return []
  64. def filter(self, msg):
  65. if not isinstance(msg, HttpMsg):
  66. return False
  67. return False
  68. Parser.add_parser("test", TestParser())

要注意最后return中Event第二个参数是: ACCOUNT_CERTIFICATION
这个参数决定了触发了策略的类型, 系统会根据这个类型去匹配策略, 如果触发了策略, 那么会根据策略的处理流程处理.

httpmsg是什么?

httpmsgTH-Nebula系统中的sniffer定义的请求所包含的数据, 设备信息, IP等等所有能抓到的所有信息

流量中的请求如何体现在httpmsg中?

GET请求在httpmsg的体现

注意请求参数order=88888888888888888888 在uri中, 可以以此找到所有请求参数,这是定制脚本重点!

  1. {
  2. 'uid': '',
  3. 'status_code': 404,
  4. 'resp_content_type': 'text/plain; charset=utf-8',
  5. 'resp_headers': {
  6. 'CONTENT-LENGTH': '356',
  7. 'VARY': 'Accept-Encoding',
  8. 'SERVER': 'openresty',
  9. 'CONNECTION': 'close',
  10. 'DATE': 'Fri, 26 Oct 2018 03:58:30 GMT',
  11. 'CONTENT-TYPE': 'text/plain; charset=utf-8'
  12. },
  13. 'id': ObjectId('5bd290e612a3650c2c1c440f'),
  14. 'dest_port': 9001,
  15. 'resp_body_len': 356,
  16. 'log_body': False,
  17. 'resp_body': '',
  18. 'req_content_type': '',
  19. 'debug_processing': False,
  20. 'req_headers': {
  21. 'ACCEPT-ENCODING': 'gzip, deflate',
  22. 'HOST': '112.74.58.210:9001',
  23. 'ACCEPT': '*/*',
  24. 'USER-AGENT': 'python-requests/2.11.1',
  25. 'CONNECTION': 'close',
  26. 'COOKIE': 'auth=2|1:0|10:1540368603|4:auth|44:NGJlZTQwMDI0NTExYjM2NDVkNjkzOTM1ZTJmMDllMWY=|34718dcbcac603ee1a38daaa0a05fbafc524873af0fafb0013077a53a28b2d4b; group_id=2|1:0|10:1540368603|8:group_id|4:Mg==|30789028a7e8399f5f5ef115fc050e1b3424df25b966755be7554e0048dd29a4; user=2|1:0|10:1540368603|4:user|16:Ymlnc2VjX3Rlc3Q=|4a26e0eb0a60184839666d67af744a3fc869efc82d4c413182814d8732c2875b; user_id=2|1:0|10:1540368603|7:user_id|4:Mg==|141555b29c711f95ddebea3987820fb90c30a48f506eae9f8cee9b334372ae79'
  27. },
  28. 'method': 'GET',
  29. 'req_body': '',
  30. 'req_body_len': 0,
  31. 'host': '112.74.58.210',
  32. 'referer': '',
  33. 'xforward': '',
  34. 'did': '',
  35. 'status_msg': 'not found',
  36. 'source_ip': '116.24.64.28',
  37. // 这里需要注意 uri 字段值
  38. 'uri': '112.74.58.210/history?order_id=88888888888888888888',
  39. 'user_agent': 'python-requests/2.11.1',
  40. 'source_port': 60563,
  41. 'dest_ip': '172.18.16.169'
  42. }

POST请求在httpmsg的体现

注意请求字段 在req_body字段中,可以以此找到所有请求参数,这是定制脚本重点

  1. {
  2. 'uid': '',
  3. 'status_code': 404,
  4. 'resp_content_type': 'text/plain;charset=utf-8',
  5. 'resp_headers': {
  6. 'CONTENT-LENGTH': '356',
  7. 'VARY': 'Accept-Encoding',
  8. 'SERVER': 'openresty',
  9. 'CONNECTION': 'close',
  10. 'DATE': 'Fri,26Oct201808:13:52GMT',
  11. 'CONTENT-TYPE': 'text/plain;charset=utf-8'
  12. },
  13. 'id': ObjectId('5bd2ccc012a365682aee1fbd'),
  14. 'dest_port': 9001,
  15. 'resp_body_len': 356,
  16. 'log_body': False,
  17. 'resp_body': 'Traceback(mostrecentcalllast):\nFile"/home/threathunter/nebula/nebula_web/venv/lib/python2.7/site-packages/tornado/web.py",line1422,in_execute\nresult=self.prepare()\nFile"/home/threathunter/nebula/nebula_web/venv/lib/python2.7/site-packages/tornado/web.py",line2149,inprepare\nraiseHTTPError(self._status_code)\nHTTPError:HTTP404:NotFound\n',
  18. 'req_content_type': 'application/json',
  19. 'debug_processing': False,
  20. 'req_headers': {
  21. 'CONTENT-LENGTH': '78',
  22. 'ACCEPT-ENCODING': 'gzip,deflate',
  23. 'HOST': '112.74.58.210:9001',
  24. 'ACCEPT': '*/*',
  25. 'USER-AGENT': 'python-requests/2.11.1',
  26. 'CONNECTION': 'close',
  27. 'COOKIE': 'auth=2|1:0|10:1540368603|4:auth|44:NGJlZTQwMDI0NTExYjM2NDVkNjkzOTM1ZTJmMDllMWY=|34718dcbcac603ee1a38daaa0a05fbafc524873af0fafb0013077a53a28b2d4b;group_id=2|1:0|10:1540368603|8:group_id|4:Mg==|30789028a7e8399f5f5ef115fc050e1b3424df25b966755be7554e0048dd29a4;user=weihong;user_id=2|1:0|10:1540368603|7:user_id|4:Mg==|141555b29c711f95ddebea3987820fb90c30a48f506eae9f8cee9b334372ae79',
  28. 'CONTENT-TYPE': 'application/json'
  29. },
  30. 'method': 'POST',
  31. // 这里需要注意 req_body 字段值
  32. 'req_body': '{"password":"777777777","order":"999999999999999999","user":"weihong999"}',
  33. 'req_body_len': 78,
  34. 'host': '112.74.58.210',
  35. 'referer': '',
  36. 'xforward': '',
  37. 'did': '',
  38. 'status_msg': 'notfound',
  39. 'source_ip': '61.141.65.209',
  40. 'uri': '112.74.58.210/history',
  41. 'user_agent': 'python-requests/2.11.1',
  42. 'source_port': 62840,
  43. 'dest_ip': '172.18.16.169'
  44. }