{"id":159,"date":"2022-11-29T13:53:00","date_gmt":"2022-11-29T06:53:00","guid":{"rendered":"https:\/\/n45ht.or.id\/blog\/?p=159"},"modified":"2025-10-14T22:48:29","modified_gmt":"2025-10-14T15:48:29","slug":"stored-xss-on-laporbugid-injecting-payloads-through-profile-images","status":"publish","type":"post","link":"https:\/\/n45ht.or.id\/blog\/stored-xss-on-laporbugid-injecting-payloads-through-profile-images\/","title":{"rendered":"Stored XSS on LaporBug.id: Injecting Payloads through Profile Images"},"content":{"rendered":"\n<p><strong>LaporBug.id<\/strong> is a Bug Bounty Platform based in Indonesia. If you want to know more about LaporBug.id, visit their website at <a href=\"https:\/\/laporbug.id\">https:\/\/laporbug.id<\/a>.<\/p>\n\n\n\n<p>I spent a few minutes exploring LaporBug.id and examining the various URLs, parameters, and forms available on the site. During my reconnaissance, I came across a feature that allows users to upload a profile image on the following page:<\/p>\n\n\n\n<p><strong>URL:<\/strong><br><a href=\"https:\/\/laporbug.id\/myprofile\/edit\">https:\/\/laporbug.id\/myprofile\/edit<\/a><\/p>\n\n\n\n<p>Here\u2019s the process I followed to identify and exploit a potential vulnerability:<\/p>\n\n\n\n<p>On this page, there is a form to upload a profile image. I started by uploading a regular image file.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"396\" src=\"https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w2-1024x396.png\" alt=\"\" class=\"wp-image-160\" style=\"width:487px;height:auto\" srcset=\"https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w2-1024x396.png 1024w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w2-300x116.png 300w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w2-768x297.png 768w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w2.png 1194w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next, I decided to experiment with the file extension. Instead of uploading a common image file (e.g., <code>.jpg<\/code> or <code>.png<\/code>), I changed the extension of my image file to <code>.html<\/code>.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1010\" height=\"730\" src=\"https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w3.jpg\" alt=\"\" class=\"wp-image-161\" style=\"width:481px;height:auto\" srcset=\"https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w3.jpg 1010w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w3-300x217.jpg 300w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w3-768x555.jpg 768w\" sizes=\"auto, (max-width: 1010px) 100vw, 1010px\" \/><\/figure>\n\n\n\n<p>Despite changing the extension to <code>.html<\/code>, the file was uploaded successfully without any errors. This indicated that the server wasn&#8217;t properly validating or restricting file extensions, which is a potential security risk.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"493\" src=\"https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w4-1024x493.png\" alt=\"\" class=\"wp-image-162\" style=\"width:507px;height:auto\" srcset=\"https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w4-1024x493.png 1024w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w4-300x145.png 300w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w4-768x370.png 768w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w4.png 1366w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>I tried uploading several sensitive extensions such as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>.php<\/code><\/li>\n\n\n\n<li><code>.php5<\/code><\/li>\n\n\n\n<li><code>.phtml<\/code><\/li>\n\n\n\n<li><code>.PhP;.png<\/code><\/li>\n\n\n\n<li><code>.php%00.png<\/code><\/li>\n<\/ul>\n\n\n\n<p>However, the server blocked these attempts and didn\u2019t allow these files to be uploaded.<\/p>\n\n\n\n<p>Although the server blocked dangerous extensions, it allowed the <code>.html<\/code> extension. This gave me an opportunity to craft a Stored XSS payload.<\/p>\n\n\n\n<p>To inject the payload, I used <strong>ExifTool<\/strong> to manipulate the image metadata. I inserted the following XSS payload into the image:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\">&lt;img src=1 onerror=confirm(document.domain)\/\/><\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9FF\">&quot;&gt;<\/span><span style=\"color: #81A1C1\">&lt;img<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">src<\/span><span style=\"color: #ECEFF4\">=<\/span><span style=\"color: #A3BE8C\">1<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">onerror<\/span><span style=\"color: #ECEFF4\">=<\/span><span style=\"color: #88C0D0\">confirm<\/span><span style=\"color: #A3BE8C\">(<\/span><span style=\"color: #D8DEE9\">document<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">domain<\/span><span style=\"color: #A3BE8C\">)<\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #81A1C1\">\/&gt;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This payload uses an image tag with an <code>onerror<\/code> attribute that triggers a JavaScript <code>confirm()<\/code> dialog box. If successful, it would pop up the domain of the website in the dialog.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1014\" height=\"517\" src=\"https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w5.png\" alt=\"\" class=\"wp-image-163\" style=\"width:499px;height:auto\" srcset=\"https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w5.png 1014w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w5-300x153.png 300w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w5-768x392.png 768w\" sizes=\"auto, (max-width: 1014px) 100vw, 1014px\" \/><\/figure>\n\n\n\n<p><strong>Boom!<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"488\" src=\"https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w6-1024x488.png\" alt=\"\" class=\"wp-image-164\" style=\"width:501px;height:auto\" srcset=\"https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w6-1024x488.png 1024w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w6-300x143.png 300w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w6-768x366.png 768w, https:\/\/n45ht.or.id\/blog\/wp-content\/uploads\/2024\/12\/lb-w6.png 1366w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>This was an example of how improper file extension validation and failure to sanitize user inputs in file metadata can lead to a Stored XSS vulnerability. By uploading a file with an <code>.html<\/code> extension and injecting a JavaScript payload into the image metadata, I was able to execute arbitrary JavaScript on the victim&#8217;s browser.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>LaporBug.id is a Bug Bounty Platform based in Indonesia. If you want to know more about LaporBug.id, visit their website at https:\/\/laporbug.id. I spent a few minutes exploring LaporBug.id and examining the various URLs, parameters, and forms available on the site. During my reconnaissance, I came across a feature that allows users to upload a [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":315,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21],"tags":[9,27,8],"class_list":["post-159","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-research","tag-cross-site-scripting","tag-exiftool","tag-xss"],"_links":{"self":[{"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/posts\/159","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/comments?post=159"}],"version-history":[{"count":3,"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/posts\/159\/revisions"}],"predecessor-version":[{"id":314,"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/posts\/159\/revisions\/314"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/media\/315"}],"wp:attachment":[{"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/media?parent=159"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/categories?post=159"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/n45ht.or.id\/blog\/wp-json\/wp\/v2\/tags?post=159"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}