常见漏洞分析
本课程深入解析常见网络安全漏洞,包括漏洞识别、防御技术、风险管理和攻防演练等关键内容。

总字符数: 10.40K

代码: 3.73K, 文本: 5.40K

预计阅读时间: 40 分钟

安全工程师的核心

在信息安全领域,我们没有安全与否的选择,只有风险权衡的选择

​ —-达拉·卡恩(Dan Geer)

这句话强调了安全是一个持续的权衡过程,并且对于安全理解的深度以及由此引申的看待安全问题的角度和高度具有重要意义.

安全工程师的定义是什么

  1. 体(安全本质):这部分涉及到安全的核心和基础.具体而言,它包括培养安全思维的方法、深入探究漏洞产生的根本原因以及理解漏洞的共性特点.理解安全的本质有助于建立更为健壮的安全体系,从根本上解决安全问题.
  2. 用(思考者):这一部分强调在安全工作中需要运用的思维方式.这包括挖掘漏洞时的思维方式、进行代码审计时的思维方式以及在安全开发中需要采用的思维方式.这些思维方式有助于更好地理解和应对安全挑战,而且在各种安全工作中都有一些共性.
  3. 术(一线技巧):这部分主要关注在利用漏洞时所使用的工具、代码和框架的选择.具体来说,就是为了攻击不同的漏洞和目标网站,攻击者需要选择合适的工具和技术.这还包括一些”骚操作”和技巧,可以帮助攻击者更有效地利用漏洞.

安全的本质

一切的安全方案设计的基础,都是建立在信任关系上的.我们必须相信一些东西,必须有一些最基本的假设,安全方案才能得以建立;如果我们否定一切,安全方案就会如无源之水,无根之木,无法设计,也无法完成. —道哥

  1. 是否信任普通用户的输入? (前台漏洞)
    • 信任水平: 通常不完全信任.前台漏洞涉及用户直接与应用程序交互的部分,因此需要对用户输入进行适当的验证和过滤,以防止恶意输入和攻击.
  2. 是否信任管理员用户的输入? (后台漏洞)
    • 信任水平: 一般来说,相对信任.管理员用户通常拥有更高的权限,但仍需要进行输入验证和过滤,以防止误操作或滥用权限.
  3. 是否信任升级包、离线升级、在线升级、自动化升级? (供应链攻击)
    • 信任水平: 警惕供应链攻击.对于升级包和各种形式的软件更新,需要确保来自可信源的数字签名和验证.供应链攻击可能通过篡改升级包来引入恶意代码,因此对升级的源头要有高度的信任.
  4. 不信任任何输入 (信任对输入的检测逻辑)(逻辑漏洞)
    • 信任水平: 谨慎使用.即便不信任用户输入,也要确保对输入数据的检测逻辑是可靠的.逻辑漏洞可能导致应用程序在处理输入时产生未预期的结果,因此需要仔细审查和测试输入验证的逻辑.

安全方案模型

所有的安全模型,都可以简化成一种最简易的模型.

输入–>检测是否有安全风险–>输出

在这个模型中:

  1. 输入: 表示系统接收到的各种数据和信息,通常来自用户、外部系统或其他数据源.
  2. 检测是否有安全风险: 表示系统对输入进行安全性检测,以识别和防范潜在的威胁和漏洞.这包括输入验证、身份认证、访问控制等措施.
  3. 输出: 表示系统处理输入后生成的结果.这可能是用户界面的响应、数据库更新、或其他对输入做出的具体反应.

尽管这个简单模型强调了输入的安全性检测,但在实际应用中,一个全面的安全模型需要考虑到各个层面,包括但不限于:

  • 系统架构安全: 保护整个系统的设计和组织,防范攻击和非法访问.
  • 访问控制: 控制用户和系统组件对资源的访问权限.
  • 身份认证和授权: 确保只有授权用户能够访问敏感信息和执行关键操作.
  • 加密算法: 保障数据的传输和存储安全,防止数据泄露.
  • 日志记录和监控: 收集和分析系统活动日志,及时发现和应对潜在的安全威胁.

综合考虑这些方面,一个完整的安全模型能够更全面地保障系统的稳定性和用户数据的安全.

