(一)
马上做一个分布式漏洞扫描与攻击的项目,这段时间一直选技术路线以及做大量的demo。这篇是记录我在主控端与各个漏洞扫描节点协调通信上的一个demo代码。我选择使用类似于WebService的技术,即各个节点暴露WebService接口,主控端去调用并且拿到回调。WebService基于SOAP协议通信我觉得太麻烦,因为我的需求是主控端分发任务队列给节点,节点执行,完成之后回调,主控端进行处理,异步调用,逻辑简单。最终我试了一下基于GET方法的类似于WebService的方法,自己写交互的过程,这样编码也比较方便: - )。 这里我不禁要吐槽下Python开发一个WebService配置运行环境实在是蛋疼,但是调用WebService倒是很容易: - (。
(二)
我使用了BaseHTTPServer,在主控端和各个节点上都维护一个多线程Server进行交互,这样编码也比较简单,可以把精力主要投放在设计好通信细节以及处理各种突发事件即可。
这个过程的逻辑如下:
-
主控端接收外部调用
-
判断调用合法性,并重新组装请求并发给扫描节点
-
节点收到请求,进行身份验证,并提取出信息,根据信息(服务名、参数)开启工作线程。
-
节点执行完进行回调。
整个过程异步实现,画了张图如下:
(三)
使用Python的BaseHTTPServer实现,这个模块可以轻易实现一个HTTP服务器,使用里面的do_GET回调函数执行具体的逻辑,识别是外部调用请求还是回调请求,并对请求进行解析以及处理异常情况。
由于只是探测阶段,所以只是简单做了一个小demo,证明这种处理方式的可行性。下面就是贴代码了:
控制端:
#coding=utf-8
'''
控制节点逻辑:
1.接收外部接口的调用,格式为:
如控制节点1,执行其内部的print_job任务
http://127.0.0.1:8000/1/print_job
2.接收节点的Callback
如收到:
http://127.0.0.1:8001/SUCCESS/NODE_ONE
表示节点1的任务完成
如果是:
http://127.0.0.1:8001/ERROR/NODE_ONE
表示节点1执行出错
3.转发给节点
各个节点分析URL
http://127.0.0.1:8001/1/print_job
表示执行方法为print_job
'''
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SocketServer import ThreadingMixIn
import urllib
import re
import threading
NODE_ONE = ('127.0.0.1','8001')
NODE_TWO = ('127.0.0.1','8002')
class MyRequestHandler(BaseHTTPRequestHandler):
'''/1/print|1,2,3(判断url后面跟的参数列表)'''
def getArguments(self,path):
'''
传递参数:/1/print|1,2,3 or /1/print|
'''
if path.count('|') == 0:
#无参数
return []
else:
args = path[1:].split('/')[1].split('|')[1].split(',')
return args
def parseArgs(self,args):
return str(args).replace('[','').replace(']','')
def _writeheader(self,wtype):
if wtype == 'OK':
self.send_response(200)
else:
self.send_response(404)
self.send_header('Content-type','text/html')
self.end_headers()
'''
给任务节点分配任务(请求中转)
http://127.0.0.1:8001/1/print_job|1,2,3
'''
def send_work(self,path):
info = path[1:].split('/')
node = info[0] #识别节点
job = info[1][:info[1].find('|')] #调用节点的函数
args = self.getArguments(path)
print 'Node %s will do this: %s, args ====> %s' % (node,job,args)
if node == '1': #如果是控制节点1
#判断功能是否合法
if job == 'print_job':
self._writeheader('OK')
url = 'http://%s:%s/%s/%s|%s' % (NODE_ONE[0],NODE_ONE[1],node,job,self.parseArgs(args))
urllib.urlopen(url.replace("'",''))
print 'Assign success!'
return 'SENT_JOBS'
elif job == 'xxx': #添加节点可调用的服务列表
'''write other functions'''
pass
else:
return 'INVALID_JOB'
elif node == '2': #如果是节点2
if job == 'print_job':
self._writeheader('OK')
url = 'http://%s:%s/%s/%s|%s' % (NODE_TWO[0],NODE_TWO[1],node,job,self.parseArgs(args))
urllib.urlopen(url.replace("'",''))
print 'Assign success!'
return 'SENT_JOBS'
else:
self._writeheader('ERR')
return 'INVALID_JOB'
else:
return 'NODE_NOT_EXISTS'
def do_GET(self):
'''callback:http://127.0.0.1:8001/SUCCESS/NODE_ONE'''
pattern1 = re.compile(r'^/([A-Z]+)/(NODE_[A-Z]+)$')
'''command:http://127.0.0.1:8002/1/print_job|1,2,3'''
pattern2 = re.compile(r'^/(\d+)/([a-z_]+)\|([\w,]*?)$')
callback = pattern1.findall(self.path) #节点回调
command = pattern2.findall(self.path) #外部调用
#收到callback
if len(callback) == 1:
status = callback[0][0] #识别状态
who = callback[0][1] #识别节点
if status == 'SUCCESS':
print '%s : %s Completed!' % (who,status)
self._writeheader('OK')
else:
print '%s : %s Error!' % (who,status)
self._writeheader('OK')
#如果收到指令,就分配任务
elif len(command) == 1:
self._writeheader('OK')
job = command[0][0] #具体的task
who = command[0][1] #指定节点
print 'Send to %s : %s' % (who,job)
ret = self.send_work(self.path) #分配task
print 'send %s' % ret
else:
#路径出错
print 'Path: %s Error!' % self.path
self._writeheader('ERR')
class ThreadingHTTPServer(ThreadingMixIn,HTTPServer):
pass
if __name__ == '__main__':
serveraddr = ('127.0.0.1',8000)
myCtrl = ThreadingHTTPServer(serveraddr,MyRequestHandler)
myCtrl.serve_forever()
节点1:
#coding=utf-8
'''
工作节点:
提供的接口:
<a target=_blank href="http://127.0.0.1:8001/1/print_job">http://127.0.0.1:8001/1/print_job</a>
最好的方式:
把整个业务逻辑做成线程类
'''
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SocketServer import ThreadingMixIn
import urllib
import re
CONTROLLER = "http://127.0.0.1:8000/"
class MyRequestHandler(BaseHTTPRequestHandler):
'''身份验证(是否为主控节点)'''
def IDConfirm(self,path):
req_addr = self.client_address
if req_addr[0] != '127.0.0.1':
return False
else:
return True
'''请求响应'''
def _writeheader(self,wtype):
if wtype == 'OK':
self.send_response(200)
elif wtype == 'INVLID_HOST': #授权失败
self.send_response(401)
else:
self.send_response(404)
self.send_header('Content-type','text/html')
self.end_headers()
def getArguments(self,path):
'''
传递参数:/1/print|1,2,3 or /1/print|
'''
if path.count('|') == 0:
#无参数
return []
else:
args = path[1:].split('/')[1].split('|')[1].split(',')
return args
'''解析请求参数'''
def parseRequests(self,request):
#/1/print_job|1,2,3
info = request[1:].split('/')
node = info[0] #识别节点
job = info[1].split('|')[0] #调用节点的函数
args = self.getArguments(self.path) #获取参数列表
return node,job,args
'''暴露的服务'''
def print_job(self,name='NoneArgs'):
print 'Node_ONE: receive jobs from controller,the args is %s' % name
return 'SUCCESS'
'''GET回调'''
def do_GET(self):
#授权失败,拒绝服务
if not self.IDConfirm(self.path):
self._writeheader('INVLID_HOST')
#授权成功,提供服务
else:
print self.path
'''解析请求并执行服务'''
(node,job,args) = self.parseRequests(self.path)
print (node,job,args)
if job == 'print_job': #判断服务合法性
self._writeheader('OK')
print 'Print Job Working...'
ret = self.print_job(args[0])
urllib.urlopen('%s%s%s' % (CONTROLLER,ret,'/NODE_ONE'))
elif job == 'xxx':
pass
else:
self._writeheader('ERR')
print 'Unknown Job...'
urllib.urlopen('%s%s' % (CONTROLLER,"ERROR/NODE_ONE"))
class ThreadingHTTPServer(ThreadingMixIn,HTTPServer):
pass
if __name__ == '__main__':
serveraddr = ('127.0.0.1',8001)
node1 = ThreadingHTTPServer(serveraddr,MyRequestHandler)
node1.serve_forever()
节点2的代码和节点1的代码基本一致,除了端口不同,还是贴上去算了。
#coding=utf-8
'''
工作节点:
提供的接口:
http://127.0.0.1:8001/2/print_job
'''
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SocketServer import ThreadingMixIn
import urllib
import re
CONTROLLER = "http://127.0.0.1:8000/"
class MyRequestHandler(BaseHTTPRequestHandler):
'''身份验证(是否为主控节点)'''
def IDConfirm(self,path):
req_addr = self.client_address
if req_addr[0] != '127.0.0.1':
return False
else:
return True
'''请求响应'''
def _writeheader(self,wtype):
if wtype == 'OK':
self.send_response(200)
elif wtype == 'INVLID_HOST': #授权失败
self.send_response(401)
else:
self.send_response(404)
self.send_header('Content-type','text/html')
self.end_headers()
def getArguments(self,path):
'''
传递参数:/1/print|1,2,3 or /1/print|
'''
if path.count('|') == 0:
#无参数
return []
else:
args = path[1:].split('/')[1].split('|')[1].split(',')
return args
'''解析请求参数'''
def parseRequests(self,request):
#/1/print_job|1,2,3
info = request[1:].split('/')
node = info[0] #识别节点
job = info[1].split('|')[0] #调用节点的函数
args = self.getArguments(self.path) #获取参数列表
return node,job,args
'''暴露的服务'''
def print_job(self,name='NoneArgs'):
print 'Node_TWO: receive jobs from controller,the args is %s' % name
return 'SUCCESS'
'''GET回调'''
def do_GET(self):
#授权失败,拒绝服务
if not self.IDConfirm(self.path):
self._writeheader('INVLID_HOST')
#授权成功,提供服务
else:
print self.path
'''解析请求并执行服务'''
(node,job,args) = self.parseRequests(self.path)
print (node,job,args)
if job == 'print_job': #判断服务合法性
self._writeheader('OK')
print 'Print Job Working...'
ret = self.print_job(args[0])
urllib.urlopen('%s%s%s' % (CONTROLLER,ret,'/NODE_ONE'))
elif job == 'xxx':
pass
else:
self._writeheader('ERR')
print 'Unknown Job...'
urllib.urlopen('%s%s' % (CONTROLLER,"ERROR/NODE_ONE"))
class ThreadingHTTPServer(ThreadingMixIn,HTTPServer):
pass
if __name__ == '__main__':
serveraddr = ('127.0.0.1',8002)
node1 = ThreadingHTTPServer(serveraddr,MyRequestHandler)
node1.serve_forever()
(四)
以上代码经过测试,解释器顺利执行,过程如下:
(1)向主控端发送调用信息:
(2)主控端接收消息:
(3)节点1执行: