1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | #!/usr/bin/env python # encoding: utf-8 import random import string import signal import sys import os import time from hashlib import sha256 from urlparse import parse_qsl os.chdir(os.path.dirname(os.path.abspath(__file__))) signkey = ''.join([random.choice(string.letters+string.digits) for _ in xrange(random.randint(8,32))]) items = [('Intel Core i9-7900X', 999), ('Intel Core i7-7820X', 599), ('Intel Core i7-7700K', 349), ('Intel Core i5-7600K', 249), ('Intel Core i3-7350K', 179), ('AMD Ryzen Threadripper 1950X', 999), ('AMD Ryzen 7 1800X', 499), ('AMD Ryzen 5 1600X', 249), ('AMD Ryzen 3 1300X', 149), ('Flag', 99999)] money = random.randint(1000, 10000) def list_items(): for i,item in enumerate(items): print '%2d - %-30s$%d' % (i, item[0], item[1]) def order(): n = input_int('Product ID: ') if n < 0 or n >= len(items): print 'Invalid ID!' return payment = 'product=%s&price=%d×tamp=%d' % (items[n][0], items[n][1], time.time()*1000000) sign = sha256(signkey+payment).hexdigest() payment += '&sign=%s' % sign print 'Your order:\n%s\n' % payment def pay(): global money print 'Your order:' sys.stdout.flush() payment = raw_input().strip() sp = payment.rfind('&sign=') if sp == -1: print 'Invalid Order!' return sign = payment[sp+6:] try: sign = sign.decode('hex') except TypeError: print 'Invalid Order!' return payment = payment[:sp] signchk = sha256(signkey+payment).digest() if signchk != sign: print 'Invalid Order!' return for k,v in parse_qsl(payment): if k == 'product': product = v elif k == 'price': try: price = int(v) except ValueError: print 'Invalid Order!' return if money < price: print 'Go away you poor bastard!' return money -= price print 'Your current money: $%d' % money print 'You have bought %s' % product if product == 'Flag': print 'Good job! Here is your flag: %s' % open('flag').read().strip() def input_int(prompt): sys.stdout.write(prompt) sys.stdout.flush() try: n = int(raw_input()) return n except: return 0 def menu(): print "CPU Shop" while True: print "Money: $%d" % money print "1. List Items" print "2. Order" print "3. Pay" print "4. Exit" sys.stdout.flush() choice = input_int("Command: ") { 1: list_items, 2: order, 3: pay, 4: exit, }.get(choice, lambda *args:1)() sys.stdout.flush() if __name__ == "__main__": signal.alarm(60) menu() | cs |
다른분들 많이 풀길래 열심히 잡아봤는데 결국 포기한 문제였다..ㅠㅠ
대회가 끝난 후 write up을 바로 봤는데 hash length extension attack 의 취약점을 이용한 문제인걸 알 수 있었다.
그래서 hash length extension attack에 대해 문서들을 조금 찾아보았다. 핵심내용을 요약해보자면
key를 몰라도 encrypt_hash(key + plain_text)와 key의 길이를 알면 encrypt_hash(key + plain_txt + 원하는문자열) 의 hash값을 알아낼 수 있다는 것이다.
위의 내용이 해당 문제에서 왜 문제가 되냐면
37 : sp = payment.rfind('&sign=')
이 문제에서는 &sign= 의 문자열을 기준으로 앞뒤 문자열을 자르고 진행하기 때문이다
또한 필터링 부분이 제일 앞글자를 기준으로 순서대로 product 와 price에 값을 넣기 때문에 앞 부분에 살 수 있는 물품을 넣어두고 뒤에 추가로 원하는 문자열에 &product=Flag 을 넣어두면 마지막 product에 들어가는 물품의 이름은 Flag가 되고 price 는 앞 부분에 넣은 물품의 가격이 되기 때문에 Flag를 살 수 있다.
위쪽에서 말한 encrypt_hash(key + plain_txt + 원하는문자열)는 hashpumpy 모듈을 이용해서 알아낼 수 있다.
[Solve]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import hashpumpy from pwn import * p = remote("cpushop.2018.teamrois.cn", 43000) p.recvuntil("Command: ") p.sendline('2') p.recvuntil("ID: ") p.sendline('2') p.recvuntil("Your order:\n") payment = p.recvuntil("\nMoney")[:-7] sp = payment.rfind('&sign=') sign = payment[sp + 6:] payment = payment[:sp] p.recvuntil("Command: ") for i in range(8, 32): newsign, newpayment = hashpumpy.hashpump(sign, payment,'&product=Flag', i) newpayment += '&sign=%s' % newsign p.sendline('3') p.recvuntil("order:") p.sendline(newpayment) s = p.recvuntil("Command: ") if not "Invalid Order!" in s: print s break p.close() # Good job! Here is your flag: RCTF{ha5h_l3ngth_ex7ens10n_a77ack_1s_ez} |
'CTF' 카테고리의 다른 글
[ISITDTU CTF 2018] write up (0) | 2018.07.29 |
---|---|
[SCTF 2018] dingJMax (0) | 2018.07.14 |
[RCTF 2018] simple vm (4) | 2018.05.21 |
[ASIS CTF 2018] Density (0) | 2018.05.18 |
[Hitb-xctf 2018] multicheck (0) | 2018.05.13 |