“Vulnado, Ownado? WTF!?”
O que é?
Seguindo a própria explicação do github do projeto:
É uma aplicação vulnerável feita em Java, que contém algumas das vulnerabilidades do OWASP TOP 10. –> Github Vulnado.
Aplicação
Essa é a página inicial da aplicação, assim que inicializamos o docker.
No próprio github da aplicação, existem os desafios e suas respectivas resoluções. Porém a nível de aplicação e como disse aqui vamos olhar linha a linha. 🥲
- Os desafios são os seguintes:
- SQLi
- XSS
- SSRF
- RCE
SQLi
Essa aplicação possui uma falha de SQLi no login, abaixo temos um trecho do código da aplicação que concatena uma variável que é controlada pelo usuário diretamente na query SQL.
User.java
public static User fetch(String un) {
Statement stmt = null;
User user = null;
try {
Connection cxn = Postgres.connection();
stmt = cxn.createStatement();
System.out.println("Opened database successfully");
String query = "select * from users where username = '" + un + "' limit 1";
System.out.println(query);
ResultSet rs = stmt.executeQuery(query);
if (rs.next()) {
String user_id = rs.getString("user_id");
String username = rs.getString("username");
String password = rs.getString("password");
user = new User(user_id, username, password);
}
Se prestarmos atenção na linha que o SELECT é feito conseguimos perceber nitidamente que a variável un
vem do argumento do método User fetch que é o nome do usuário.
Então se utilizarmos o comando admin' OR 1=1 --
, vamos conseguir bypassar o login?
SQN!!
Essa aplicação foi construida “propositalmente”, para não realizar o seu login, mesmo que seu payload esteja correto!. Exemplo:
Nessa imagem vemos que a conexão com o Banco de dados é realizada com sucesso, porém não conseguimos logar como admin. O que podemos fazer então para ter uma prova de conceito real de que nosso payload está funcionando? Sleep 4ever!!.
Sou da seguinte opnião que todo payload para realizar PoC de SQLi deveria ter uma função sleep, mesmo que não seja blind, pois irá funcionar em ambos os casos..
Executando o seguinte payload: '; select pg_sleep(10) --
, conseguimos ter certeza de que nossos comando SQL estão funcionando…
LoginController.java
@CrossOrigin(origins = "*")
@RequestMapping(value = "/login", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
LoginResponse login(@RequestBody LoginRequest input) {
User user = User.fetch(input.username);
if (Postgres.md5(input.password).equals(user.hashedPassword)) {
return new LoginResponse(user.token(secret));
} else {
throw new Unauthorized("Access Denied");
}
Continuando nossa Saga, iremos analisar um segundo trecho de código que nos diz o seguinte:
O Postgres compara o MD5 da senha inputada pelo usuário com a hash que existe no Banco da dados. (Não vou nem entrar no mérito do MD5 ser uma hash ultrapassada)
Mas com as informações do primeiro trecho de código que faz um select na tabela users e com o segundo trecho que mostra como realiza o md5 da senha, podemos tentar alterar a senha do usuário admin e logo em seguida logar com as novas credenciais..
Agora nosso payload fica algo mais ou menos assim:
'; update users set password=md5('parad0x_0xff') where username = 'admin' --
No meu caso se eu tentar logar “admin:para0x_0xff”, vai funcionar, porque uma vez que a query SQL foi quebrada através das aspas simples e o comando de update de senha foi interpretado pelo DB. Mesmo que cause um erro na aplicação o comando já vai ter sido executado.
Agora, espero que você tenha conseguido entender a nível de código o que aconteceu para existir a vulnerabilidade e como explora-la.
Misconfiguration
Uma vez que analisamos o código é possível encontrar usuário e senha chumbados no código.
Postgres.java
// Insert seed data
insertUser("admin", "!!SuperSecretAdmin!!");
insertUser("alice", "AlicePassword!");
insertUser("bob", "BobPassword!");
insertUser("eve", "$EVELknev^l");
insertUser("rick", "!GetSchwifty!");
Além de vulnerabilidades, quando estamos realizando um code review, deve-se buscar por fragilidades também.