보안정보

전문화된 보안 관련 자료, 보안 트렌드를 엿볼 수 있는
차세대 통합보안관리 기업 이글루코퍼레이션 보안정보입니다.

WordPress Plug-in에 따른 SQL Injection 분석 및 대응 방안 : CVE-2022-3689, CVE-2023-6360

2024.09.23

16,167

01. WordPress와 SQL Injection

웹 기술 및 트렌드 조사 기관 W3techs의 ‘WordPress의 사용 통계 및 시장 점유율(Usage statistics and market share of WordPress)’에 따르면 2024년 6월 CMS(Contents Management System) 기반의 웹 사이트 기준으로 62.7%를 차지하고 이는 전체 웹 사이트의 43.4%를 차지한다고 밝혔다. PHP 환경에서 OSS로 CMS를 제공하는 WordPress는 사용자 친화적인 인터페이스와 다양한 플러그인(Plug-in)을 제공하면서 손쉽게 웹 사이트 구현이 가능하게 되면서 다양한 사용자의 니즈를 만족시킬 수 있게 되었다.

일각에서는 WordPress의 근간인 PHP가 ASP나 JSP 등에 비해 시장 점유율이나 발전 가능성이 낮다는 시각도 존재하지만, Jetpack, Table of Contents Plus, TinyMCE 등 웹 사이트를 유연하게 관리할 수 있는 다양한 오픈소스 플러그인을 제공하는 WordPress 생태계로 인해 여전히 높은 점유율을 유지하고 있다.

다만 무분별한 플러그인 적용으로 인해 관리되지 않은 플러그인이나 플러그인 취약점 관리 미흡으로 인해 새로운 공격벡터로 악용되기 시작하였다. SolidWP에서 발표한 ‘WordPress Vulnerability Report — May 29, 2024’에 따르면 한 주간 발생한 WordPress 플러그인 취약점 118건 중 86개 취약점이 패치되었으며, 이 중 12개 취약점은 SQL Injection으로 인해 발생하였다.

SQL Injection은 SQL쿼리 조회 시에 공격자에 의해 삽입한 공격코드가 실행되는 취약점으로 입력값 검증 부재, 동적쿼리 실행, 파라미터화된 쿼리 미사용, 오류메시지 노출 등으로 발생된다. 따라서 이번 호에서는 WordPress Plug-in에서 발생하는 SQL Injection을 대응하기 위한 방안으로 HTML Forms의 CVE-2022-3689와 My Calendar의 CVE-2023-6360을 살펴보고자 한다.

[표 1] WordPress 플러그인별 SQL Injection 취약점 목록

02. WordPress Plug-in 취약점 분석

WordPress 플러그인의 SQL Injection에 대해서 살펴보기 위해서 My Calendar에서 발생한 CVE-2023-6360과 HTML Forms에서 발생한 CVE-2022-3689을 살펴보고자 한다. Ubuntu24.04LTS 및 WordPress6.5.3환경에서 취약점 분석을 진행하였으며 자세한 정보는 [표 2]와 같다.

[표 2] WordPress 플러그인 취약점 테스트 조건

2.1. My Calendar SQL Injection 취약점 분석 및 대응방안 : CVE-2023-6360

SQL Injection 발생 원인의 상당수는 사용자가 입력한 입력값의 부적절한 필터링으로 문제가 발생된다. CVE-2023-6360은 WordPress 플러그인 My Calendar 3.4.22 미만 버전의 '/my-calendar/v1/events’경로내 파라미터의 필터링 부재로 인해 blind, generic, time-based SQL Injection이 발생되고 3.4.22버전 에서 해당 문제가 조치되었다.

My Calendar는 일정을 관리하는 캘린더 플러그인으로 사용자가 REST API를 통해 특정 엔드포인트를 호출할 때 발생하는 요청 패킷의 파라미터인 from이나 to에 인증되지 않은 쿼리를 주입하면 임의의 SQL 명령을 실행할 수 있는 권한을 획득하게 된다. [그림 1]의 CVE-2023-6360 공격 흐름도를 기반으로 from파라미터에 Time-based SQL Injection을 유발하는 공격 코드를 입력해서 분석해 보고자 한다.