综合安全防御体系

  1. 输入:
    • NDR (IPS/IDS): 流量和 pcap 包,网络层面的数据.
    • EDR: 进程事件、文件事件、主机 API 调用,主机层面的活动.
    • Webshell 查杀: 文本文件,例如源代码或脚本文件.
    • WAF: API 调用,通常是 HTTP 请求.
  2. 检测:
    • NDR (IPS/IDS):
      • 利用 WAF、正则表达式、流量解析、文件分析、威胁情报等手段,检测网络流量中的异常行为.
      • 行为分析,通过分析网络流量中的异常行为来检测潜在的威胁.
      • 结合机器学习算法,提高检测准确性.
    • EDR:
      • 通过监控父子进程关系、检测关系异常、观察非常规 API 调用、敏感文件读取等方式,检测主机上的异常行为.
      • 可能包括沙箱分析,以隔离和分析潜在的威胁.
      • 行为分析,包括检测异常登录模式、异常文件访问模式等.
    • Webshell 查杀:
      • 通过检查是否包含恶意关键字、数据进入是否可疑、是否调用了敏感函数等方式,对文本文件进行恶意代码的检测.
      • 可能包括对文件进行静态和动态分析,以识别隐藏的威胁.
      • 可能使用基于签名和行为的检测方法.
    • WAF:
      • 通过检查 HTTP 请求中是否包含 XSS、SQL 注入等关键字,可能包括使用 AI 进行检测.
      • 对网络流量进行深度包检测,以检测更复杂的攻击.
      • 支持定制规则,允许根据特定应用程序的需求进行定制化.
  3. 输出:
    • NDR (IPS/IDS): 对正常流量无动作,对恶意流量进行阻断.
    • EDR: 对异常进行阻断,可能需要杀死相关进程.
    • Webshell 查杀: 对恶意文件进行隔离或删除.
    • WAF: 对异常连接进行中断,防止攻击成功.

这个整合的安全防御体系能够在多个层面上协同工作,形成更为全面和强大的安全保护.在实际应用中,还需要考虑日志管理、事件响应、持续监控等方面的技术和流程.

程序的输入

  1. “所有的输入都是恶意的”,是一种过于绝对和悲观的观点.虽然黑客和攻击者会尝试利用系统的弱点进行攻击,但并不是所有的输入都有恶意,大多数输入是合法的且有用的.因此,我们需要通过输入验证、身份认证、访问控制等手段来筛选和过滤输入数据,以确保系统安全.

  2. “一切都与输入息息相关”,则更准确地描述了安全设计中输入的重要性.输入是系统中最重要的组成部分之一,也是漏洞存在的主要因素.从输入入手能够帮助我们挖掘系统的漏洞和弱点,进一步完善安全措施.因此,对输入进行充分的检测和过滤至关重要.

Tip:

  1. 紧紧抓住输入:

    • 重点关注用户输入: 用户输入通常是潜在的安全风险来源.这包括通过表单、URL 参数、文件上传等方式输入的数据.

    • 考虑各种输入场景: 不仅仅局限于用户直接输入,还需要考虑通过 API 调用、数据库查询等方式引入的数据.

  2. 结合不同语言的特点:

    • 理解语言的特性: 每种编程语言都有其独特的特点和安全风险.例如,在 PHP 中可能涉及到变量覆盖,而在 SQL 中可能存在 SQL 注入的风险.

    • 考虑框架和库的影响: 如果应用程序使用了特定的框架或库,需要了解其特有的安全考虑,因为这些框架和库可能会有自己的安全隐患和最佳实践.

  3. 跟踪传播链条是否有漏洞:

    • 了解信息流的传播路径: 想象用户输入如何通过应用程序的不同部分传播.从接收输入的地方开始,跟踪数据如何在系统内传递和处理.

    • 关注数据的转换和处理: 注意输入数据在系统中的处理过程,包括解析、验证、处理逻辑等环节.

  4. 审计传播链是否有撸点:

  • 查找潜在的撸点: 在传播链中寻找潜在的漏洞和弱点.这可能涉及到未经检查的用户输入、缺乏适当的数据验证、绕过机制的可能性等.

  • 关注漏洞的连锁反应: 如果在传播链中的某个环节发现了漏洞,考虑它可能对系统其他部分的影响,因为漏洞往往会在整个传播链上传播.

