2.漏洞分析
我们先随便下载一个主题:
1 2 |
wget https://downloads.wordpress.org/theme/illdy.1.0.29.zip unzip -x illdy.1.0.29.zip |
然后对illdy/style.css
进行如下更改:
1 2 3 4 |
/* Theme Name: <svg onload=alert(1234)> ... DO NOT CHANGES HERE ... */ |
接着更改文件夹名字再打包:
1 2 |
mv illdy "<svg onload=alert(5678)>" zip -r theme.zip "<svg onload=alert(5678)>" |
构造好之后我们登录后台上传该主题文件,同时开始动态调试。
首先进入wp-admin/includes/class-theme-installer-skin.php
中第55-82行:
1 2 3 4 5 6 7 |
$name = $theme_info->display('Name'); ... if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { $install_actions['preview'] = '<a href="' . wp_customize_url( $stylesheet ) . '" class="hide-if-no-customize load-customize"><span aria-hidden="true">' . __( 'Live Preview' ) . '</span><span class="screen-reader-text">' . sprintf( __( 'Live Preview “%s”' ), $name ) . '</span></a>'; } $install_actions['activate'] = '<a href="' . esc_url( $activate_link ) . '" class="activatelink"><span aria-hidden="true">' . __( 'Activate' ) . '</span><span class="screen-reader-text">' . sprintf( __( 'Activate “%s”' ), $name ) . '</span></a>'; |
其中$theme_info
的值如下:
其中stylesheet
和template
的值为我们更改的文件夹名,headers.Name
为更改的style.css
中的Name
。$theme_info
中有我们可控的payload,其调用display
函数后赋值给$name
,$name
直接与html拼接,所以关键点在display
函数上,动态调试跟进到wp-includes/class-wp-theme.php
中第630-646行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public function display( $header, $markup = true, $translate = true ) { $value = $this->get( $header ); if ( false === $value ) { return false; } if ( $translate && ( empty( $value ) || ! $this->load_textdomain() ) ) $translate = false; if ( $translate ) $value = $this->translate_header( $header, $value ); if ( $markup ) $value = $this->markup_header( $header, $value, $translate ); return $value; } |
由之前的调用可知,这里的$header
的值为Name
。首先看$this-get($header)
,在wp-includes/class-wp-theme.php
中第594-617行:
1 2 3 4 5 6 |
public function get( $header ) { ... $this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] ); ... return $this->headers_sanitized[ $header ]; } |
这里省略了与漏洞无关的部分,程序进入了$this->sanitize_header
,在wp-includes/class-wp-theme.php
第661-705行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<span class="token keyword">private</span> <span class="token keyword">function</span> <span class="token function">sanitize_header<span class="token punctuation">(</span></span> <span class="token variable">$header</span><span class="token punctuation">,</span> <span class="token variable">$value</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">switch</span> <span class="token punctuation">(</span> <span class="token variable">$header</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">case</span> <span class="token string">'Name'</span> <span class="token punctuation">:</span> <span class="token keyword">static</span> <span class="token variable">$header_tags</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span> <span class="token string">'abbr'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token keyword">array</span><span class="token punctuation">(</span> <span class="token string">'title'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token boolean">true</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">'acronym'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token keyword">array</span><span class="token punctuation">(</span> <span class="token string">'title'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token boolean">true</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">'code'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token string">'em'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token string">'strong'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$value</span> <span class="token operator">=</span> <span class="token function">wp_kses<span class="token punctuation">(</span></span> <span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token variable">$header_tags</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> |
这里执行了Name
这个分支,可以看到程序使用wp_kses
对$value
的值进行了过滤,仅允许$header_tags
中的html符号,所以我们headers.Name
的值<svg onload=alert(1234)>
是不合法的,$value
值被赋为空。
然后程序回到了display
函数,根据动态调试可以知道程序执行了$value = $this->markup_header( $header, $value, $translate );
这个条件分支,再跟进,在wp-includes/class-wp-theme.php
中第720-748行:
1 2 3 4 5 6 7 8 9 |
<span class="token keyword">private</span> <span class="token keyword">function</span> <span class="token function">markup_header<span class="token punctuation">(</span></span> <span class="token variable">$header</span><span class="token punctuation">,</span> <span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token variable">$translate</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">switch</span> <span class="token punctuation">(</span> <span class="token variable">$header</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token string">'Name'</span> <span class="token punctuation">:</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token function">empty<span class="token punctuation">(</span></span> <span class="token variable">$value</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token variable">$value</span> <span class="token operator">=</span> <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">get_stylesheet<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">return</span> <span class="token variable">$value</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |