diff --git a/cmd/frostfs-cli/modules/control/locate.go b/cmd/frostfs-cli/modules/control/locate.go
new file mode 100644
index 000000000..d10e2a896
--- /dev/null
+++ b/cmd/frostfs-cli/modules/control/locate.go
@@ -0,0 +1,118 @@
+package control
+
+import (
+	"bytes"
+
+	"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
+	"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
+	object "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/object"
+	commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
+	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
+	rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
+	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
+	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
+
+	"github.com/mr-tron/base58"
+	"github.com/spf13/cobra"
+)
+
+const (
+	FullInfoFlag      = "full"
+	FullInfoFlagUsage = "Print full ShardInfo."
+)
+
+var locateObjectCmd = &cobra.Command{
+	Use:   "locate-object",
+	Short: "List shards storing the object",
+	Long:  "List shards storing the object",
+	Run:   locateObject,
+}
+
+func initControlLocateObjectCmd() {
+	initControlFlags(locateObjectCmd)
+
+	flags := locateObjectCmd.Flags()
+
+	flags.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
+	_ = locateObjectCmd.MarkFlagRequired(commonflags.CIDFlag)
+
+	flags.String(commonflags.OIDFlag, "", commonflags.OIDFlagUsage)
+	_ = locateObjectCmd.MarkFlagRequired(commonflags.OIDFlag)
+
+	flags.Bool(commonflags.JSON, false, "Print shard info as a JSON array. Requires --full flag.")
+	flags.Bool(FullInfoFlag, false, FullInfoFlagUsage)
+}
+
+func locateObject(cmd *cobra.Command, _ []string) {
+	var cnr cid.ID
+	var obj oid.ID
+
+	_ = object.ReadObjectAddress(cmd, &cnr, &obj)
+
+	pk := key.Get(cmd)
+
+	body := new(control.ListShardsForObjectRequest_Body)
+	body.SetContainerId(cnr.EncodeToString())
+	body.SetObjectId(obj.EncodeToString())
+	req := new(control.ListShardsForObjectRequest)
+	req.SetBody(body)
+	signRequest(cmd, pk, req)
+
+	cli := getClient(cmd, pk)
+
+	var err error
+	var resp *control.ListShardsForObjectResponse
+	err = cli.ExecRaw(func(client *rawclient.Client) error {
+		resp, err = control.ListShardsForObject(client, req)
+		return err
+	})
+	commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
+
+	verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
+
+	shardIDs := resp.GetBody().GetShard_ID()
+
+	isFull, _ := cmd.Flags().GetBool(FullInfoFlag)
+	if !isFull {
+		for _, id := range shardIDs {
+			cmd.Println(base58.Encode(id))
+		}
+		return
+	}
+
+	// get full shard info
+	listShardsReq := new(control.ListShardsRequest)
+	listShardsReq.SetBody(new(control.ListShardsRequest_Body))
+	signRequest(cmd, pk, listShardsReq)
+	var listShardsResp *control.ListShardsResponse
+	err = cli.ExecRaw(func(client *rawclient.Client) error {
+		listShardsResp, err = control.ListShards(client, listShardsReq)
+		return err
+	})
+	commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
+
+	verifyResponse(cmd, listShardsResp.GetSignature(), listShardsResp.GetBody())
+
+	shards := listShardsResp.GetBody().GetShards()
+	sortShardsByID(shards)
+	shards = filterShards(shards, shardIDs)
+
+	isJSON, _ := cmd.Flags().GetBool(commonflags.JSON)
+	if isJSON {
+		prettyPrintShardsJSON(cmd, shards)
+	} else {
+		prettyPrintShards(cmd, shards)
+	}
+}
+
+func filterShards(info []control.ShardInfo, ids [][]byte) []control.ShardInfo {
+	var res []control.ShardInfo
+	for _, id := range ids {
+		for _, inf := range info {
+			if bytes.Equal(inf.Shard_ID, id) {
+				res = append(res, inf)
+			}
+		}
+	}
+	return res
+}
diff --git a/cmd/frostfs-cli/modules/control/root.go b/cmd/frostfs-cli/modules/control/root.go
index b20d3618e..3abfe80cb 100644
--- a/cmd/frostfs-cli/modules/control/root.go
+++ b/cmd/frostfs-cli/modules/control/root.go
@@ -39,6 +39,7 @@ func init() {
 		listRulesCmd,
 		getRuleCmd,
 		listTargetsCmd,
+		locateObjectCmd,
 	)
 
 	initControlHealthCheckCmd()
