从补丁到漏洞分析 –记一次joomla漏洞应急
作者:LoRexxar’@知道创宇404实验室
2018年1月30日,joomla更新了3.8.4版本,这次更新修复了4个安全漏洞,以及上百个bug修复。
https://www.joomla.org/announcements/release-news/5723-joomla-3-8-4-release.html
为了漏洞应急这几个漏洞,我花费了大量的时间分析漏洞成因、寻找漏洞触发位置、回溯逻辑,下面的文章比起漏洞分析来说,更接近我思考的思路,希望能给大家带来不一样的东西。
背景
其中的4个安全漏洞包括
1 |
<ol class="linenums"><li class="L0"><code><span class="pun">-</span><span class="pln"> </span><span class="typ">Low</span><span class="pln"> </span><span class="typ">Priority</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="typ">Core</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> XSS vulnerability </span><span class="kwd">in</span><span class="pln"> </span><span class="kwd">module</span><span class="pln"> chromes </span><span class="pun">(</span><span class="pln">affecting </span><span class="typ">Joomla</span><span class="pln"> </span><span class="lit">3.0</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> through </span><span class="lit">3.8</span><span class="pun">.</span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span></code></li><li class="L1"><code><span class="pun">-</span><span class="pln"> </span><span class="typ">Low</span><span class="pln"> </span><span class="typ">Priority</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="typ">Core</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> XSS vulnerability </span><span class="kwd">in</span><span class="pln"> com_fields </span><span class="pun">(</span><span class="pln">affecting </span><span class="typ">Joomla</span><span class="pln"> </span><span class="lit">3.7</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> through </span><span class="lit">3.8</span><span class="pun">.</span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span></code></li><li class="L2"><code><span class="pun">-</span><span class="pln"> </span><span class="typ">Low</span><span class="pln"> </span><span class="typ">Priority</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="typ">Core</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> XSS vulnerability </span><span class="kwd">in</span><span class="pln"> </span><span class="typ">Uri</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="pun">(</span><span class="pln">affecting </span><span class="typ">Joomla</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> through </span><span class="lit">3.8</span><span class="pun">.</span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span></code></li><li class="L3"><code><span class="pun">-</span><span class="pln"> </span><span class="typ">Low</span><span class="pln"> </span><span class="typ">Priority</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="typ">Core</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="typ">SQLi</span><span class="pln"> vulnerability </span><span class="kwd">in</span><span class="pln"> </span><span class="typ">Hathor</span><span class="pln"> postinstall message </span><span class="pun">(</span><span class="pln">affecting </span><span class="typ">Joomla</span><span class="pln"> </span><span class="lit">3.7</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> through </span><span class="lit">3.8</span><span class="pun">.</span><span class="lit">3</span><span class="pun">)</span></code></li></ol> |
根据更新,我们去到github上的joomla项目,从中寻找相应的修复补丁,可以发现,4个安全漏洞的是和3.8.4的release版同时更新的。
https://github.com/joomla/joomla-cms/commit/0ec372fdc6ad5ad63082636a0942b3ea39acc7b7
通过补丁配合漏洞详情中的简单描述我们可以确定漏洞的一部信息,紧接着通过这部分信息来回溯漏洞成因。
SQLi vulnerability in Hathor postinstall message
https://developer.joomla.org/security-centre/722-20180104-core-sqli-vulnerability.html
1 |
<ol class="linenums"><li class="L0"><code><span class="typ">Description</span></code></li><li class="L1"><code><span class="typ">The</span><span class="pln"> lack of type casting of a variable </span><span class="kwd">in</span><span class="pln"> SQL statement leads to a SQL injection vulnerability </span><span class="kwd">in</span><span class="pln"> the </span><span class="typ">Hathor</span><span class="pln"> postinstall message</span><span class="pun">.</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="typ">Affected</span><span class="pln"> </span><span class="typ">Installs</span></code></li><li class="L4"><code><span class="typ">Joomla</span><span class="pun">!</span><span class="pln"> CMS versions </span><span class="lit">3.7</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> through </span><span class="lit">3.8</span><span class="pun">.</span><span class="lit">3</span></code></li></ol> |
补丁分析
第一个漏洞说的比较明白,是说在Hathor的postinstall信息处,由于错误的类型转换导致了注入漏洞。
我们来看看相应的补丁
符合漏洞描述的点就是这里,原来的取第一位改为了对取出信息做强制类型转换,然后拼接入sql语句。
这里假设我们可以控制$adminstyle
,如果我们通过传入数组的方式设置该变量为数组格式,并且第1个字符串可控,那么这里就是一个可以成立的漏洞点。
现在我们需要找到这个功能的位置,并且回溯变量判断是否可控。
找到漏洞位置
hathor是joomla自带的两个后台模板之一,由于hathor更新迭代没有isis快,部分功能会缺失,所以在安装完成之后,joomla的模板为isis,我们需要手动设置该部分。
1 |
<ol class="linenums"><li class="L0"><code><span class="typ">Templates</span><span class="pun">-></span><span class="pln">styles</span><span class="pun">-></span><span class="pln">adminnistrator</span><span class="pun">-></span><span class="pln">hathor</span></code></li></ol> |
修改完成后回到首页,右边就是postinstallation message
回溯漏洞
回到代码中,我们需要找到$adminstyle
这个变量进入的地方。
1 |
<ol class="linenums"><li class="L0"><code><span class="pln">$adminstyle </span><span class="pun">=</span><span class="pln"> $user</span><span class="pun">-></span><span class="pln">getParam</span><span class="pun">(</span><span class="str">'admin_style'</span><span class="pun">,</span><span class="pln"> </span><span class="str">''</span><span class="pun">);</span></code></li></ol> |
这里user为JFactory::getUser()
,跟入getParam方法
1 |
<ol class="linenums"><li class="L0"><code><span class="str">/libraries/</span><span class="pln">src</span><span class="pun">/</span><span class="typ">User</span><span class="pun">/</span><span class="typ">User</span><span class="pun">.</span><span class="pln">php line </span><span class="lit">318</span></code></li><li class="L1"><code></code></li><li class="L2"><code><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> getParam</span><span class="pun">(</span><span class="pln">$key</span><span class="pun">,</span><span class="pln"> $default </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span></code></li><li class="L3"><code><span class="pun">{</span></code></li><li class="L4"><code><span class="pln"> </span><span class="kwd">return</span><span class="pln"> $this</span><span class="pun">-></span><span class="pln">_params</span><span class="pun">-></span><span class="kwd">get</span><span class="pun">(</span><span class="pln">$key</span><span class="pun">,</span><span class="pln"> $default</span><span class="pun">);</span></code></li><li class="L5"><code><span class="pun">}</span></code></li></ol> |
这里$this->_params
来自$this->_params = new Registry;
跟入Registry的get方法
1 |
<ol class="linenums"><li class="L0"><code><span class="pln">libraries</span><span class="pun">/</span><span class="pln">vendor</span><span class="pun">/</span><span class="pln">joomla</span><span class="pun">/</span><span class="pln">registry</span><span class="pun">/</span><span class="pln">src</span><span class="pun">/</span><span class="typ">Registry</span><span class="pun">.</span><span class="pln">php line </span><span class="lit">201</span></code></li><li class="L1"><code></code></li><li class="L2"><code><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">$path</span><span class="pun">,</span><span class="pln"> $default </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span></code></li><li class="L3"><code><span class="pln"> </span><span class="pun">{</span></code></li><li class="L4"><code><span class="pln"> </span><span class="com">// Return default value if path is empty</span></code></li><li class="L5"><code><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">empty</span><span class="pun">(</span><span class="pln">$path</span><span class="pun">))</span></code></li><li class="L6"><code><span class="pln"> </span><span class="pun">{</span></code></li><li class="L7"><code><span class="pln"> </span><span class="kwd">return</span><span class="pln"> $default</span><span class="pun">;</span></code></li><li class="L8"><code><span class="pln"> </span><span class="pun">}</span></code></li><li class="L9"><code></code></li><li class="L0"><code><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">strpos</span><span class="pun">(</span><span class="pln">$path</span><span class="pun">,</span><span class="pln"> $this</span><span class="pun">-></span><span class="pln">separator</span><span class="pun">))</span></code></li><li class="L1"><code><span class="pln"> </span><span class="pun">{</span></code></li><li class="L2"><code><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">isset</span><span class="pun">(</span><span class="pln">$this</span><span class="pun">-></span><span class="pln">data</span><span class="pun">-></span><span class="pln">$path</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&&</span><span class="pln"> $this</span><span class="pun">-></span><span class="pln">data</span><span class="pun">-></span><span class="pln">$path </span><span class="pun">!==</span><span class="pln"> </span><span class="kwd">null</span><span class="pln"> </span><span class="pun">&&</span><span class="pln"> $this</span><span class="pun">-></span><span class="pln">data</span><span class="pun">-></span><span class="pln">$path </span><span class="pun">!==</span><span class="pln"> </span><span class="str">''</span><span class="pun">)</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> $this</span><span class="pun">-></span><span class="pln">data</span><span class="pun">-></span><span class="pln">$path </span><span class="pun">:</span><span class="pln"> $default</span><span class="pun">;</span></code></li><li class="L3"><code><span class="pln"> </span><span class="pun">}</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="pln"> </span><span class="com">// Explode the registry path into an array</span></code></li><li class="L6"><code><span class="pln"> $nodes </span><span class="pun">=</span><span class="pln"> explode</span><span class="pun">(</span><span class="pln">$this</span><span class="pun">-></span><span class="pln">separator</span><span class="pun">,</span><span class="pln"> trim</span><span class="pun">(</span><span class="pln">$path</span><span class="pun">));</span></code></li><li class="L7"><code></code></li><li class="L8"><code><span class="pln"> </span><span class="com">// Initialize the current node to be the registry root.</span></code></li><li class="L9"><code><span class="pln"> $node </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-></span><span class="pln">data</span><span class="pun">;</span></code></li><li class="L0"><code><span class="pln"> $found </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span></code></li><li class="L1"><code></code></li><li class="L2"><code><span class="pln"> </span><span class="com">// Traverse the registry to find the correct node for the result.</span></code></li><li class="L3"><code><span class="pln"> </span><span class="kwd">foreach</span><span class="pln"> </span><span class="pun">(</span><span class="pln">$nodes </span><span class="kwd">as</span><span class="pln"> $n</span><span class="pun">)</span></code></li><li class="L4"><code><span class="pln"> </span><span class="pun">{</span></code></li><li class="L5"><code><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">is_array</span><span class="pun">(</span><span class="pln">$node</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&&</span><span class="pln"> isset</span><span class="pun">(</span><span class="pln">$node</span><span class="pun">[</span><span class="pln">$n</span><span class="pun">]))</span></code></li><li class="L6"><code><span class="pln"> </span><span class="pun">{</span></code></li><li class="L7"><code><span class="pln"> $node </span><span class="pun">=</span><span class="pln"> $node</span><span class="pun">[</span><span class="pln">$n</span><span class="pun">];</span></code></li><li class="L8"><code><span class="pln"> $found </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span></code></li><li class="L9"><code></code></li><li class="L0"><code><span class="pln"> </span><span class="kwd">continue</span><span class="pun">;</span></code></li><li class="L1"><code><span class="pln"> </span><span class="pun">}</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">isset</span><span class="pun">(</span><span class="pln">$node</span><span class="pun">-></span><span class="pln">$n</span><span class="pun">))</span></code></li><li class="L4"><code><span class="pln"> </span><span class="pun">{</span></code></li><li class="L5"><code><span class="pln"> </span><span class="kwd">return</span><span class="pln"> $default</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pln"> </span><span class="pun">}</span></code></li><li class="L7"><code></code></li><li class="L8"><code><span class="pln"> $node </span><span class="pun">=</span><span class="pln"> $node</span><span class="pun">-></span><span class="pln">$n</span><span class="pun">;</span></code></li><li class="L9"><code><span class="pln"> $found </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span></code></li><li class="L0"><code><span class="pln"> </span><span class="pun">}</span></code></li><li class="L1"><code></code></li><li class="L2"><code><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">$found </span><span class="pun">||</span><span class="pln"> $node </span><span class="pun">===</span><span class="pln"> </span><span class="kwd">null</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> $node </span><span class="pun">===</span><span class="pln"> </span><span class="str">''</span><span class="pun">)</span></code></li><li class="L3"><code><span class="pln"> </span><span class="pun">{</span></code></li><li class="L4"><code><span class="pln"> </span><span class="kwd">return</span><span class="pln"> $default</span><span class="pun">;</span></code></li><li class="L5"><code><span class="pln"> </span><span class="pun">}</span></code></li><li class="L6"><code></code></li><li class="L7"><code><span class="pln"> </span><span class="kwd">return</span><span class="pln"> $node</span><span class="pun">;</span></code></li><li class="L8"><code><span class="pln"> </span><span class="pun">}</span></code></li></ol> |
根据这里的调用方式来看,这里会通过这里的的判断获取是否存在adminstyle,如果没有则会返回default(这里为空)
接着回溯$this->data
,data来自$this->data = new \stdClass;
回溯到这里可以发现$admin_style
的地方是从全局变量中中读取的。
默认设置为空/administrator/components/com_users/models/forms/user.xml
但我们是可以设置这个的
后台users->users->super user
设置,右边我们可以设置当前账户使用的后台模板,将右边修改为使用hathor型模板。
通过抓包我们可以发现,这里显式的设置了当前账户的admin_type
,这样如果我们通过传入数组,就可以设置admin_type
为任意值
然后进入代码中的数据库操作/administrator/templates/hathor/postinstall/hathormessage.php function hathormessage_postinstall_condition
访问post_install页面触发
XSS vulnerability in com_fields
https://developer.joomla.org/security-centre/720-20180102-core-xss-vulnerability.html
1 |
<ol class="linenums"><li class="L0"><code><span class="typ">Description</span></code></li><li class="L1"><code><span class="typ">Inadequate</span><span class="pln"> input filtering </span><span class="kwd">in</span><span class="pln"> com_fields leads to a XSS vulnerability </span><span class="kwd">in</span><span class="pln"> multiple field types</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">.</span><span class="pln">e</span><span class="pun">.</span><span class="pln"> list</span><span class="pun">,</span><span class="pln"> radio </span><span class="kwd">and</span><span class="pln"> checkbox</span><span class="pun">.</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="typ">Affected</span><span class="pln"> </span><span class="typ">Installs</span></code></li><li class="L4"><code><span class="typ">Joomla</span><span class="pun">!</span><span class="pln"> CMS versions </span><span class="lit">3.7</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> through </span><span class="lit">3.8</span><span class="pun">.</span><span class="lit">3</span></code></li></ol> |
补丁分析
漏洞详情写了很多,反而是补丁比较模糊,我们可以大胆猜测下,当插入的字段类型为list、radio、checkbox多出的部分变量没有经过转义
首先我们需要先找到触发点
后台content->fields->new
,然后设置type为radio
,在键名处加入相应的payload
然后保存新建文章
成功触发
漏洞分析
由于补丁修复的方式比较特殊,可以猜测是在某些部分调用时使用了textContent而不是nodeValue,在分析变量时以此为重点。
漏洞的出发点/administrator/components/com_fields/libraries/fieldslistplugin.php line 31
由于找不到该方法的调用点,所以我们从触发漏洞的点分析流程。
编辑文章的上边栏是通过administrator/components/com_content/views/article/tmp/edit.php line 99
载入的
这里JLayoutHelper:render
会进入/layouts/joomla/edit/params.php
然后在129行进入JLayoutHelper::render('joomla.edit.fieldset', $displayData);
跟入/layouts/joomla/edit/fieldset.php line 16
,代码在这里通过执行form
的getFieldset
获取了提交的自定义字段信息。
跟入/libraries/src/Form/Form.php line 329 function getFieldset
跟如1683行 findFieldsByFieldset
函数。
这里调用xml来获取数据,从全局的xml变量中匹配。
这里的全局变量中的xml中的option字段就来自于设置时的$option->textContent
,而只有list, radio and checkbox.
这三种是通过这里的函数做处理,其中list比较特殊,在后面的处理过程中,list类型的自定义字段会在/libraries/cms/html/select.php line 742 function options
被二次处理,但radio不会,所以漏洞存在。
整个xss漏洞从插入到触发限制都比较大,实战价值较低。
XSS vulnerability in Uri class
https://developer.joomla.org/security-centre/721-20180103-core-xss-vulnerability.html
1 |
<ol class="linenums"><li class="L0"><code><span class="typ">Description</span></code></li><li class="L1"><code><span class="typ">Inadequate</span><span class="pln"> input filtering </span><span class="kwd">in</span><span class="pln"> the </span><span class="typ">Uri</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="pun">(</span><span class="pln">formerly </span><span class="typ">JUri</span><span class="pun">)</span><span class="pln"> leads to a XSS vulnerability</span><span class="pun">.</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="typ">Affected</span><span class="pln"> </span><span class="typ">Installs</span></code></li><li class="L4"><code><span class="typ">Joomla</span><span class="pun">!</span><span class="pln"> CMS versions </span><span class="lit">1.5</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> through </span><span class="lit">3.8</span><span class="pun">.</span><span class="lit">3</span></code></li></ol> |
补丁分析
比起其他几个来说,这里的漏洞就属于特别清晰的,就是在获取系统变量时,没做相应的过滤。
前台触发方式特别简单,因为这里的script_name
是获取基础url路径的,会拼接进所有页面的和链接有关系的地方,包括js或者css的引入。
漏洞利用
让我们来看看完整的代码
1 |
<ol class="linenums"><li class="L0"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">strpos</span><span class="pun">(</span><span class="pln">php_sapi_name</span><span class="pun">(),</span><span class="pln"> </span><span class="str">'cgi'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!==</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">&&</span><span class="pln"> </span><span class="pun">!</span><span class="pln">ini_get</span><span class="pun">(</span><span class="str">'cgi.fix_pathinfo'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&&</span><span class="pln"> </span><span class="pun">!</span><span class="pln">empty</span><span class="pun">(</span><span class="pln">$_SERVER</span><span class="pun">[</span><span class="str">'REQUEST_URI'</span><span class="pun">]))</span></code></li><li class="L1"><code><span class="pun">{</span></code></li><li class="L2"><code><span class="pln"> </span><span class="com">// PHP-CGI on Apache with "cgi.fix_pathinfo = 0"</span></code></li><li class="L3"><code></code></li><li class="L4"><code><span class="pln"> </span><span class="com">// We shouldn't have user-supplied PATH_INFO in PHP_SELF in this case</span></code></li><li class="L5"><code><span class="pln"> </span><span class="com">// because PHP will not work with PATH_INFO at all.</span></code></li><li class="L6"><code><span class="pln"> $script_name </span><span class="pun">=</span><span class="pln"> $_SERVER</span><span class="pun">[</span><span class="str">'PHP_SELF'</span><span class="pun">];</span></code></li><li class="L7"><code><span class="pun">}</span></code></li><li class="L8"><code><span class="kwd">else</span></code></li><li class="L9"><code><span class="pun">{</span></code></li><li class="L0"><code><span class="pln"> </span><span class="com">// Others</span></code></li><li class="L1"><code><span class="pln"> $script_name </span><span class="pun">=</span><span class="pln"> $_SERVER</span><span class="pun">[</span><span class="str">'SCRIPT_NAME'</span><span class="pun">];</span></code></li><li class="L2"><code><span class="pun">}</span></code></li><li class="L3"><code></code></li><li class="L4"><code><span class="kwd">static</span><span class="pun">::</span><span class="pln">$base</span><span class="pun">[</span><span class="str">'path'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> rtrim</span><span class="pun">(</span><span class="pln">dirname</span><span class="pun">(</span><span class="pln">$script_name</span><span class="pun">),</span><span class="pln"> </span><span class="str">'/\\'</span><span class="pun">);</span></code></li></ol> |
很明显只有当$script_name = $_SERVER['PHP_SELF']
的时候,漏洞才有可能成立
只有当php是fastcgi运行,而且cgi.fix_pathinfo = 0时才能进入这个判断,然后利用漏洞还有一个条件,就是服务端对路径的解析存在问题才行。
1 |
<ol class="linenums"><li class="L0"><code><span class="pln">http</span><span class="pun">:</span><span class="com">//127.0.0.1/index.php/{evil_code}/321321</span></code></li><li class="L1"><code></code></li><li class="L2"><code><span class="pun">---></span></code></li><li class="L3"><code></code></li><li class="L4"><code><span class="pln">http</span><span class="pun">:</span><span class="com">//127.0.0.1/index.php</span></code></li></ol> |
当该路径能被正常解析时,http://127.0.0.1/index.php/{evil_code}
就会被错误的设置为基础URL拼接入页面中。
一个无限制的xss就成立了
XSS vulnerability in module chromes
https://developer.joomla.org/security-centre/718-20180101-core-xss-vulnerability.html
1 |
<ol class="linenums"><li class="L0"><code><span class="typ">Description</span></code></li><li class="L1"><code><span class="typ">Lack</span><span class="pln"> of escaping </span><span class="kwd">in</span><span class="pln"> the </span><span class="kwd">module</span><span class="pln"> chromes leads to XSS vulnerabilities </span><span class="kwd">in</span><span class="pln"> the </span><span class="kwd">module</span><span class="pln"> system</span><span class="pun">.</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="typ">Affected</span><span class="pln"> </span><span class="typ">Installs</span></code></li><li class="L4"><code><span class="typ">Joomla</span><span class="pun">!</span><span class="pln"> CMS versions </span><span class="lit">3.0</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> through </span><span class="lit">3.8</span><span class="pun">.</span><span class="lit">3</span></code></li></ol> |
补丁分析
漏洞存在的点比较清楚,修复中将$moduleTag
进行了一次转义,同样的地方有三处,但都是同一个变量导致的。
这个触发也比较简单,当我们把前台模板设置为protostar(默认)时,访问前台就会触发这里的modChrome_well
函数。
漏洞利用
让我们看看完整的代码
很明显后面module_tag
没有经过更多处理,就输出了,假设我们可控module_tag
,那么漏洞就成立。
问题在于怎么控制,这里的函数找不到调用的地方,能触发的地方都返回了传入的第二个值,猜测和上面的get_param
一样,如果没有设置该变量,则返回default
值。
经过一番研究,并没有找到可控的设置的点,这里只能暂时放弃。
ref
- Joomla 3.8.4 https://www.joomla.org/announcements/release-news/5723-joomla-3-8-4-release.html
- Joomla security patches https://developer.joomla.org/security-centre/