紧紧抓住输入

N0va7
2025-09-10 / 0 评论 / 3 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2025年09月10日,已超过266天没有更新,若内容或图片失效,请留言反馈。

安全的本质

以下知识仅为个人学习过程中的记录

安全的本质是信任问题。--道哥

⼀切的安全⽅案设计的基础,都是建⽴在信任关系上的。我们必须相信⼀些东⻄,必须要有⼀ 些最基本的假设,安全⽅案才能得以建⽴。反之,如果我们否定⼀切,安全⽅案就会变成⽆源之⽔、⽆本之⽊,⽆法设计,也⽆法完成。 --道哥

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)也可以⽤相同的⽅式来理解。

Twigphp的⼀套模板渲染的组件,但是不规范的渲染参数输⼊⽅式,可能导致模板注⼊。

<?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的取值来⾃于cookiePHPSESSION的输⼊,如果我们不输⼊cookie,这⾥就完全绕过这个校验。

为什么呢?因为没有输入Cookie,那么$_SESSION的值为NULL,同时is_login的值也不输入,它也为NULL,进一步发现$_SESSION中的user_id也为NULL,所以就饶过了所有的校验。

业务流程检测逻辑不规范导致漏洞,是代码审计过程中最常遇到的

代码审计的最终心法

内容只有两点:

  1. 代码审计过程中能否让数据流侵入到控制流
  2. 代码审计过程中,业务逻辑可能存在问题的点有哪些?(如何快速通过污点分析找出有问题的的业务?)
0

评论 (0)

取消