WeCTF — FasterShop Write-Up

Neptunian
4 min readJun 18, 2020

--

WeCTF

Este CTF, iniciado este ano, é focado apenas em desafios web e, embora eu tenha chegado no finalzinho, deu tempo de um pouquinho de diversão e aprendizado.

WeCTF

FasterShop

Como eu não fiz todos os prints a tempo (explico no final), vou fazer algumas adaptações aqui. Foi um pouco mais fácil do que os posts anteriores, mas foi interessante.

Neste desafio, você tem uma loja onde pode comprar uma lista de produtos. Cada produto que você compra entra na lista de histórico abaixo e você pode vender novamente, recuperando o saldo.

Ao vender todos os produtos, você fica com um saldo máximo de 20 “bucks”, mas o objetivo é comprar a “Fancy Flag”, que custa 21!

Saldo máximo não compra a Fancy Flag

Tentando o básico

As primeiras tentativas de ataque foram nas urls /buy (comprar) e /sell (vender).

  • /buy/1: Compra produto com ID 1
  • /sell/45: vende o produto com histórico de compra ID 45

Exemplos de chamadas:

curl 'http://faster.w-va.cf:1002/sell/45' \
-X 'POST' \
-H 'Connection: keep-alive' \
-H 'Content-Length: 0' \
-H 'Pragma: no-cache' \
-H 'Cache-Control: no-cache' \
-H 'Upgrade-Insecure-Requests: 1' \
-H 'Origin: http://faster.w-va.cf:1002' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36' \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
-H 'Referer: http://faster.w-va.cf:1002/' \
-H 'Accept-Language: en-US,en;q=0.9,pt-BR;q=0.8,pt;q=0.7,es;q=0.6,fr;q=0.5' \
-H 'Cookie: token=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
--compressed \
--insecure
curl 'http://faster.w-va.cf:1002/buy/1' \
-X 'POST' \
-H 'Connection: keep-alive' \
-H 'Content-Length: 0' \
-H 'Pragma: no-cache' \
-H 'Cache-Control: no-cache' \
-H 'Upgrade-Insecure-Requests: 1' \
-H 'Origin: http://faster.w-va.cf:1002' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36' \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
-H 'Referer: http://faster.w-va.cf:1002/' \
-H 'Accept-Language: en-US,en;q=0.9,pt-BR;q=0.8,pt;q=0.7,es;q=0.6,fr;q=0.5' \
-H 'Cookie: token=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
--compressed \
--insecure

Após várias tentativas de acesso indevido brincando com os IDs, o máximo que consegui foi causar alguns erros 500 incluindo aspas duplas, mas sem SQL Injections gerados com sucesso. Segui a vida.

Fuçando IDs

Também tentei vender o produto dos outros, pra ver se funcionava e aumentava o meu saldo. Fiz um loop pra tentar encontrar históricos diferentes pra vender.

Loop pra vender os produtos dos outros

Encontro alguns IDs dos outros, mas recebo a mensagem “Not the purchase you made bro…”. O maldito validava isso :)

Já não tinha muita esperança de resolver dessa forma, porque o desafio anterior foi mais ou menos assim (mas não custava nada tentar).

Tudo ao mesmo tempo

Provavelmente, o algoritmo de compra era algo como isso (pseudo-código simplificado):

// "db" é o objeto de banco de dados
saldo_atual = session['saldo_atual']
id_usuario = session['id_usuario']
id_produto = request['id']
db.start_transaction()valor_produto = db.get("select valor from produtos where prod_id = :prod_id", id_produto)
db.execute("insert into historico_transacao (id_usuario) values (:id_usuario, :id_produto)", id_usuario, id_produto)
db.commit()
session['saldo_atual'] = saldo_atual - valor_produto

Se esta chamada for feita várias vezes em paralelo, existe a probabilidade de que a primeira linha, que recebe o saldo atual, pegue um saldo desatualizado de alguma transação que ainda não terminou. Quando a subtração for feita, o saldo continua mais alto do que deveria. Race Condition!

Não custa nada testar. Neste caso, opto por um script NodeJS, que já trabalha nativamente de forma assíncrona, pra chamar vários requests praticamente ao mesmo tempo:

De repente, me vejo com mais produtos do que deveria!

Após a venda, tenho 25 “bucks” de saldo! O suficiente pra comprar a Fancy Flag. Race Condition!

25 bucks de saldo!!

Final Infeliz

Pouco após aumentar o meu saldo, o site parou de funcionar. Até aguardei um tempinho pra ver se colocariam no ar de novo, mas não tinha percebido que o maldito CTF havia terminado!!

Achei que iria até 19h, mas finalizava 14h. Ainda daria tempo de pegar a flag e submeter no site, mas eu parei pra tentar aumentar ainda mais o saldo e perdi o timing! Por isso não peguei a flag e nem o restante dos prints.

Fica pra próxima. O próximo evento do WeCTF está previsto pra dezembro :)

O site do CTF já saiu do ar, mas descobri agorinha que o código-fonte dos desafios foi disponibilizado not github (link nas referências) e você pode checar o verdadeiro código de comprar produto (/buy), que foi atacado neste desafio.

Referências

--

--

Neptunian
Neptunian

Written by Neptunian

Hacker tiozão do pavê de final de semana

No responses yet