GET /wp-json/wp/v2/posts/1獲取一個(gè)ID為1的單獨(dú)的Post:

可以看到ID為1的文章標(biāo)題為Hello World,包括文章的路由也有。

路由

路由是用于訪問端點(diǎn)的“名稱”,在URL中使用(在非法情況下可控,就像這個(gè)漏洞一樣)。

例如,使用URLhttp://example.com/wp-json/wp/v2/posts/123:

路由(route)是wp/v2/posts/123,不包括wp-json,因?yàn)閣p-json是API本身的基本路徑。

這個(gè)路由有三個(gè)端點(diǎn):

GET觸發(fā)一個(gè)get_item方法,將post數(shù)據(jù)返回給客戶端。

PUT觸發(fā)一個(gè)update_item方法,使數(shù)據(jù)更新,并返回更新的發(fā)布數(shù)據(jù)。

DELETE觸發(fā)delete_item方法,將現(xiàn)在刪除的發(fā)布數(shù)據(jù)返回給客戶端。 靜態(tài)追蹤

知道了WP-API的路由信息以及其操作方式,可以根據(jù)其運(yùn)行的思路來看一下具體實(shí)現(xiàn)的代碼。

我們看一下/wp-includes/rest-api/endpoints/class-wp-rest-post-controller.php:

根據(jù)上面的信息,我們可以知道這是注冊(cè)controller對(duì)象的路由,實(shí)現(xiàn)路由中端點(diǎn)方法。

在這里,如果我們向/wp-json/wp/v2/posts/1發(fā)送請(qǐng)求,則ID參數(shù)將被設(shè)置為1:

同時(shí),注意一下這里:

可以看到在registerrestroute中對(duì)路由進(jìn)行了正則限制:

也就是防止攻擊者惡意構(gòu)造ID值,但是我們可以發(fā)現(xiàn)$GET和$POST值優(yōu)先于路由正則表達(dá)式生成的值:

這邊沒有找到ID為123hh的項(xiàng)目,所以返回rest_invalid。

現(xiàn)在我們可以忽略路由正則的限制,來傳入我們自定義的ID。

接下來在審查各個(gè)端點(diǎn)方法中,找到了updateitem這個(gè)方法,及其權(quán)限檢查方法updateitempermissionscheck:

可以看到,此函數(shù)通過檢查文章是否實(shí)際存在,以及我們的用戶是否有權(quán)限編輯這邊文章來驗(yàn)證請(qǐng)求。

但是當(dāng)我們發(fā)送一個(gè)沒有響應(yīng)文章的ID時(shí),就可以通過權(quán)限檢查,并允許繼續(xù)執(zhí)行對(duì)update_item方法的請(qǐng)求。

具體到代碼,就是讓$post為空,就可以通過權(quán)限檢查,接下來跟進(jìn)get_post方法中看一下:

從代碼中可以看出,它是用wpposts中的getinstance靜態(tài)方法來獲取文章的,跟進(jìn)wp_posts類,位于/wp-includes/class-wp-post.php中:

public static function get_instance( $post_id ) {  
global $wpdb;

if ( ! is_numeric( $post_id ) || $post_id != floor
( $post_id ) || ! $post_id ) {
return false;
}

回頭再看一下可執(zhí)行方法upload_item:

public function update_item( $request ) {  

$id = (int) $request['id'];

$post = get_post( $id );

if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
}

$post = $this->prepare_item_for_database( $request );

if ( is_wp_error( $post ) ) {
return $post;
}

// convert the post object to an array, otherwise wp_update_post will expect non-escaped input.

$post_id = wp_update_post( wp_slash( (array) $post ), true );

在這邊將ID參數(shù)裝換為一個(gè)整數(shù),然后傳遞給get_post。而PHP類型轉(zhuǎn)換的時(shí)候回出現(xiàn)這樣的情況:

所以,也就是說,當(dāng)攻擊者發(fā)起/wp-json/wp/v2/posts/1?id=1hhh請(qǐng)求時(shí),便是發(fā)起了對(duì)ID為1的文章的請(qǐng)求。

下面為利用[exploit-db][2]上的POC來進(jìn)行測試:

新建文章:

測試:

測試結(jié)果:

多想了一下

乍一看,感覺這個(gè)洞并沒有什么太大的影響,但是仔細(xì)想了一下,危害還是很大的。

先不說WordPress頁面執(zhí)行php代碼的各種插件,還有相當(dāng)一部分的WordPress文章可以調(diào)用短代碼的方式來輸出特定的內(nèi)容,以及向日志中添加內(nèi)容,這是一個(gè)思路。

另一個(gè)思路就是可以進(jìn)行對(duì)原來文章中的指定超鏈接進(jìn)行修改,從而進(jìn)行釣魚。

還有一個(gè)思路,就是利用WordPress文章中解析html以及JavaScript文件包含的做法,輔助其他方法,進(jìn)行攻擊。

0x03 diff比較

對(duì)于該漏洞,關(guān)鍵的修改在/wp-includes/class-wp-post.php中:

更改了對(duì)于$post_id的參數(shù)的傳入順序和判斷條件,防止了我們傳入數(shù)字+字母這樣的格式進(jìn)行繞過。

0x04 修補(bǔ)方案

將WordPress更新到最新版本。

0x05 參考鏈接

https://www.seebug.org/vuldb/ssvid-92637

https://www.exploit-db.com/exploits/41223/

原文鏈接地址:http://paper.seebug.org/208/,點(diǎn)擊“閱讀原文”,即可進(jìn)入。

本文章轉(zhuǎn)載微信公眾號(hào)@懸鏡安全說

上一篇:

API安全:基于令牌的驗(yàn)證 vs 基于密鑰的驗(yàn)證,哪種更可靠?

下一篇:

深入解析API Gateway:微服務(wù)架構(gòu)中的關(guān)鍵組件及其重要功能
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場景實(shí)測,選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)