安全的本质
以下知识仅为个人学习过程中的记录
安全的本质是信任问题。--道哥
⼀切的安全⽅案设计的基础,都是建⽴在信任关系上的。我们必须相信⼀些东⻄,必须要有⼀ 些最基本的假设,安全⽅案才能得以建⽴。反之,如果我们否定⼀切,安全⽅案就会变成⽆源之⽔、⽆本之⽊,⽆法设计,也⽆法完成。 --道哥
eg
- 信任普通用户的输入会导致前台漏洞
- 信任管理员的输入会导致后台漏洞
- 信任升级包、离线升级等会导致供应链攻击
那么不信任任何用户的输入呢?难道这不是无源之水吗?
其实这个是将信任关系转移到了对于输入的检测逻辑上,在这种情况下,一旦输入检测逻辑出现问题,信任关系就会被打破,从而出现漏洞。
所有的安全模型都可以简化为:输入 -> 检测输入逻辑 -> 输出
像是WAF、IDS、IPS、EDR等都是对输入进行安全检测,进而输出是否安全的结果
结论
通过上述的简单阐述,可以看到一切都是跟输入息息相关的,对于我们而言,我们可以掌控和能掌控的只有输入,所以挖掘漏洞的最好的入手点就是从输入去入手
在代码审计的过程中,要紧紧抓住我们可控的输入,逐步跟踪传播链条判断是否有撸点
程序希望用户输入什么
控制流与数据流
<html>
<body>Hello my name is :
<?php
echo $_GET["name"];
?>
</body>
</html><html>
<script>
console.log("Hello my name is :" + "<?php echo $_GET['name']; ?>")
</script>
</html>第一部分的代码是控制代码走向的控制流代码
第二部分的代码是展示数据的数据流代码,不具备控制代码走向的能力
开发者一定希望用户输入的是数据流的代码,当用户能够控制代码走向,那么将导致程序出现不可预料的结果,这是他们不愿意看到的。
同时这种不可预料的结果,往往伴随着漏洞点的出现,所以当我们可以通过数据流侵入控制流时,漏洞就产生了。
SQL注入
<?php
$db = init_db();
$username = $_GET['username']; //input: fuck' and 1=1#
$db->query("select * from table where username = '$username'");
?>输入的流转:输入->PHP的字符串->SQL语句->数据库查询
在整个数据流流转的过程中,其实我们可以发现任何语言都可以形成这种查询结构,不管是PHP、Java、Python等任何编程语言,所以这时候我们可以明白这种结构跟用什么编程语言是没有关系的,编程语言仅仅是我们用来表达这个结构的,我们真正要考虑的是数据库的层面。
在数据库层面:控制流是SQL语句
action: select
object: table
subject: *
condition:
key: username
value: $username # 输入要确保程序始终执行原意,那么开发者就需要在编写程序时对输入进行限制,确保用户输入只能影响value的位置,如果没有确保这个条件,那么漏洞就会出现
action: select
object: table
subject: *
condition:
key1: username
value1: $username # 输入jack
key2: 1 # and 1=1
value2: 1我们在代码层的输入,在数据库层面的数据流侵入到了控制流,从而造成了SQL注入。原本数据库的数据流就是只输入jack的值,结果为select * from table where username = 'jack',但是却变成select * from table where username = 'jack' and 1=1#,从数据流侵入控制流,让输出出现了不可预料的结果。
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["name"])); // 将⽤户输⼊作为模版变量的值
echo $output;
?>输入的流转:输入->$_GET["name"]->Twig渲染字符串->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['name']}");// 将⽤户输⼊作为模版内容的⼀部分
echo $output;
?>输入的流转:输入->$_GET["name"]->拼接name为新的PHP字符串->字符串内容被Twig渲染展示
上面两种写法觉得哪种存在毛病,或者说可能存在漏洞?
第一种写法是先经过Twig框架渲染再将变量渲染进去,第二种写法是先将变量拼接为一个新字符串,然后再将整个字符串通过Twig渲染。
第一种方法遵循了框架的写法,通常遵循框架写法的方式基本没有漏洞。
模版引擎一般都默认对渲染的变量值进行编码和转义,所以并不会造成跨站脚本攻击
而第二种方法它是先拼接后渲染,并没有作为模板渲染的变量进行渲染,所以说没有做安全过滤,故而第二种写法存在漏洞,可以进行XSS攻击。
同样的,此处模板渲染的过程中并非只有PHP的代码能够实现SSTI漏洞,所有的编程语言都可能存在这个漏洞,PHP只是我们表述这个漏洞的一种方式。
在此处,我们在代码层的输入,造成了Twig模板层中数据流侵入到控制流,从而导致SSTI。
命令执行RCE
<?php
$domain = $_GET["domain"]; //input:baidu.com";whoami;echo "fuck
echo system('ping "'.$domain.'"');
?>程序的原意
execute:
process:
exe: ping --> /bin/ping
arg: $domain在此处,开发者应该保证用户输入仅能影响arg的值,如果没有做到这点,那么就会造成漏洞的出现。
execute:
process1:
exe: ping --> /bin/ping
arg: $domain
process2:
exe: whoami --> /bin/whoami
arg: -
process3:
exe: echo
arg: "fuck"同样任何编程语言都存在这种写法,也存在这种漏洞,PHP仅为我们表述的一种方式。
我们在代码层的输入导致了bash层的数据流侵入控制流,进而造成了RCE。
总结
漏洞并非一种语言独有,任何编程语言都是一种表述方式,我们的关注点要放在数据流和控制流上,一旦数据流可以侵入到控制流,那么信任关系就被打破,漏洞就会出现。
业务流程问题
除了上述的数据流侵入到控制流,还有一些业务流程、逻辑上的问题。
很多逻辑漏洞、越权漏洞,往往来⾃于此。
这也是现代MVC结构会出现的比较多的问题。
现代mvc结构⼀般不会出现sql注⼊、xss、webshell上传,这是由于他们的框架已经自己集成了对于这些攻击的过滤,只要按照框架来写,自然就很少会出现这些漏洞。当然,不按框架写还是会出现这些漏洞的。
过度信任用户输入
<?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数据进行int转换,虽然不存在SQL注入问题,但是过度信任用户的输入,导致用户输入任何数据都可以进行查询(越权),进而造成了逻辑漏洞。
不信任用户输入-信任检测逻辑
那我们换一种方式,不信任用户的所有输入,转而信任开发者编写的检测逻辑。
<?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();
}
?>// http://192.168.215.129/login.php?password=test&user_id=123
<?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,这⾥就完全绕过这个校验。
为什么呢?因为没有输入Cookie,那么$_SESSION的值为NULL,同时is_login的值也不输入,它也为NULL,进一步发现$_SESSION中的user_id也为NULL,所以就饶过了所有的校验。
业务流程检测逻辑不规范导致漏洞,是代码审计过程中最常遇到的。
代码审计的最终心法
内容只有两点:
- 代码审计过程中能否让数据流侵入到控制流
- 代码审计过程中,业务逻辑可能存在问题的点有哪些?(如何快速通过污点分析找出有问题的的业务?)
评论 (0)