[그림 1] CVE-2023-6360 SQL Injection 공격 흐름도

1) SQL Injection 취약점 분석

[그림 2] My Calendar SQL Injection

My Calendar 플러그인의 from파라미터의 처리는 my-calendar-api.php에서 수행한다. from 파라미터로 입력된 값은 $from변수에 저장되어 WordPress에서 제공하는 sanitize_test_field 함수를 통해 HTML태그, 슬래시, 공백을 제거한 후 [그림 3]과 같이 L657부터 L667을 수행한 결과가 배열로 저장된다. $args에 저장된 값은 my_calendar_events의 인자로 사용되어 최종적으로는 SQL Injection이 발현되는 my-calendar-events.php의 my_calendar_get_events함수 인자로 전달된다.

[그림 3] from파라미터로 입력된 SQL Injection 공격구문 처리 흐름

my_calendar_get_events의 매개변수는 다시 한번 isset 함수를 통해 변수의 존재 여부와 null값 여부를 확인하면 $ from에 저장된다. 이 과정에서 사용자 입력값에 대한 SQL Injection 필터링이 수행되지 않아 사용자가 입력한 쿼리문이 그대로 저장되게 된다.

[그림 4] my_calendar_get_events 동작 확인

[그림 5]는 my_calendar_get_events서 사용자 입력값 검증 누락으로 인해 $ event_query변수에 SQL Injection공격구문이 저장되는 것을 비교한 결과다. From파라미터로 전달된 사용자 입력값이 $from변수에 저장되면서 REST API를 통해 호출돼야 하는 날짜 값이 아닌 공격자가 삽입한 SQL Injection 코드가 그대로 삽입되는 것을 알 수 있다.

[그림 5] $event_query변수에 저장된 데이터 SQL Injection 쿼리 입력 전후 비교

2) 사용자 입력값 검증을 이용한 SQL Injection 대응방안

My Calendar 플러그인의 CVE-2023-6360 SQL Injection은 my_calendar_get_events 함수에서 날짜 포맷에 대한 검증 부재로 인해 발생되기 때문에 날짜 포맷에 대한 검증으로 SQL Injection을 대응할 수 있다. PHP는 [표 3]과 같이 PHP 4 버전 이상에서 날짜를 표현하기 위한 다양한 내장함수를 제공하고 있다.

[표 3] PHP 4.0이상에서 지원하는 날짜 처리 내장함수 목록

My Calendar 3.4.22 버전부터는 사용자 입력값에 대한 날짜 포맷을 검증하여 SQL Injection을 대응하고 있다. [그림 6]에서 보안 패치가 적용되기 전 버전인 3.4.0과 후 최신 버전인 3.5.7 버전을 통해 취약점 조치 현황을 확인할 수 있다. My Calendar 3.4.0 버전의 L144에서는 my_calendar_get_events의 if 문에 위치한 mc_checkdate의 결과가 false일 경우 빈 배열을 반환해 종료하고 있다. 이와 달리 패치 후인 My Calendar 3.5.7 버전에서는 L196과 L197에서 mc_checkdate의 결괏값을 $from과 $to에 먼저 저장한 후 if 문을 처리하도록 수정하여 SQL Injection 공격에 대응하고 있다. 자세한 변경 사항을 확인하기 위해 mc_checkdate 함수의 패치 전후 코드를 살펴보고자 한다.

[그림 6] My Calendar 보안 패치 전 후 코드 비교

[그림 7]은 보안 패치 전후의 my_checkdate를 비교한 내용이다. 두 버전 모두 L234에서 strtotime 함수를 통해 사용자의 입력값이 올바른 날짜 포맷일 경우 정수(integer) 형태의 타임스탬프를 반환하고, 아닐 경우 false를 반환해 $time에 저장하고 있다. 또한 패치 후 버전인 3.5.7 버전에서는 L239~L244에서는 PHP 내장함수인 checkdate 함수의 반환 값을 $check에 저장해 결과가 참일 경우 mc_date 함수의 결과를 반환하고 있으며, 그 결과에 따라서 mc_checkdate의 반환 값이 정해져 $from과 $to에 저장되는 것을 확인할 수 있다.