@@ -52,4 +53,5 @@ func init() {
 	initControlListRulesCmd()
 	initControGetRuleCmd()
 	initControlListTargetsCmd()
+	initControlLocateObjectCmd()
 }
diff --git a/go.mod b/go.mod
index 18378466e..9d0988bcd 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.22
 
 require (
 	code.gitea.io/sdk/gitea v0.17.1
-	git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1-0.20241205083807-762d7f9f9f08
+	git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1
 	git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0
 	git.frostfs.info/TrueCloudLab/frostfs-locode-db v0.4.1-0.20240710074952-65761deb5c0d
 	git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20250212111929-d34e1329c824
@@ -12,7 +12,7 @@ require (
 	git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250306092416-69b0711d12d9
 	git.frostfs.info/TrueCloudLab/hrw v1.2.1
 	git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972
-	git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240814080254-96225afacb88
+	git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240822104152-a3bc3099bd5b
 	git.frostfs.info/TrueCloudLab/tzhash v1.8.0
 	git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
 	github.com/VictoriaMetrics/easyproto v0.1.4
diff --git a/go.sum b/go.sum
index 5205dddef..3ec679ee7 100644
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,7 @@
 code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8=
 code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM=
-git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1-0.20241205083807-762d7f9f9f08 h1:tl1TT+zNk1lF/J5EaD3syDrTaYbQwvJKVOVENM4oQ+k=
-git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1-0.20241205083807-762d7f9f9f08/go.mod h1:5fSm/l5xSjGWqsPUffSdboiGFUHa7y/1S0fvxzQowN8=
+git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1 h1:k1Qw8dWUQczfo0eVXlhrq9eXEbUMyDLW8jEMzY+gxMc=
+git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1/go.mod h1:5fSm/l5xSjGWqsPUffSdboiGFUHa7y/1S0fvxzQowN8=
 git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk=
 git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
 git.frostfs.info/TrueCloudLab/frostfs-locode-db v0.4.1-0.20240710074952-65761deb5c0d h1:uJ/wvuMdepbkaV8XMS5uN9B0FQWMep0CttSuDZiDhq0=
@@ -18,8 +18,8 @@ git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 h1:/96
 git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972/go.mod h1:2hM42MBrlhvN6XToaW6OWNk5ZLcu1FhaukGgxtfpDDI=
 git.frostfs.info/TrueCloudLab/neoneo-go v0.106.1-0.20241015133823-8aee80dbdc07 h1:gPaqGsk6gSWQyNVjaStydfUz6Z/loHc9XyvGrJ5qSPY=
 git.frostfs.info/TrueCloudLab/neoneo-go v0.106.1-0.20241015133823-8aee80dbdc07/go.mod h1:bZyJexBlrja4ngxiBgo8by5pVHuAbhg9l09/8yVGDyg=
-git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240814080254-96225afacb88 h1:vgbfkcnIexZUm3vREBBSa/Gv1Whjd1SFCUd0A+IaGPQ=
-git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240814080254-96225afacb88/go.mod h1:SgioiGhQNWqiV5qpFAXRDJF81SEFRBhtwGEiU0FViyA=
+git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240822104152-a3bc3099bd5b h1:M50kdfrf/h8c3cz0bJ2AEUcbXvAlPFVC1Wp1WkfZ/8E=
+git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240822104152-a3bc3099bd5b/go.mod h1:GZTk55RI4dKzsK6BCn5h2xxE28UHNfgoq/NJxW/LQ6A=
 git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
 git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
 git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=