- 周末简单看了看TestLink,觉得它的登录过程还比较有意思,涉及到了常见的开发错误
- 因此简单整理下,抛砖引玉
- 如果你也有阅读Bug report的习惯,相信会有共鸣
0x00 安装
git clone https://github.com/TestLinkOpenSourceTRMS/testlink-code
git branch -a
git checkout -b testlink_1_9_20_fixed origin/testlink_1_9_20_fixed
其后,需要修改两个路径
- 日志目录 (
$tlCfg->log_path
) - 上传目录 (
$g_repositoryPath
)
最后附上我的环境
● Apache 2.4.39 + PHP 7.4.3nts
● MySQL 5.7.26
● extension ldap enabled(None-Default)
0x01 风险概览
与其说是“代码审计”,不如说是“代码风险评估”。
一套系统的安全水位,从"安装”的时候就可初见端倪
(1)默认密码
成功安装后,TestLink Administrator默认的账号密码,登录后不强制要求更改密码
○ login name: admin
○ password : admin
(2)默认重装漏洞
默认重装漏洞, 安装好之后不会自动删除/install/
文件夹,也没有.lock
之类的文件来记录“安装状态”,因此即便之前已经安装好了——只要运维没删文件夹,就能重新安装(默认如此)
0x02 基础
这套CMS中, 不涉及路由转换, 直接访问文件即可。
倒是接受参数的形式,可以介绍下:定义了init_args()
方法,规定了参数的类型、范围等
- 一眼看过去,全是
String
,基本等于没校验嘛。 - 需要注意151-152行的,
urldecode()
。常见的二次解码,若参数filter使用顺序不当,极易造成二次编码绕过
0x03 漏洞
(1)开放重定向->XSS
直接参考这个pr即可
vuln OpenRedirect 导致 login.php 中的 XSS 攻击
redirect_url
限制不当,导致任意重定向;- 重定向由
<SCript>
标签实现,XSS filter不当,最终导致XSS;
(2)登录处用户名LDAP注入
LDAP注入不太常见,一般认为危害没有SQL注入大,可参考LDAP_Injection_Prevention_Cheat_Sheet
前置要求:
- 启用LDAP拓展,正确配置了LDAP认证服务器。
- 我这里采用的LDAP实现是 https://github.com/wshihadeh/ldap_server ,自己又加了几千条
直接上代码
lib/functions/ldap_api.php#161
攻击者可能通过此漏洞:
- 构造模糊查询,如*,耗尽LDAP服务器资源
- 更改LDAP查询语句的逻辑,进而间接控制查询结果为True、False
See:https://cloud.tencent.com/developer/article/1584896
我这里还希望举例说明一种不常见的利用方案:Heavy Query
将用户名改为*
,使该查询匹配当前cn下的所有用户,危害跟LDAP中的用户规模有关
发包过去,服务器处理了四十多秒...
(3)第三方认证后的设计缺陷:硬编码
在特定配置下,系统会将所有通过LDAP/Oauth登录的用户数据,在DB做插入,且会设置一个固定的密码。相关代码在:
lib/functions/doAuthorize.php#L137
# lib/functions/doAuthorize.php#L137
if( $forceUserCreation ) {
// first & last name are mandatory
$user->firstName = trim($user->firstName);
$user->lastName = trim($user->lastName);
$porsi = explode('@',$user->emailAddress);
if ($user->firstName == '') {
$user->firstName = 'DynGen ' . trim($porsi[0]);
}
if ($user->lastName == '') {
$user->lastName = 'DynGen ' . trim($porsi[1]);
}
// Anyway, write a password on the DB.
$fake = md5('the quick brown fox jumps over the lazy dog');//【默认密码!】
$fake = md5(md5($fake));
$user->setPassword($fake);
$doLogin = ($user->writeToDB($db) == tl::OK);
}
若满足下面的任意一种条件,即会触发自动添加账号的流程:
- 采用LDAP认证且配置开启了
ldap_automatic_user_creation。
- 采用Oauth认证。这一项,也是需要在
config.inc.php
里面配置滴。
要进入【添加账户】的流程,首先,要确保$loginExists
为False,采用LDAP认证肯定就查不到当前用户嘛...
// Think not using else make things a little bit clear
// Will Try To Create a New User
if( FALSE == $loginExists ) {//【condition 1】
$authCfg = config_get('authentication');
$forceUserCreation = false;
$user = new tlUser();
$user->login = $login;
$user->isActive = true;
if ($isOauth){//【condition 2'】
$forceUserCreation = true;
$user->authentication = 'OAUTH';
$user->emailAddress = $login;
$user->firstName = $options->givenName;
$user->lastName = $options->familyName;
} else {
if( $authCfg['ldap_automatic_user_creation'] ) {
$user->authentication = 'LDAP'; // force for auth_does_password_match
$check = auth_does_password_match($db,$user,$pwd);
if( $check->status_ok ) {
$forceUserCreation = true;//【condition 2''】
$uf = getUserFieldsFromLDAP($user->login,
$authCfg['ldap'][$check->ldap_index]);
$user->emailAddress = $uf->emailAddress;
$user->firstName = $uf->firstName;
$user->lastName = $uf->lastName;
}
}
}
if( $forceUserCreation ) {//【在DB中添加用户+默认密码】
这个例子,不禁让我想起Sam Curry在他那篇《We Hacked Apple for 3 Months...》中的漏洞,(Full Compromise of Apple Distinguished Educators Program via Authentication and Authorization Bypass)[https://samcurry.net/hacking-apple/#vuln1]
只不过那个场景更加明显,是在Form中携带了硬编码的密码。
但漏洞的原理都是一样的,开发者希望在不同的认证间做同步。
0x04 总结&&闲聊
总结
简单总结了以下登录的过程
闲聊
开源项目,很多都是“用爱发电”,,以TestLink为例,从2011年就开始开发了,
十年转瞬烟云,很多代码,连开发者自己都忘了吧。
更别提写代码的时候还可能犯困
(I'm very tired)......
而且LDAP注入的转义函数,开发是定义了的呀,为啥没用上。。。
# lib/functions/ldap_api.php
/**
* Escapes the LDAP string to disallow injection.
*
* @param string $p_string The string to escape.
* @return string The escaped string.
*/
function ldap_escape_string( $p_string )
{
$t_find = array( '\\', '*', '(', ')', '/', "\x00" );
$t_replace = array( '\5c', '\2a', '\28', '\29', '\2f', '\00' );
$t_string = str_replace( $t_find, $t_replace, $p_string );
return $t_string;
}
所以也是想分享一下体会:刚开始时,大可不必妄自菲薄,哪怕有文化差异(TestLink的界面不怎么符合我审美),慢慢钻进去,不断分析,肯定能找到漏洞的。