[그림 7] my_checkdate 패치 전후 코드 비교

[그림 8]를 보면 보안 패치 전후 버전 모두 mc_checkdate서 사용자의 입력값이 $date 변수에 입력되어 strtotime의 결과에 따라 날짜 포맷의 유무를 확인한 후 true 또는 false 값을 $time 변수에 저장하는 것을 확인할 수 있다. 또한 입력된 결과와 관계없이 $m, $d, $y 각각의 변수에는 API를 호출한 날짜의 달, 일, 년에 해당하는 날짜가 저장되고 있으며 mc_date 함수에 전달된 날짜 포맷에 따라 API 호출 날짜를 저장하고 있다.

[그림 8] mc_checkdate 내 변수 확인

[그림 9]는 앞서 전달된 날짜 포맷에 따라 API 호출 날짜를 반환하는 my_date 함수의 코드다. $format 변수에 전달된 날짜 포맷을 저장하고 있으며, $timestamp와 $offset 변수는 각각 false와 true로 설정되어 있다. L491서 사용자의 입력값과 관계없이 time 함수를 이용해 현재 시각을 초 단위로 반환해 $timestamp에 저장하고 있으며, L494에서는 $offse에 따라 GMT(Greenwich Mean Time) 오프셋을 적용해 WordPress 데이터베이스의 gmt_offset 옵션을 가져와 정수로 변환하여 $offset 변수에 저장하고 있다. 이후 $forma에 저장된 날짜 포맷에 따라 입력값에 상관없이 API 호출 시간을 반환하고 있다.

[그림 9] my_checkdate 코드 확인

결과적으로 mc_checkdate는 API를 호출한 시간을 반환하고 있으며 $from과 $to에 저장되어 SQL 쿼리에 입력된다. 이는 사용자 입력값의 데이터를 문자열로 표현된 날짜를 mc_checkdate의 mc_date를 이용해 $time에 저장된 값에 상관없이 REST API를 호출한 날짜를 $from과 $to에 저장함으로써 SQL Injection에 대응하고 있다.

[그림 10] mc_checkdate의 반환값 확인

보안 패치 이후에는 Time based SQL Injection을 입력해도 응답 값 지연이 발생하지 않아 SQL Injection이 정상적으로 조치된 것을 알 수 있다.

[그림 11] 보안 패치 후 My calendar SQL Injection 공격 결과

2.2. HTML Forms SQL Injection 취약점 분석 및 대응방안 : CVE-2022-3689

Prepare Statement는 SQL 쿼리를 사전에 컴파일하고 파라미터 값을 바인딩 하여 사용자 입력이 SQL 쿼리의 일부로 해석되는 것을 방지함으로써 SQL Injection 공격을 방지하는 방법이다. WordPress 플러그인 HTML Forms 1.3.24 이하 버전에서 SQL Injection인 CVE-2022-3689이 존재한다. 해당 취약점은 1.3.25 버전부터는 wpdb 클래스를 이용해 SQL Injection을 대응하고 있어서 이를 통해 Prepare Statement 방식을 통한 SQL Injection 대응방안을 알아보고자 한다.

[그림 12] CVE-2022-3689 SQL Injection 공격 흐름도

1) SQL Injection 취약점 분석

HTML Forms는 HTML 양식을 쉽게 추가하는 플러그인으로 추가된 양식을 통해 전달된 데이터는 [그림 13]과 같이 wp_hf_submissions 테이블에 저장된다. 또한 Submssions 메뉴의 Move to Trash 기능을 이용해 테이블에 저장된 값을 제거할 수 있다. CVE-2022-3689는 HTML Forms의 매개변수를 사용 시에 적절하게 치환하지 않으면서 높은 권한을 가진 사용자가 SQL Injection을 할 수 있는 공격 방식이다.

[그림 13] wp_hf_submissions 테이블
[그림 14] HTML Forms SQL Injection

