# SECRET CONSTANTS
# JWT_SECRET = 'JWT_KEY'
# FLAG = 'DH{FLAG_EXAMPLE}'
from secret import JWT_SECRET, FLAG
# PUBLIC CONSTANTS
COUPON_EXPIRATION_DELTA = 45
RATE_LIMIT_DELTA = 10
FLAG_PRICE = 2000
PEPERO_PRICE = 1500
def handle_errors(error):
return jsonify({'status': 'error', 'message': str(error)}), error.code
for de in default_exceptions:
app.register_error_handler(code_or_exception=de, f=handle_errors)
def get_session():
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
uuid = request.headers.get('Authorization', None)
if uuid is None:
raise BadRequest("Missing Authorization")
data = r.get(f'SESSION:{uuid}')
if data is None:
raise Unauthorized("Unauthorized")
kwargs['user'] = loads(data)
return function(*args, **kwargs)
return wrapper
return decorator
@app.route('/flag/claim')
@get_session()
def flag_claim(user):
if user['money'] < FLAG_PRICE:
raise BadRequest('Not enough money')
user['money'] -= FLAG_PRICE
return jsonify({'status': 'success', 'message': FLAG})
@app.route('/pepero/claim')
@get_session()
def pepero_claim(user):
if user['money'] < PEPERO_PRICE:
raise BadRequest('Not enough money')
user['money'] -= PEPERO_PRICE
return jsonify({'status': 'success', 'message': 'lotteria~~~~!~!~!'})
@app.route('/coupon/submit')
@get_session()
def coupon_submit(user):
coupon = request.headers.get('coupon', None)
if coupon is None:
raise BadRequest('Missing Coupon')
try:
coupon = jwt.decode(coupon, JWT_SECRET, algorithms='HS256')
except:
raise BadRequest('Invalid coupon')
if coupon['expiration'] < int(time()):
raise BadRequest('Coupon expired!')
rate_limit_key = f'RATELIMIT:{user["uuid"]}'
if r.setnx(rate_limit_key, 1):
r.expire(rate_limit_key, timedelta(seconds=RATE_LIMIT_DELTA))
else:
raise BadRequest(f"Rate limit reached!, You can submit the coupon once every {RATE_LIMIT_DELTA} seconds.")
coupon을 얻고난 후 제출submit을 하면
대략 3단계가 있다
1) rate어쩌고 하면서 제출한 직후 10초를 기다리게 하는 것
2) already submitted 하면서 거의 막바지까지 뜨는 내용
3) 그리고 최종 expiration (정확히 45초)
당연히 expiration한 이후에는 쿠폰 자체를 없애버리는 것이기 때문에
재사용할 수 있는 권한은 없다 당연한 일이다
그러면 논리적으로 따져 봤을 때
already submitted < x < expiration
이 사이에 거의 1~2초 차이를 두고 coupon을 다시 submit하는 타이밍이
존재할 수도 있다 (실제로 이 가설은 맞았다)
실험 결과 : 쿠폰을 받은 후에 submit하고 int(time())이후 45초에 맞춰서 매우 근소하게
쿠폰을 재차 submit 했다 그리고 받은 답은 "you already submitted" 어쩌고 하는 답이 나왔다
최종 실험 결과 : 정상적인 웹 페이지에서는 똑같은 가설을 여러번 실험했으나 효과가 없었다
그러나 (비밀~~~~~) 실험했더니 바로 재사용 되면서 플래그를 획득하게 되었다
'호그와트' 카테고리의 다른 글
호박부수기 (Dreamhack) (0) | 2021.10.13 |
---|---|
php-1(Dreamhack) (0) | 2021.10.11 |
file-csp (Dreamhack) (0) | 2021.10.11 |
기본적인 문제 (0) | 2021.10.11 |
영지식 대화형 증명시스템 (0) | 2021.07.24 |