初始代码审计
一、安全的本质
- 安全的本质就是信任
- 信任导致的安全问题
- 是否该信任普通用户的输入—>前台漏洞
- 是否该信任管理员用户的输入—>后台漏洞
- 是否该信任升级包、离线升级、在线升级、自动化升级—>供应链攻击
- 不信任任何输入—>对输入进行检测
- 实际上就是把信任关系绑定到了对输入检测逻辑上,一旦输入检测逻辑出现了问题,信任关系就被打破,就出现了漏洞
二、安全问题模型化
-
将复杂的安全问题抽象成简单的模型,整体的流程就是输入—>检测是否有安全风险—>输出
-
举例
输入 检测 输出 NDR(网络检测与响应) 流量、pcap包等 waf、正则、流量解析、文件分析、威胁情报等等 正常流量无动作,恶意流量阻断 EDR(终端检测与响应) 进程事件、文件事件、主机API调用 父子进程关系异常、非常规API调用、敏感文件读取 异常阻断杀死进程 WAF(web应用防火墙) api调用 是否包含xss、sql注入等关键字(包括AI检测) 中断该次异常连接 WEBSHELL查杀 文本文件 含恶意关键字、数据进入敏感函数 隔离、删除 -
我们能掌控的只有输入,挖掘漏洞最好的入口点,也是也只能从输入去入手,结合不同语言的特点,跟踪传播链条是否有漏洞
三、控制和数据
-
我们要搞清楚程序希望用户输入什么
-
我们将程序员的代码分割为两个部分
<html> <script> console.log("my name is :" + "<?php echo $_GET['glc'];?>") </script> </html> 对于内部php语言来说,glc为数据流,echo $_GET['glc']为控制流 对于外部的JS语言my name is :" + "<?php echo $_GET['glc'];?>是数据流,console.log为控制
- 一部分是控制代码走向的控制流代码
- 一部分是用来被展示、被存储、被流转的数据流(包括输入数据,和程序员本来就硬编码的数据)
由此可得程序员希望用户输入的一定是数据流,而不是控制流
-
所以一旦我们输入的数据能够以某种方式侵入到控制流时,漏洞就产生了
四、简单分析漏洞产生原因
1.SQL注入
<?php
$db = init_db();
$username = $_GET['username']; //input: glc' and 1=1#
$db->query("select * from table where username = '$glc'");
?>
-
输入流转:输入—>php字符串变量—>sql语句—>数据库
-
其实这里与php语言无关了,编程语言只是我们用来表述这件事情的,我们要考虑的是数据库层面。在数据库层面控制流是sql语句,整个控制流程程序员的原意是这样的
action(动作): select object(对象): table subject(目标客体): * condition(条件): key: username value: $glc // 用户输入
在这里程序员应该通过编程保证用户的输入只能影响结构中的value位置。如果不能保障,就会出现漏洞
action(动作): select object(对象): table subject(目标客体): * condition(条件): expression: and(逻辑与) key1: username value1: $glc // 用户输入 glc key2: 1 value2: 1
-
我们在代码层(php)的输入,导致了数据库(mysql)层的数据流入侵到了控制流
2.SSTI
- 服务端模板注入(Server-Side Template Injection)也可以用相同的方式来理解
- Twig是php的一套模板渲染的组件,但是不规范的渲染参数输入方式,可能导致模板注入
<?php
require_once dirname(__FILE__).'\twig\lib\Twig\Autoloader.php';
Twig_Autoloader::register(true);
$twig = new Twig_Environment(new Twig_Loader_String());
$output = $twig->render("Hello {{name}}", array("name" => $_GET["glc"])); // 将用户输入作为模版变量的值
echo $output;
?>
-
输入流转:输入—>php字符串变量$_GET[“glc”]—>Twig模板渲染—>发现变量{{name}}—>找到变量name的绑定—>解析字符串—>渲染展示
-
错误的写法
<?php require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php'; Twig_Autoloader::register(true); $twig=newTwig_Environment(newTwig_Loader_String()); $output=$twig->render("Hello {$_GET['glc']}");// 将用户输入作为模版内容的一部分 echo $output; ?>
-
输入流转:输入—>php字符串变量KaTeX parse error: Expected '}', got 'EOF' at end of input: …—>拼接字符串"Hello {GET[‘name’]}"作为新变量—>Twig模板渲染—>渲染展示
-
我们在代码层(php)的输入,导致了Twig模板层的数据流入侵到了控制流
3.命令执行
<?php
$domain = $_GET["domain"]; //input:baidu.com";whoami
echo system('ping "'.$domain.'"');
?>
-
程序员的原意,在bash程序中
execute: process: exe: tracert —> /bin/tracert arg: $domain
-
在这里程序员应该通过编程保证用户的输入只能影响结构中的arg位置。如果不能保障,就会出现漏洞
ping "baidu.com";whoami execute: process1: exe: tracert arg: "baidu.com" or —> process2: exe: whoami arg: -
-
我们在代码层(php)的输入,导致了bash程序层的数据流入侵到了控制流
4.业务流程问题
-
很多逻辑漏洞、越权漏洞,往往来自于此。也是现代mvc结构会出现的比较多的问题
-
过度信任用户输入
<?php $user_id = int($_GET['user_id']); $db = init_db(); $data = $db->query("select * from user where user_id = $user_id"); echo parse_user_profile($data); ?>
这是一个典型的越权读取原型了,在业务逻辑上的校验有缺陷,信任了用户输入的user_id,导致漏洞的产生
-
不信任用户输入但是信任检测逻辑
//login.php?password=test&user_id=123 <?php session_start(); $password = "test"; $_SESSION['is_login'] = 0; if ($_GET['password'] == $password){ $_SESSION['is_login'] = 1; $_SESSION['user_id'] = $_GET["user_id"]; // 假定管理员user_id=0 header("location: /user.php?is_login=1"); exit(); }else{ echo "密码错误"; exit(); } ?>
//user.php <?php header("content-type: text/html; charset=utf-8"); if ($_SESSION['is_login'] == $_GET['is_login']){ //NULL == NULL echo "你登录了"; if ($_SESSION['user_id'] == 0){ //NULL == 0 echo "你是管理员"; }else{ echo "你不是管理员,付钱"; } }else{ echo "没登陆"; } ?>
可以看到整个流程里面,开发者完全没有信任用户的输入,对密码进行了校验,校验通过才存session,并且用户id也是存储于session中的,没法通过cookie伪造绕过鉴权。实际上就是把信任关系绑定到了对输入检测逻辑上,一旦输入检测逻辑出现了问题,信任关系就被打破,就出现了漏洞
-
php是弱类型的语言,在php下,NULL == false == 0 == “”;而$_SESSION的取值来自于cookie中PHPSESSION的输入,如果我们不输入cookie,这里就完全绕过这个校验
-
业务流程检测逻辑不规范导致漏洞