SECARMY CTF: BabyPhp — Write-Up

E aí, galera, de boas? Meu nick é v3ntur4X, dessa vez vou fazer a resolução de um desafio web que aconteceu no SecArmy CTF, que rolou nos dias 18/04 e 19/04.

Vamos lá!

O desafio falava para acessar uma url: https://sec-army.ml/babyphp/babyphp.php

E, ao acessar, era possível ver isso:

...
Página "babyphp.php"


Eu percebi que era um base64 e decodei ele, que retornou um join:

...
Decode do base64

E eu não sabia ao certo se estava se tratando de um MySQL ou Python, então fui testar. Testei no próprio Python e deu certo!

...
Execução o método join


Como é possível ver na imagem acima, ele pedia para passar o parâmetro "viewsource" via GET e assim eu fiz.

E ele mostrou o código fonte do arquivo "babyphp.php":

...
Código fonte do "babyphp.php"


A partir daí comecei a procurar entender mais o código.

Até a função "highlight_file" é possível entender que se não fosse passado o parâmetro "viewsource" ele retornaria o base64.

Procurei me localizar pelas saídas que o código retornava para me orientar em que parte do código eu estava:

...
Primeiro IF


A partir do primeiro IF eu enviei o parâmetro "key" e agora foi retornado uma saída diferente:

...
Segundo IF


Agora nesse segundo IF, ele faz uso da função de comparação, "strcmp". Ele compara o valor passado em "key" com uma variável "$key", mas não sabemos o valor de $key e ficar tentando possibilidades não seria tão inteligente, pois existe uma forma de dar bypass na função strcmp :).

Precisamos entender como funciona o strcmp (deixarei o link tanto do bypass quanto da documentação do php sobre o strcmp no final do writeup): retorna < 0 se a primeira string é menor do que a segunda; > 0 se a primeira string é maior do que a segunda, e 0 se forem iguais.

Agora para dar o bypass, passaremos o parâmetro no key um array vazio: &key[], pois passando tal array o strcmp retorna o valor NULL e devido algumas complicações no PHP em relação ao que tange comparações, o PHP trata NULL igual a 0, e o IF considera o valor 0 como false, logo o que está dentro do IF não será executado.

...
Bypass do "strcmp"


Agora nesse terceiro IF, é preciso passar um novo parâmetro chamado "secret":

...
Novo parâmetro passado


Nesse quarto e último IF é preciso entender algumas coisas antes...

Algumas linhas antes do último IF são declaradas 4 variáveis: $_p, $_l, $l, $_i. As duas primeiras variáveis são de fácil entendimento, apenas foram declarados alguns valores para elas: 1337 e 13, respectivamente. As duas ultimas precisam de mais atenção. Na $l é chamado uma função para retornar o comprimento (strlen — link da documentação do php sobre essa função estará no final do writeup) do valor/string que será recebido do parâmetro "secret". E na $_i é chamado uma função para retornar o valor inteiro (intval — link da documentação sobre essa função estará no final do writeup) de uma variável, no caso, também será o valor/string que será recebido de "secret".

E no quarto IF, temos que ele quer que $l seja diferente de $_l ou que $_i seja diferente de $p, mas nós não vamos atender esse IF, precisamos que ambas condições sejam falsas, ou seja, que $l seja igual $_l e que $_i seja igual $p.

Para isso, passaremos o valor "1337.00000001", pois, contando o ponto (para demarcar como um valor não inteiro), temos 13 caracteres (assim, igualará $l à $_l) e quando o intval entrar execução ele irá apenas retornar 1337 (assim, igualando $_i à $p).

...
Quase lá


No source da página:

...
Flag!


É isso, qualquer crítica ou dúvida podem fazer!


Links:

Manual do PHP

Manual do PHP - função strcmp

Manual do PHP - função strlen

Manual do PHP - função intval

Bypassing PHP strcmp() (ABCTF2016 – L33t H4xx0r)

Unauthorized Access: Bypassing PHP strcmp()