滲透測試中,經常遇到無法使用wireshark、無法嗅探和分析流量的情況,可以通過部署簡單的TCP代理指令碼來瞭解未知的協議,修改資料包,或者為模糊測試建立環境。 介紹 原理
代理工作原理大致如下: [需要代理方]向伺服器發出請求資訊。 [代理方]應答。 [需要代理方]接到應答後發送向[代理方]發送目的ip和連接埠。 [代理方]與目的串連。 [代理方]將[需要代理方]發出的資訊傳到目的方,將目的方發出的資訊傳到[需要代理方]。 代理完成。 Usage Usage: ./proxy.py [localhost] [localport] [remotehost] [remoteport] [receive_first] Example: ./proxy.py 127.0.0.1 9000 10.12.132.1 9000 True 編寫程式 main() 解析使用者命令列輸入 設定本地監聽參數和遠程目標 確認串連後是否需要先從遠程主機接收資料 進入監聽loop
def main(): # 沒有華麗的命令列解析 if len(sys.argv[1:]) != 5: print("Usage: ./proxy.py [localhost] [localport] [remotehost] [remoteport] [receive_first]") print("Example: ./proxy.py 127.0.0.1 9000 10.12.132.1 9000 True") sys.exit(0) # 設定本地監聽參數 local_host = sys.argv[1] local_port = int(sys.argv[2]) # 設定遠程目標 remote_host = sys.argv[3] remote_port = int(sys.argv[4]) # 告訴代理在發送給遠程主機之前串連和接受資料 if "True" in sys.argv[5]: receive_first = True else: receive_first = False # 現在設定好我們的監聽socket server_loop(local_host, local_port, remote_host, remote_port, receive_first)__author__ = 'xy'if __name__ == '__main__': main()server_loop() 建立TCP socket。 檢查本地連接埠是否可用,並彈出提示。 監聽本地連接埠 開啟線程與遠程主機通訊,當一個請求到達時,使用proxy_handler()函數處理之,並發送到遠程主機。
def server_loop(local_host, local_port, remote_host, remote_port, receive_first): server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 對綁定連接埠做錯誤檢查 try: server.bind((local_host, local_port)) except: print("[!!] Failed to listen on %s:%d" % (local_host, local_port)) print("[!!] Check for other listening sockets or correcct permissions") sys.exit(0) print("[*] Listening on %s:%d" % (local_host, local_port)) server.listen(5) while True: client_socket, addr = server.accept() # 列印出串連進來的IP和port print("[==>] Received incoming connection from %s:%d" % (addr[0], addr[1])) # 開啟一個線程與遠程主機通訊 proxy_thread = threading.Thread(target=proxy_handler, args=(client_socket, remote_host, remote_port, receive_first)) proxy_thread.start()proxy_handler() 串連遠程主機。 如有必要,先從主機接受資料,使用response_handler()函數處理資料,並將其傳給本地。 實現資料從 本地->代理->遠程主機 的功能。
def proxy_handler(client_socket, remote_host, remote_port, receive_first): # 串連遠程主機 remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) remote_socket.connect((remote_host, remote_port)) # 如果必要,從遠程主機接受資料 if receive_first: remote_buffer = receive_from(remote_socket) hexdump(remote_buffer) # 發送給我們的響應處理 remote_buffer = response_handler(remote_buffer) # 如果我們有資料傳遞給本地用戶端,發送之 if len(remote_buffer): print("[<==] Sending %d bytes to localhost." % len(remote_buffer)) client_socket.send(remote_buffer) # 現在我們從本地迴圈讀取資料,發送給遠程主機和本地主機 while True: # 從本地提取資料 local_buffer = receive_from(client_socket) if len(local_buffer): print("[==>] Received %d bytes from localhost." % len(local_buffer)) hexdump(local_buffer) # 發送給我們的本地請求 local_buffer = request_handler(local_buffer) # 向遠程主機發送資料 remote_socket.send(local_buffer) print("[==>] Sent to remote") # 接收響應的資料 remote_buffer = receive_from(remote_socket) if len(remote_buffer): print("[<==] Received %d bytes from remote." % len(remote_buffer)) hexdump(remote_buffer) # 發送到響應處理函數 remote_buffer = response_handler(remote_buffer) # 將響應發送到本地socket client_socket.send(remote_buffer) print("[<==] Sent to localhost.") # 如果兩邊都沒有資料,關閉串連 if not len(local_buffer) or not len(remote_buffer): client_socket.close() remote_socket.close() print("[*] No more data. Closing Connections.") breakrequset_handler() 修改請求包(依據實際需要添加函數功能)
# 對目標是遠程主機的請求進行修改def requset_handler(buffer): # 執行包修改 return buffer
response_handler() 修改響應包(依據實際需要添加函數功能)
# 對目標是本地主機的響應進行修改def response_handler(buffer): # 執行包修改 return buffer
receive_from() 使用recv()接收並返回資料
def receive_from(connection): buffer = '' # 我們設定了兩秒的逾時,這取決於目標的情況,可能需要調整 connection.settimeout(2) try: # 持續從緩衝中讀取資料直到沒有資料或者逾時 while True: data = connection.recv(4096) if not data: break buffer += data except: pass return buffer
hexdump() 漂亮的十六進位匯出函數
def hexdump(src, length=16): result = [] digits = 4 if isinstance(sec, unicode) else 2 for i in xrange(0, len(src), length): s = src[i:i + length] hexa = b''.join(["%0*X" % (digits, ord(x)) for x in s]) text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s]) result.append(b"%04X %-*s %s" % (i, length * (digits + 1), hexa, text)) print(b'\n'.join(result))