php wp foo,【翻译】WordPress WPDB SQL注入攻击(技术文档)
WordPress 4.8.3中修復(fù)了一個(gè)重要的SQL注入漏洞。漏洞是今年9月20日由Hacker-One報(bào)告的。本文主要講了漏洞的技術(shù)細(xì)節(jié)和解決方法。
升級到最新版本
網(wǎng)站管理員應(yīng)該升級WordPress到4.8.3版本并更新重寫 $wpdb的所有插件,就可以預(yù)防此類問題。為客戶機(jī)升級wp-db.php,可能需要修改一些防火墻規(guī)則,比如攔截 %s 和其他sprintf() 值。
插件開發(fā)者應(yīng)該?
一般來說,檢查所有的查詢段的用戶輸入,不要將用戶輸入傳遞到查詢端,如:
$where?=?$wpdb->prepare("?WHERE?foo?=?%s",?$_GET['data']);$query?=?$wpdb->prepare("SELECT?*?FROM?something?$where?LIMIT?%d,?%d",?1,?2);
$where?=?"WHERE?foo?=?'"?.?esc_sql($_GET['data'])?.?"'";$query?=?$wpdb->prepare("SELECT?*?FROM?something?$where?LIMIT?%d,?%d",?1,?2);
以上兩種方法從概念是講都是不安全的,
安全的查詢方法為:
$where?=?"WHERE?foo?=?%s";$args?=?[$_GET['data']];$args[]?=?1;$args[]?=?2;$query?=?$wpdb->prepare("SELECT?*?FROM?something?$where?LIMIT?%d,?%d",?$args);
漏洞
WPDB::prepare源碼(4.8.2之前版本):
public?function?prepare(?$query,?$args?)?{
if?(?is_null(?$query?)?)
return;
//?This?is?not?meant?to?be?foolproof?--?but?it?will?catch?obviously?incorrect?usage.
if?(?strpos(?$query,?'%'?)?===?false?)?{
_doing_it_wrong(?'wpdb::prepare',?sprintf(?__(?'The?query?argument?of?%s?must?have?a?placeholder.'?),?'wpdb::prepare()'?),?'3.9.0'?);
}
$args?=?func_get_args();
array_shift(?$args?);
//?If?args?were?passed?as?an?array?(as?in?vsprintf),?move?them?up
if?(?isset(?$args[0]?)?&&?is_array($args[0])?)
$args?=?$args[0];
$query?=?str_replace(?"'%s'",?'%s',?$query?);?//?in?case?someone?mistakenly?already?singlequoted?it
$query?=?str_replace(?'"%s"',?'%s',?$query?);?//?doublequote?unquoting
$query?=?preg_replace(?'|(?
$query?=?preg_replace(?'|(?
array_walk(?$args,?array(?$this,?'escape_by_ref'?)?);
return?@vsprintf(?$query,?$args?);}
用vsprintf(與sprintf基本等價(jià))的值來替換占位符;
用str_replace來適當(dāng)?shù)匾谜嘉环?#xff1b;
如果傳遞了一個(gè)參數(shù),而這個(gè)參數(shù)是數(shù)組的話,用數(shù)組的值來替換參數(shù)。
這意味著調(diào)用$wpdb->prepare($sql, [1, 2]) 與調(diào)用$wpdb->prepare($sql, 1, 2)是等價(jià)的。
最初報(bào)告的漏洞依賴與下面的服務(wù)端代碼:
$items?=?implode(",?",?array_map([$wpdb,?'_real_escape'],?$_GET['items']));$sql?=?"SELECT?*?FROM?foo?WHERE?bar?IN?($items)?AND?baz?=?%s";
$query?=?$wpdb->prepare($sql,?$_GET['baz']);
漏洞利用vsprintf的特征來允許絕對引用參數(shù),例子如下:
vsprintf('%s,?%d,?%s',?["a",?1,?"b"]);?//?"a,?1,?b"vsprintf('%s,?%d,?%1$s',?["a",?2,?"b"]);?//?"a,?2,?a"
注意%n$s不會讀下一個(gè)參數(shù),但是會讀第n個(gè)位置的參數(shù)??梢愿鶕?jù)這個(gè)特性在原始查詢中進(jìn)行注入。假設(shè)傳遞下面的信息到請求中:
$_GET['items']?=?['%1$s'];$_GET['baz']?=?"test";
查詢會變成?SELECT?*?FROM?foo?WHERE?bar?IN?('test')?AND?baz?=?'test';我們成功地改變了查詢的本意。
There’s?one?other?key?piece?of?information?that?the?original?report?included?to?change?this?into?a?full-blown?SQL?Injection.?sprintf?also?accepts?another?type?of?parameter:?%c?which?acts?like?chr()?and?converts?a?decimal?digit?into?a?character.?So?now,?the?attacker?can?do?this:
最初的漏洞報(bào)告中還有一個(gè)關(guān)鍵的信息是可以把這個(gè)變成成熟的SQL注入。Sprintf也會接受其他類型的參數(shù),%c與chr()含義相同,可以把小叔變成字符,所以攻擊者可以:
$_GET['items']?=?['%1$c)?OR?1?=?1?/*'];$_GET['baz']?=?39;
ASCII表中39代表’(單引號),所以查詢就變成了這樣:
SELECT?*?FROM?foo?WHERE?bar?IN?('')?OR?1?=?1?/*'?AND?baz?=?'test';
注入就完成了。
這個(gè)過程看似很復(fù)雜,需要提前準(zhǔn)備好輸入的參數(shù)等,實(shí)際上該漏洞也存在于核心文件/wp-includes/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?)?);}
最早的補(bǔ)丁
WordPress4.8.2發(fā)布時(shí),就包含上述問題的一個(gè)補(bǔ)丁。補(bǔ)丁整個(gè)包含在WPDB::prepare()中,補(bǔ)丁只加了1行代碼:
$query?=?preg_replace(?'/%(?:%|$|([^dsF]))/',?'%%\\1',?$query?);
這1行代碼做了2件事情。1是移除了除%d,%s,%F之外的sprintf令牌,因?yàn)槁┒词且蕾?c的,因此使漏洞無效。2是移除了位置替換的能力,即%1$s這樣的參數(shù)就無效了。
這引起了開發(fā)人員的不滿,因?yàn)閃ordPress在官方文檔中說只能使用%d,%s,%F。即使官方文檔是這么寫的,上百萬的第三方查詢代碼都使用了前面的語法規(guī)則。
WordPress的回應(yīng)是“won’t fix, sorry”,并以安全為由拒絕提供更多細(xì)節(jié)。
最初補(bǔ)丁的第一個(gè)問題
漏洞是傳遞用戶輸入到prepare的服務(wù)端。最初漏洞的POC是這樣的,安全查詢代碼如下:
$db->prepare("SELECT?*?FROM?foo?WHERE?name=?'%4s'?AND?user_id?=?%d",?$_GET['name'],?get_current_user_id());
4.8.2中的變化是%4s會被重寫成%%4s,也就是說%d會反彈到$_GET['name'],給了攻擊者用戶id的機(jī)會。這可以被用來進(jìn)行權(quán)限提升攻擊。
WordPress的回應(yīng)是:“thank you, we don’t support that”。
全面攻擊
然后作者設(shè)計(jì)了一個(gè)不同的POC,利用另一個(gè)重要的事實(shí)來證明該漏洞不是%1$s,而是傳遞用戶輸入到prepare查詢端。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?/*'];
產(chǎn)生了下面的查詢:
SELECT?type?FROM?table?WHERE?meta_key?=?'dump'?AND?meta_value?=?''?OR?1=1?/*'
成功注入了核心文件,$meta_value 和 $meta_key都來自于用戶的輸入。會產(chǎn)生下面的賦值子句:
AND?meta_value?=?'?%s?'
未引用的%s通過prepare被引用的%代替,第二次調(diào)用->prepare()把clause變成AND meta_value = ' '%s' ' ,就可以注入了。
作者強(qiáng)調(diào)該漏洞不能在WPDB::prepare() 修復(fù),但是是meta.php中的問題??梢酝ㄟ^預(yù)防double prepare calls緩解該漏洞。但是不能修復(fù)原始漏洞。
簡單補(bǔ)丁
簡單的補(bǔ)丁不是傳遞用戶輸入的$query參數(shù)到meta.php中的WPDB::prepare()。傳遞用戶輸入到$query是錯誤的。
緩解補(bǔ)丁
下一步是在預(yù)查詢中引用占位符,然后在執(zhí)行查詢前恢復(fù)占位符,這個(gè)補(bǔ)丁已經(jīng)有了?;旧?#xff0c;補(bǔ)丁會修改WPDB::prepare()把隨機(jī)字?jǐn)?shù)穿用%占位符代替,比如:
$query?=?str_replace('%',?"{$this->placeholder_escape}",?$query?);
然后,在WPDB::_do_query()去除占位符來恢復(fù)最初的用戶的用戶輸入。
我仍然認(rèn)為傳遞用戶輸入到prepare的查詢端是存在潛在危險(xiǎn)的而且是不安全的。即使你解決了已知的安全漏洞,double-preparing字符串是及其危險(xiǎn)的,因?yàn)閜repare的結(jié)果會傳遞到另一個(gè)。
正確的補(bǔ)丁
正確的補(bǔ)丁應(yīng)該是拋棄整個(gè)prepare機(jī)制。像正常的查詢那樣,返回一個(gè)statement或query的對象,或者直接執(zhí)行查詢。這種方式可以預(yù)防double prepare字符串的情況。值得一提的是這將會是WP的主要變化。其他平臺已經(jīng)有成功的先例了,比如PHPBB經(jīng)歷了同樣的事情,從大規(guī)模的SQL注入漏洞到幾乎沒有SQL注入漏洞。也不需要很快解決,可以與現(xiàn)有的API并行處理,慢慢地去取代老的API。目前的系統(tǒng)在設(shè)計(jì)之處就是不安全的,但這也不意味著會經(jīng)常被黑,但是你要盡量去讓它不被黑。最好使用默認(rèn)安全的設(shè)計(jì),并讓不安全成為特例。其中最佳的實(shí)踐方法是使用PDO/MySQLi和real prepared statements。這些變化并不能防止被誤用,但是會讓誤用變得更難。
https://blog.ircmaxell.com/2017/10/disclosure-wordpress-wpdb-sql-injection-technical.html
總結(jié)
以上是生活随笔為你收集整理的php wp foo,【翻译】WordPress WPDB SQL注入攻击(技术文档)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 工行房贷宽限期
- 下一篇: php jquery实现弹窗,jquer