호그와트

초코샵 냠냠 (Dreamhack)

영웅*^%&$ 2021. 10. 11. 14:53
728x90

# 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" 어쩌고 하는 답이 나왔다

최종 실험 결과 : 정상적인 웹 페이지에서는 똑같은 가설을 여러번 실험했으나 효과가 없었다

그러나 (비밀~~~~~) 실험했더니 바로 재사용 되면서 플래그를 획득하게 되었다

728x90

'호그와트' 카테고리의 다른 글

호박부수기 (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