{"id":782,"date":"2021-05-21T08:34:17","date_gmt":"2021-05-21T08:34:17","guid":{"rendered":"https:\/\/shreyapohekar.com\/blogs\/?p=782"},"modified":"2022-02-09T19:06:41","modified_gmt":"2022-02-09T19:06:41","slug":"dont-just-sanitize-but-also-escape-a-fable-of-sanitize_text_field","status":"publish","type":"post","link":"https:\/\/shreyapohekar.com\/blogs\/dont-just-sanitize-but-also-escape-a-fable-of-sanitize_text_field\/","title":{"rendered":"Dont just sanitize but also escape &#8211; A fable of sanitize_text_field"},"content":{"rendered":"\n<p class=\"has-drop-cap\">Hey infosec fam!! Recently I was testing out an image upload functionality on a wordpress plugin that led me to stored XSS.  In this blog post, I will be writing about an interesting story of how I got XSS even when a filter (sanitize_text_field) was in place.<\/p>\n\n\n\n<p>Let&#8217;s get started!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Scenario<\/h2>\n\n\n\n<p>The plugin has the image upload functionality where image can be uploaded either through the default list or from the WordPress uploads folder. I was testing out other parameters for XSS where I found that after the image is taken from the wp-uploads, the image path is mentioned in the response as a URL to wp-content\/uploads.<\/p>\n\n\n\n<p>This was interesting and the first thing that came to my mind was to break out of the src to insert an XSS payload. I inserted &#8221; to break out of src attribute and insert onerror attribute so as to execute javascript. <\/p>\n\n\n\n<p>I used this payload:- <strong><code>http:\/\/x.x.x.x\/image\"onerror=alert(1);\/\/.png<\/code><\/strong> while intercepting through burp.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"654\" height=\"195\" src=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-13.png\" alt=\"\" class=\"wp-image-786\" srcset=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-13.png 654w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-13-300x89.png 300w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-13-640x191.png 640w\" sizes=\"(max-width: 654px) 100vw, 654px\" \/><\/figure>\n\n\n\n<p>When I opened the list of the added items, the malformed HTTP URL of the image got loaded and I got an XSS popup. Yayyyyy!!<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-12-1024x280.png\" alt=\"\" class=\"wp-image-784\" width=\"839\" height=\"229\" srcset=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-12-1024x280.png 1024w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-12-300x82.png 300w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-12-768x210.png 768w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-12-1536x419.png 1536w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-12-640x175.png 640w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/04\/image-12.png 1872w\" sizes=\"(max-width: 839px) 100vw, 839px\" \/><\/figure>\n\n\n\n<p>But did you noticed one thing? The <code>double quotes<\/code> in the payload got escaped. <\/p>\n\n\n\n<p>Wondering why XSS happened in the first place?! Lets get to the code review and find out.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Code Review<\/h2>\n\n\n\n<p>Since I was testing on a wordpress plugin, I had the option to see whats happening behind the scenes.<\/p>\n\n\n\n<p>So here comes the interesting part! The reason why you see a backslash(\\) is because <strong>sanitize_text_field<\/strong> is used while taking the image input.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted wpf-blue-background\">$results = $wpdb-&gt;insert(\n                     $table_name,\n                     array(\n                         'title' =&gt; sanitize_text_field($_POST['title']),\n                         'url' =&gt; sanitize_text_field($_POST['url']),\n                      <strong>   'image_url' =&gt; sanitize_text_field($_POST['image_file']),<\/strong>\n                         'sortorder' =&gt; sanitize_text_field($_POST['sortorder']),\n                         'date_upload' =&gt; time(),\n                         'target' =&gt; sanitize_text_field($_POST['target']),\n                     ),\n                     array(\n                         '%s',\n                         '%s',\n                         '%s',\n                         '%d',\n                         '%s',\n                         '%d',\n                     )\n                 );<\/pre>\n\n\n\n<p>Let&#8217;s talk about what this filter is and what does it sanitize!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">sanitize_text_field<\/h2>\n\n\n\n<p>From the wordpress official documentation<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Sanitizes a string from user input or from the database.<\/p><\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Basic usage<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted wpf-blue-background\"><code>&lt;?php sanitize_text_field( $str<\/code> <code>) ?&gt;<\/code><\/pre>\n\n\n\n<p>So if $str contains <code>\" | ' | &lt; | &amp;<\/code>  characters, the function will add a backslash(\\) so as to escape the original functionality of that character. <\/p>\n\n\n\n<p>So when I inserted <code>\"<\/code> as part of the payload, it did escaped the double quotes by adding \\ therefore making it <code>\\\"<\/code><\/p>\n\n\n\n<p>But the thing to note here is, we don&#8217;t us sanitize_text_fields when the reflection\/sink from your input is gonna be the part of <code>src<\/code> attribute<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Still wondering why??<\/h2>\n\n\n\n<p>Let&#8217;s simplify. Here comes the concept of <strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">XSS contexts<\/span><\/strong>.  You as an attacker need to understand and identify where in response,  the user-controlled data appears. The next thing to identify is the filters or any other processing that is in place.<\/p>\n\n\n\n<p>The user controlled data can be reflected\/stored in:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>HTML tags<\/strong>: When the context is between the HTML tags, you need to introduce new HTML tags that can execute javascript.<\/li><li><strong>HTML tag attributes<\/strong>: When the context is into HTML tag attribute, you can terminate the attribute value with <code>double quotes<\/code> and insert a new attribute that can execute javascript. There&#8217;s also a possibility to close the current tag with &#8220;&gt; and insert &lt;script&gt;alert(1)&lt;\/script&gt;. But you can expect &lt;&gt; to be escaped\/blocked 99% times so mostly you will have to play around the `double quotes\/single quotes`.<\/li><li>In javascript: When the context is in javascript, you can introduce new HTML tags that can trigger the execution of javascript. Example:<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-preformatted wpf-blue-background\">&lt;\/script&gt;&lt;img src=x onerror=prompt(1)&gt;<\/pre>\n\n\n\n<p>If the user controlled input is inside a string literal, then you can easily breakout with<\/p>\n\n\n\n<pre class=\"wp-block-preformatted wpf-blue-background\">';alert(1)\/\/<\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Javascript template literals<\/strong>: These are the string literals that allow embedded Javascript expressions. These expressions are evaluated and then concatenated with the surrounding text.<\/li><\/ul>\n\n\n\n<p>Consider a script that takes in username and print Hi &lt;username&gt;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted wpf-blue-background\">document.getElementById('username').innerText = `Hi there!, ${user.userName}.`;<\/pre>\n\n\n\n<p>With template literals, there is no need to escape from the quotes. You just simply have to write your payload inside ${..}<\/p>\n\n\n\n<pre class=\"wp-block-preformatted wpf-blue-background\">&lt;script&gt;\n...\nvar userName = '${alert(document.dmain}';  \/\/user controlled input\n...\n&lt;\/script&gt;<\/pre>\n\n\n\n<p>And the alert will work.<\/p>\n\n\n\n<p>That was the gist of what contexts are and how they work. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Let&#8217;s get back to context that we&#8217;ll be dealing with.<\/h3>\n\n\n\n<p>In our scenario, the context is inside <strong>HTML tag attribute<\/strong>. The backslash(\\) added by sanitize_text_field now becomes a part of URL and therefore is unable to escape the double quotes and hence the src attribute is closed. The other part of the payload <code><strong>onerror=alert(1);\/\/<\/strong><\/code> now becomes another valid attribute. <code>\/\/<\/code> is added so that the remaining part of <code><strong>&lt;img<\/strong><\/code> tag doesnt cause any barrier.<\/p>\n\n\n\n<p>And tada!!!  Its XSS \ud83d\ude42<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Research on other methods<\/h2>\n\n\n\n<p>In the recital above, what we learn is: Just having a filter in place doesnt necessarily mean that your code is attack-proof. A proper understanding of what filters are filtering out is important.<\/p>\n\n\n\n<p>I tested down the payload on all the available filters an found 2 more places giving the same effect as sanitize_text_field. <\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>wp_strip_all_tags<\/li><li>strip_tags<\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"653\" height=\"75\" src=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-5.png\" alt=\"\" class=\"wp-image-804\" srcset=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-5.png 653w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-5-300x34.png 300w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-5-640x74.png 640w\" sizes=\"(max-width: 653px) 100vw, 653px\" \/><\/figure>\n\n\n\n<p>Here the outcome is pretty obvious from the name of filter that it will just stip tags.<\/p>\n\n\n\n<p>So If by any chance these methods are used for a parameter whose sink is in src attribute, it still makes you vulnerable to the above scenario,<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Method that can be used<\/h2>\n\n\n\n<p>These are a bunch of filters that can do the work for you. In this section. I have collated a list of such filters that behave differently for a single payload: <code>\" onerror=alert(1);\/\/<\/code><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>urlencode<\/strong> : URL-encodes string<\/li><\/ul>\n\n\n\n<p>If the method is changed from sanitize_text_field to urlencode, the xss is now gone. See image below.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"659\" height=\"98\" src=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image.png\" alt=\"\" class=\"wp-image-798\" srcset=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image.png 659w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-300x45.png 300w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-640x95.png 640w\" sizes=\"(max-width: 659px) 100vw, 659px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>rawurlencode<\/strong>: URL-encode according to RFC 3986<\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"657\" height=\"94\" src=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-1.png\" alt=\"\" class=\"wp-image-799\" srcset=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-1.png 657w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-1-300x43.png 300w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-1-640x92.png 640w\" sizes=\"(max-width: 657px) 100vw, 657px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>esc_textarea<\/strong>: Escaping for textarea values.<\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"657\" height=\"71\" src=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-2.png\" alt=\"\" class=\"wp-image-801\" srcset=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-2.png 657w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-2-300x32.png 300w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-2-640x69.png 640w\" sizes=\"(max-width: 657px) 100vw, 657px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>esc_html<\/strong>: Escaping for HTML blocks.<\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"651\" height=\"78\" src=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-3.png\" alt=\"\" class=\"wp-image-802\" srcset=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-3.png 651w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-3-300x36.png 300w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-3-640x77.png 640w\" sizes=\"(max-width: 651px) 100vw, 651px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>esc_js<\/strong>: Escape single quotes, htmlspecialchar \u201d &amp;, and fix line endings.<\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"655\" height=\"71\" src=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-4.png\" alt=\"\" class=\"wp-image-803\" srcset=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-4.png 655w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-4-300x33.png 300w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-4-640x69.png 640w\" sizes=\"(max-width: 655px) 100vw, 655px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>sanitize_key<\/strong>: Sanitizes a string key. Keys are used as internal identifiers. Lowercase alphanumeric characters, dashes, and underscores are allowed.<\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"668\" height=\"73\" src=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-7.png\" alt=\"\" class=\"wp-image-806\" srcset=\"https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-7.png 668w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-7-300x33.png 300w, https:\/\/shreyapohekar.com\/blogs\/wp-content\/uploads\/2021\/05\/image-7-640x70.png 640w\" sizes=\"(max-width: 668px) 100vw, 668px\" \/><\/figure>\n\n\n\n<p>Every filter method is created for a reason and developers need to understand which one to use when.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Key takeaways<\/h2>\n\n\n\n<p>Finally, in this last section, I want to list a few takeaways<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Always go through the documentation of methods and read about what are they exactly filtering and does that match your use case.<\/li><li>If you are still unsure if the filter can protect you from XSS, try using filters that are nested. For example <code>$str = esc_attr(esc_js($GET['param']))<\/code>. The combination can be any according to the case in point. This is a nice approach I found while doing source-code review of different plugins.<\/li><li>As a pentester, do not skip testing out a functionality if you have already found filters in the source code review. Coz stories like that of sanitize_text_field can happen to anyone.<\/li><\/ul>\n\n\n\n<p> That&#8217;s all for this blog post. Thanks for reading! Hope you enjoyed reading my research.<\/p>\n\n\n\n<p>See you in the next one! Until then, happy hunting \ud83d\ude42 <\/p>\n","protected":false},"excerpt":{"rendered":"<p>The post talks about an interesting find of XSS even when the filter was used. It also covers the mistakes that a developer makes while sanitizing input.<\/p>\n","protected":false},"author":1,"featured_media":820,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ocean_post_layout":"","ocean_both_sidebars_style":"","ocean_both_sidebars_content_width":0,"ocean_both_sidebars_sidebars_width":0,"ocean_sidebar":"","ocean_second_sidebar":"","ocean_disable_margins":"enable","ocean_add_body_class":"","ocean_shortcode_before_top_bar":"","ocean_shortcode_after_top_bar":"","ocean_shortcode_before_header":"","ocean_shortcode_after_header":"","ocean_has_shortcode":"","ocean_shortcode_after_title":"","ocean_shortcode_before_footer_widgets":"","ocean_shortcode_after_footer_widgets":"","ocean_shortcode_before_footer_bottom":"","ocean_shortcode_after_footer_bottom":"","ocean_display_top_bar":"default","ocean_display_header":"default","ocean_header_style":"","ocean_center_header_left_menu":"","ocean_custom_header_template":"","ocean_custom_logo":0,"ocean_custom_retina_logo":0,"ocean_custom_logo_max_width":0,"ocean_custom_logo_tablet_max_width":0,"ocean_custom_logo_mobile_max_width":0,"ocean_custom_logo_max_height":0,"ocean_custom_logo_tablet_max_height":0,"ocean_custom_logo_mobile_max_height":0,"ocean_header_custom_menu":"","ocean_menu_typo_font_family":"","ocean_menu_typo_font_subset":"","ocean_menu_typo_font_size":0,"ocean_menu_typo_font_size_tablet":0,"ocean_menu_typo_font_size_mobile":0,"ocean_menu_typo_font_size_unit":"px","ocean_menu_typo_font_weight":"","ocean_menu_typo_font_weight_tablet":"","ocean_menu_typo_font_weight_mobile":"","ocean_menu_typo_transform":"","ocean_menu_typo_transform_tablet":"","ocean_menu_typo_transform_mobile":"","ocean_menu_typo_line_height":0,"ocean_menu_typo_line_height_tablet":0,"ocean_menu_typo_line_height_mobile":0,"ocean_menu_typo_line_height_unit":"","ocean_menu_typo_spacing":0,"ocean_menu_typo_spacing_tablet":0,"ocean_menu_typo_spacing_mobile":0,"ocean_menu_typo_spacing_unit":"","ocean_menu_link_color":"","ocean_menu_link_color_hover":"","ocean_menu_link_color_active":"","ocean_menu_link_background":"","ocean_menu_link_hover_background":"","ocean_menu_link_active_background":"","ocean_menu_social_links_bg":"","ocean_menu_social_hover_links_bg":"","ocean_menu_social_links_color":"","ocean_menu_social_hover_links_color":"","ocean_disable_title":"default","ocean_disable_heading":"default","ocean_post_title":"","ocean_post_subheading":"","ocean_post_title_style":"","ocean_post_title_background_color":"","ocean_post_title_background":0,"ocean_post_title_bg_image_position":"","ocean_post_title_bg_image_attachment":"","ocean_post_title_bg_image_repeat":"","ocean_post_title_bg_image_size":"","ocean_post_title_height":0,"ocean_post_title_bg_overlay":0.5,"ocean_post_title_bg_overlay_color":"","ocean_disable_breadcrumbs":"default","ocean_breadcrumbs_color":"","ocean_breadcrumbs_separator_color":"","ocean_breadcrumbs_links_color":"","ocean_breadcrumbs_links_hover_color":"","ocean_display_footer_widgets":"default","ocean_display_footer_bottom":"default","ocean_custom_footer_template":"","ocean_post_oembed":"","ocean_post_self_hosted_media":"","ocean_post_video_embed":"","ocean_link_format":"","ocean_link_format_target":"self","ocean_quote_format":"","ocean_quote_format_link":"post","ocean_gallery_link_images":"on","ocean_gallery_id":[],"footnotes":""},"categories":[328,321,329,327,257,330],"tags":[298,322,323,71,126,326,324,325,8],"class_list":["post-782","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-code-vigilant","category-owasp-top-10","category-php","category-source-code-review","category-web-application","category-xss","tag-code-review","tag-codeviilant","tag-filters","tag-linux","tag-php","tag-research","tag-sanitize_text_field","tag-stroed-xss","tag-xss","entry","has-media"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/posts\/782"}],"collection":[{"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/comments?post=782"}],"version-history":[{"count":17,"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/posts\/782\/revisions"}],"predecessor-version":[{"id":931,"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/posts\/782\/revisions\/931"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/media\/820"}],"wp:attachment":[{"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/media?parent=782"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/categories?post=782"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shreyapohekar.com\/blogs\/wp-json\/wp\/v2\/tags?post=782"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}