-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathc13.py
120 lines (95 loc) · 3.2 KB
/
c13.py
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
from Crypto import Random
from pals import utils
import re
random_key = Random.get_random_bytes(16)
def kv_parse(s):
fields = s.split('&')
pairs = [f.split('=') for f in fields]
return {p[0]:p[1] for p in pairs}
def kv_encode(d):
pairs = []
for key, value in d.items():
pairs.append('{}={}'.format(key, value))
return "&".join(pairs)
def profile_for(email):
email = re.sub("[&=?]", "", email)
return kv_encode({
"email": email,
"uid": 10,
"role": 'user'
})
#the way that the application treats the cookies it gets
def parse_encrypted_profile(b):
s = utils.aes_ecb_decrypt(b, random_key).decode('ascii')
return kv_parse(utils.unpad_string(s))
#the cookie the attacker gets back (we'd b64 this)
def black_box(user_input):
return utils.aes_ecb_encrypt(bytes(profile_for(user_input), 'ascii'), random_key)
#we need to remove the padding from the encryption
#which is where the real problem is; we can figure
#out what the ecb 16 byte result for 'admin' plus
#padding, then make sure that the first two params
#('email' and 'uid') are neatly contained within
#two 16 byte blocks, and then just append the admin
#block to that 'real' user. parse method will then
#just strip off the padding at the end, and we have
#an admin user.
#first we jam in a bunch of 16 byte blocks of 'admin'
#plus padding to take us to the end of the block
enc = black_box('A' * 10 + (utils.pad_string('admin', 16)) * 10)
dec = parse_encrypted_profile(enc)
print('===== bad ======')
print(len(enc), dec)
blocks = utils.get_blocks(enc, 16)
[print(len(block), block) for block in blocks]
#grab one of the admin blocks
tail = blocks[3]
print('===== good =====')
enc = black_box('[email protected]')
dec = parse_encrypted_profile(enc)
print(dec)
blocks = utils.get_blocks(enc, 16)
[print(len(block), block) for block in blocks]
#grab the first two blocks for the user who will get access
head = blocks[0] + blocks[1]
#submit the combined blocks as an encrypted cookie
print('==== combined =====')
print(parse_encrypted_profile(head + tail))
#email=&uid=10&role=
# ^5 ^12
'''
ECB cut-and-paste
Write a k=v parsing routine, as if for a structured cookie.
The routine should take:
foo=bar&baz=qux&zap=zazzle
... and produce:
{
foo: 'bar',
baz: 'qux',
zap: 'zazzle'
}
(you know, the object; I don't care if you convert it to JSON).
Now write a function that encodes a user profile in that format,
given an email address. You should have something like:
profile_for("[email protected]")
... and it should produce:
{
email: '[email protected]',
uid: 10,
role: 'user'
}
... encoded as:
[email protected]&uid=10&role=user
Your "profile_for" function should not allow encoding
metacharacters (& and =). Eat them, quote them, whatever you
want to do, but don't let people set their email address to
"[email protected]&role=admin".
Now, two more easy functions. Generate a random AES key, then:
Encrypt the encoded user profile under the key; "provide"
that to the "attacker".
Decrypt the encoded user profile and parse it.
Using only the user input to profile_for() (as an oracle to
generate "valid" ciphertexts) and the ciphertexts themselves,
make a role=admin profile.
'''