通过紧密关注输入、理解编程语言的特性,并深入审计数据传播链,审计人员可以更全面地发现和理解潜在的安全漏洞,从而提高代码审计的效果.

控制流与数据流

控制流:

定义: 控制流描述了程序中代码执行的路径和流程.它由条件语句(如ifswitch)和循环语句(如forwhile)以及函数调用等构成.

重要性:

  • 程序逻辑: 控制流决定了程序的逻辑结构,即哪些代码块会被执行,执行的顺序是怎样的.
  • 条件执行: 通过条件语句,可以根据不同的条件选择执行不同的代码块,实现灵活的程序行为.
  • 循环控制: 循环语句允许一部分代码块重复执行,实现对某些任务的重复处理.
示例
1
2
3
4
5
6
7
if x > 0:
print("x is positive")
else:
print("x is non-positive")

for i in range(5):
print(i)

数据流:

定义: 数据流描述了程序中数据的传递和变化.它包括对变量、常量、数组、对象等数据进行定义、赋值、使用和传递的操作.

重要性:

  • 数据传递: 数据流展示了数据在程序中的传递路径,表达了数据从一个地方到另一个地方的流动.
  • 变量状态: 通过数据流,可以了解变量在程序执行过程中的状态变化,追踪数据的来源和去向.
  • 数据处理: 数据流揭示了数据的处理方式,例如对数据的计算、转换和处理等.
示例
1
2
3
x = 10
y = x * 2
z = y + 5

通过理解控制流和数据流,程序员可以更好地设计和理解程序的结构,代码审计人员也可以通过分析这两个流程来识别潜在的漏洞和安全风险.

1
2
3
4
5
<html>
<body>
Hello My Name is: <?php $_GET['name'];?>
</body>
</html>

我们将程序员的代码划分为两个部分:控制流代码和数据流代码.

  1. 控制流代码用于控制程序的执行路径,决定代码走向和逻辑流程;
  2. 数据流则包括输入数据和程序员事先编写的硬编码数据,用于展示、存储和传递数据.

程序员希望用户输入的一定是数据流,而不是控制流

一旦我们输入的数据 能够以某种方式侵入到控制流时,漏洞就产生了

SQL注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
// 定义数据库连接参数
$hostname = 'localhost'; // 数据库主机名
$username = 'root'; // 数据库用户名
$password = 'root'; // 数据库密码
$database = 'frontenddb'; // 数据库名

// 使用 mysqli_connect() 函数建立数据库连接
$conn = mysqli_connect($hostname, $username, $password, $database);

// 从 URL 参数中获取 'id' 参数
$id = $_GET['id'];

// 构造 SQL 查询语句,查询 'users' 表中 'id' 列等于传入的 'id' 参数的数据
$sql = "SELECT * FROM users WHERE id = '$id'";

// 使用 mysqli_query() 函数执行 SQL 查询,并将结果保存在 $result 变量中
$result = mysqli_query($conn, $sql);
?>

我们来看输入流:

1
输入?id---> php字符串变量`$id`--->sql语句 `id = '固定的值(用户输入的)'`--->数据库

这里与语言无关,编程语言知识我们用来表述这就事情的,要考虑的是数据库层面

控制流是SQL语句,整个控制流程,程序员的原意是这样的

1
2
3
4
5
6
7
?id='1 select * from user -- - 
动作: select
对象: users
目标: *
条件:
key:id
value:$id // 用户输入

在这里程序员应该通过编程保证用户的输入只能影响结构中的value位置,如果不能保障此结构,就会出现漏洞

假如我们用户输入的是2' and 1=1 #,则到达数据库执行的语句则是select * from users where id = '2' and 1=1 #'

1
2
3
4
5
6
7
8
9
动作:  select
对象: users
目标: *
条件:
and(逻辑与)
key:id
value:$id // 用户输入2'
key2:1
value2:1

我们在代码层的输入,导致了数据库层的数据流入侵到了控制流

SSTI

服务器端模板注入也可以用相同的方式来理解

Twig是php的一套模板渲染的组件,但是不规范的渲染参数方式,可能导致模板注入.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
// 引入 Twig 模板引擎的自动加载器
require_once dirname(__FILE__).'/twig/lib/Twig/Autoloader.php';
// 注册 Twig 自动加载器
Twig_Autoloader::register(true);
// 初始化 Twig 模板环境,并指定 Twig_Loader_String 作为加载器
$twig = new Twig_Environment(new Twig_Loader_String());