[그림 15]는 SQL Injection 취약점이 발견된 1.3.24버전의 class-admin.php 파일 코드이다. L365에서 POST 방식으로 전달받은 id의 empty 유무를 확인해 값이 존재할 경우 $ids 변수에 저장하고 있다. 이후 L370에서 데이터가 저장된 테이블의 이름을 $table에 저장한 후 wpdb 클래스의 query 메서드를 이용해 L372와 L373에서 SQL 쿼리를 실행하고 있다. 이러한 과정에서 $ids 값에 대한 별도의 검증이 이뤄지지 않는 것을 확인할 수 있는데, 이는 공격자가 요청 패킷 내 악의적인 SQL 문을 삽입할 경우 SQL Injection이 발현될 것을 예측할 수 있는 부분이다.

[그림 15] SQL query 실행 코드

[그림 16]서 class-admin.php에 저장된 변숫값을 확인할 수 있다. $table에는 테이블 이름인 wp_hf_submissions가 저장되고 있으며, $ids 변수에는 POST 형식으로 전달된 id 값이 파싱 되어 저장되고 있다. $ids 변수에는 사용자 입력값에 SQL 구문인 삽입된 데이터가 그대로 저장되고 있으며, 이에 따라 정상적인 SQL 문을 우회해 권한이 없는 사용자도 데이터베이스에 접근할 수 있다.

[그림 16] process_bulk_delete_submissions 내 변수 확인

2) Prepare Statement 방식을 이용한 SQL Injection 대응

WordPress에서는 기본적으로 데이터베이스와의 상호작용을 위한 wpdb 클래스를 제공하고 있으며, 이는 플러그인이나 테마 개발자들이 쿼리를 실행하고 데이터를 가져오기 위해 사용된다. HTML Forms 1.3.25 이상 버전에서는 해당 클래스의 prepare 메서드를 이용해 SQL Injection에 대응하고 있으며 [그림 17]에서 보안 패치가 적용되기 전 버전인 1.3.24 버전과 후 버전인 1.3.25 버전의 변경 사항을 확인할 수 있다.

패치 후 1.3.25 버전의 코드를 살펴보면 L369에서 파싱 된 데이터를 정수(integer)형으로 변환시켜 $ids에 저장하고 있으며, L372와 L373에서 파라미터 값을 바인딩 하여 사용자 입력이 SQL 쿼리의 일부로 해석되는 것을 방지하고 있다.

[그림 17] HTML Forms 보안 패치 전후 코드 비교

그 결과 앞서 발생한 응답 값에 대한 지연이 발생하지 않는 것을 확인할 수 있으며, 코드 내 prepare statement 방식을 구현할 경우 SQL Injection 공격에 적절하게 대응할 수 있음을 보여주고 있다.

[그림 18] 보안 패치 후 HTML Forms SQL Injection

03. PHP 기반 SQL Injection 대응 방안

앞서 살펴본 WordPress의 플러그인에서 발생한 SQL Injection은 소스코드 레벨 이외에도 다양한 대응방안이 존재한다. PHP 5.1.0버전 이후부터 기본 지원되는 라이브러리인 PDO(PHP Data Objects)는 MySQL, Oracle, PostgreSQL 등 다양한 데이터베이스와 호환이 가능하며 Prepared Statements를 통한 SQL Injection 대응을 지원하고 있다. 이를 통해 DB 쿼리에 사용되는 외부 입력값에 대한 특수문자 및 쿼리 예약어 필터링도 가능하게 된다.

PDO와 PDOStatement클래스에서 자주 사용되는 메서드는 다양한 데이터베이스 작업에 중요한 역할을 수행한다. 주요 메서드로 PDO 클래스의 connect(), prepare(), execute(), query()와 PDOStatement 클래스의 fetch(), fetchAll(), bindParam(), bindValue() 등이 있다. 이러한 메서드들을 적절히 활용하여 데이터베이스와의 상호작용을 보다 효율적이고 안전하게 처리할 수 있다.

[표 4] PDO 내장 메서드 목록

