步骤

代码分析

用ai格式化代码

#!/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模块
sys.setdefaultencoding('latin1')    //将默认编码设置为latin1

app = Flask(__name__)
secert_key = os.urandom(16)    //设置key为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:  //这里用的in,scan在action中
                tmpfile = open("./%s/result.txt" % self.sandbox, 'w') //打开result.txt,以可写的权限
                resp = scan(self.param)
                if (resp == "Connection Timeout"):
                    result['data'] = resp
                else:
                    print resp
                    tmpfile.write(resp) //将scan读取的前50个字符写入result.txt中
                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", "")) //从get参数中获取param值并url解码
    action = "scan"
    return getSign(action, param)


@app.route('/De1ta', methods=['GET', 'POST'])
def challenge():
    action = urllib.unquote(request.cookies.get("action")) //获取cookies中的action值并url解码
    param = urllib.unquote(request.args.get("param", "")) //获取get参数中的param值并url解码
    sign = urllib.unquote(request.cookies.get("sign")) //获取cookies中的sign值并url解码
    ip = request.remote_addr //获取请求端的IP地址
    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) //设置默认超时为1s
    try:
        return urllib.urlopen(param).read()[:50] //发起HTTP请求并读取响应内容的前50个字符
    except:
        return "Connection Timeout"


def getSign(action, param):
    return hashlib.md5(secert_key + param + action).hexdigest() //生成md5hash值


def md5(content):
    return hashlib.md5(content).hexdigest()


def waf(param):
    check = param.strip().lower()  //转小写
    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)

漏洞代码分析:

Task内:

if "scan" in self.action:
if "read" in self.action:

这里用的判断是 in ,只要里面包含字符串就返回真,所以如果是 scanread 两个if都返回真。

if (self.checkSign())

这里判断sign值是否相等,而sign通过cookie传入。需要md5(secert_key + param + action).hexdigest() == self.sign,param和acton可以自己传入控制,而secert_key在服务端,所以可以通过geneSign路由获取sing值。

在Task类内,result.txt的位置也可以知道,为md5(ip)/result.txt。

方法一:字符串拼接

得到flag的大概流程:

首先通过/geneSign,得到sign值来绕过self.checkSign(),之后在传入的action中需要同时包含read和scan,在"scan" in self.action中将flag.txt写入result.txt(题目提示)中,然后在"read" in self.action中奖result.txt中的内容读取出来。

1.绕过checkSign

访问/gensSign,action已经默认设置为scan,param是要读取的文件+read。

所以拼接为:secret_key+'flag.txtreadscan'。

/geneSign?param=flag.txt&action=readscan

2.读取flag.txt

访问/De1ta,action为cookies传入,值为readscan,param为get参数,值为flag.txt,sign为上面得到的,cookies传入。

方法二:哈希长度拓展攻击

如果你知道一个消息(message)和密钥(key)的组合的哈希值,即使不知道密钥的具体值,只要知道密钥的长度,你就能在这个消息后面添加额外的信息,并计算出新的哈希值。

首先通过/geneSign得到md5(secret_key + flag.txtscan)的md5值。

然后使用hashpump计算出添加read后的新hash值。

秘钥长度:secret_key(16)+flag.txt(8) = 24

sign值就为1fee4e944f01a5da4ae1cf86514ad435,action为scan\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x00\x00read,这里需要将\x替换为%。

方法三:local_file

这里使用 urllib.urlopen(param) 去包含文件,所以可以直接加上文件路径 flag.txt,但是file被过滤了,无法使用,但是可以使用local_file去代替。

但是如果使用 urllib2.urlopen(param) 去包含文件就必须加上 file,否则会报错。

过程和字符串拼接差不多。

1.获取sign

2.读取flag.txt

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