https://github.com/hstuk713/Wordpress-plugin-scanner
GitHub - hstuk713/Wordpress-plugin-scanner
Contribute to hstuk713/Wordpress-plugin-scanner development by creating an account on GitHub.
github.com
Plugin 수집
워드프레스 플러그인의 취약점을 스캔하기 위해서는 일단 플러그인들을 다운받아야 한다. 나는 wp를 로컬에서 열어서 wp-admin/plugin-install에 있는 인기 태그들을 기준으로 플러그인을 수집했다.

다운로드 자동화를 위해 bs4를 활용하여 wp plugin 만개 정도를 다운 받았다.

취약점 분석
이렇게 다운 받아진 파일들을 보고 나는 바로 자동화 스캐닝을 진행하였다. 내가 첫번째로 찾고자 하는 취약점은 SQL Injection이었다.
그리고 취약점 분석을 위해 semgrep이라는 툴을 사용했다.
semgrep 사용시에 rule을 지정해줄 수 있다. 쉽게 설명하자면 semgrep이 코드를 한줄한줄 읽으면서 해당 rule에 해당하는 코드가 있다면 취약하다고 판단하는 것이다. 따라서 나는 rule을 아래와 같이 지정해주었다.
rules:
- id: tainted-sql-string
languages:
- php
severity: ERROR
message: User data flows into this manually-constructed SQL string. User data
can be safely inserted into SQL strings using prepared statements or an
object-relational mapper (ORM). Manually-constructed SQL strings is a
possible indicator of SQL injection, which could let an attacker steal or
manipulate data from the database. Instead, use prepared statements
(`$mysqli->prepare("INSERT INTO test(id, label) VALUES (?, ?)");`) or a
safe library.
metadata:
cwe:
- "CWE-89: Improper Neutralization of Special Elements used in an SQL
Command ('SQL Injection')"
owasp:
- A01:2017 - Injection
- A03:2021 - Injection
references:
- https://owasp.org/www-community/attacks/SQL_Injection
category: security
technology:
- php
cwe2022-top25: true
cwe2021-top25: true
subcategory:
- vuln
likelihood: HIGH
impact: MEDIUM
confidence: MEDIUM
license: Semgrep Rules License v1.0. For more details, visit
semgrep.dev/legal/rules-license
vulnerability_class:
- SQL Injection
mode: taint
pattern-sanitizers:
- pattern-either:
- pattern: mysqli_real_escape_string(...)
- pattern: real_escape_string(...)
- pattern: $MYSQLI->real_escape_string(...)
pattern-sources:
- patterns:
- pattern-either:
- pattern: $_GET
- pattern: $_POST
- pattern: $_COOKIE
- pattern: $_REQUEST
pattern-sinks:
- pattern-either:
- patterns:
- pattern: |
sprintf($SQLSTR, ...)
- metavariable-regex:
metavariable: $SQLSTR
regex: (?is).*\b(select|delete|insert|create|update|alter|drop)\b.*
- patterns:
- pattern: |
"...$EXPR..."
- metavariable-regex:
metavariable: $EXPR
regex: (?is).*\b(select|delete|insert|create|update|alter|drop)\b.*
- patterns:
- pattern: |
"$SQLSTR".$EXPR
- metavariable-regex:
metavariable: $SQLSTR
regex: (?is).*\b(select|delete|insert|create|update|alter|drop)\b.*
예를 들어서 보통 SQLI가 발생하는 코드는 prepared statement가 없거나, 파라미터에 대한 필터링이 부족한 경우에 나타난다. 따라서 rule에 앞서 말한 패턴을 넣어주면 semgrep이 더욱 수월하게 취약한 코드를 찾아줄 것이다.
이제 rule까지 정해줬으니 파이썬으로 plugin 디렉터리에 저장되어 있는 모든 파일을 semgrep으로 검사하도록 코드를 작성하고 실행해보았다. 나는 semgrep이 취약점을 발견하면 해당 코드들을 Json 파일로 저장하게 만들어두었다. 그렇기 때문에 코드 실행이 끝난 후에 저장된 json 파일을 보면서 코드를 하나하나 찾아보며 audit을 진행하였다.
0-day-analytics(plugin)에서 SQLI 발견

json 파일에 위와 같이 sqli 코드를 발견했다고 적혀있었다. 따라서 해당 파일에 들어가서 한번 코드를 직접 분석해보았다.
$orderby = ( isset( $_GET['orderby'] ) && '' != $_GET['orderby'] ) ? \esc_sql( \wp_unslash( $_GET['orderby'] ) ) : self::$table::get_real_id_name();
$order = ( isset( $_GET['order'] ) && '' != $_GET['orderby'] ) ? \esc_sql( \wp_unslash( $_GET['order'] ) ) : 'ASC';
$query = 'SELECT
' . implode( ', ', self::$table::get_column_names() ) . '
FROM ' . $wpdb_table . ' WHERE 1=1 ' . $search_sql . ' ORDER BY ' . $orderby . ' ' . $order;
class-table-list.php에 들어가서 직접 코드를 보니 딱봐도 취약해보인다. 코드에서는 sqli를 막기 위해 파라미터에 esc_sql 함수를 사용했지만 해당 함수는 sqli를 막기엔 충분하지 않다. $orderby, $order 둘다 sqli에 대한 필터링이 충분하지 않기 때문에 두개의 파라미터에서 모두 취약점이 터질 것이다. 따라서 바로 로컬에서 wp-admin에 접속하여 플러그인을 추가하고 재현을 해보았다.
PoC

위의 사진을 확인해보면 order 파라미터에 sleep을 걸었을 때 시간 지연이 생기는 것을 확인할 수 있다. 그리고 order 뿐만이 아니라 orderby 파라미터에서도 똑같이 sleep이 걸린다.
하지만 order, orderby 파라미터는 class-table-list.php에서만 터지지 않고 해당 파라미터를 사용하는 모든 페이지에서 터진다. 내가 발견한 SQLI가 발생하는 엔드포인트는 아래와 같다.
- request viewer(class-requests-list.php)
- table viewer(class-table-list.php)
- mail viewer(class-wp-mail-list.php)
제보

해당 취약점을 발견하고 나는 바로 patchstack에 제보했고, patchstack 측에서 해당 플러그인에 sqli가 있었다는 이메일까지 받았다.
취약점 여러개 찾는건 시간문제인 것 같다.