// 从 GET 请求中获取用户输入的 name 参数,并使用 htmlspecialchars 函数进行 HTML 实体转义
// ENT_QUOTES 表示同时转义单引号和双引号,使用 'UTF-8' 确保字符编码正确处理
$userName = htmlspecialchars($_GET["name"], ENT_QUOTES, 'UTF-8');

// 使用 Twig 的 render 方法渲染模板,传入之前转义后的用户名作为模板变量
// "Hello {{name}}" 是模板字符串,其中 {{name}} 会被替换为传入的变量值
$output = $twig->render("Hello {{name}}", array("name" => $userName));
// 输出渲染后的模板内容
echo $output;
?>

数据流转:

1
输入--->php字符串变量`$_GET['name']`--->Twig模板渲染--->发现变量({{name}})--->找到变量name的绑定--->解析字符串--->渲染展示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
// 引入 Twig 模板引擎的自动加载器
require_once dirname(__FILE__).'/twig/lib/Twig/Autoloader.php';
// 注册 Twig 自动加载器
Twig_Autoloader::register(true);

// 创建 Twig 环境并设置模板加载器,这里使用字符串作为模板
$twig = new Twig_Environment(new Twig_Loader_String());

// 从用户输入中获取 name 参数
// 注意这里存在安全隐患,未对用户输入进行转义,直接嵌入模板,可能会导致 XSS 攻击
$output = $twig->render("Hello {$_GET['name']}"); // 错误地将用户输入直接嵌入到模板中

// 输出渲染后的内容,如果用户输入包含恶意脚本,将执行这些脚本
echo $output;
?>
1
2
数据流转:
输入--->php字符串变量`$_GET['name']`--->拼接字符串`Hello {$_GET['name']}`作为新变量-->Twig模板渲染--->渲染展示

这样就可以进行攻击输入了

