The Grep room is an OSINT challenge from TryHackMe’s red team path.
I enjoy OSINT. I think it’s fun! Before I even knew what cybersecurity was or that it was a career path, I already knew my way around a few OSINT techniques.
Even though I’m more interested in Blue Team work now, I’ll always be a sucker for OSINT based CTFs.
SuperSecure Corp, a fast-paced startup, is currently creating a blogging platform inviting security professionals to assess its security. The challenge involves using OSINT techniques to gather information from publicly accessible sources and exploit potential vulnerabilities in the web application.
Cool, we’re looking for vulnerabilities (or vulns we can find using OSINT) in the web app.
Like with any CTFs I do, the first thing I do is run an nmap
scan.
I don’t worry about a stealth scan or specifying any ports. For this room (which is noted as being easy) the default options should work just fine.
root@ip-10-10-249-189:~# nmap 10.10.68.155
Starting Nmap 7.80 ( https://nmap.org ) at 2025-07-23 09:22 BST
Nmap scan report for ip-10-10-68-155.eu-west-1.compute.internal (10.10.68.155)
Host is up (0.0047s latency).
Not shown: 997 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
MAC Address: 02:74:42:E0:62:61 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 0.49 seconds
Cool. There’s an https
port open on the expected 443
port.
Taking a look at the page gives me a 403. That’s a bit unexpected
There’s a self-signing certificate here. We can view the certificate details and see the common name of this site: grep.thm:
Let’s head over and edit out /etc/hosts
file to add the FQDN:
10.10.68.155 grep.thm
Wonderful. Now we can access grep.thm and see the site:
There is a login page, and a register page also available. I took a quick look at the source of the initial page but it all looked pretty generic, nothing crazy.
The first question of this room is:
What is the API key that allows a user to register on the website?
I took a closer look at register.php
, which does have some interesting source code. If we check the register.js
referenced within we can find an API token, but this doesn’t work.
Using some meta-knowledge here - The header image for this room is a person holding binoculars pointing at the viewer with the Google and GitHub logo showing on each lens. That gives us a hint for what direction we should be going in here.
So, what do we know about this website?
- The product is called ‘SearchME’
- The project is written in php (Every page has been php)
- The room was released around August of 2023. (We know this based on the TryHackMe room creation date).
With that knowledge we can head over to Github and search for ‘SearchME’, specifically projects written in PHP.
This is an interesting repo. I like to look at the commit history to see if any API keys were removed, and here’s where we find the answer to question one!
There was an update to api/register.php
which removed an API key. We’ll keep this for later.
What is the first flag?
This is a bit of a weirdly worded question. Flag and Answer are sometimes used interchangably in rooms. The first flag is different from the answer we just gave (obviously).
Let’s try register an account and see if logging in gives us any more information. When we try to log in though, we get an error message: “Invalid or Expired API key”.
No worries. We have another API key we can use!
Open Burpsuite, turn the interceptor on and go to the register page. When you try to register send the POST request to the Repeater and replace API key to the one we found in the GitHub repo:
Now we can log in with the user we’ve just created, and we see the first flag waiting for us:
What is the email of the “admin” user?
If we look at the source of dashboard.php
we don’t see anything too interesting. dashboard.js
is referenced as well, but there’s nothing in here we can immediately see to answer the question.
When we were looking through the GitHub there were two commits. One for removing the API key, and another one just called ‘Feature update’ which made changes to upload.php
. The update checks the ‘magic bytes’ for allowed filetypes: jpg
, png
, and bmp
.
Initially I thought that we would need to use an SQL Injection on the register form to get the admin email address, however the upload page feels like it’s more clearly hinted at. Let’s take a look!
Knowing that we need to edit the magic bytes, we can just grab a reverse shell and edit the IP/Port as need be, then edit the magic bytes.
I used hexedit
here, and added FF D8 FF E0
to the beginning of the file. Once this is done we can upload it successfully!
After the reverse shell has been uploaded we can set up a listener. I just used nc -lvnp 4444
. Then go to the grep.thm/api/uploads
endpoint and click on our reverse shell. Eh Voilà!
I ran ls
to see what we can see:
bin
boot
dev
etc
home
lib
lib32
lib64
libx32
lost+found
media
mnt
opt
proc
root
run
sbin
snap
srv
sys
tmp
usr
var
There’s a lot to go through here. I checked the /home/
folders but couldn’t see anything, so it’s time to get digging.
We know this is a web server. So let’s see what’s in /var/www
:
$ cd /var/www
$ ls
backup
certificate.crt
certificate.csr
default_html
html
leak_certificate.crt
leak_certificate.csr
***********
private.key
private_unencrypted.key
This looks interesting! There’s another page here that we didn’t see in the Github OSINT (This has been censored above)
Inside of the backup
folder we can see the users.sql
file. At first I wasn’t too sure how we could check this. I decided to just try cat
ing it, and that worked just fine:
$ cd backup
$ ls
users.sql
$ cat users.sql
-- phpMyAdmin SQL Dump
-- version 5.2.1
-- https://www.phpmyadmin.net/
--
-- Host: 127.0.0.1
-- Generation Time: May 30, 2023 at 01:25 PM
-- Server version: 10.4.28-MariaDB
-- PHP Version: 8.0.28
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `postman`
--
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE `users` (
`id` int(11) NOT NULL,
`username` varchar(50) NOT NULL,
`password` varchar(255) NOT NULL,
`email` varchar(100) NOT NULL,
`name` varchar(100) DEFAULT NULL,
`role` varchar(20) DEFAULT 'user'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Dumping data for table `users`
--
INSERT INTO `users` (`id`, `username`, `password`, `email`, `name`, `role`) VALUES
(1, 'test', '$2y$10$dE6VAdZJCN4repNAFdsO2ePDr3StRdOhUJ1O/41XVQg91qBEBQU3G', 'test@grep.thm', 'Test User', 'user'),
(2, 'admin', '$2y$10$3V62f66VxzdTzqXF4WHJI.Mpgcaj3WxwYsh7YDPyv1xIPss4qCT9C', 'admin@##########.####.###', 'Admin User', 'admin');
--
-- Indexes for dumped tables
--
--
-- Indexes for table `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `username` (`username`),
ADD UNIQUE KEY `email` (`email`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `users`
--
ALTER TABLE `users`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
I’ve censored out the admin account email above, but there’s the answer to question three.
What is the host name of the web application that allows a user to check an email for a possible password leak?
We actually saw this above. There was a new name we haven’t seen before. This is a subdomain and the answer to the question.
What is the password of the “admin” user?
The wording of the above question gives us a major clue for the next steps here. We just need to connect to the web application and enter the admin username. Seems simple!
I tried navigating there but couldn’t find the site. We might need to finally run an nmap
scan.
The first nmap
scan I ran didn’t show us anything new. I re-ran it to search all ports. Why not:
root@ip-10-10-238-7:~# nmap 10.10.14.30
Starting Nmap 7.80 ( https://nmap.org ) at 2025-07-24 17:26 BST
Nmap scan report for grep.thm (10.10.14.30)
Host is up (0.0066s latency).
Not shown: 997 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
MAC Address: 02:49:9A:0D:D5:05 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 0.49 seconds
root@ip-10-10-238-7:~# nmap 10.10.14.30 -p-
Starting Nmap 7.80 ( https://nmap.org ) at 2025-07-24 17:26 BST
Nmap scan report for grep.thm (10.10.14.30)
Host is up (0.0075s latency).
Not shown: 65531 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
51337/tcp open unknown
MAC Address: 02:49:9A:0D:D5:05 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 4.96 seconds
There we see another service running on port 51337. When we try to visit this in the browser we can see that this only accepts HTTPS requests. We’ll need to edit our /etc/hosts
file again to add this new subdomain:
127.0.0.1 localhost
127.0.0.1 vnc.tryhackme.tech
127.0.1.1 tryhackme.lan tryhackme
10.10.14.30 grep.thm **********.grep.thm
Now we can visit the subdomain:
Wonderful!
Now all we need to do is enter the admin email we found above, and we have the final flag. That’s all there is to this room! Pretty fun.