作者:SeaFood@知道創宇404實驗室
0x00 背景
近日,WordPress爆出了一個SQLi漏洞,漏洞發生在WP的後台上傳圖片的位置,通過修改圖片在數據庫中的參數,以及利用php的sprintf
函數的特性,在刪除圖片時,導致'
單引號的逃逸。漏洞利用較為困難,但思路非常值得學習。
0x01 漏洞分析
漏洞發生在wp-admin/upload.php的157行,進入刪除功能,
https://medium.com/websec/wordpress-sqli-poc-f1827c20bf8e
http://php.net/manual/zh/function.sprintf.php
https://www.seebug.org/vuldb/ssvid-96376
0x07 WordPress 4.8.2補丁問題
國外安全研究人員Anthony Ferrara給出了另一種此漏洞的利用方式,並指出了WordPress 4.8.2補丁存在的問題。
如下代碼
<?php $input1 = '%1$c) OR 1 = 1 /*'; $input2 = 39; $sql = "SELECT * FROM foo WHERE bar IN ('$input1') AND baz = %s"; $sql = sprintf($sql, $input2); echo $sql; //result: SELECT * FROM foo WHERE bar IN ('') OR 1 = 1 /*') AND baz = 39
%c
起到了類似chr()
的效果,將數字39轉化為'
,從而導致了sql注入。
對此,WordPress 4.8.2補丁在WPDB::prepare()
中加入
$query = preg_replace( '/%(?:%|$|([^dsF]))/', '%%//1', $query );
從而,禁用了除%d
,%s
,%F
之外的格式,這種方法導致了三個問題。
1.大量開發者在開發過程中使用了例如%1$s
的格式,此次補丁導致代碼出錯。
2.在例如以下代碼中
$db->prepare("SELECT * FROM foo WHERE name= '%4s' AND user_id = %d", $_GET['name'], get_current_user_id());
%4s
會被替換成%%4s
,%%
在sprintf中代表字符%
,沒有格式化功能。所以,$_GET['name']
會被寫到%d
處,攻擊者可以控制user id,可能導致越權問題的出現。
3.補丁可以被繞過
在meta.php
的漏洞處
if ( $delete_all ) { $value_clause = ''; if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value ) { $value_clause = $wpdb->prepare( " AND meta_value = %s", $meta_value ); } $object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s $value_clause", $meta_key ) ); }
如果輸入
$meta_value = ' %s '; $meta_key = ['dump', ' OR 1=1 /*'];
之後兩次進入prepare()
,因為
$query = preg_replace( '|(?<!%)%s|', "'%s'", $query );
使得%s
變為''%s''
最後結果
SELECT type FROM table WHERE meta_key = 'dump' AND meta_value = '' OR 1=1 /*''
WordPress也承認這是一個錯誤的修復。
在WordPress 4.8.3的補丁中,一是修改了meta.php
中兩次使用prepare()
的問題,二是使用隨機生成的佔位符替換%
,在進入數據庫前再替換回來。