1
{{_self.env..registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id ")}}

我们在代码层的输入,导致了Twig模板层的数据流入侵到了控制流

命令执行

1
2
3
4
5
6
7
<?php
# 从用户输入中获取 admin 参数
$domain = $_GET["admin"];
# 使用 system 函数执行 ping 命令
echo system('ping '.$domain);
?>

命令执行是一个常见的功能,特别是在路由器或其他设备产品中.它用于测试网络联通性和性能.

程序员的原意:在bash中:

1
2
exec:ping
arg:$domain

在这里程序员应该通过编程保证用户的输入只能影响结构中的arg位置,如果不能保障结构,就会出现漏洞

1
2
3
4
5
6
7
8
ping baidu.com|whoami
exec:
process1:
exe:ping
arg:baidu.com
process2:
exe:whoami
arg:-

我们在代码层的输入,导致了bash程序层的数据流入侵到了控制流

业务流程问题(逻辑漏洞)

很多逻辑漏洞、越权漏洞,往往来自于此.也是现代mvc结构会出现比较多的问题

过度信任用户输入

业务功能:输出当前用户的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
# 数据库连接参数
$hostname = 'localhost';
$username = 'root';
$password = 'root';
$database = 'frontenddb';

# 连接数据库
$conn = mysqli_connect($hostname, $username, $password, $database);

# 从用户输入中获取 user_id 参数,并将其转为整型
$user_id = intval($_GET['user_id']);

# 构造 SQL 查询语句
$sql = "select * from users where user_id = $user_id";

# 执行 SQL 查询
$result = mysqli_query($conn, $sql);
?>

这是一个典型的越权读取原型了,虽然此处没有产生SQL注入问题,但是在业务逻辑上的校验有缺陷,信任了用户输入的user_id,导致漏洞的产生

不信任用户输入—>信任检测逻辑

login.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
// 启动会话
session_start();

// 预设密码
$password = "test";

// 初始化登录状态
$_SESSION['is_login'] = 0;

// 检查密码是否正确
if ($_GET['password'] == $password) {
// 设置登录状态为已登录
$_SESSION['is_login'] = 1;

// 设置用户 ID(假定管理员 user_id = 0)
$_SESSION['user_id'] = $_GET["user_id"];

// 跳转到用户页面
header("location:/user.php?is_login=1");
exit();
} else {
// 密码错误的情况
echo "密码错误";
exit();
}
?>

// http://192.168.215.129/login.php?password=test&user_id=123

// user.php

<?php
// 设置响应内容类型及字符集
header("content-type:text/html;charset=utf-8");

// 启动会话
session_start();

// 检查登录状态和参数是否匹配
if ($_SESSION['is_login'] == $_GET['is_login']) {
// 已登录
echo "你登录了";

// 判断用户是否为管理员
if ($_SESSION['user_id'] == 0) {
echo "你是管理员";
} else {
echo "你不是管理员,付钱";
}
} else {
// 未登录的情况
echo "没登陆,滚";
}
?>

这是一个经典的逻辑绕过漏洞,我们可以看到整个流程里面,开发者完全没有信任用户的输入.

对密码进行了校验,校验通过才存Session,并且用户id也是存储于Session中的,没法通过Cookie伪造绕过鉴权

不信任任何输入—>对输入进行检测(实际上就是把信任关系绑定到了对输入检测逻辑上,一旦输入检测逻辑出现了问题,信任关系就被打破,就出现了漏洞)

这套代码就是对输入的检测逻辑出现了漏洞.

php是弱类型的语言,在PHP下,NULL==false==0==””

$_SESSION的取值来自于Cookie中PHP Session的输入,如果我们不输入Cookie,这里就完全绕过这个校验.

业务流程检测逻辑不规范导致漏洞,是大家今后进行代码审计最经常遇到的.因为业务情况不同,遇到的情况多种多样.

总结与讨论

体-用-术看待问题

  1. 体:
    • 简单说: 想象漏洞就像污点一样,从某个地方开始,然后在系统中传播.要找到问题,就得分析这个污点的源头(输入触发)、最后停留的地方(汇聚点)、以及中途是如何传播的(传播链gadget).
    • 比喻: 想象成一滴颜料滴在水池中,我们要找到它是从哪儿来的、最后停在哪儿了,以及在水中是怎么扩散的.
  2. 用:
    • 简单说: 着重总结那些常常被忽视的入口,考虑到不同编程语言可能有一些小巧妙的”技巧”,特别是在使用PHP等语言时,有很多绕过的方法.
    • 比喻: 想象成在房子中找隐藏的门,有些可能平时被忽视了.而在不同的房间(编程语言)里,有些找门的方法可能会有点”奇技淫巧”.
  3. 术:
    • 简单说: 使用各种工具,就像用一把大型探测器一样,先扫描系统中有哪些可能的问题,然后再深入看代码,找到具体的漏洞.
    • 比喻: 像在房间中使用各种仪器,先找到哪里可能有问题,再仔细检查每个角落,确保没有隐藏的风险.

紧紧抓住输入

代码审计中的两个主要方面:数据流到控制流的逃逸业务逻辑可能产生问题的点.让我们深入了解这两点:

数据流到控制流的逃逸

  • 定义: 数据流到控制流的逃逸指的是通过恶意输入或错误处理导致的数据控制权转移到程序的控制流中,可能导致漏洞或不安全的行为.
  • 审计重点: 在代码审计中,需要特别关注用户输入进入程序时,如何被处理并如何影响程序的控制流.这可能包括对输入验证、过滤和编码的审查,以及对程序中涉及控制流判断的地方的仔细分析.

业务逻辑可能产生问题的点

  • 定义: 业务逻辑问题指的是与程序预期功能不符或可能导致安全问题的程序行为.
  • 审计重点:
    • 使用污点分析:通过跟踪数据流中的污点,可以识别出哪些输入影响了关键的控制流判断点.
    • 关注敏感操作:审计人员应关注可能导致业务逻辑问题的关键功能点,例如身份验证、权限检查、加密和解密操作等.
    • 了解业务规则:深入理解应用程序的业务规则和预期行为,以便在审计过程中更好地识别异常或潜在的漏洞.

通过这两个方面的审计,审计人员可以更全面地理解应用程序的运行方式,并识别潜在的安全风险和漏洞.

在审计过程中,结合合适的工具和技术,以及深入的污点分析,可以更快速地筛选出问题,并提供有针对性的建议和修复方案.