내장 라이브러리 외에도 외부 라이브러리인 MeekroDB을 이용해 Prepared Statements를 구현할 수 있다. 이는 SQL Injection에 대응하기 위한 PHP-MySQL 오픈소스 라이브러리로 LGPLv3 License를 지원한다. Prepare statement 방식으로 쿼리를 준비하여 쿼리에 사용되는 사용자 입력값이 미리 쿼리에 바인딩 되어 실행되기 전에 쿼리의 구조가 정적으로 결정되기 때문에 악의적인 입력값을 방지할 수 있다.

[표 5] MeekroDB 내장 메서드 목록

PHP의 내장 함수에서도 SQL Injection을 대응할 수 있는 사용자 입력값 필터링 기능을 제공하고 있다. [표 6]은 PHP에서 기본적으로 제공하는 필터링 함수 목록으로 SQL Injection에 자주 사용되는 특수문자를 치환하거나 제거 하는 게 사용할 수 있다.

[표 6] PHP 내장 필터링 함수 목록

04. 마무리

지금까지 WordPress Plug-in My Calendar에서 발생한 CVE-2023-6360과 HTML Forms에서 발생한 CVE-2022-3689의 사례를 통해 SQL Injection 취약점 대응 방안에 대해 알아보았다. WordPress와 같은 OSS 기반의 CMS의 경우 빠른 시간 내에 웹 사이트를 구성해야 하는 경우에 자주 사용되나 주기적인 보안 관리가 필요하다는 점을 간과하는 경우가 있다.

취약점 목록을 제공하는 CVE Detail에 따르면 WordPress에서 발생하는 취약점을 EPSS 스코어가 높은 기준으로 정렬하면 2024년 6월 기준으로 상위 5개의 취약점(CVE-2016-10033, CVE-2009-2335, CVE-2016-10045, CVE-2007-1277, CVE-2019-8942)으로 나타나고 있다. 상위 5개의 취약점 모두 취약점에 영향받는 버전이 공개된 지 10년 가까이 된 취약점들로 여전히 높은 취약점 영향도를 보이고 있다는 것을 알 수 있다.

다양한 플러그인을 제공하는 WordPress를 보다 안전하게 사용하기 위해서는 현재 운영 중인 웹 사이트 내에 적용되어 있는 플러그인의 현황을 파악하고 해당 플러그인이 SQL Injection을 포함한 취약점에 영향받지 않는 안전한 버전인지 주기적으로 확인하는 것이 필요하다. 이를 위해서는 WPScan, Defender Security, Wordfence 등의 워드프레스 전용 스캐너를 이용해 취약점 존재 여부를 확인하고 주기적인 보안패치를 수행하는 것이 필요하다.

05. 참고자료

1. WordPress Vulnerability Report — May 29, 2024 : https://solidwp.com/blog/wordpress-vulnerability-report-may-29-2024/
2.W3techs(World Wide Web Technology Surveys) Usage statistics and market shares of content management systems : https://w3techs.com/technologies/overview/content_management
3.CVE-2023-6360 Detail : https://nvd.nist.gov/vuln/detail/CVE-2023-6360
4.CVE-2022-3689 Detail : https://nvd.nist.gov/vuln/detail/CVE-2022-3689
5.HTML Forms < 1.3.25 - Admin+ SQLi : https://wpscan.com/vulnerability/e9c551a3-7482-4421-8197-5886d028776c/
6.HTML Forms – Simple WordPress Forms Plugin : https://WordPress.org/plugins/html-forms/advanced/
7.WordPress My Calendar <3.4.22 - SQL Injection : https://pentest-tools.com/vulnerabilities-exploits/WordPress-my-calendar-3422-sql-injection_22498
8.My Calendar – Accessible Event Manager : https://WordPress.org/plugins/my-calendar/
9.wpdb – Class - WordPress Developer Resources : https://developer.WordPress.org/reference/classes/wpdb/
10.PHP: PHP Manual – Manual : https://www.php.net/manual/en/index.php
11.MeekroDB :: The Simple PHP MySQL Library : https://meekro.com/