Continuous delivery is awesome.
We deploy our code to production whenever we can.
No code, no vulnerability.
Everything works great.
Overview, Concept and Design Criteria
Some participants found a unintended solution by copying
__builtins__ to module attribute, which is much more powerful than intended solution.
But intended solution is still quite interesting and worth to take a look.
Many things got implemented in this version:
def login(self): with open('../flag.txt', 'rb') as f: flag = f.read() flag = bytes(a ^ b for a, b in zip(self.key, flag)) user = input().encode('ascii') user = codecs.decode(user, 'base64') user = pickle.loads(user) print('Login as ' + user.name + ' - ' + user.group) user.privileged = False user.flag = flag self.user = user def cmd_flag(self): if not self.user.privileged: print('flag: Permission denied') else: print(bytes(a ^ b for a, b in zip(self.user.flag, self.key)))
And import is switched back to
find_class, so that our monkey patch won't work.
The flag will pop out if
user.privileged is truthy value, but it's set to False after our pickle loaded. One solution is overwrite
__setattr__ so that the assignment is ignored. It won't work because we also need the
user.flag to be set. (Actually, this method is the reason why this strange flag assignment logic exists)
The trick of this version is descriptor, it can overwrite the setter or getter of one specific field.
So we can ignore the assignment of
user.privileged while leaving assignment of
user.flag keep working.
To build a descriptor, we need to have a class with
__set__ method. We can reuse our
User class here by adding a
__set__ method to it.
Pickle cannot create function, we can only use existed function. Constructor of
User is suitable for this case, both of its arity and side effect fit.
__set__ function, we can create a descriptor for
privileged field. Just create a
User instance and store it at
When we accessing
user.privileged, it will return the descriptor itself, which is truthy. And the flag will pop out as expected.
You can find the final pickle bytecode here.