Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2020-21038: typecho登录后的url重定向漏洞 | Typecho latest version url jump vulnerability after login · Issue #952 · typecho/typecho

Open redirect vulnerability in typecho 1.1-17.10.30-release via the referer parameter to Login.php.

CVE
#xss#vulnerability#linux#debian#apache#php#nginx#auth#chrome

1. 该问题的重现步骤是什么?

Typecho latest version url jump vulnerability after login
code:

admin > Login.php
public function action()
{
    // protect
    $this->security->protect();

    /** 如果已经登录 */
    if ($this->user->hasLogin()) {
        /** 直接返回 */
        $this->response->redirect($this->options->index);

Typecho first determines whether the user has logged in. If it has already logged in, it directly calls the redirect () function to redirect to the home page. This jump is mainly used to determine the user’s status.
typecho首先判断用户是否已经登录,如果已经登录,则直接调用redirect()函数重定向至首页,这个跳转主要用于判断用户的状态。

admin > Login.php
if (NULL != $this->request->referer) {
    $this->response->redirect($this->request->referer);
} else if (!$this->user->pass('contributor', true)) {
    /** 不允许普通用户直接跳转后台 */
    $this->response->redirect($this->options->profileUrl);
} else {
    $this->response->redirect($this->options->adminUrl);
}

If there is no login, first verify whether the user’s identity is correct, if the address of the referer is not empty, then call the redirect () function to redirect through the referer parameter, the referer parameter can be controlled
如果没有登录,则首先校验用户的身份是否正确,如果referer的地址不为空,则调用redirect()函数通过referer的参数进行重定向,referer参数可控

Follow up: redirect () function, which defines two parameters, of which the default is not permanent redirect
跟进:redirect()函数,该函数定义了两个参数,其中默认不为永久重定向

// Response.php 
 public function redirect($location, $isPermanently = false)
{
    /** Typecho_Common */
    $location = Typecho_Common::safeUrl($location);

    if ($isPermanently) {
        header('Location: ' . $location, false, 301);
        exit;
    } else {
        header('Location: ' . $location, false, 302);
        exit;
    }
}

After the referer’s address is passed to the redirect () function, it will first call Typecho_Common> static method safeUrl () to check the url, and finally determine whether to use permanent or temporary redirection according to the status of isPermanently
referer的地址传递到redirect()函数中以后,首先会调用Typecho_Common > 静态方法safeUrl()对url进行检查,最后根据isPermanently的状态,判断使用永久重定向还是临时重定向

Follow up: /var/Typecho/Common.php
跟进:/var/Typecho/Common.php

public static function safeUrl($url)
{
    //~ 针对location的xss过滤, 因为其特殊性无法使用removeXSS函数
    //~ fix issue 66
    $params = parse_url(str_replace(array("\r", "\n", "\t", ' '), '', $url));

    /** 禁止非法的协议跳转 */
    if (isset($params['scheme'])) {
        if (!in_array($params['scheme'], array('http', 'https'))) {
            return '/';
        }
    }

    /** 过滤解析串 */
    $params = array_map(array('Typecho_Common', '__removeUrlXss'), $params);
    return self::buildUrl($params);
}

Use safe_rl to replace string in str_replace (), replace all characters in array () list with empty string, and then parse the address of referer by calling parse_url () function: scheme, host, port, path, etc Array to $ params variable
Next, determine whether the protocol name is empty. If the source address is not the URL of the http protocol and https protocol, return the “/” path directly
safeUrl内使用str_replace()进行字符串替换,将array()列表中的所有字符替换为空字符串,然后通过调用parse_url()函数对referer的地址进行解析:scheme,host,port,path等返回一个数组到$params变量
接着判断,协议名称是否为空,如果来源地址不是http协议与https协议的url则直接返回 “/”路径

/**
 * 根据parse_url的结果重新组合url
 *
 * @access public
 * @param array $params 解析后的参数
 * @return string
 */
public static function buildUrl($params)
{
    return (isset($params['scheme']) ? $params['scheme'] . '://' : NULL)  // 如果已经设置了协议名称,则默认在协议后面拼接''://',否则为空
    . (isset($params['user']) ? $params['user'] . (isset($params['pass']) ? ':' . $params['pass'] : NULL) . '@' : NULL)
    . (isset($params['host']) ? $params['host'] : NULL) // 如果已经设置了主机名,则使用host参数
    . (isset($params['port']) ? ':' . $params['port'] : NULL) // 如果已经设置了端口,则默认在端口后面拼接":",否则为空
    . (isset($params['path']) ? $params['path'] : NULL) // 如果已经设置了路径,则使用path参数
    . (isset($params['query']) ? '?' . $params['query'] : NULL) // 如果已经设置了查询,则默认在query前拼接'?' 
    . (isset($params['fragment']) ? '#' . $params['fragment'] : NULL); 如果已经设置了分段,则默认在分段前拼接''#' 
}

The formatted URLs are recombined and finally returned
格式化以后的url进行重新组合,最后返回

/**
 * 将url中的非法xss去掉时的数组回调过滤函数
 *
 * @access private
 * @param string $string 需要过滤的字符串
 * @return string
 */
public static function __removeUrlXss($string)
{
    $string = str_replace(array('%0d', '%0a'), '', strip_tags($string)); // 使用strip_tags()函数,强制过滤html代码,然后使用str_replace()函数将%0d,%0a(回车,换行)字符替换为空
    return preg_replace(array(
        "/\(\s*(\"|')/i",           //函数开头
        "/(\"|')\s*\)/i",           //函数结尾
    ), '', $string);
}

poc:

http://127.0.0.1/admin/login.php?referer=http://www.baidu.com

登录前:

登录后跳转:

response:

HTTP/1.1 302 Found
Date: Wed, 29 Apr 2020 07:37:06 GMT
Server: Apache/2.4.41 (Debian)
Set-Cookie: 122609301a375f23d5f0237df98400e5__typecho_uid=1; path=/
Set-Cookie: 122609301a375f23d5f0237df98400e5__typecho_authCode=%24T%24VThLGyoLF07ae80963105613a00721934cce56d0a; path=/
Location: http://www.baidu.com
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8

2. 你期待的结果是什么?实际看到的又是什么?

1.typecho首先判断用户登录状态
2.如果已经登录则返回主页
3.如果没有登录则先判断用户帐号密码是否正确,然后判断referer的来源是否为空
4.如果不为空则调用redirect()函数进行重定向,location参数可控,即用户的referer的请求来源,恶意的攻击者可利用登录后的重定向将用户引诱至一个恶意的钓鱼页面。并且可以通过注册相似域名提高信任度,只需要将注册好的域名安装typecho,将用户重定向至自己本域的登录地址,然后通过查看本地的登录日志即可获取其他管理用户的帐号密码。
5.rediect() > Typecho_Common::safeUrl()函数对referer的参数进行过滤及校验协议是否为http/https
6.rediect() > self::buildUrl()函数根据parse_url的结果重新组合url
7.redirect()根据isPermanently参数进行301/302跳转

修复方案:
1.校验用户登录以后的referer地址为本域
2.并且只允许本域内进行跳转

3. 问题出现的环境

  • 操作系统版本:Linux kali 5.3.0-kali2-amd64 70,能整一个 todolist 么? #1 SMP Debian 5.3.9-3kali1 (2019-11-20) x86_64 GNU/Linux
  • Apache/NGINX 版本:Server version: Apache/2.4.41 (Debian),Server built: 2019-08-14T04:42:29
  • 数据库版本:mariadb Ver 15.1 Distrib 10.3.20-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2
  • PHP 版本:PHP 7.3.10-1+b1 (cli) (built: Oct 13 2019 23:50:57) ( NTS )
    Copyright © 1997-2018 The PHP Group
    Zend Engine v3.3.10, Copyright © 1998-2018 Zend Technologies
    with Zend OPcache v7.3.10-1+b1, Copyright © 1999-2018, by Zend Technologies
  • Typecho 版本:1.1-17.10.30-release
  • 浏览器版本:Chromium 81.0.4044.92 built on Debian bullseye/sid, running on Debian kali-rolling
    [//]: # (如有图片请附上截图)

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