• 有关python的ssrf


    有关python的ssrf

    前言

    • 环境:buuctf中[De1CTF 2019]SSRF Me
    • 知识点:python代码审计,ssrf

    做题

    审计源码

    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):	#python类的构造函数
        	self.action = action
    		self.param = param
    		self.sign = sign
    		self.sandbox = md5(ip)
    		if (not os.path.exists(self.sandbox)):
        		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", "")) \对参数Param进行url解码
        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())		\打印出一个json对象
    @ app.route('/') #显示源代码
    def index(): 
    	return open("code.txt", "r").read() 
    def scan(param): 
    	socket.setdefaulttimeout(1) 
        try: 
        	return urllib.urlopen(param).read()[: 50] #造成ssrf的漏洞,对param进行读取,返回前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() \strip()移除两边的空格
        if check.startswith("gopher") or check.startswith("file"): #字符串开头不能是gopher和file
        	return True
    	else :return False
    if __name__ == '__main__': app.debug = False app.run(host = '0.0.0.0', port = 80)
    

    根据题目提示这里存在ssrf漏洞

    我们找到可以读取文件的函数,urllib.urlopen(param).read ,可以看到没有对param进行过滤,明显存在ssrf漏洞

    def scan(param): 
    	socket.setdefaulttimeout(1) 
        try: 
        	return urllib.urlopen(param).read()[: 50] #造成ssrf的漏洞,对param进行读取,返回前50个字符
        except: 
        	return "Connection Timeout"
    

    追踪scan,在类Task里定义了Exec函数,如果满足了checkSign这个函数并且action这个字符串里面包含了scan,就会调用scan函数


    先追踪哪里使用了这个类,可以看到在De1ta这个路由内,定义了一个Task的对象task,并且调用了Exec方法,将其结果以json的形式打印出来,看来注入点就是这个路由


    调用Exec函数前,waf函数会对param参数进行过滤,过滤了协议file和gopher


    接下来我们要做的就是满足checkSign函数,调用了getSign函数


    而getSian函数,可以看到我们不知道secert_key是什么,也就无法直接构造sign了


    但是我们在geneSign这个路由里,看到会返回getSign这个值,所以我们就可以通过这个路由,来获取sign值


    根据代码逻辑,这里为什么param是flag.txtread呢

    首先我们要搞清楚为什么要加个read,调用scan函数后,会将我们读取的文件写进result.txt内,如果read也在action这个字符串内,那么就会读取result.txt的内容,但是明明有个print,为什么还要read呢?这里print并不会输出,原因我也不晓得。

    其次我们要搞清楚read的位置,根据getSign函数,加密顺序是secert_key+param+action,注意传进去是action在前

    在geneSign
    (scan,flag.txtread)=>flag.txtreadscan

    那么在De1ta里

    (readscan,flag.txt,sign)=>flag.txtreadscan



    值得一试的是:通过urllib.urlopen(param).read(),读取内部文件不要file协议就可以读取,

    而php的curl读取内部文件时,要通过file协议才可以,不然会报错


  • 相关阅读:
    (转)正则表达式与Python(RE)模块
    (转)【面试】【MySQL常见问题总结】【03】
    (转)MySQL性能调优my.cnf详解
    (转)python logging模块
    (转)python collections模块详解
    mysql故障总结
    rocksdb 编译安装 日志
    c++11 gcc4.8.x安装
    Install ssdb-rocks on CentOS 6
    在Shell里面判断字符串是否为空
  • 原文地址:https://www.cnblogs.com/NineOne/p/14100172.html
Copyright © 2020-2023  润新知