{"id":125071,"date":"2022-05-23T16:38:34","date_gmt":"2022-05-23T16:38:34","guid":{"rendered":"https:\/\/blog.finxter.com\/?p=385265"},"modified":"2022-05-23T16:38:34","modified_gmt":"2022-05-23T16:38:34","slug":"delegatecall-or-storage-collision-attack-on-smart-contracts","status":"publish","type":"post","link":"https:\/\/sickgaming.net\/blog\/2022\/05\/23\/delegatecall-or-storage-collision-attack-on-smart-contracts\/","title":{"rendered":"DelegateCall or Storage Collision Attack on Smart Contracts"},"content":{"rendered":"<div class=\"kk-star-ratings kksr-valign-top kksr-align-left \" data-payload=\"{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;385265&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;top&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;count&quot;:&quot;1&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;5&quot;,&quot;greet&quot;:&quot;Rate this post&quot;,&quot;legend&quot;:&quot;5\\\/5 - (1 vote)&quot;,&quot;size&quot;:&quot;24&quot;,&quot;width&quot;:&quot;142.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} - ({count} {votes})&quot;}\">\n<div class=\"kksr-stars\">\n<div class=\"kksr-stars-inactive\">\n<div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<div class=\"kksr-stars-active\" style=\"width: 142.5px;\">\n<div class=\"kksr-star\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<\/div>\n<div class=\"kksr-legend\"> 5\/5 &#8211; (1 vote) <\/div>\n<\/div>\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\">\n<div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Storage Collision Attack on Smart Contracts - DelegateCall\" width=\"780\" height=\"439\" src=\"https:\/\/www.youtube.com\/embed\/Qcb37lr_c1c?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe>\n<\/div>\n<\/figure>\n<p>The post is part 6 and continues our <strong>Smart Contract Security Series<\/strong>:<\/p>\n<ul>\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/smart-contract-security-series-part-1-ownership-exploit\/\" target=\"_blank\">Ownership Exploit<\/a><\/li>\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/private-exploit-smart-contract-security-series-part-2\/\" target=\"_blank\">Private Variable Exploit<\/a><\/li>\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/reentrancy-attack-hacking-smart-contract-security-series-part-3\/\" target=\"_blank\">Reentrancy Attack<\/a><\/li>\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/tx-origin-phishing-attack-smart-contract-security-series-part-4\/\" target=\"_blank\">tx.origin Phishing Attack<\/a><\/li>\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/?p=318951\" target=\"_blank\">Denial of Service Attack<\/a><\/li>\n<li><a href=\"https:\/\/blog.finxter.com\/delegatecall-or-storage-collision-attack-on-smart-contracts\/\" data-type=\"URL\" data-id=\"https:\/\/blog.finxter.com\/delegatecall-or-storage-collision-attack-on-smart-contracts\/\" target=\"_blank\" rel=\"noreferrer noopener\">Storage Collision Attack<\/a><\/li>\n<\/ul>\n<p>The <code>DelegateCall<\/code> attack or storage collision is expounded in this post. <\/p>\n<p>Before you can grasp this exploit, you must first understand how <a href=\"https:\/\/academy.finxter.com\/university\/solidity-basics\/\" target=\"_blank\" rel=\"noreferrer noopener\">Solidity<\/a> saves state variables as explained <a href=\"https:\/\/blog.finxter.com\/private-exploit-smart-contract-security-series-part-2\/\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.\u00a0 <\/p>\n<p>We start with the differences between <code>call<\/code> and <code>delegatecall<\/code> in Solidity, followed by exploiting the vulnerability of the <code>delegatecall<\/code> using the proxy contracts (mostly in smart contract upgrades), and then a solution for the attack. <\/p>\n<p>Let&#8217;s start the journey!<\/p>\n<h2><a><\/a>Call VS DelegateCall<\/h2>\n<p>Solidity supports two low-level interfaces for interaction or sending messages to the contract functions. <\/p>\n<p>These interfaces operate on addresses rather than contract instances (using <code>this<\/code> keyword). The key differences are highlighted with an example.<\/p>\n<h3><a><\/a>Call<\/h3>\n<p>It allows you to call the code of the callee contract from the caller with the storage context of the callee.<\/p>\n<p>In order to understand this confusing sentence, let\u2019s consider two contracts <code>A<\/code>, and <code>CallA<\/code>, with the naming convention as below:<\/p>\n<ul>\n<li><code>A<\/code> is the <strong><em>callee,<\/em><\/strong><\/li>\n<li><code>CallA<\/code> is the <strong><em>caller<\/em><\/strong><\/li>\n<\/ul>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Callee\ncontract A\n{ uint256 public x; function foo(uint256 _x) public { x = _x; }\n} \/\/ Caller\ncontract CallA\n{ uint256 public x; function callfoo(address _a) public { (bool success,) = _a.call(abi.encodeWithSignature(\"foo(uint256)\", 15)); require(success, \"Call was not successful\"); }\n}<\/pre>\n<p>To test, deploy the contracts on <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/top-10-solidity-tutorials\/\" data-type=\"post\" data-id=\"262867\" target=\"_blank\">Remix<\/a>, and when you execute the caller (<code>CallA -> callfoo<\/code>), you can verify that <code>foo()<\/code> gets called, and the value of \u2018<code>x<\/code>\u2018 in the <code>callee(A ->x)<\/code> is set to <code>15<\/code>.<\/p>\n<p class=\"has-global-color-8-background-color has-background\"><img decoding=\"async\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/13.1.0\/72x72\/1f4a1.png\" alt=\"\ud83d\udca1\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> <strong>Note<\/strong>: It is also possible to send <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/introduction-to-ethereums-gas-in-solidity-development\/\" data-type=\"post\" data-id=\"37644\" target=\"_blank\">Ether and gas<\/a> as part of the call using <code>value<\/code> and <code>gas<\/code> as params.<\/p>\n<p>The above scenario is described in the figure as shown.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"541\" height=\"121\" src=\"https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-266.png\" alt=\"\" class=\"wp-image-385277\" srcset=\"https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-266.png 541w, https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-266-300x67.png 300w\" sizes=\"auto, (max-width: 541px) 100vw, 541px\" \/><figcaption><strong>Fig<\/strong>: call flow<\/figcaption><\/figure>\n<\/div>\n<h3><a><\/a>DelegateCall<\/h3>\n<p>It allows you to call the code of the callee contract from the caller with the storage context of the caller. <\/p>\n<p>As previously mentioned, let\u2019s consider two contracts <code>A<\/code> and <code>DelegateCallA<\/code>, with the naming convention as below:<\/p>\n<ul>\n<li><code>A<\/code> is the <strong><em>callee,<\/em><\/strong><\/li>\n<li><code>DelegateCallA<\/code> is the <strong><em>caller<\/em><\/strong><\/li>\n<\/ul>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">contract A\n{ uint256 public x; function foo(uint256 _x) public { x = _x; }\n} contract DelegateCallA\n{ uint256 public x; function callfoo(address _a) public { (bool success,) = _a.delegatecall(abi.encodeWithSignature(\"foo(uint256)\", 15)); require(success, \"Delegate Call was not successful\"); }\n}<\/pre>\n<p>To test, deploy the contracts on Remix, and when you execute the caller (<code>DelegateCallA -> \u2018callfoo\u2019<\/code>), you can verify that <code>foo()<\/code> gets called and the value of <code>\u2018x\u2018<\/code> in the <code>callee(A ->x)<\/code> is still 0, while the value of <code>x<\/code> in the caller (<code>DelegateCallA -> x<\/code>) is <code>15<\/code>.<\/p>\n<p>Equipped with the above examples, it is evident that the <code>delegatecall<\/code><em>,<\/em> executes in the caller&#8217;s context, while the <code>call<\/code> executes in the callee context.<\/p>\n<p>A picture speaks a thousand words. The above scenario is in the below figure.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"491\" height=\"141\" src=\"https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-267.png\" alt=\"\" class=\"wp-image-385284\" srcset=\"https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-267.png 491w, https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-267-300x86.png 300w\" sizes=\"auto, (max-width: 491px) 100vw, 491px\" \/><figcaption><strong>Fig<\/strong>: <code>delegatecall<\/code> flow<\/figcaption><\/figure>\n<\/div>\n<p>One use case of <code>call<\/code> is the transfer of <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/ethereum-top-10-articles-to-get-started\/\" target=\"_blank\">Ether<\/a> to a contract, and it passes all the gas to the receiving function, while the use cases of the <code>delegatecall<\/code> are when a contract invokes a library with public functions or uses a proxy contract to write smart upgradeable contracts.<\/p>\n<h2><a><\/a>Exploit with delegatecall<\/h2>\n<p>The most widely adopted technique to upgrade contracts is utilizing a <strong>proxy contract<\/strong>. <\/p>\n<p>A proxy interposes the actual logical contract and the <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/smart-contracts-and-evm\/\" data-type=\"post\" data-id=\"92507\" target=\"_blank\">dapp<\/a> interface. To update the logical contract with a new version (say V2), only the new deployed address of the logical contract is passed to the proxy. <\/p>\n<p>This helps achieve minimal or no changes in the dapp\/web3 interface, saving a lot of development time.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"548\" height=\"251\" src=\"https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-268.png\" alt=\"\" class=\"wp-image-385288\" srcset=\"https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-268.png 548w, https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-268-300x137.png 300w\" sizes=\"auto, (max-width: 548px) 100vw, 548px\" \/><figcaption><strong>Fig<\/strong>: Contract upgrade with proxy<\/figcaption><\/figure>\n<\/div>\n<p>Let us write a quick and short proxy, and a logical contract (say V1). For the same, create a file <code>DelegateCall.sol<\/code> with Proxy and V1 contracts as below.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">contract Proxy\n{ uint256 public x; address public owner; address public logicalAddr; constructor(address _Addr) { logicalAddr = _Addr; owner = msg.sender; } function upgrade(address _newAddr) public { logicalAddr = _newAddr; } \/\/ To call any function of the logical contract fallback() external payable { (bool success, ) = logicalAddr.delegatecall(msg.data); require(success , \" Error calling logical contract\"); }\n}<\/pre>\n<p><strong>V1, <\/strong>This represents version 1 of the logical contract.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">contract V1\n{ uint256 public x; \/\/ abi.encodedWithSignature(\"increment_X()\") = 0xeaf2926e function increment_X() public { x += 1; }\n}<\/pre>\n<p>Compile, deploy and run the contracts in Remix with the constructor param in proxy as the address of the V1 contract. <\/p>\n<p>You can observe that, when the <code><strong>abi.encodedWithSignature(\"increment_X()\"))<\/strong><\/code><em>\u00a0 <\/em>is passed as <code>calldata<\/code> to Proxy (<code>fallback()<\/code> is triggered), the function <code>increment_X()<\/code> in V1 is called. <\/p>\n<p>Abi encoding is calculated using the tool,<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ npm install web3-eth-abi<\/pre>\n<p>and then<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const Web3EthAbi = require('web3-eth-abi'); > Web3EthAbi.encodeFunctionSignature(\"increment_X()\") '0xeaf2926e'<\/pre>\n<p>As discussed above in <code>delegatecall<\/code><em>,<\/em> the storage context of the caller (i.e., Proxy) is used, and the value of <code>x<\/code> in Proxy is incremented by 1. <\/p>\n<p>So far, this is all good.<\/p>\n<p>At some point in the future, it is decided to upgrade the V1 contract with new functionality, let\u2019s call it V2. <\/p>\n<p>Create a new contract<strong> V2<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">contract V2\n{ uint256 public x; uint256 public y; function increment_X() public { x += 1; } \/\/ abi.encodedWithSignature(\"set_Y(uint256)\", 10) \/\/0x1675b4f5000000000000000000000000000000000000000000000000000000000000000a function set_Y(uint256 _y) public { y = _y; }\n}<\/pre>\n<p>Compile and deploy V2. <\/p>\n<p>Pass the address of V2, to <code>upgrade()<\/code><em> <\/em>in Proxy as V2 is the new contract we need. <\/p>\n<p>When <code><strong>abi.encodedWithSignature(\"set_Y(uint256)\", 10))<\/strong><\/code><strong><em> <\/em><\/strong>is passed as <code>calldata<\/code> to <code>proxy<\/code>, the function <code>increment_Y()<\/code> in V2 is called. <\/p>\n<p>The value of <code>y<\/code> is 10, but wait a minute, surprise, surprise!<\/p>\n<p>As there is no <code>y<\/code> in the <code>Proxy<\/code> contract, and as the storage context of Proxy is used, it has overwritten the second param in Proxy (i.e., owner) with 10 (or <code>0x000000000000000000000000000000000000000A<\/code>).<\/p>\n<p>With the owner address changed, the attacker is now in complete control of all the contracts.<\/p>\n<h2>How to Prevent the Attack<\/h2>\n<p>The <code><strong>delegatecall<\/strong><\/code> is tricky to use, and erroneous usage might have disastrous consequences. <\/p>\n<p>For example, possible solutions to the above problem can be<\/p>\n<ol type=\"1\">\n<li>If possible, avoid using additional storage variables or go stateless in the upgraded contract &#8211; V2.<\/li>\n<li>Mirror the storage layout in V2, in other words, the contract calling <code>delegatecall<\/code> and the contract being called must have the same storage layout.<\/li>\n<li>By implementing unstructured storage in proxy with the help of assembly code as in <a href=\"https:\/\/docs.openzeppelin.com\/upgrades-plugins\/1.x\/proxies#unstructured-storage-proxies\">O<\/a><a href=\"https:\/\/docs.openzeppelin.com\/upgrades-plugins\/1.x\/proxies#unstructured-storage-proxies\" target=\"_blank\" rel=\"noreferrer noopener\">p<\/a><a href=\"https:\/\/docs.openzeppelin.com\/upgrades-plugins\/1.x\/proxies#unstructured-storage-proxies\">enZeppelins proxy<\/a> and not having any storage variables in proxy apart from the logical contract address.<\/li>\n<\/ol>\n<h2><a><\/a>Outro<\/h2>\n<p>In this tutorial, we saw how <code>delegatecall<\/code> can lead to disastrous results with an incorrect understanding or usage. <\/p>\n<p>While using <code>delegatecall<\/code><em>,<\/em> it is vital to keep it in our minds that <code>delegatecall<\/code> keeps context intact (<a href=\"https:\/\/blog.finxter.com\/smart-contracts-and-evm\/\" target=\"_blank\" rel=\"noreferrer noopener\">storage<\/a>, caller, etc&#8230;). <\/p>\n<p>Even though there are certain problems associated with <code>delegatecall<\/code>, it is very often used in many contracts such as OpenZeppelin, Solidity libraries, EIP2535 diamonds, and many more.<\/p>\n<p>To conclude, use <code>delegatecall<\/code><em>,<\/em> but with care!<\/p>\n<hr class=\"wp-block-separator\"\/>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/academy.finxter.com\/university\/creating-an-nft-marketplace\/\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" loading=\"lazy\" width=\"363\" height=\"650\" src=\"https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/02\/image-21.png\" alt=\"\" class=\"wp-image-170334\" srcset=\"https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/02\/image-21.png 363w, https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/02\/image-21-168x300.png 168w\" sizes=\"auto, (max-width: 363px) 100vw, 363px\" \/><\/a><\/figure>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>5\/5 &#8211; (1 vote) The post is part 6 and continues our Smart Contract Security Series: Ownership Exploit Private Variable Exploit Reentrancy Attack tx.origin Phishing Attack Denial of Service Attack Storage Collision Attack The DelegateCall attack or storage collision is expounded in this post. Before you can grasp this exploit, you must first understand how [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[857],"tags":[73,468,528],"class_list":["post-125071","post","type-post","status-publish","format-standard","hentry","category-python-tut","tag-programming","tag-python","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/125071","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/comments?post=125071"}],"version-history":[{"count":0,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/125071\/revisions"}],"wp:attachment":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/media?parent=125071"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/categories?post=125071"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/tags?post=125071"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}